libgdx怎么碰撞_带有libgdx的Android游戏开发–碰撞检测,第4部分

libgdx怎么碰撞

这是libgdx教程的第四部分,在该教程中,我们创建了一个以Star Guard为蓝本的2d Platformer原型。 如果您对我们如何到达这里感兴趣,可以阅读以前的文章。

到目前为止,在完成本教程之后,我们设法拥有一个由一些街区组成的小世界,我们的英雄叫鲍勃,他可以很好地移动,但是问题是,他

与世界没有任何互动。 如果将瓷砖渲染切换回去,我们将看到鲍勃愉快地走来走去,而障碍物并没有阻止他。 所有的块都会被忽略。 发生这种情况是因为我们从不检查Bob实际是否与块碰撞。 碰撞检测仅是检测两个或多个对象何时碰撞。 在我们的案例中,我们需要检测 Bob何时与 碰撞 。 确切要检查的是Bob的边界框是否与各自块的边界框相交。 如果确实如此,我们已经检测到碰撞 。 我们注意到对象(鲍勃和块)并采取相应的行动。 在我们的案例中,我们需要阻止Bob前进,下降或跳跃,具体取决于Bob撞到的障碍物的哪一侧。

快速而肮脏的方式

一种简单快捷的方法是遍历世界上所有的块,并检查这些块是否与Bob当前的边界框发生冲突。 这在我们很小的10×7的世界中效果很好,但是如果我们有一个拥有成千上万个块的巨大世界,则在不影响性能的情况下进行每帧检测都是不可能的。

更好的方法

为了优化上述解决方案,我们将选择性地选择可能与Bob碰撞的图块。
按照设计,游戏世界由块组成,这些块的边界框是轴向对齐的,其宽度和高度均为1个单位。
在这种情况下,我们的世界如下图所示(所有块/图块均以单位块为单位):

积木

红色方块代表将放置块的边界(如果有)。 黄色的被放置块。
现在,我们可以为我们的世界选择一个简单的二维数组(矩阵),并且每个单元格都将保留一个Blocknull如果没有)。 这是地图容器。 我们总是知道Bob在哪里,因此很容易确定我们在哪个单元格中。 获取Bob可能与之碰撞的候选块的简便方法是:拾取所有周围的单元格,并检查Bob的当前边界框是否与具有该块的图块之一重叠。

碰撞放大
因为我们还控制Bob的运动,所以我们可以了解他的方向和运动速度。 这进一步缩小了我们的选择范围。 例如,如果鲍勃(Bob)离开,我们将遇到以下情况:

碰撞候选
上图为我们提供了2个候选单元格(平铺),以检查这些单元格中的对象是否与Bob发生碰撞。 请记住,重力不断将Bob拉下,因此我们将始终必须检查Y轴上的图块。 根据垂直速度的符号,我们知道Bob何时跳跃或下降。 如果Bob跳跃,则候选人将是他上方的图块(单元)。 垂直速度为负值表示鲍勃正在倒下,因此我们从他下方挑选一块作为候选对象。 如果他向左行驶(他的速度<0),那么我们在他的左边选择候选人。 如果他向右行驶(速度> 0),那么我们在他的右边选择瓷砖。 如果水平速度为0,则意味着我们不需要打扰水平候选者。 我们需要使其达到最佳状态,因为我们将在每个帧中都执行此操作,并且必须对每个敌人,子弹以及游戏中任何可碰撞的实体执行此操作。

碰撞后会发生什么?

在我们的情况下,这非常简单。 鲍勃在该轴上的移动停止了。 他在该轴上的速度将设置为0。仅在单独检查2轴时才能执行此操作。 我们将首先检查水平碰撞,如果鲍勃碰撞了,那么我们将停止他的水平运动。
我们在垂直(Y)轴上执行完全相同的操作。 就这么简单。

先模拟然后渲染

我们在检查碰撞时需要小心。 我们人类倾向于在行动之前思考。 如果我们正面对一堵墙,我们不仅会走进去,还会看到并估算出距离,然后在撞墙之前停下来。 想象一下,如果你是盲人。 您将需要与眼睛不同的传感器。 您会用胳膊伸手,如果您感觉到墙壁,则在进入墙壁之前会停下来。 我们可以将其翻译为Bob,但是我们将使用他的边界框代替他的手臂。 首先,我们将Bob的边界框按照Bob的速度移动其距离,然后检查新位置是否会碰到墙(如果边界框与块的边界框相交)。 如果是,则检测到冲突。 鲍勃可能离墙壁有一段距离,在那个框架中,他会遮盖到墙壁的距离等等。 如果是这种情况,我们只需将Bob放在墙壁旁边,然后将其边界框与当前位置对齐。 我们还将该轴的Bob速度设置为0。 下图是尝试显示我所描述的内容。

碰撞位置
绿框是Bob当前所在的位置。 移位的蓝色框是Bob在此框之后的位置。 紫色是鲍勃进入墙壁的数量。 这是我们需要推开Bob的距离,以便他站在墙旁边。 我们只是将他的位置设置在墙旁边即可实现此目标,而无需进行过多的计算。 冲突检测的代码实际上非常简单。 所有这些都位于BobController.java 。 我还要在控制器之前提到其他一些更改。 World.java具有以下更改:

public class World {

	/** Our player controlled hero **/
	Bob bob;
	/** A world has a level through which Bob needs to go through **/
	Level level;

	/** The collision boxes **/
	Array<Rectangle> collisionRects = new Array<Rectangle>();

	// Getters -----------

	public Array<Rectangle> getCollisionRects() {
		return collisionRects;
	}
	public Bob getBob() {
		return bob;
	}
	public Level getLevel() {
		return level;
	}
	/** Return only the blocks that need to be drawn **/
	public List<Block> getDrawableBlocks(int width, int height) {
		int x = (int)bob.getPosition().x - width;
		int y = (int)bob.getPosition().y - height;
		if (x < 0) {
			x = 0;
		}
		if (y < 0) {
			y = 0;
		}
		int x2 = x + 2 * width;
		int y2 = y + 2 * height;
		if (x2 > level.getWidth()) {
			x2 = level.getWidth() - 1;
		}
		if (y2 > level.getHeight()) {
			y2 = level.getHeight() - 1;
		}

		List<Block> blocks = new ArrayList<Block>();
		Block block;
		for (int col = x; col <= x2; col++) {
			for (int row = y; row <= y2; row++) {
				block = level.getBlocks()[col][row];
				if (block != null) {
					blocks.add(block);
				}
			}
		}
		return blocks;
	}

	// --------------------
	public World() {
		createDemoWorld();
	}

	private void createDemoWorld() {
		bob = new Bob(new Vector2(7, 2));
		level = new Level();
	}
}

#09collisionRects只是一个简单的数组,我将Bob碰到的矩形放在该特定帧中。 这仅用于调试目的,并在屏幕上显示框。 它可以并且将从最终游戏中删除。
#13 –仅提供对碰撞箱的访问
#23getDrawableBlocks(int width, int height)是一种方法,该方法返回相机窗口中将被渲染的Block对象的列表。 此方法只是准备应用程序以渲染巨大的世界而不会降低性能。 这是一个非常简单的算法。 使鲍勃周围的块在一定距离内,然后将其返回进行渲染。 这是一种优化。 #61 –创建在#06行中声明的Level 。 我们希望我们的游戏具有多个关卡,因此最好将关卡从世界上移开。 这是显而易见的第一步。 Level.java可以在这里找到。

如前所述,实际的冲突检测在BobController.java

public class BobController {
	// ... code omitted ... // 
	private Array<Block> collidable = new Array<Block>();
	// ... code omitted ... // 

	public void update(float delta) {
		processInput();
		if (grounded && bob.getState().equals(State.JUMPING)) {
			bob.setState(State.IDLE);
		}
		bob.getAcceleration().y = GRAVITY;
		bob.getAcceleration().mul(delta);
		bob.getVelocity().add(bob.getAcceleration().x, bob.getAcceleration().y);
		checkCollisionWithBlocks(delta);
		bob.getVelocity().x *= DAMP;
		if (bob.getVelocity().x > MAX_VEL) {
			bob.getVelocity().x = MAX_VEL;
		}
		if (bob.getVelocity().x < -MAX_VEL) {
			bob.getVelocity().x = -MAX_VEL;
		}
		bob.update(delta);
	}

	private void checkCollisionWithBlocks(float delta) {
		bob.getVelocity().mul(delta);
		Rectangle bobRect = rectPool.obtain();
		bobRect.set(bob.getBounds().x, bob.getBounds().y, bob.getBounds().width, bob.getBounds().height);
		int startX, endX;
		int startY = (int) bob.getBounds().y;
		int endY = (int) (bob.getBounds().y + bob.getBounds().height);
		if (bob.getVelocity().x < 0) {
			startX = endX = (int) Math.floor(bob.getBounds().x + bob.getVelocity().x);
		} else {
			startX = endX = (int) Math.floor(bob.getBounds().x + bob.getBounds().width + bob.getVelocity().x);
		}
		populateCollidableBlocks(startX, startY, endX, endY);
		bobRect.x += bob.getVelocity().x;
		world.getCollisionRects().clear();
		for (Block block : collidable) {
			if (block == null) continue;
			if (bobRect.overlaps(block.getBounds())) {
				bob.getVelocity().x = 0;
				world.getCollisionRects().add(block.getBounds());
				break;
			}
		}
		bobRect.x = bob.getPosition().x;
		startX = (int) bob.getBounds().x;
		endX = (int) (bob.getBounds().x + bob.getBounds().width);
		if (bob.getVelocity().y < 0) {
			startY = endY = (int) Math.floor(bob.getBounds().y + bob.getVelocity().y);
		} else {
			startY = endY = (int) Math.floor(bob.getBounds().y + bob.getBounds().height + bob.getVelocity().y);
		}
		populateCollidableBlocks(startX, startY, endX, endY);
		bobRect.y += bob.getVelocity().y;
		for (Block block : collidable) {
			if (block == null) continue;
			if (bobRect.overlaps(block.getBounds())) {
				if (bob.getVelocity().y < 0) {
					grounded = true;
				}
				bob.getVelocity().y = 0;
				world.getCollisionRects().add(block.getBounds());
				break;
			}
		}
		bobRect.y = bob.getPosition().y;
		bob.getPosition().add(bob.getVelocity());
		bob.getBounds().x = bob.getPosition().x;
		bob.getBounds().y = bob.getPosition().y;
		bob.getVelocity().mul(1 / delta);
	}

	private void populateCollidableBlocks(int startX, int startY, int endX, int endY) {
		collidable.clear();
		for (int x = startX; x <= endX; x++) {
			for (int y = startY; y <= endY; y++) {
				if (x >= 0 && x < world.getLevel().getWidth() && y >=0 && y < world.getLevel().getHeight()) {
					collidable.add(world.getLevel().get(x, y));
				}
			}
		}
	}
	// ... code omitted ... // 
}

完整的源代码位于github上,我已经尝试对其进行了文档记录,但在这里我将详细介绍这些重要的内容。

#03 –可collidable数组将在每个帧中保留与Bob碰撞的候选块。
现在, update方法更加简洁。
#07 –像往常一样处理输入,并且在那里没有任何更改 #08 –#09 –如果Bob不在空中,则重置他的状态。 #12 –鲍勃的加速度转化为帧时间。 这很重要,因为帧可能非常小(通常为1/60秒),并且我们只想在一帧中进行一次转换。 #13 –计算帧时的速度 #14 –高亮显示,因为这是发生碰撞检测的地方。 我将稍后介绍该方法。 #15 –#22 –将DAMP应用于Bob阻止他,并确保Bob不超过其最大速度。 #25checkCollisionWithBlocks(float delta)方法,它根据Bob与关卡中的块是否碰撞来设置Bob的状态,位置和其他参数。 #26 –将速度转换为帧时间 #27 –#28 –我们使用Pool获取Rectangle,它是Bob当前边界框的副本。 该矩形将在应为bob的位置移动,并对照候选块进行检查。 #29 –#36 –这些行标识了要检查碰撞的水平矩阵中的开始和结束坐标。 水平矩阵只是二维数组,每个单元代表一个单位,因此可以容纳一个块。 检查Level.java #31 –设置了Y坐标,因为我们现在仅查找水平。 #32 –检查鲍勃是否在向左行驶,如果是,则确定他左侧的磁贴。 数学很简单,我使用了这种方法,因此,如果我决定需要对单元格进行其他一些测量,那么仍然可以使用。 #37 –使用所提供范围内的块填充可collidable数组。 在这种情况下,取决于Bob的方位,左边还是右边的砖块。 还要注意,如果该单元格中没有块,则结果为null。 38号 –这是我们放置鲍勃边界框副本的位置。 bobRec的新职位是Bob在正常情况下应处于的位置。 但仅在X轴上。 #39 –还记得来自世界各地的冲撞人进行调试吗? 现在,我们清除该数组,以便可以用Bob碰撞的矩形填充它。 #40 –#47 –在X轴上实际发生碰撞检测的位置。 我们遍历所有候选块(在我们的示例中为1),并检查块的边界框是否与Bob的位移边界框相交。 我们使用bobRect.overlaps方法,该方法是bobRect.overlaps中Rectangle类的一部分,如果两个矩形重叠,则返回true。 如果存在重叠,则会发生碰撞,因此我们将Bob的速度设置为0( 第43行),将矩形添加到world.collisionRects并中断检测。 #48 –我们重置边界框的位置,因为我们正在移动以检查X轴在Y轴上的碰撞。 #49 –#68 –与以前完全相同,但是它发生在Y轴上。 还有一条附加的指令#61-#63 ,如果在Bob跌倒时检测到碰撞,则将grounded状态设置为true#69 –重置鲍勃的矩形副本 #70 –正在设置鲍勃的新速度,该速度将用于计算鲍勃的新位置。 #71 –#72 – Bob的真实边界位置已更新 #73 –我们将速度转换回基本测量单位。 这个非常重要。

这就是鲍勃与瓷砖的碰撞。 当然,随着添加更多实体,我们将对此进行改进,但目前为止已经足够了。 我们在这里作了一些欺骗,就像我说的那样,在碰撞时将Bob放在Block旁边,但是在代码中我完全忽略了替换。 因为距离太小了,我们甚至看不到它,所以可以。 可以添加它,不会有太大区别。 如果您决定添加它,确保务必毗邻未来 Bob的位置来将挡,一点点远,因此重叠功能会导致
falseWorldRenderer.java也有少量添加。

public class WorldRenderer {
	// ... code omitted ... // 
	public void render() {
		spriteBatch.begin();
			drawBlocks();
			drawBob();
		spriteBatch.end();
		drawCollisionBlocks();
		if (debug)
			drawDebug();
	}

	private void drawCollisionBlocks() {
		debugRenderer.setProjectionMatrix(cam.combined);
		debugRenderer.begin(ShapeType.FilledRectangle);
		debugRenderer.setColor(new Color(1, 1, 1, 1));
		for (Rectangle rect : world.getCollisionRects()) {
			debugRenderer.filledRect(rect.x, rect.y, rect.width, rect.height);
		}
		debugRenderer.end();
	}
	// ... code omitted ... // 
}

增加了drawCollisionBlocks()方法,该方法将在发生碰撞的任何地方绘制一个白框。 这一切都是为了您的观看乐趣。 到目前为止,我们所做的工作应类似于以下视频:

本文应该总结基本的碰撞检测。 接下来,我们将研究扩展世界,摄像机运动,使用武器创建敌人,添加声音。 请分享您的想法,因为最重要的是什么。 该项目的源代码可以在这里找到: https : //github.com/obviam/star-assault 。 您需要签出分支part4 。 要使用git进行检查: git clone -b part4 git@github.com:obviam/star-assault.git 。 您也可以将其下载为zip文件 。 libgdx tests目录中还有一个不错的平台程序。 超级Koalio 。 它展示了到目前为止我已经讲过的很多内容,并且它要简短得多,对于那些有libgdx经验的人来说,它非常有帮助。

参考: 使用libgdx进行Android游戏开发–碰撞检测,来自JCG合作伙伴 Impaler的第4部分,网址Against the Grain博客。

翻译自: https://www.javacodegeeks.com/2013/03/android-game-development-with-libgdx-collision-detection-part-4.html

libgdx怎么碰撞

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值