碰撞 - 识别物体何时停止碰撞
Collision - recognizing when object has stopped colliding
我的意思是,当我与一个实体的侧面发生碰撞并想要跳跃时,我无法移动 right/left 因为我有一个标志可以防止我在 right/left 边,像这样:
这是我用来检测碰撞的代码:
public void onCollision(Wrapper wrapper) {
if (horizontal_velocity == 0 && vertical_velocity == 0) {
return;
}
Entity e1 = wrapper.thisEntity; //the object we are controlling
Entity e2 = wrapper.otherEntity; //the object we are impacting with
Rectangle rect1 = wrapper.thisEntityRectangle;
Rectangle rect2 = wrapper.otherEntityRectangle;
int e1y = (int) rect1.getY(), e1x = (int) rect1.getX(), e1w = (int) rect1.getWidth(), e1h = (int) rect1.getHeight();
int e2y = (int) rect2.getY(), e2x = (int) rect2.getX(), e2w = (int) rect2.getWidth(), e2h = (int) rect2.getHeight();
e1x += e1w / 2;
e1w /= 2;
e1y += e1h / 2;
e1h /= 2;
//horizontal interaction -- x
/* ---+\\\+---
* |------|
* | |
* e1 | e2 | e1
* | |
* |------|
* ---+\\\+---
*/
boolean touchingSide = false;
if(e1y > e2y && e1y < e2y + e2h) {
//touchingSide = true;
if(e1x > e2x - e1w && e1x < e2x) { //going right || hitting left side
collisionsHappening.put(wrapper.otherEntity.getID(), "right");
canMoveRight = false;
horizontal_velocity = 0;
setX(e2x - e1w*2);
}
if(e1x < e2x + e2w + e1w && e1x > e2x) { //going left || hitting right side
collisionsHappening.put(wrapper.otherEntity.getID(), "left");
canMoveLeft = false;
horizontal_velocity = 0;
setX(e2x + e2w);
}
}
//vertical interaction -- y
/*
* \| e1 |\
* \+--+------+--+\
* \\| |\\
* \\| e2 |\\
* \\| |\\
* \+--+------+--+\
* \| e1 |\
*
*/
if(e1x > e2x - e1w && e1x < e2x + e2w && !touchingSide) {
if(e1y + e1h > e2y && e1y < e2y) { //going down || hitting top side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|top");
setY(e2y - e1h * 2);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
if(e1y - e1h < e2y + e2h && e1y > e2y) { //going up || hitting bottom side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|bottom");
setY(e2y + e2h + e1h);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
}
}
..代码受 canMoveRight
和 canMoveLeft
标志影响的地方:
public void key(int keyCode) {
try {
onEdge();
switch (keyList.get(keyCode)) {
...
case MOVE_RIGHT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "right") {
canMoveRightNextFrame = true;
}
}
canMoveLeft = true;
if (!canMoveRight && !canMoveRightNextFrame) break;
if(canMoveRightNextFrame) {
canMoveRightNextFrame = false;
System.out.println(canMoveRightNextFrame);
}
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(horizontal_velocity);
direction = "right";
//horizontal_velocity = 0;
break;
case MOVE_LEFT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
canMoveLeftNextFrame = canMoveLeftNextFrame ? true : false; //if true, make false
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(-horizontal_velocity);
direction = "left";
//horizontal_velocity = 0;
break;
case JUMP:
jump();
}
} catch (NullPointerException e) {
//ignore - a key with no action pressed
}
}
我试过的
当我寻找冲突时 - 没有像上面的代码那样对它们做出反应 - 我尝试实现一个 HashMap,在其中我将一个字符串 - "left"
或 "right"
- 放入key Entity.getID() 其中 returns 一个唯一的名字,就像这样(在相关的代码行之后是 // <<<
):
public Wrapper[] collisions(Entity thisEntity) {
ArrayList<Wrapper> wrappers = new ArrayList<Wrapper>();
Player p = null;
boolean isPlayer = thisEntity instanceof Player;
boolean onGround = false;
if (isPlayer) {
p = (Player) thisEntity;
}
try {
for (Entity e : getLevel().getEntities()) {
if (!thisEntity.equals(e)) {
collisionsHappening.put(e.getID(), ""); // <<<
objectsCollidingWith.add(e); // <<<
if (thisEntity.getRect().intersects(e.getRect())) {
wrappers.add(new Wrapper(thisEntity, e, thisEntity.getRect(), e.getRect(),
thisEntity.getRect().intersection(e.getRect())));
onGround = true;
//collisionsHappening.put(e.getID(), true);
//System.out.println("collision between " + e.getID() + " + " + thisEntity.getID());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (isPlayer) {
p.isTouching = onGround;
if (onGround) {
//System.out.println(onGround);
}
}
//Entity[] result = list.toArray(new Entity[1]);
return wrappers.toArray(new Wrapper[0]);
}
该系统依赖于在碰撞停止后将标志保留 1 帧,以便在代码行
中删除标志 canMoveRight
或 canMoveLeft
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
if(canMoveLeftNextFrame) {
canMoveLeftNextFrame = false; //if true, make false
}
...
...canMoveRight
标志也一样...
然而,这是行不通的,我在 "scrape" 实体的边缘后仍然无法在跳跃中移动,除非我向 "reset" 所需标志的相反方向移动方向(行:case MOVE_RIGHT:
...
canMoveLeft = true;
和 case MOVE_LEFT:
...
canMoveRight = true;
),如动画中所示。
所以,总结一下:
- 我怎样才能让这个系统工作?
- 有没有更多的efficient/better方法来做这种事情?
- 你建议我重做碰撞吗?
谢谢。
对于一款比贪吃蛇更高级的游戏,实际上不可能以这种事件驱动的方式来解决这个问题。我建议您专注于更新每个实体的简单更新循环。然后每个实体更新自己的速度和位置。然后检查碰撞并处理响应。关键事件处理程序不应有任何逻辑,而只是更改实体可以轮询的标志。
至于碰撞和响应部分,这是一个简单的平台游戏引擎的核心所在,并且有许多不同的方法。在现成物理引擎的现代世界中,例如Box2D,你可以简单地让物理系统处理事情。但从事物的声音来看,你不是在寻找那个,你想要的是我所说的基于逻辑的物理学。
如果您的世界和实体由轴对齐的框组成,您可以将每个实体的水平和垂直运动分成两个不连续的步骤。对于每个轴,每个对象都会尝试尽可能远地移动(取决于速度和碰撞),如果对象撞到某物,则生成碰撞事件并将该轴上的速度设置为零。对另一个轴重复,您将拥有一个简单的平台游戏引擎。对于此方法,由于浮点数值错误,您需要仔细处理两个接触对象的状态。我建议使用整数进行碰撞检测(在屏幕坐标的范围内)。
如果您希望拥有一个不受轴对齐框限制的环境和实体,那么您将不得不变得更加复杂,不再依赖于简单地处理 X 然后 Y 运动来为您提供诸如盒子在地板上或墙上滑动。在这种情况下,我建议您跟踪您的实体站立或行走的表面,以便您可以在每种状态下以不同方式处理它们的运动。当它们在空中时,正常运动,但当它们在某个表面上时,运动被限制为水平运动,并调整垂直位置以使其保持在表面上。然后您将需要检测它们何时更改状态。
希望对您有所帮助。事实证明,Google并不是一个容易的话题。 (更新循环、碰撞检测、运动物理学、数值积分和碰撞响应的几何学都很容易 Google)。大多数程序员似乎只是采取不断摆弄它直到它起作用的方法,因此它从来没有得到很好的记录。
P.S。隐式欧拉积分法适用于大多数游戏
我的意思是,当我与一个实体的侧面发生碰撞并想要跳跃时,我无法移动 right/left 因为我有一个标志可以防止我在 right/left 边,像这样:
这是我用来检测碰撞的代码:
public void onCollision(Wrapper wrapper) {
if (horizontal_velocity == 0 && vertical_velocity == 0) {
return;
}
Entity e1 = wrapper.thisEntity; //the object we are controlling
Entity e2 = wrapper.otherEntity; //the object we are impacting with
Rectangle rect1 = wrapper.thisEntityRectangle;
Rectangle rect2 = wrapper.otherEntityRectangle;
int e1y = (int) rect1.getY(), e1x = (int) rect1.getX(), e1w = (int) rect1.getWidth(), e1h = (int) rect1.getHeight();
int e2y = (int) rect2.getY(), e2x = (int) rect2.getX(), e2w = (int) rect2.getWidth(), e2h = (int) rect2.getHeight();
e1x += e1w / 2;
e1w /= 2;
e1y += e1h / 2;
e1h /= 2;
//horizontal interaction -- x
/* ---+\\\+---
* |------|
* | |
* e1 | e2 | e1
* | |
* |------|
* ---+\\\+---
*/
boolean touchingSide = false;
if(e1y > e2y && e1y < e2y + e2h) {
//touchingSide = true;
if(e1x > e2x - e1w && e1x < e2x) { //going right || hitting left side
collisionsHappening.put(wrapper.otherEntity.getID(), "right");
canMoveRight = false;
horizontal_velocity = 0;
setX(e2x - e1w*2);
}
if(e1x < e2x + e2w + e1w && e1x > e2x) { //going left || hitting right side
collisionsHappening.put(wrapper.otherEntity.getID(), "left");
canMoveLeft = false;
horizontal_velocity = 0;
setX(e2x + e2w);
}
}
//vertical interaction -- y
/*
* \| e1 |\
* \+--+------+--+\
* \\| |\\
* \\| e2 |\\
* \\| |\\
* \+--+------+--+\
* \| e1 |\
*
*/
if(e1x > e2x - e1w && e1x < e2x + e2w && !touchingSide) {
if(e1y + e1h > e2y && e1y < e2y) { //going down || hitting top side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|top");
setY(e2y - e1h * 2);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
if(e1y - e1h < e2y + e2h && e1y > e2y) { //going up || hitting bottom side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|bottom");
setY(e2y + e2h + e1h);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
}
}
..代码受 canMoveRight
和 canMoveLeft
标志影响的地方:
public void key(int keyCode) {
try {
onEdge();
switch (keyList.get(keyCode)) {
...
case MOVE_RIGHT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "right") {
canMoveRightNextFrame = true;
}
}
canMoveLeft = true;
if (!canMoveRight && !canMoveRightNextFrame) break;
if(canMoveRightNextFrame) {
canMoveRightNextFrame = false;
System.out.println(canMoveRightNextFrame);
}
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(horizontal_velocity);
direction = "right";
//horizontal_velocity = 0;
break;
case MOVE_LEFT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
canMoveLeftNextFrame = canMoveLeftNextFrame ? true : false; //if true, make false
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(-horizontal_velocity);
direction = "left";
//horizontal_velocity = 0;
break;
case JUMP:
jump();
}
} catch (NullPointerException e) {
//ignore - a key with no action pressed
}
}
我试过的
当我寻找冲突时 - 没有像上面的代码那样对它们做出反应 - 我尝试实现一个 HashMap,在其中我将一个字符串 - "left"
或 "right"
- 放入key Entity.getID() 其中 returns 一个唯一的名字,就像这样(在相关的代码行之后是 // <<<
):
public Wrapper[] collisions(Entity thisEntity) {
ArrayList<Wrapper> wrappers = new ArrayList<Wrapper>();
Player p = null;
boolean isPlayer = thisEntity instanceof Player;
boolean onGround = false;
if (isPlayer) {
p = (Player) thisEntity;
}
try {
for (Entity e : getLevel().getEntities()) {
if (!thisEntity.equals(e)) {
collisionsHappening.put(e.getID(), ""); // <<<
objectsCollidingWith.add(e); // <<<
if (thisEntity.getRect().intersects(e.getRect())) {
wrappers.add(new Wrapper(thisEntity, e, thisEntity.getRect(), e.getRect(),
thisEntity.getRect().intersection(e.getRect())));
onGround = true;
//collisionsHappening.put(e.getID(), true);
//System.out.println("collision between " + e.getID() + " + " + thisEntity.getID());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (isPlayer) {
p.isTouching = onGround;
if (onGround) {
//System.out.println(onGround);
}
}
//Entity[] result = list.toArray(new Entity[1]);
return wrappers.toArray(new Wrapper[0]);
}
该系统依赖于在碰撞停止后将标志保留 1 帧,以便在代码行
中删除标志canMoveRight
或 canMoveLeft
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
if(canMoveLeftNextFrame) {
canMoveLeftNextFrame = false; //if true, make false
}
...
...canMoveRight
标志也一样...
然而,这是行不通的,我在 "scrape" 实体的边缘后仍然无法在跳跃中移动,除非我向 "reset" 所需标志的相反方向移动方向(行:case MOVE_RIGHT:
...
canMoveLeft = true;
和 case MOVE_LEFT:
...
canMoveRight = true;
),如动画中所示。
所以,总结一下:
- 我怎样才能让这个系统工作?
- 有没有更多的efficient/better方法来做这种事情?
- 你建议我重做碰撞吗?
谢谢。
对于一款比贪吃蛇更高级的游戏,实际上不可能以这种事件驱动的方式来解决这个问题。我建议您专注于更新每个实体的简单更新循环。然后每个实体更新自己的速度和位置。然后检查碰撞并处理响应。关键事件处理程序不应有任何逻辑,而只是更改实体可以轮询的标志。
至于碰撞和响应部分,这是一个简单的平台游戏引擎的核心所在,并且有许多不同的方法。在现成物理引擎的现代世界中,例如Box2D,你可以简单地让物理系统处理事情。但从事物的声音来看,你不是在寻找那个,你想要的是我所说的基于逻辑的物理学。
如果您的世界和实体由轴对齐的框组成,您可以将每个实体的水平和垂直运动分成两个不连续的步骤。对于每个轴,每个对象都会尝试尽可能远地移动(取决于速度和碰撞),如果对象撞到某物,则生成碰撞事件并将该轴上的速度设置为零。对另一个轴重复,您将拥有一个简单的平台游戏引擎。对于此方法,由于浮点数值错误,您需要仔细处理两个接触对象的状态。我建议使用整数进行碰撞检测(在屏幕坐标的范围内)。
如果您希望拥有一个不受轴对齐框限制的环境和实体,那么您将不得不变得更加复杂,不再依赖于简单地处理 X 然后 Y 运动来为您提供诸如盒子在地板上或墙上滑动。在这种情况下,我建议您跟踪您的实体站立或行走的表面,以便您可以在每种状态下以不同方式处理它们的运动。当它们在空中时,正常运动,但当它们在某个表面上时,运动被限制为水平运动,并调整垂直位置以使其保持在表面上。然后您将需要检测它们何时更改状态。
希望对您有所帮助。事实证明,Google并不是一个容易的话题。 (更新循环、碰撞检测、运动物理学、数值积分和碰撞响应的几何学都很容易 Google)。大多数程序员似乎只是采取不断摆弄它直到它起作用的方法,因此它从来没有得到很好的记录。
P.S。隐式欧拉积分法适用于大多数游戏