android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理
转载声明:http://www.cnblogs.com/htynkn/archive/2012/01/13/libgdx_14.html
本文紧跟上文:http://www.cnblogs.com/htynkn/archive/2012/01/13/libgdx_13.html
上文说到绘制了Map,然后我们的主角也可以四处活动了,但是仍有一些不完善的地方。
1.地图的边界没有控制。Camera的位置其实是viewport的位置,不是屏幕边界,所以如果直接按照上文的做法做的话主角走到屏幕边缘的时候就有问题了。
2.没有障碍,主角的行动没有约束。
现在先来解决第一个问题。
解决方案很简单,我们时刻注意viewport的位置,根据viewport计算Screen的边界,让其不超过地图。
代码如下:
1 private void CameraMove(Vector3 vector3, Actor mainActor) { 2 Vector3 viewport = stage.getCamera().position.cpy(); 3 viewport = viewport.add(vector3); 4 Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); 5 if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { 6 return; 7 } 8 Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); 9 if (fbound.x < 0 || fbound.y < 0) { 10 return; 11 } 12 stage.getCamera().position.add(vector3); 13 for (Actor actor : stage.getActors()) { 14 actor.x += vector3.x; 15 actor.y += vector3.y; 16 } 17 }
运行一下,恩,感觉还行。但是又有一个问题出现了…当地图达到边界时地图不能滚动了,但是主角应该还是可以前进的。
处理方法我采用的是将Camera和主角分开处理,还是判断一下,主角如果移动后不超出屏幕,就继续移动。
1 Vector3 viewport = stage.getCamera().position.cpy(); 2 viewport = viewport.add(vector3); 3 Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); 4 if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { 5 isCameraMove = false; 6 } 7 Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); 8 if (fbound.x < 0 || fbound.y < 0) { 9 isCameraMove = false; 10 } 11 12 Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0); 13 stage.getCamera().project(v3); 14 Vector3 a = v3.cpy().add(vector3); 15 if (a.x > width || a.y > height) { 16 isActorMove = false; 17 } 18 if (a.x < 0 || a.y < 0) { 19 isActorMove = false; 20 } 21 22 if (isCameraMove) { 23 stage.getCamera().position.add(vector3); 24 for (Actor actor : stage.getActors()) { 25 if (!actor.equals(player)) { 26 actor.x += vector3.x; 27 actor.y += vector3.y; 28 } 29 } 30 } 31 if (isActorMove) { 32 player.x += vector3.x; 33 player.y += vector3.y; 34 }
第一个问题基本解决,为什么说是基本解决?因为主角和Camera的位置可能会变动。造成主角在屏幕一角行走的问题。我一般是判断主角位置,当位于中心区域时在让二者联动。
现在来解决第二个问题,障碍物的问题。
重新编辑我们的TMX文件。添加一个新图块:
大小还是32*32.
然后新建一个图层,将地图中不能穿越的部分用红色的方块填充。
编辑红色块的属性,添加一个Pass-False键值(这个值是随意的,只要你能懂就行)
最后隐藏该图层,保存文件。(我在Editor里面确实隐藏了这个层,但是libgdx还是绘制了,只有自己处理一下了)
1 map = TiledLoader.createMap(mapHandle); 2 3 for (int i = 0; i < map.layers.size(); i++) { 4 if ("NoPass".equals(map.layers.get(i).name)) { 5 nopassLayer = map.layers.get(i); 6 map.layers.remove(i); 7 break; 8 } 9 }
map实例化以后,先寻找我们的障碍层,将其从map中移除。为了方便调试,暂时没有移除它。
在主角每次移动时我们就检查主角移动后的位置是在哪块里面,这块是不是不能通过的,如果不能就不移动,否则就移动。
先遍历所有tiles,看一下pass=false的是多少ID的那块。
1 int nopassId = 0; 2 TileSet set = map.tileSets.get(map.tileSets.size() - 1); 3 int masSize = set.firstgid + layer.tiles.length; 4 for (int i = 0; i < masSize; i++) { 5 if ("False".equals(map.getTileProperty(i, "Pass"))) { 6 nopassId = i; 7 Gdx.app.log("Find!", i + " "); 8 break; 9 } 10 }
然后推算移动后会处于哪块中,哪一块是不是不能通过的。
1 int xid = MathUtils.ceilPositive(pos.x / map.tileWidth); 2 int yid = MathUtils.ceilPositive(pos.y / map.tileWidth); 3 if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) { 4 return true; 5 } else { 6 return false; 7 }
在移动时先判断一下,如果会达到不能到达的块就直接退出。
1 Vector2 pos = new Vector2(mainActor.x, mainActor.y); 2 if (CheckMoveable(map, nopassLayer, vector3, pos)) { 3 return; 4 }
完整代码:(代码有点乱…)
1 package com.cnblogs.htynkn.game; 2 3 import javax.swing.text.ZoneView; 4 import javax.swing.text.html.MinimalHTMLWriter; 5 6 import com.badlogic.gdx.ApplicationListener; 7 import com.badlogic.gdx.Gdx; 8 import com.badlogic.gdx.InputMultiplexer; 9 import com.badlogic.gdx.InputProcessor; 10 import com.badlogic.gdx.files.FileHandle; 11 import com.badlogic.gdx.graphics.Color; 12 import com.badlogic.gdx.graphics.GL10; 13 import com.badlogic.gdx.graphics.OrthographicCamera; 14 import com.badlogic.gdx.graphics.Texture; 15 import com.badlogic.gdx.graphics.g2d.BitmapFont; 16 import com.badlogic.gdx.graphics.g2d.SpriteBatch; 17 import com.badlogic.gdx.graphics.g2d.TextureRegion; 18 import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas; 19 import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer; 20 import com.badlogic.gdx.graphics.g2d.tiled.TileSet; 21 import com.badlogic.gdx.graphics.g2d.tiled.TiledLayer; 22 import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader; 23 import com.badlogic.gdx.graphics.g2d.tiled.TiledMap; 24 import com.badlogic.gdx.graphics.g2d.tiled.TiledObject; 25 import com.badlogic.gdx.graphics.g2d.tiled.TiledObjectGroup; 26 import com.badlogic.gdx.graphics.glutils.ShaderProgram; 27 import com.badlogic.gdx.math.MathUtils; 28 import com.badlogic.gdx.math.Vector2; 29 import com.badlogic.gdx.math.Vector3; 30 import com.badlogic.gdx.scenes.scene2d.Actor; 31 import com.badlogic.gdx.scenes.scene2d.Stage; 32 import com.badlogic.gdx.scenes.scene2d.ui.Image; 33 import com.badlogic.gdx.scenes.scene2d.ui.Label; 34 import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; 35 36 public class JavaGame implements ApplicationListener, InputProcessor { 37 38 Stage stage; 39 float width; 40 float height; 41 private TiledMap map; 42 private TileAtlas atlas; 43 private TileMapRenderer tileMapRenderer; 44 Image player; 45 Vector3 camDirection = new Vector3(1, 1, 0); 46 Vector2 maxCamPosition = new Vector2(0, 0); 47 Vector3 moveVector = new Vector3(0, 0, 0); 48 boolean isPress; 49 TiledLayer nopassLayer; 50 51 // Image image; 52 53 @Override 54 public void create() { 55 final String path = "map/"; 56 final String mapname = "tilemap"; 57 FileHandle mapHandle = Gdx.files.internal(path + mapname + ".tmx"); 58 map = TiledLoader.createMap(mapHandle); 59 60 for (int i = 0; i < map.layers.size(); i++) { 61 if ("NoPass".equals(map.layers.get(i).name)) { 62 nopassLayer = map.layers.get(i); 63 // map.layers.remove(i); 64 break; 65 } 66 } 67 68 atlas = new TileAtlas(map, new FileHandle("map/")); 69 tileMapRenderer = new TileMapRenderer(map, atlas, 10, 10); 70 maxCamPosition.set(tileMapRenderer.getMapWidthUnits(), tileMapRenderer 71 .getMapHeightUnits()); 72 73 width = Gdx.graphics.getWidth(); 74 height = Gdx.graphics.getHeight(); 75 stage = new Stage(width, height, true); 76 Label label = new Label("FPS:", new LabelStyle(new BitmapFont(Gdx.files 77 .internal("font/blue.fnt"), 78 Gdx.files.internal("font/blue.png"), false), Color.WHITE), 79 "fpsLabel"); 80 label.y = height - label.getPrefHeight(); 81 label.x = 0; 82 stage.addActor(label); 83 84 for (TiledObjectGroup group : map.objectGroups) { 85 for (TiledObject object : group.objects) { 86 if ("play1".equals(object.name)) { 87 player = new Image(new TextureRegion(new Texture(Gdx.files 88 .internal("map/player.png")), 0, 0, 27, 40)); 89 player.x = object.x; 90 player.y = tileMapRenderer.getMapHeightUnits() - object.y; // map是左上角,Stage是左下角 91 stage.addActor(player); 92 } 93 } 94 } 95 96 InputMultiplexer inputMultiplexer = new InputMultiplexer(); 97 inputMultiplexer.addProcessor(this); 98 inputMultiplexer.addProcessor(stage); 99 Gdx.input.setInputProcessor(inputMultiplexer); 100 } 101 102 @Override 103 public void dispose() { 104 // TODO Auto-generated method stub 105 106 } 107 108 @Override 109 public void pause() { 110 // TODO Auto-generated method stub 111 112 } 113 114 @Override 115 public void render() { 116 Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); 117 OrthographicCamera c = (OrthographicCamera) stage.getCamera(); 118 if (isPress) { 119 CameraMove(moveVector, player); 120 } 121 ((Label) stage.findActor("fpsLabel")).setText("FPS: " 122 + Gdx.graphics.getFramesPerSecond()); 123 stage.act(Gdx.graphics.getDeltaTime()); 124 tileMapRenderer.render(c); 125 stage.draw(); 126 } 127 128 private void CameraMove(Vector3 vector3, Actor mainActor) { 129 boolean isCameraMove = true; 130 boolean isActorMove = true; 131 132 Vector2 pos = new Vector2(mainActor.x, mainActor.y); 133 if (CheckMoveable(map, nopassLayer, vector3, pos)) { 134 return; 135 } 136 if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add( 137 mainActor.width, 0))) { 138 return; 139 } 140 if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add( 141 mainActor.width, mainActor.height))) { 142 return; 143 } 144 if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add(0, 145 mainActor.height))) { 146 return; 147 } 148 149 Vector3 viewport = stage.getCamera().position.cpy(); 150 viewport = viewport.add(vector3); 151 Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); 152 if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { 153 isCameraMove = false; 154 } 155 Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); 156 if (fbound.x < 0 || fbound.y < 0) { 157 isCameraMove = false; 158 } 159 160 Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0); 161 stage.getCamera().project(v3); 162 Vector3 a = v3.cpy().add(vector3); 163 if (a.x > width || a.y > height) { 164 isActorMove = false; 165 } 166 if (a.x < 0 || a.y < 0) { 167 isActorMove = false; 168 } 169 170 if (isCameraMove) { 171 stage.getCamera().position.add(vector3); 172 for (Actor actor : stage.getActors()) { 173 if (!actor.equals(player)) { 174 actor.x += vector3.x; 175 actor.y += vector3.y; 176 } 177 } 178 } 179 if (isActorMove) { 180 player.x += vector3.x; 181 player.y += vector3.y; 182 } 183 } 184 185 private boolean CheckMoveable(TiledMap map, TiledLayer layer, 186 Vector3 vector3, Vector2 playpos) { 187 Vector3 pos = new Vector3(playpos.x, playpos.y, 0).add(vector3); 188 int nopassId = 0; 189 TileSet set = map.tileSets.get(map.tileSets.size() - 1); 190 int masSize = set.firstgid + layer.tiles.length; 191 for (int i = 0; i < masSize; i++) { 192 if ("False".equals(map.getTileProperty(i, "Pass"))) { 193 nopassId = i; 194 Gdx.app.log("Find!", i + " "); 195 break; 196 } 197 } 198 int xid = MathUtils.ceilPositive(pos.x / map.tileWidth); 199 int yid = MathUtils.ceilPositive(pos.y / map.tileWidth); 200 if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) { 201 return true; 202 } else { 203 return false; 204 } 205 } 206 207 @Override 208 public void resize(int width, int height) { 209 // TODO Auto-generated method stub 210 211 } 212 213 @Override 214 public void resume() { 215 // TODO Auto-generated method stub 216 217 } 218 219 @Override 220 public boolean keyDown(int keycode) { 221 222 return false; 223 } 224 225 @Override 226 public boolean keyTyped(char character) { 227 // TODO Auto-generated method stub 228 return false; 229 } 230 231 @Override 232 public boolean keyUp(int keycode) { 233 // TODO Auto-generated method stub 234 return false; 235 } 236 237 @Override 238 public boolean scrolled(int amount) { 239 // TODO Auto-generated method stub 240 return false; 241 } 242 243 private void ChangeDirect(int typeId) { 244 switch (typeId) { 245 case 1: 246 moveVector.set(0, 1, 0); 247 Gdx.app.log("方向变动", "向上"); 248 break; 249 case 2: 250 moveVector.set(0, -1, 0); 251 Gdx.app.log("方向变动", "向下"); 252 break; 253 case 3: 254 moveVector.set(-1, 0, 0); 255 Gdx.app.log("方向变动", "向左"); 256 break; 257 case 4: 258 moveVector.set(1, 0, 0); 259 Gdx.app.log("方向变动", "向右"); 260 break; 261 } 262 } 263 264 @Override 265 public boolean touchDown(int x, int y, int pointer, int button) { 266 Vector3 tmp = new Vector3(x, y, 0); 267 stage.getCamera().unproject(tmp); 268 float newx = tmp.x - player.x; 269 float newy = tmp.y - player.y; 270 if (newx > 0 && newy > 0) { 271 if (newx > newy) { 272 ChangeDirect(4); 273 } else { 274 ChangeDirect(1); 275 } 276 } else if (newx > 0 && newy < 0) { 277 if (newx > -newy) { 278 ChangeDirect(4); 279 } else { 280 ChangeDirect(2); 281 } 282 } else if (newx < 0 && newy > 0) { 283 if (-newx > newy) { 284 ChangeDirect(3); 285 } else { 286 ChangeDirect(1); 287 } 288 } else { 289 if (-newx > -newy) { 290 ChangeDirect(3); 291 } else { 292 ChangeDirect(2); 293 } 294 } 295 isPress = true; 296 return false; 297 } 298 299 @Override 300 public boolean touchDragged(int x, int y, int pointer) { 301 // TODO Auto-generated method stub 302 return false; 303 } 304 305 @Override 306 public boolean touchMoved(int x, int y) { 307 // TODO Auto-generated method stub 308 return false; 309 } 310 311 @Override 312 public boolean touchUp(int x, int y, int pointer, int button) { 313 isPress = false; 314 Gdx.app.log("Info", "touchUp: x:" + x + " y: " + y + " pointer: " 315 + pointer + " button: " + button); 316 return false; 317 } 318 }
最终效果:
写在最后:
1.调试好了就要在绘制前把障碍层删除。
2.注意各种坐标转化。
3.如果需要变化地图,直接操作Layer里面的那个二维数组。
本文用的检测方法只是一种可行方案而已,也可以直接看角色占的块数。








浙公网安备 33010602011771号