Android游戏开发十日通(4)-行走,跳跃,碰撞检测

发布时间:2020-07-01 23:10:00 作者:拳四郎
来源:网络 阅读:383

提要

经过前面的三篇文章,我们已经对libgdx有了一定的了解,并且搭建了一个简单的游戏场景,下面我们就继续在之前场景上添加更多的元素。
今天要实现的是精灵的行走、跳跃动画,还有碰撞检测。

行走

今天要实现的东西都是基于物理和数学的原理,首先我们来分析一下行走。
游戏中的动画都是帧动画,比如对Bob的行走一步的动画分解:

Android游戏开发十日通(4)-行走,跳跃,碰撞检测
当这个序列连续播放的时候,就有了行走的效果。
之前已经有了移动的效果,所以只要在移动的时候循环播放动画就可以了。

现在需要的是决定帧的持续时间。
假设游戏渲染的是60FPS, 那么一帧的持续时间就是1/60s = 0.016 s。

对于一个成年人,每分钟大约走180步,那么每秒走的步数就是180/60 = 3,需要播放的帧为3×5 = 15.
那么每一帧持续时间就是1/15 = 0.066s.及66毫秒,即人物在运动的时候只要以66ms/帧对速度播放帧序列,就可以产生行走的效果了。


下面是代码实现。

首先是加载贴图。
和上一篇不同,这里用的是一次性加载贴图的方式,算是对游戏的一种优化。
Android游戏开发十日通(4)-行走,跳跃,碰撞检测
需要和贴图textures.png一起添加到Android工程的asset/image/textures文件下的还有一个textures.pack文件,类似于一个xml,用于读取序列。


textures.png format: RGBA8888 filter: Nearest,Nearest repeat: none block   rotate: false   xy: 1, 11   size: 48, 48   orig: 48, 48   offset: 0, 0   index: -1 bob-01   rotate: false   xy: 51, 31   size: 24, 28   orig: 24, 28   offset: 0, 0   index: -1 /*..omit..*/


接下来需要修改Bob类。
添加一个statetime成员用于记录Bob生存的时间,后面会用它来决定显示帧序列的哪一帧。
package com.me.testgdxgame.model;  import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2;  public class Bob {  	public enum State { 		IDLE, WALKING, JUMPING, DYING 	}  	public static final float SPEED = 4f;	// unit per second 	static final float JUMP_VELOCITY = 1f; 	public static final float SIZE = 0.5f; // half a unit 	  	Vector2 	position = new Vector2(); 	Vector2 	acceleration = new Vector2(); 	Vector2 	velocity = new Vector2(); 	Rectangle 	bounds = new Rectangle(); 	State		state = State.IDLE; 	boolean		facingLeft = true; 	 	float		stateTime = 0;  	public Bob(Vector2 position) { 		this.position = position; 		this.bounds.height = SIZE; 		this.bounds.width = SIZE; 	} 	public boolean isFacingLeft() { 		return facingLeft; 	} 	public void setFacingLeft(boolean facingLeft) { 		this.facingLeft = facingLeft; 	}  	public Vector2 getPosition() { 		return position; 	}  	public Vector2 getAcceleration() { 		return acceleration; 	}  	public Vector2 getVelocity() { 		return velocity; 	}  	public Rectangle getBounds() { 		return bounds; 	}  	public State getState() { 		return state; 	}  	public void setState(State newState) { 		this.state = newState; 	}  	public float getStateTime() { 		return stateTime; 	}   	public void update(float delta) { 		stateTime += delta; 		position.add(velocity.cpy().mul(delta)); 	} } 


在libgdx中用于处理这种序列图的工具类是TextureAtlas。
getKeyFrame用于获取动画中的关键帧。

修改WorldRender类。
package com.me.testgdxgame.view;   import com.me.testgdxgame.*; import com.me.testgdxgame.model.Block; import com.me.testgdxgame.model.Bob; import com.me.testgdxgame.model.Bob.State; import com.me.testgdxgame.model.World; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.math.Rectangle;  public class WorldRenderer {  	private World world; 	private OrthographicCamera cam;  	private static final float RUNNING_FRAME_DURATION = 0.06f; 	 	private SpriteBatch spriteBatch; 	private boolean debug=false; 	private int width; 	private int height; 	private float ppuX; // pixels per unit on the X axis 	private float ppuY; // pixels per unit on the Y axis 	private static final float CAMERA_WIDTH = 10f; 	private static final float CAMERA_HEIGHT = 7f;  	/** Textures **/ 	private TextureRegion bobIdleLeft; 	private TextureRegion bobIdleRight; 	private TextureRegion blockTexture; 	private TextureRegion bobFrame; 	 	/** Animations **/ 	private Animation walkLeftAnimation; 	private Animation walkRightAnimation;  	/** for debug rendering **/ 	ShapeRenderer debugRenderer = new ShapeRenderer();  	public WorldRenderer(World world) { 		this.world = world; 		this.cam = new OrthographicCamera(10, 7); 		this.cam.position.set(5, 3.5f, 0); 		this.cam.update(); 		spriteBatch=new SpriteBatch(); 		loadTextures(); 	} 	public void setSize (int w, int h) { 		this.width = w; 		this.height = h; 		ppuX = (float)width / CAMERA_WIDTH; 		ppuY = (float)height / CAMERA_HEIGHT; 	}  	private void loadTextures() { 		 		TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack")); 		bobIdleLeft = atlas.findRegion("bob-01"); 		bobIdleRight = new TextureRegion(bobIdleLeft); 		bobIdleRight.flip(true, false); 		blockTexture = atlas.findRegion("block"); 		TextureRegion[] walkLeftFrames = new TextureRegion[5]; 		for (int i = 0; i < 5; i++) { 			walkLeftFrames[i] = atlas.findRegion("bob-0" + (i + 2)); 		} 		walkLeftAnimation = new Animation(RUNNING_FRAME_DURATION, walkLeftFrames);  		TextureRegion[] walkRightFrames = new TextureRegion[5];  		for (int i = 0; i < 5; i++) { 			walkRightFrames[i] = new TextureRegion(walkLeftFrames[i]); 			walkRightFrames[i].flip(true, false); 		} 		walkRightAnimation = new Animation(RUNNING_FRAME_DURATION, walkRightFrames); 	}   	public void render() { 		spriteBatch.begin(); 		drawBlocks(); 		drawBob(); 		spriteBatch.end(); 		if(debug) drawDebug(); 	}  	private void  drawBlocks(){ 		for (Block block : world.getBlocks()) { 			spriteBatch.draw(blockTexture, block.getPosition().x * ppuX, block.getPosition().y * ppuY, Block.SIZE * ppuX, Block.SIZE * ppuY); 		} 	} 	private void drawBob(){ 		Bob bob = world.getBob(); 	    bobFrame = bob.isFacingLeft() ? bobIdleLeft : bobIdleRight; 		//spriteBatch.draw(bobIdleLeft, bob.getPosition().x * ppuX, bob.getPosition().y * ppuY, Bob.SIZE * ppuX, Bob.SIZE * ppuY); 		if(bob.getState().equals(State.WALKING)){ 			bobFrame = bob.isFacingLeft() ? walkLeftAnimation.getKeyFrame(bob.getStateTime(),true):walkRightAnimation.getKeyFrame(bob.getStateTime(),true); 		} 		spriteBatch.draw(bobFrame, bob.getPosition().x * ppuX, bob.getPosition().y * ppuY, Bob.SIZE * ppuX, Bob.SIZE * ppuY); 		 	}  	private void drawDebug(){ 		// render blocks 		debugRenderer.setProjectionMatrix(cam.combined); 		debugRenderer.begin(ShapeType.Rectangle); 		for (Block block : world.getBlocks()) { 			Rectangle rect = block.getBounds(); 			float x1 = block.getPosition().x + rect.x; 			float y1 = block.getPosition().y + rect.y; 			debugRenderer.setColor(new Color(1, 0, 0, 1)); 			debugRenderer.rect(x1, y1, rect.width, rect.height); 		} 		// render Bob  		Bob bob = world.getBob(); 		Rectangle rect = bob.getBounds(); 		float x1 = bob.getPosition().x + rect.x; 		float y1 = bob.getPosition().y + rect.y; 		debugRenderer.setColor(new Color(0, 1, 0, 1)); 		debugRenderer.rect(x1, y1, rect.width, rect.height); 		debugRenderer.end(); 	} } 

跑一下mian.java,键盘左右键就可以控制小人跑了。

Android游戏开发十日通(4)-行走,跳跃,碰撞检测

跳跃

首先来复习一下初中的牛顿三定律...

第一定律:倘物体处于静止状态,或呈等速直线运动,只要没外力作用,物体将保持静止状态,或呈等速直线运动之状态。这定律又称为惯性定律。
第二定律:物体的加速度,与所受的净外力成正比。加速度的方向与净外力的方向相同。即 ;其中,是加速度, 是净外力, 是质量。
第三定律:两个物体的相互作用力总是大小相等,方向相反,同时出现或消失。强版第三定律还额外要求两支作用力的方向都处于同一直线。

分析一下Bob的状态。

Android游戏开发十日通(4)-行走,跳跃,碰撞检测

最初始的时候,Bob处于静止状态,受两个力,一个是重力,一个是地面的支持力,二力平衡。
上图左边是Bob处于起跳状态,此时Bob用力蹬地面,地面给的反作用力大于Bob受的重力,Bob离开地面,起跳。
上图右边是Bob处于空中的状态,只受重力影响。

要用到一个速度计算公式:
v=u+at
u为初速度,a为加速度,t为时间。

下面看具体的代码实现。只需要在WorldController中添加对应的按键事件处理就可以了。
添加一些静态变量作为世界的参数。修改inputProcess和update函数。
注意仔细分析跳跃处理的逻辑。

package com.me.testgdxgame.controller;  import java.util.HashMap; import java.util.Map;  import com.me.testgdxgame.model.Bob; import com.me.testgdxgame.model.Bob.State; import com.me.testgdxgame.model.World;  public class WorldController { 	 	enum Keys{ 		LEFT,RIGHT,JUMP,FIRE 	} 	private World world; 	private Bob bob; 	private long jumpPressedTime; 	private boolean jumpingPressed; 	 	static Map<Keys,Boolean> keys = new HashMap<WorldController.Keys,Boolean>(); 	static { 		keys.put(Keys.LEFT, false); 		keys.put(Keys.RIGHT, false); 		keys.put(Keys.JUMP, false); 		keys.put(Keys.FIRE, false); 	}; 	 	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.8f; 	private static final float MAX_VEL = 4f; 	 	private static final float WIDTH =10f; 	 	public WorldController(World w){ 		world=w; 		bob=world.getBob(); 	} 	 	//Key presses and touches 	public void leftPressed(){ 		keys.get(keys.put(Keys.LEFT, true)); 	} 	public void rightPressed() { 		keys.get(keys.put(Keys.RIGHT, true)); 	}  	public void jumpPressed() { 		keys.get(keys.put(Keys.JUMP, true)); 		 	}  	public void firePressed() { 		keys.get(keys.put(Keys.FIRE, false)); 		 	}  	public void leftReleased() { 		keys.get(keys.put(Keys.LEFT, false)); 	}  	public void rightReleased() { 		keys.get(keys.put(Keys.RIGHT, false)); 	}  	public void jumpReleased() { 		keys.get(keys.put(Keys.JUMP, false)); 		jumpingPressed = false; 	}  	public void fireReleased() { 		keys.get(keys.put(Keys.FIRE, false)); 	} 	 	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); 		 		//Set Bob's state to State.IDLE when Bob touch edge 		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); 			} 		} 	} 	 	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)){ 			bob.setFacingLeft(true); 			if(!bob.getState().equals(State.JUMPING)){ 				bob.setState(State.WALKING); 			}	 			bob.getVelocity().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.getVelocity().x=ACCELERATION; 		}else { 			if(!bob.getState().equals(State.JUMPING)){ 				bob.setState(State.IDLE); 			}	; 			// acceleration is 0 on the x 			bob.getAcceleration().x = 0; 		} 		return false; 	} } 

在WorldRender中还需要添加与跳跃对应的纹理还有修改drawBob函数。
加载纹理:

private void loadTextures() { 		 		TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack")); 		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); 		 		bobIdleLeft = atlas.findRegion("bob-01"); 		bobIdleRight = new TextureRegion(bobIdleLeft); 		bobIdleRight.flip(true, false); 		blockTexture = atlas.findRegion("block"); 		TextureRegion[] walkLeftFrames = new TextureRegion[5]; 		for (int i = 0; i < 5; i++) { 			walkLeftFrames[i] = atlas.findRegion("bob-0" + (i + 2)); 		} 		walkLeftAnimation = new Animation(RUNNING_FRAME_DURATION, walkLeftFrames);  		TextureRegion[] walkRightFrames = new TextureRegion[5];  		for (int i = 0; i < 5; i++) { 			walkRightFrames[i] = new TextureRegion(walkLeftFrames[i]); 			walkRightFrames[i].flip(true, false); 		} 		walkRightAnimation = new Animation(RUNNING_FRAME_DURATION, walkRightFrames); 	}


drawBob函数
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); 		 	}


运行一下:
Android游戏开发十日通(4)-行走,跳跃,碰撞检测


碰撞检测

现在的demo是可以穿墙的-_-!,这里的碰撞检测指的是Bob与Blocks之间的碰撞,当Bob撞墙的时候,Bob需要停止前进跳跃或是落下。

最简单的方法就是每update一次就检测Bob与每一个Block是否相撞,当然效率会非常低。

仔细来分析一下:每次与Bob可能发生碰撞的Block只有Bob身边的8块砖,那么在每次Update的时候与检测是否与身边的砖块相碰就可以了。

Android游戏开发十日通(4)-行走,跳跃,碰撞检测

还可以再简单么?
看下面的图片。
Android游戏开发十日通(4)-行走,跳跃,碰撞检测


假设Bob正在向右行走,那么在可能与Bob产生碰撞的Block中,只有下面的那个。所以,根据Bob当前的状态进行碰撞检测可以高效地进行碰撞检测。

我们用一个二维数组来保存地形,用于碰撞检测。

首先我们创建一个level,就是关卡,用world来装载,而不是直接在world里面生成。
package com.me.testgdxgame.model;  import com.badlogic.gdx.math.Vector2;  public class Level {  	private int width; 	private int height; 	private Block[][] blocks;  	public int getWidth() { 		return width; 	}  	public void setWidth(int width) { 		this.width = width; 	}  	public int getHeight() { 		return height; 	}  	public void setHeight(int height) { 		this.height = height; 	}  	public Block[][] getBlocks() { 		return blocks; 	}  	public void setBlocks(Block[][] blocks) { 		this.blocks = blocks; 	}  	public Level() { 		loadDemoLevel(); 	} 	 	public Block get(int x, int y) { 		return blocks[x][y]; 	}  	private void loadDemoLevel() { 		width = 10; 		height = 7; 		blocks = new Block[width][height]; 		for (int col = 0; col < width; col++) { 			for (int row = 0; row < height; row++) { 				blocks[col][row] = null; 			} 		} 		 		for (int col = 0; col < 10; col++) { 			blocks[col][0] = new Block(new Vector2(col, 0)); 			blocks[col][6] = new Block(new Vector2(col, 6)); 			if (col > 2) { 				blocks[col][1] = new Block(new Vector2(col, 1)); 			} 		} 		blocks[9][2] = new Block(new Vector2(9, 2)); 		blocks[9][3] = new Block(new Vector2(9, 3)); 		blocks[9][4] = new Block(new Vector2(9, 4)); 		blocks[9][5] = new Block(new Vector2(9, 5));  		blocks[6][3] = new Block(new Vector2(6, 3)); 		blocks[6][4] = new Block(new Vector2(6, 4)); 		blocks[6][5] = new Block(new Vector2(6, 5)); 	} } 

World类做对应的修改:
package com.me.testgdxgame.model;  import java.util.ArrayList; import java.util.List;  import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array;  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(); 	} } 


然后在WorldController中添加一个checkCollisionWithBlocks函数,函数里面首先检测X方向是否有碰撞,再检测Y方向是否有碰撞。
在update函数中,每运行一次就进行一次检测。
完整的WorldController类如下:
package com.me.testgdxgame.controller;  import java.util.HashMap; import java.util.Map;  import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Pool; import com.me.testgdxgame.model.Block; import com.me.testgdxgame.model.Bob; import com.me.testgdxgame.model.Bob.State; import com.me.testgdxgame.model.World;   public class WorldController {  	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; 	 	private World 	world; 	private Bob 	bob; 	private long	jumpPressedTime; 	private boolean jumpingPressed; 	private boolean grounded = false;  	// This is the rectangle pool used in collision detection 	// Good to avoid instantiation each frame 	private Pool<Rectangle> rectPool = new Pool<Rectangle>() { 		@Override 		protected Rectangle newObject() { 			return new Rectangle(); 		} 	}; 	 	static Map<Keys, Boolean> keys = new HashMap<WorldController.Keys, Boolean>(); 	static { 		keys.put(Keys.LEFT, false); 		keys.put(Keys.RIGHT, false); 		keys.put(Keys.JUMP, false); 		keys.put(Keys.FIRE, false); 	};  	// Blocks that Bob can collide with any given frame 	private Array<Block> collidable = new Array<Block>(); 	 	public WorldController(World world) { 		this.world = world; 		this.bob = world.getBob(); 	}  	// ** Key presses and touches **************** // 	 	public void leftPressed() { 		keys.get(keys.put(Keys.LEFT, true)); 	} 	 	public void rightPressed() { 		keys.get(keys.put(Keys.RIGHT, true)); 	} 	 	public void jumpPressed() { 		keys.get(keys.put(Keys.JUMP, true)); 	} 	 	public void firePressed() { 		keys.get(keys.put(Keys.FIRE, false)); 	} 	 	public void leftReleased() { 		keys.get(keys.put(Keys.LEFT, false)); 	} 	 	public void rightReleased() { 		keys.get(keys.put(Keys.RIGHT, false)); 	} 	 	public void jumpReleased() { 		keys.get(keys.put(Keys.JUMP, false)); 		jumpingPressed = false; 	} 	 	public void fireReleased() { 		keys.get(keys.put(Keys.FIRE, false)); 	} 	 	/** The main update method **/ 	public void update(float delta) { 		// Processing the input - setting the states of Bob 		processInput(); 		 		// If Bob is grounded then reset the state to IDLE  		if (grounded && bob.getState().equals(State.JUMPING)) { 			bob.setState(State.IDLE); 		} 		 		// Setting initial vertical acceleration  		bob.getAcceleration().y = GRAVITY; 		 		// Convert acceleration to frame time 		bob.getAcceleration().mul(delta); 		 		// apply acceleration to change velocity 		bob.getVelocity().add(bob.getAcceleration().x, bob.getAcceleration().y);  		// checking collisions with the surrounding blocks depending on Bob's velocity 		checkCollisionWithBlocks(delta);  		// apply damping to halt Bob nicely  		bob.getVelocity().x *= DAMP; 		 		// ensure terminal velocity is not exceeded 		if (bob.getVelocity().x > MAX_VEL) { 			bob.getVelocity().x = MAX_VEL; 		} 		if (bob.getVelocity().x < -MAX_VEL) { 			bob.getVelocity().x = -MAX_VEL; 		} 		 		// simply updates the state time 		bob.update(delta);  	}  	/** Collision checking **/ 	private void checkCollisionWithBlocks(float delta) { 		// scale velocity to frame units  		bob.getVelocity().mul(delta); 		 		// Obtain the rectangle from the pool instead of instantiating it 		Rectangle bobRect = rectPool.obtain(); 		// set the rectangle to bob's bounding box 		bobRect.set(bob.getBounds().x, bob.getBounds().y, bob.getBounds().width, bob.getBounds().height); 		 		// we first check the movement on the horizontal X axis 		int startX, endX; 		int startY = (int) bob.getBounds().y; 		int endY = (int) (bob.getBounds().y + bob.getBounds().height); 		// if Bob is heading left then we check if he collides with the block on his left 		// we check the block on his right otherwise 		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); 		}  		// get the block(s) bob can collide with 		populateCollidableBlocks(startX, startY, endX, endY);  		// simulate bob's movement on the X 		bobRect.x += bob.getVelocity().x; 		 		// clear collision boxes in world 		world.getCollisionRects().clear(); 		 		// if bob collides, make his horizontal velocity 0 		for (Block block : collidable) { 			if (block == null) continue; 			if (bobRect.overlaps(block.getBounds())) { 				bob.getVelocity().x = 0; 				world.getCollisionRects().add(block.getBounds()); 				break; 			} 		}  		// reset the x position of the collision box 		bobRect.x = bob.getPosition().x; 		 		// the same thing but on the vertical Y axis 		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; 			} 		} 		// reset the collision box's position on Y 		bobRect.y = bob.getPosition().y; 		 		// update Bob's position 		bob.getPosition().add(bob.getVelocity()); 		bob.getBounds().x = bob.getPosition().x; 		bob.getBounds().y = bob.getPosition().y; 		 		// un-scale velocity (not in frame time) 		bob.getVelocity().mul(1 / delta); 		 	}  	/** populate the collidable array with the blocks found in the enclosing coordinates **/ 	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)); 				} 			} 		} 	}  	/** 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;  				grounded = false; 			} 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; 	}  } 

运行截图:
Android游戏开发十日通(4)-行走,跳跃,碰撞检测


总结

这篇blog的内容有点多,需要多花时间去体会。
特别是碰撞检测,思路一定要缕清晰。

这个游戏也暂时告一段落了,主要原因是国外的那位大牛也没有更新此系列的文章了...
当然,游戏还在继续。

工程下载

参考:Getting Started in Android Game Development with libgdx-http://obviam.net/


推荐阅读:
  1. Android游戏开发十日通(8)- Box2D的例子和学习小结
  2. 【移动开发】Android游戏开发SurfaceView应用----手指发动小球(小球碰撞检测例子)

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

android android游戏 roi

上一篇:虚拟主机.htaccess伪静态(URL重写)建立多站点

下一篇:简单爬取《小丑》电影豆瓣短评生成词云

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》