这篇我们主要实现游戏的主要框架 并且绘制一条蛇 让它动起来
首先我们看看那本篇完成的最终效果图吧
配置工程 如果你没有配置运行工程的话,会报错。
配置destop
工程
在菜单栏找到Run
选择 Edit Configurations
然后配置Working directory
配置工作区域选择 android 工程下的assets
目录 重新运行,OK!
编写代码框架 在core
工程下的src 目录下新建 WorldController.java
(控制游戏)和WorldRenderer.java
(渲染游戏)
在 WorldController.java
添加update
方法
1 2 3 public void update (float deltaTime) { }
在WorldRenderer.java
添加 render
方法
1 2 3 public void render () { }
WorldRenderer.java
修改为下面一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class WorldRenderer implements Disposable { private WorldController worldController; private SpriteBatch spriteBatch; public WorldRenderer (WorldController worldController) { this .worldController = worldController; init(); } private void init () { spriteBatch = new SpriteBatch (); } public void render () { spriteBatch.begin(); spriteBatch.end(); } @Override public void dispose () { spriteBatch.dispose(); } }
然后修改SnakeIo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SnakeIo extends ApplicationAdapter { private WorldController worldController; private WorldRenderer worldRenderer; @Override public void create () { worldController = new WorldController (); worldRenderer = new WorldRenderer (worldController); } @Override public void render () { worldController.update(Gdx.graphics.getDeltaTime()); Gdx.gl.glClearColor(1 , 1 , 1 , 1 ); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); worldRenderer.render(); } @Override public void dispose () { worldRenderer.dispose(); worldController.dispose(); } }
这个很简单 将WorldController
和WorldRenderer
实例化 然后在render
方法里面调用更新和绘制方法
编写蛇对象 在这之前我们需要准备两张图片
一张是眼睛图片,一张是身体的图片 将图片copy 进destop工程 目录下的images下
我们先把图片打包 在DesktopLauncher.java
添加如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void main (String[] arg) { if (true ) { TexturePacker.Settings settings = new TexturePacker .Settings(); settings.maxHeight = 1024 ; settings.maxWidth = 1024 ; settings.duplicatePadding = false ; settings.debug = false ; TexturePacker.process(settings, "../../desktop/images" , "../../android/assets/images" , "snake" ); } LwjglApplicationConfiguration config = new LwjglApplicationConfiguration (); config.width = 800 ; config.height = 480 ; config.samples = 3 ; new LwjglApplication (new SnakeIo (), config); }
运行下工程 就会在android 工程下的 assets/images 目录下生成 snake.png
和 snake.atlas
把两张图打包成一张图片
我们在core
工程的src 目录下新建objects
包 然后新建AbstractGameObject.java
和Snake.java
AbstractGameObject
为 基础的抽象游戏对象 具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public abstract class AbstractGameObject { public Vector2 position; public Vector2 dimension; public Vector2 origin; public Vector2 scale; public float rotation; public Vector2 velocity; public Vector2 terminalVelocity; public Vector2 friction; public Vector2 acceleration; public Rectangle bounds; public Body body; public float stateTime; public Animation<TextureRegion> animation; public AbstractGameObject () { position = new Vector2 (); dimension = new Vector2 (1 , 1 ); origin = new Vector2 (); scale = new Vector2 (1 , 1 ); rotation = 0 ; velocity = new Vector2 (); terminalVelocity = new Vector2 (); friction = new Vector2 (); acceleration = new Vector2 (); bounds = new Rectangle (); } public void update (float deltaTime) { stateTime += deltaTime; if (body == null ) { updateMotionX(deltaTime); updateMotionY(deltaTime); position.x += velocity.x * deltaTime; position.y += velocity.y * deltaTime; } else { position.set(body.getPosition()); rotation = body.getAngle() * MathUtils.radiansToDegrees; } } protected void updateMotionX (float deltaTime) { if (velocity.x != 0 ) { if (velocity.x > 0 ) { velocity.x = Math.max(velocity.x - friction.x * deltaTime, 0 ); } else { velocity.x = Math.min(velocity.x + friction.x * deltaTime, 0 ); } } velocity.x += acceleration.x * deltaTime; velocity.x = MathUtils.clamp(velocity.x, -terminalVelocity.x, terminalVelocity.x); } protected void updateMotionY (float deltaTime) { if (velocity.y != 0 ) { if (velocity.y > 0 ) { velocity.y = Math.max(velocity.y - friction.y * deltaTime, 0 ); } else { velocity.y = Math.min(velocity.y + friction.y * deltaTime, 0 ); } } velocity.y += acceleration.y * deltaTime; velocity.y = MathUtils.clamp(velocity.y, -terminalVelocity.y, terminalVelocity.y); } public void setAnimation (Animation<TextureRegion> animation) { this .animation = animation; stateTime = 0 ; } public abstract void render (SpriteBatch batch) ; }
Snake.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public class Snake extends AbstractGameObject implements Disposable { private static final String TAG = Snake.class.getSimpleName(); TextureRegion eyes; TextureRegion body; private AssetManager assetManager; private int length = 100 * 80 ; private float speed = 8 * 10 ; public Array<Movement> movements; public Snake () { initRegion(); init(); } private void init () { dimension.set(20 , 20 ); origin.set(dimension.x / 2 , dimension.y / 2 ); terminalVelocity.set(100 , 100 ); position.set(50 , 120 ); rotation = -90 ; scale.set(0.7f , 0.7f ); bounds.set(0 , 0 , dimension.x, dimension.y); movements = new Array <Movement>(); velocity.set(speed, 0 ); } private void initRegion () { assetManager = new AssetManager (); assetManager.setErrorListener(new AssetErrorListener () { @Override public void error (AssetDescriptor asset, Throwable throwable) { Gdx.app.error(TAG, "Couldn't load asset '" + asset.fileName + "'" , throwable); } }); assetManager.load("images/snake.atlas" , TextureAtlas.class); assetManager.finishLoading(); TextureAtlas atlas = assetManager.get("images/snake.atlas" ); for (Texture texture : atlas.getTextures()) { texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); } eyes = atlas.findRegion("eyes" ); body = atlas.findRegion("body" ); } @Override public void render (SpriteBatch batch) { float snakeLength = this .length; for (int i = movements.size - 1 ; i >= 0 ; i--) { Movement movement = movements.get(i); float x = movement.position.x; float y = movement.position.y; if (snakeLength > 0 && snakeLength < movement.speed) { if (i != movements.size - 1 ) { Movement lm = movements.get(i + 1 ); float ratio = snakeLength / movement.speed; x = lm.position.x - (lm.position.x - x) * ratio; y = lm.position.y - (lm.position.y - y) * ratio; } } else if (snakeLength < 0 ) { break ; } snakeLength -= movement.speed; TextureRegion reg = body; batch.setColor(Color.valueOf("#F65454" )); batch.draw(reg.getTexture(), x, y, origin.x, origin.y, dimension.x, dimension.y, scale.x, scale.y, rotation, reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(), reg.getRegionHeight(), false , false ); } TextureRegion reg = eyes; batch.setColor(Color.WHITE.toFloatBits()); batch.draw(reg.getTexture(), position.x, position.y, origin.x, origin.y, dimension.x - 0.1f , dimension.y - 0.1f , scale.x, scale.y, rotation, reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(), reg.getRegionHeight(), false , false ); } @Override public void update (float deltaTime) { movements.add(new Movement (new Vector2 (position), speed)); if (movements.size > length) { movements.removeValue(movements.first(), true ); } super .update(deltaTime); } @Override public void dispose () { assetManager.dispose(); } public class Movement { public Vector2 position; public float speed; public Movement (Vector2 position, float speed) { this .position = position; this .speed = speed; } } }
在WorldController.java
添加Snake
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class WorldController extends InputAdapter implements Disposable { Snake snake; public WorldController () { snake = new Snake (); } public void update (float deltaTime) { snake.update(deltaTime); } @Override public void dispose () { snake.dispose(); } }
运行工程 就可以看到结果了
代码已经放在GITHUB 可以切换到tag1
来查看本篇文章代码
使用 git checkout tag1