我一直在尝试使用碰撞检测来阻止物体相互穿过。我不知道怎么做。
当物体发生碰撞时,我试图反转它们的速度矢量方向(因此它会远离它碰撞的地方),但有时物体会相互卡在一起。
我已经尝试过切换它们的速度,但这只是父母互相反对。
是否有一种简单的方法来限制对象的移动,以便它们不会通过其他对象?我一直在使用矩形相交进行碰撞,我也尝试过圆碰撞检测(使用物体之间的距离)。
想法?
package objects;
import java.awt.Rectangle;
import custom.utils.Vector;
import sprites.Picture;
import render.Window;
// Super class (game objects)
public class Entity implements GameObject{
private Picture self;
protected Vector position;
protected Vector velocity = new Vector(0,0);
private GameObject[] obj_list = new GameObject[0];
private boolean init = false;
// Takes in a "sprite"
public Entity(Picture i){
self = i;
position = new Vector(i.getXY()[0],i.getXY()[1]);
ObjectUpdater.addObject(this);
}
public Object getIdentity() {
return this;
}
// position handles
public Vector getPosition(){
return position;
}
public void setPosition(double x,double y){
position.setValues(x,y);
self.setXY(position);
}
public void setPosition(){
position.setValues((int)Window.getWinSize()[0]/2,(int)Window.getWinSize()[1]/2);
}
// velocity handles
public void setVelocity(double x,double y){ // Use if you're too lazy to make a vector
velocity.setValues(x, y);
}
public void setVelocity(Vector xy){ // Use if your already have a vector
velocity.setValues(xy.getValues()[0], xy.getValues()[1]);
}
public Vector getVelocity(){
return velocity;
}
// inferface for all game objects (so they all update at the same time)
public boolean checkInit(){
return init;
}
public Rectangle getBounds() {
double[] corner = position.getValues(); // Get the corner for the bounds
int[] size = self.getImageSize(); // Get the size of the image
return new Rectangle((int)Math.round(corner[0]),(int)Math.round(corner[1]),size[0],size[1]); // Make the bound
}
// I check for collisions where, this grabs all the objects and checks for collisions on each.
private void checkCollision(){
if (obj_list.length > 0){
for (GameObject i: obj_list){
if (getBounds().intersects(i.getBounds()) && i != this){
// What happens here?
}
}
}
}
public void updateSelf(){
checkCollision();
position = position.add(velocity);
setPosition(position.getValues()[0],position.getValues()[1]);
init = true;
}
public void pollObjects(GameObject[] o){
obj_list = o;
}
}
希望阅读起来并不难。
修改 所以我一直在使用矩形交集方法来计算对象的位置并修改速度。它工作得很好。唯一的问题是一些物体会推动其他物体,但这是非常重要的。对于我正在创建的迷你游戏,碰撞几乎是一个额外的东西。非常感谢你的帮助。
所有这一切,我仍然非常欣赏所提到的想法,因为我不完全确定如何将它们应用到我的项目中。
答案 0 :(得分:3)
没有看到你的代码,我只能猜出发生了什么。我怀疑你的物体被卡住了,因为它们超过了其他物体的边界,最终在里面。确保每个对象的步骤不仅仅是velocity * delta_time,而且步长大小受潜在碰撞的限制。发生碰撞时,计算它发生的时间(在delta_time中的某个位置)并按照反弹来确定最终的物体位置。或者,只需根据动量守恒定律设置要接触的物体并改变速度。
编辑看到您的代码后,我可以扩展我的答案。首先,让我澄清一下你提到的一些术语。由于每次调用updateSelf
只是将速度向量添加到当前位置,所以实际上是单位时间增量(增量时间始终为1)。换句话说,你的“速度”实际上是自上次调用updateSelf
以来所经过的距离(速度*增量时间)。我建议使用显式(浮点)时间增量作为模拟的一部分。
其次,跟踪多个移动物体之间碰撞的一般问题非常困难。无论使用何种时间增量,对象都可能在该增量中经历许多碰撞。 (想象一个物体被挤压在另外两个物体之间。在任何给定的时间间隔内,物体在两个周围物体之间来回反弹的次数没有限制。)此外,一个物体可能(在分辨率范围内)计算)同时与多个对象发生冲突。如果对象在移动时实际改变大小(正如您的代码所暗示的那样),问题就更加复杂了。
第三,您有一个重要的错误来源,因为您将所有对象位置四舍五入为整数坐标。我建议使用浮点对象(Rectangle2D.Float
而不是Rectangle
; Point2D.Float
而不是Vector
来表示对象。我还建议将position
字段替换为捕获位置和大小的矩形bounds
字段。这样,您不必在每次调用getBounds()
时创建新对象。如果对象大小是常量,这也将简化边界更新。
最后,在每个对象中都有一个碰撞检测逻辑存在一个重大问题:当对象A发现它会击中对象B时,对象B也会遇到对象A!但是,对象B独立于对象A进行自己的计算。如果先更新A,则B可能会错过碰撞,反之亦然。最好将整个碰撞检测和对象移动逻辑移动到全局算法,并使每个游戏对象保持相对简单。
一种方法(我推荐)是编写一个“updateGame”方法,以一个给定的时间增量推进游戏状态。它将使用记录冲突的辅助数据结构,可能如下所示:
public class Collision {
public int objectIndex1; // index of first object involved in collision
public int objectIndex2; // index of second object
public int directionCode; // encoding of the direction of the collision
public float time; // time of collision
}
整体算法将游戏从当前时间推进到由参数deltaTime
定义的新时间。它的结构可能是这样的:
void updateGame(float deltaTime) {
float step = deltaTime;
do (
Collision hit = findFirstCollision(step);
if (hit != null) {
step = Math.max(hit.time, MIN_STEP);
updateObjects(step);
updateVelocities(hit);
} else {
updateObjects(step);
}
deltaTime -= step;
step = deltaTime;
} while (deltaTime > 0);
}
/**
* Finds the earliest collision that occurs within the given time
* interval. It uses the current position and velocity of the objects
* at the start of the interval. If no collisions occur, returns null.
*/
Collision findFirstCollision(float deltaTime) {
Collision result = null;
for (int i = 0; i < obj_list.length; ++i) {
for (int j = i + 1; j < obj_list.length; ++j) {
Collision hit = findCollision(i, j, deltaTime);
if (hit != null) {
if (result == null || hit.time < result.time) {
result = hit;
}
}
}
}
return result;
}
/**
* Calculate if there is a collision between obj_list[i1] and
* obj_list[i2] within deltaTime, given their current positions
* and velocities. If there is, return a new Collision object
* that records i1, i2, the direction of the hit, and the time
* at which the objects collide. Otherwise, return null.
*/
Collision findCollision(int i1, int i2, float deltaTime) {
// left as an exercise for the reader
}
/**
* Move every object by its velocity * step
*/
void updateObjects(float step) {
for (GameObject obj : obj_list) {
Point2D.Float pos = obj.getPosition();
Point2D.Float velocity = obj.getVelocity();
obj.setPosition(
pos.getX() + step * velocity.getX(),
pos.getY() + step * velocity.getY()
);
}
}
/**
* Update the velocities of the two objects involved in a
* collision. Note that this does not always reverse velocities
* along the direction of collision (one object might be hit
* from behind by a faster object). The algorithm should assume
* that the objects are at the exact position of the collision
* and just update the velocities.
*/
void updateVelocities(Collision collision) {
// TODO - implement some physics simulation
}
MIN_STEP
常量是一个最小时间增量,以确保游戏更新循环不会卡住更新这样的小时间步骤而不会取得进展。 (对于浮点,deltaTime -= step;
可能会使deltaTime
保持不变。)
关于物理模拟:关于Elastic collision的维基百科文章为这个问题提供了一些很好的数学。