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的位置,不是屏幕边界,所以如果直接按照上文的做法做的话主角走到屏幕边缘的时候就有问题了。

zuobiao

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     }

运行一下,恩,感觉还行。但是又有一个问题出现了…当地图达到边界时地图不能滚动了,但是主角应该还是可以前进的。

13-1

处理方法我采用的是将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         } 

 

13-2

第一个问题基本解决,为什么说是基本解决?因为主角和Camera的位置可能会变动。造成主角在屏幕一角行走的问题。我一般是判断主角位置,当位于中心区域时在让二者联动。

现在来解决第二个问题,障碍物的问题。

重新编辑我们的TMX文件。添加一个新图块:

metatiles

大小还是32*32.

13-3

然后新建一个图层,将地图中不能穿越的部分用红色的方块填充。

tilemap

编辑红色块的属性,添加一个Pass-False键值(这个值是随意的,只要你能懂就行)

13-4

最后隐藏该图层,保存文件。(我在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 } 

 

最终效果:

13-5

 

写在最后:

1.调试好了就要在绘制前把障碍层删除。

2.注意各种坐标转化。

3.如果需要变化地图,直接操作Layer里面的那个二维数组。

 

本文用的检测方法只是一种可行方案而已,也可以直接看角色占的块数。

本文参考了:http://geekanddad.wordpress.com/2010/06/22/enemies-and-combat-how-to-make-a-tile-based-game-with-cocos2d-part-3/

posted @ 2013-02-04 21:06  王世桢  阅读(254)  评论(0)    收藏  举报