libgdx开发指南_带有libgdx的Android游戏开发–跳跃,引力和改进的动作,第3部分...

libgdx开发指南

这是“使用LibGdx构建游戏”系列的第三部分。 请确保您阅读了之前的文章,以构建本文的上下文。

在上一篇文章中,我们对Bob的动作进行了动画处理,但是该动作非常机器人化。 在本文中,我将尝试使鲍勃跳起来,并以更自然的方式移动。 我将通过一些物理学来实现这一点。 我还将稍微清理一下代码,并修复一些在前几篇文章中提到的问题。

跳跃–物理学

跳跃是一个实体(在我们的例子中是鲍勃)执行的动作,该实体将自己推向空中并着陆回到地面(基板)上。 这是通过向地面施加足够大的力来抵抗地面(重力)所施加的力。 识别我们拥有的对象:

  • 鲍勃 –实体
  • 地面 –基材
  • 重力(G) –作用在世界上所有实体上的恒定重力

为了实现逼真的跳跃,我们只需要应用牛顿运动定律即可 。 如果我们向鲍勃和世界添加必要的属性(质量,重力,摩擦力),我们将拥有实现跳跃所需的一切。 查看下图并检查其组件。 左侧是当我们按住“跳转”按钮时,右侧是Bob在跳跃中。

跳跃力量

让我们研究鲍勃在不同状态下的力量。

1. Bob 闲置在地( 接地 )。 在这种情况下,只有重力作用于鲍勃。 这意味着鲍勃被恒力拉下。 计算将物体拉向地面的力的公式为 F=m*a 其中m质量 (虽然不是重量,但应考虑重量), a加速度 。 我们正在简化事情,并认为鲍勃的质量为1,因此力等于加速度。 如果我们对物体施加恒定的力,其速度将无限增大。 计算物体速度的公式为: v=u+a*t 哪里

  • v –是最终速度
  • u –是初始速度( t秒钟前的速度)
  • a –是加速度
  • t –自施加加速度以来经过的时间

如果我们将鲍勃放在空中,这意味着起始速度为0。如果我们认为地球的重力加速度为9.8,鲍勃的重量(质量)为1,那么很容易计算一秒钟后他的下落速度。

v = 0 + 9.8 * 1 = 9.8m/s

因此,在一秒钟的自由落体运动后,鲍勃从0加速到9.8米/秒,即35.28公里/小时(21.92英里)。 那非常快。 如果我们想知道一秒钟后他的速度,我们将使用相同的公式。
v = 9.8 + 9.8 * 1 = 19.6m/s

那是70.56公里/小时或43.84英里/小时,非常快。 我们已经看到加速度是线性的,并且在恒定的力作用下,物体将无限加速。 这是在没有摩擦和阻力的理想环境中。 由于空气具有摩擦力,并且还会对下落的物体施加一定的力,因此下落的物体将在某个点达到最终速度 ,超过该速度则不会加速。 这取决于许多我们将忽略的因素。 一旦掉落的物体撞到地面,它将停止,重力不再影响它。 但是,这是不正确的,但是我们不是在构建完整的物理模拟器,而是在鲍勃以终极速度撞击地面时不会被杀死的游戏。 重新配置它,我们检查Bob是否撞到地面,如果是,那么我们将忽略重力。

让鲍勃跳

要使鲍勃跳起来,我们需要一个指向重力(向上)的力,该力不仅会抵消重力的作用,而且还会将鲍勃推向空中。 如果检查该图,该力( F )会更强(其大小长度比重力矢量大得多)。 通过将两个向量(G和F)相加,我们将得出作用于Bob的最终力。
为简化起见,我们可以摆脱向量,仅使用其Y分量。
在地球上, G = 9.8m/s^2 。 因为它指向下方,所以实际上 -9.8 m/s^2 。 当鲍勃跳下时,他所做的不过是产生足够的力以产生足够的加速度,从而使他在重力( G )带回到地面之前达到高度( h )。 因为鲍勃是一个像我们一样的人,他一旦空降就无法保持加速度,至少没有喷气背包。 为了模拟这一点,我们可以在按下“跳转”键时产生巨大的力量。 通过应用上述公式,初始速度将足够高,因此即使重力作用在Bob上,他仍将爬升至开始自由下落序列的点。 如果实施此方法,我们将获得一个非常逼真的外观跳跃。

如果我们仔细检查原始的《星际守卫》游戏,英雄可以根据我们按下跳转按钮的时间而跳到不同的高度。 如果只要按住跳键并在一定时间后将其切断,就可以轻松解决此问题,请确保鲍勃不会开始飞行。

实施跳跃

我认为这已经足够了,让我们看看如何实现跳跃。 我们还将做一些整理工作,并重新组织代码。 我想隔离跳跃和运动,所以我将忽略世界其他地方。 要查看代码中已修改的内容,请向下滚动至“ 重构”部分。

打开BobController.java 。 这是旧的WorldController.java但已重命名。 因为我们用它控制鲍勃,所以这很有意义。

public class BobController {

	enum Keys {
		LEFT, RIGHT, JUMP, FIRE
	}

	private static final long LONG_JUMP_PRESS 	= 150l;
	private static final float ACCELERATION 	= 20f;
	private static final float GRAVITY 			= -20f;
	private static final float MAX_JUMP_SPEED	= 7f;
	private static final float DAMP 			= 0.90f;
	private static final float MAX_VEL 			= 4f;

	// these are temporary
	private static final float WIDTH = 10f;

	private World 	world;
	private Bob 	bob;
	private long	jumpPressedTime;
	private boolean jumpingPressed;

	// ... code omitted ... //

	public void jumpReleased() {
		keys.get(keys.put(Keys.JUMP, false));
		jumpingPressed = false;
	}

	// ... code omitted ... //
	/** The main update method **/
	public void update(float delta) {
		processInput();

		bob.getAcceleration().y = GRAVITY;
		bob.getAcceleration().mul(delta);
		bob.getVelocity().add(bob.getAcceleration().x, bob.getAcceleration().y);
		if (bob.getAcceleration().x == 0) 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);
		if (bob.getPosition().y < 0) {
			bob.getPosition().y = 0f;
			bob.setPosition(bob.getPosition());
			if (bob.getState().equals(State.JUMPING)) {
					bob.setState(State.IDLE);
			}
		}
		if (bob.getPosition().x < 0) {
			bob.getPosition().x = 0;
			bob.setPosition(bob.getPosition());
			if (!bob.getState().equals(State.JUMPING)) {
				bob.setState(State.IDLE);
			}
		}
		if (bob.getPosition().x > WIDTH - bob.getBounds().width ) {
			bob.getPosition().x = WIDTH - bob.getBounds().width;
			bob.setPosition(bob.getPosition());
			if (!bob.getState().equals(State.JUMPING)) {
				bob.setState(State.IDLE);
			}
		}
	}

	/** Change Bob's state and parameters based on input controls **/
	private boolean processInput() {
		if (keys.get(Keys.JUMP)) {
			if (!bob.getState().equals(State.JUMPING)) {
				jumpingPressed = true;
				jumpPressedTime = System.currentTimeMillis();
				bob.setState(State.JUMPING);
				bob.getVelocity().y = MAX_JUMP_SPEED; 
			} else {
				if (jumpingPressed && ((System.currentTimeMillis() - jumpPressedTime) >= LONG_JUMP_PRESS)) {
					jumpingPressed = false;
				} else {
					if (jumpingPressed) {
						bob.getVelocity().y = MAX_JUMP_SPEED;
					}
				}
			}
		}
		if (keys.get(Keys.LEFT)) {
			// left is pressed
			bob.setFacingLeft(true);
			if (!bob.getState().equals(State.JUMPING)) {
				bob.setState(State.WALKING);
			}
			bob.getAcceleration().x = -ACCELERATION;
		} else if (keys.get(Keys.RIGHT)) {
			// left is pressed
			bob.setFacingLeft(false);
			if (!bob.getState().equals(State.JUMPING)) {
				bob.setState(State.WALKING);
			}
			bob.getAcceleration().x = ACCELERATION;
		} else {
			if (!bob.getState().equals(State.JUMPING)) {
				bob.setState(State.IDLE);
			}
			bob.getAcceleration().x = 0;

		}
		return false;
	}
}

花一些时间来分析我们添加到该类中的内容。 解释了以下几行: #07 –#12 –包含影响世界和鲍勃的值的常量

  • LONG_JUMP_PRESS –截断施加于跳跃的推力之前的时间(以毫秒为单位)。 请记住,我们正在跳高,并且玩家按下按钮的时间越长,鲍勃跳得越高。 为了防止飞行,我们将在150毫秒后切断跳跃推进。
  • 加速 –实际上用于步行/跑步。 它与跳跃的原理完全相同,但在水平X轴上
  • 重力 -这是重力加速度(G指向下方的图中)
  • MAX_JUMP_SPEED –这是跳跃时我们永远不会超过的最终速度
  • DAMP –当Bob停下时,这可以使运动平稳。 他不会突然停止的。 稍后再详细介绍,忽略它
  • MAX_VEL –与MAX_JUMP_SPEED相同,但用于水平轴移动

#15 –这是一个临时常数,是世界单位的世界范围。 用于限制Bob在屏幕上的移动
#19 – jumpPressedTime是用于累积按下跳转按钮的时间的变量
#20 –一个布尔值,如果按下跳转按钮,则为true #26jumpReleased()必须将jumpingReleased变量设置为false。 它只是一个简单的状态变量。 按照主要为我们完成大部分工作的主要更新方法。 #32 –照常调用processInput来检查是否按下了任何键 移至processInput #71 –检查是否按下JUMP按钮 #72 –#76 –如果Bob不在JUMPING状态(意味着他在地面上),则开始跳跃。 鲍勃已设置为跳跃状态,可以起飞了。 我们在此处作弊,而不是施加向上的力,而是将Bob的垂直速度设置为他可以跳跃的最大速度( 第76行)。 我们还存储启动跳转时的时间(以毫秒为单位)。 #77 –#85 –只要鲍勃在空中,就会执行此操作。 万一我们仍然按下跳转按钮,我们检查自跳转开始以来经过的时间是否大于我们设置的阈值,如果我们仍处于截止时间(当前为150ms),则我们保持Bob的垂直速度。 忽略#87-107行,因为它们是水平行走。 回到update方法,我们有: #34 –鲍勃的加速度设置为“重力”。 这是因为重力是一个常数,我们从这里开始: #35 –我们计算出该周期所花费时间的加速度。 我们的初始值以单位/秒为单位,因此我们需要相应地调整值。 如果我们每秒更新60次,则增量将为1/60。 所有这些都由libgdx为您处理。 #36 – Bob的当前速度随着他在两个轴上的加速度而更新。 请记住,我们正在处理欧几里得空间中的向量。 #37 –这将使Bob的停顿变得顺畅。 如果我们在X轴上没有加速度,则每个循环将其速度降低10%。 一秒钟有很多周期,Bobo会很快但非常平稳地停止。 #38 –#43 –确保Bob不会超过其最大允许速度(终端速度)。 这再次捍卫了这样的规律:如果恒力作用在物体上,物体将无限加速。 #45 –调用Bob的update方法,该方法除了根据Bob的速度来更新Bob的位置外没有其他作用。 #46 –#66 –这是非常基本的碰撞检测,可防止Bob离开屏幕。 我们只需检查Bob的位置是否在屏幕之外(使用世界坐标),如果是,则只需将Bob放回边缘即可。 值得注意的是,每当鲍勃跌落地面或到达世界边缘(屏幕)时,我们会将他的状态设置为Idle 。 这使我们能够再次跳跃。

如果我们通过上述更改运行该应用程序,则必须具有以下效果:

客房整理–重构

我们注意到,在生成的应用程序中,没有图块,并且Bob并不仅限于屏幕边缘。 当鲍勃在空中时,也有不同的图像。 他跳跃时一幅图像,跌倒时一幅图像。 我们做了以下工作:

  • 更名WorldControllerBobController 。 因为我们用它控制鲍勃,所以这很有意义。
  • WorldRenderer'render()方法中注释了drawBlocks() 。 我们现在不渲染图块,因为我们忽略了它们。
  • 加入setDebug()方法将WorldRendered和在支撑肘节功能GameScreen.java 。 现在可以通过在桌面模式下按键盘上的D来切换调试渲染。
  • WorldRenderer具有新的纹理区域,以表示跳跃和下降的鲍勃。 但是,我们仍然仅保持一种状态。 通过检查Bob的垂直速度(在Y轴上),世界渲染器如何知道何时显示。 如果为正,则鲍勃在跳;如果为负,则鲍勃在跌。
    public class WorldRenderer {
    
    	// ... omitted ... //
    
    	private TextureRegion bobJumpLeft;
    	private TextureRegion bobFallLeft;
    	private TextureRegion bobJumpRight;
    	private TextureRegion bobFallRight;
    
    	private void loadTextures() {
    		TextureAtlas atlas = new TextureAtlas(Gdx.files.internal('images/textures/textures.pack'));
    
    		// ... omitted ... //
    
    		bobJumpLeft = atlas.findRegion('bob-up');
    		bobJumpRight = new TextureRegion(bobJumpLeft);
    		bobJumpRight.flip(true, false);
    		bobFallLeft = atlas.findRegion('bob-down');
    		bobFallRight = new TextureRegion(bobFallLeft);
    		bobFallRight.flip(true, false);
    	}
    
    	private void drawBob() {
    		Bob bob = world.getBob();
    		bobFrame = bob.isFacingLeft() ? bobIdleLeft : bobIdleRight;
    		if(bob.getState().equals(State.WALKING)) {
    			bobFrame = bob.isFacingLeft() ? walkLeftAnimation.getKeyFrame(bob.getStateTime(), true) : walkRightAnimation.getKeyFrame(bob.getStateTime(), true);
    		} else if (bob.getState().equals(State.JUMPING)) {
    			if (bob.getVelocity().y > 0) {
    				bobFrame = bob.isFacingLeft() ? bobJumpLeft : bobJumpRight;
    			} else {
    				bobFrame = bob.isFacingLeft() ? bobFallLeft : bobFallRight;
    			}
    		}
    		spriteBatch.draw(bobFrame, bob.getPosition().x * ppuX, bob.getPosition().y * ppuY, Bob.SIZE * ppuX, Bob.SIZE * ppuY);
    	}
    }

    上面的代码摘录显示了重要的补充。
    #5-#8 –用于跳跃的新纹理区域。 我们需要一个左,一个右。
    #15-#20 –准备资产。 我们需要向项目添加更多的png图像。 检查star-assault-android/images/目录,在那里您将看到bob-down.pngbob-up.png 。 这些已添加,并且还使用ImagePacker2工具重新创建了纹理图集。 有关如何创建它,请参见第2部分#28-#33 –是我们确定Bob在空中时要绘制哪个纹理区域的部分。

  • Bob.java有一些错误修复。 现在,边界框的位置与bob相同,更新将解决此问题。 同样, setPosition方法更新边界框的位置。 这对drawDebug()内部的WorldRenderer drawDebug()方法产生了影响。 现在,我们不必担心根据图块的位置来计算边界框,因为这些框现在具有与实体相同的位置。 这是一个愚蠢的错误,让我溜进去。在进行碰撞检测时,这非常重要。

该列表几乎总结了所有更改,但是应该很容易理解。 该项目的源代码可以在这里找到: https : //github.com/obviam/star-assault您需要检出分支part3。 要使用git进行检查,请执行以下操作: git clone -b part3 git@github.com:obviam/star-assault.git您还可以将其下载为zip文件

参考: 使用libgdx进行Android游戏开发-跳跃,引力和改进的动作,第3部分,来自我们的JCG合作伙伴 Impaler,网址为Against the Grain博客。

翻译自: https://www.javacodegeeks.com/2013/03/android-game-development-with-libgdx-jumping-gravity-and-improved-movement-part-3.html

libgdx开发指南

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值