Android游戏引擎libgdx使用教程14:TiledMap中视角完善与障碍物处理
2014-09-28 14:31
986 查看
上文TiledMap中的角色和角色移动说到绘制了Map,然后我们的主角也可以四处活动了,但是仍有一些不完善的地方。
1.地图的边界没有控制。Camera的位置其实是viewport的位置,不是屏幕边界,所以如果直接按照上文的做法做的话主角走到屏幕边缘的时候就有问题了。
2.没有障碍,主角的行动没有约束。
现在先来解决第一个问题。
解决方案很简单,我们时刻注意viewport的位置,根据viewport计算Screen的边界,让其不超过地图。
代码如下:
处理方法我采用的是将Camera和主角分开处理,还是判断一下,主角如果移动后不超出屏幕,就继续移动。
第一个问题基本解决,为什么说是基本解决?因为主角和Camera的位置可能会变动。造成主角在屏幕一角行走的问题。我一般是判断主角位置,当位于中心区域时在让二者联动。
现在来解决第二个问题,障碍物的问题。
重新编辑我们的TMX文件。添加一个新图块:
大小还是32*32.
然后新建一个图层,将地图中不能穿越的部分用红色的方块填充。
编辑红色块的属性,添加一个Pass-False键值(这个值是随意的,只要你能懂就行)
最后隐藏该图层,保存文件。(我在Editor里面确实隐藏了这个层,但是libgdx还是绘制了,只有自己处理一下了)
在主角每次移动时我们就检查主角移动后的位置是在哪块里面,这块是不是不能通过的,如果不能就不移动,否则就移动。
先遍历所有tiles,看一下pass=false的是多少ID的那块。
写在最后:
1.调试好了就要在绘制前把障碍层删除。
2.注意各种坐标转化。
3.如果需要变化地图,直接操作Layer里面的那个二维数组。
本文用的检测方法只是一种可行方案而已,也可以直接看角色占的块数。
1.地图的边界没有控制。Camera的位置其实是viewport的位置,不是屏幕边界,所以如果直接按照上文的做法做的话主角走到屏幕边缘的时候就有问题了。
2.没有障碍,主角的行动没有约束。
现在先来解决第一个问题。
解决方案很简单,我们时刻注意viewport的位置,根据viewport计算Screen的边界,让其不超过地图。
代码如下:
private void CameraMove(Vector3 vector3, Actor mainActor) { Vector3 viewport = stage.getCamera().position.cpy(); viewport = viewport.add(vector3); Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { return; } Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); if (fbound.x < 0 || fbound.y < 0) { return; } stage.getCamera().position.add(vector3); for (Actor actor : stage.getActors()) { actor.x += vector3.x; actor.y += vector3.y; } }运行一下,恩,感觉还行。但是又有一个问题出现了…当地图达到边界时地图不能滚动了,但是主角应该还是可以前进的。
处理方法我采用的是将Camera和主角分开处理,还是判断一下,主角如果移动后不超出屏幕,就继续移动。
Vector3 viewport = stage.getCamera().position.cpy(); viewport = viewport.add(vector3); Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { isCameraMove = false; } Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); if (fbound.x < 0 || fbound.y < 0) { isCameraMove = false; } Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0); stage.getCamera().project(v3); Vector3 a = v3.cpy().add(vector3); if (a.x > width || a.y > height) { isActorMove = false; } if (a.x < 0 || a.y < 0) { isActorMove = false; } if (isCameraMove) { stage.getCamera().position.add(vector3); for (Actor actor : stage.getActors()) { if (!actor.equals(player)) { actor.x += vector3.x; actor.y += vector3.y; } } } if (isActorMove) { player.x += vector3.x; player.y += vector3.y; }
第一个问题基本解决,为什么说是基本解决?因为主角和Camera的位置可能会变动。造成主角在屏幕一角行走的问题。我一般是判断主角位置,当位于中心区域时在让二者联动。
现在来解决第二个问题,障碍物的问题。
重新编辑我们的TMX文件。添加一个新图块:
大小还是32*32.
然后新建一个图层,将地图中不能穿越的部分用红色的方块填充。
编辑红色块的属性,添加一个Pass-False键值(这个值是随意的,只要你能懂就行)
最后隐藏该图层,保存文件。(我在Editor里面确实隐藏了这个层,但是libgdx还是绘制了,只有自己处理一下了)
map = TiledLoader.createMap(mapHandle); for (int i = 0; i < map.layers.size(); i++) { if ("NoPass".equals(map.layers.get(i).name)) { nopassLayer = map.layers.get(i); map.layers.remove(i); break; } }map实例化以后,先寻找我们的障碍层,将其从map中移除。为了方便调试,暂时没有移除它。
在主角每次移动时我们就检查主角移动后的位置是在哪块里面,这块是不是不能通过的,如果不能就不移动,否则就移动。
先遍历所有tiles,看一下pass=false的是多少ID的那块。
int nopassId = 0; TileSet set = map.tileSets.get(map.tileSets.size() - 1); int masSize = set.firstgid + layer.tiles.length; for (int i = 0; i < masSize; i++) { if ("False".equals(map.getTileProperty(i, "Pass"))) { nopassId = i; Gdx.app.log("Find!", i + " "); break; } }然后推算移动后会处于哪块中,哪一块是不是不能通过的。
int xid = MathUtils.ceilPositive(pos.x / map.tileWidth); int yid = MathUtils.ceilPositive(pos.y / map.tileWidth); if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) { return true; } else { return false; }在移动时先判断一下,如果会达到不能到达的块就直接退出。
Vector2 pos = new Vector2(mainActor.x, mainActor.y); if (CheckMoveable(map, nopassLayer, vector3, pos)) { return; }完整代码:(代码有点乱…)
package com.cnblogs.htynkn.game; import javax.swing.text.ZoneView; import javax.swing.text.html.MinimalHTMLWriter; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputMultiplexer; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.files.FileHandle; 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.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas; import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer; import com.badlogic.gdx.graphics.g2d.tiled.TileSet; import com.badlogic.gdx.graphics.g2d.tiled.TiledLayer; import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader; import com.badlogic.gdx.graphics.g2d.tiled.TiledMap; import com.badlogic.gdx.graphics.g2d.tiled.TiledObject; import com.badlogic.gdx.graphics.g2d.tiled.TiledObjectGroup; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle; public class JavaGame implements ApplicationListener, InputProcessor { Stage stage; float width; float height; private TiledMap map; private TileAtlas atlas; private TileMapRenderer tileMapRenderer; Image player; Vector3 camDirection = new Vector3(1, 1, 0); Vector2 maxCamPosition = new Vector2(0, 0); Vector3 moveVector = new Vector3(0, 0, 0); boolean isPress; TiledLayer nopassLayer; // Image image; @Override public void create() { final String path = "map/"; final String mapname = "tilemap"; FileHandle mapHandle = Gdx.files.internal(path + mapname + ".tmx"); map = TiledLoader.createMap(mapHandle); for (int i = 0; i < map.layers.size(); i++) { if ("NoPass".equals(map.layers.get(i).name)) { nopassLayer = map.layers.get(i); // map.layers.remove(i); break; } } atlas = new TileAtlas(map, new FileHandle("map/")); tileMapRenderer = new TileMapRenderer(map, atlas, 10, 10); maxCamPosition.set(tileMapRenderer.getMapWidthUnits(), tileMapRenderer .getMapHeightUnits()); width = Gdx.graphics.getWidth(); height = Gdx.graphics.getHeight(); stage = new Stage(width, height, true); Label label = new Label("FPS:", new LabelStyle(new BitmapFont(Gdx.files .internal("font/blue.fnt"), Gdx.files.internal("font/blue.png"), false), Color.WHITE), "fpsLabel"); label.y = height - label.getPrefHeight(); label.x = 0; stage.addActor(label); for (TiledObjectGroup group : map.objectGroups) { for (TiledObject object : group.objects) { if ("play1".equals(object.name)) { player = new Image(new TextureRegion(new Texture(Gdx.files .internal("map/player.png")), 0, 0, 27, 40)); player.x = object.x; player.y = tileMapRenderer.getMapHeightUnits() - object.y; // map是左上角,Stage是左下角 stage.addActor(player); } } } InputMultiplexer inputMultiplexer = new InputMultiplexer(); inputMultiplexer.addProcessor(this); inputMultiplexer.addProcessor(stage); Gdx.input.setInputProcessor(inputMultiplexer); } @Override public void dispose() { // TODO Auto-generated method stub } @Override public void pause() { // TODO Auto-generated method stub } @Override public void render() { Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); OrthographicCamera c = (OrthographicCamera) stage.getCamera(); if (isPress) { CameraMove(moveVector, player); } ((Label) stage.findActor("fpsLabel")).setText("FPS: " + Gdx.graphics.getFramesPerSecond()); stage.act(Gdx.graphics.getDeltaTime()); tileMapRenderer.render(c); stage.draw(); } private void CameraMove(Vector3 vector3, Actor mainActor) { boolean isCameraMove = true; boolean isActorMove = true; Vector2 pos = new Vector2(mainActor.x, mainActor.y); if (CheckMoveable(map, nopassLayer, vector3, pos)) { return; } if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add( mainActor.width, 0))) { return; } if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add( mainActor.width, mainActor.height))) { return; } if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add(0, mainActor.height))) { return; } Vector3 viewport = stage.getCamera().position.cpy(); viewport = viewport.add(vector3); Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport); if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) { isCameraMove = false; } Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport); if (fbound.x < 0 || fbound.y < 0) { isCameraMove = false; } Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0); stage.getCamera().project(v3); Vector3 a = v3.cpy().add(vector3); if (a.x > width || a.y > height) { isActorMove = false; } if (a.x < 0 || a.y < 0) { isActorMove = false; } if (isCameraMove) { stage.getCamera().position.add(vector3); for (Actor actor : stage.getActors()) { if (!actor.equals(player)) { actor.x += vector3.x; actor.y += vector3.y; } } } if (isActorMove) { player.x += vector3.x; player.y += vector3.y; } } private boolean CheckMoveable(TiledMap map, TiledLayer layer, Vector3 vector3, Vector2 playpos) { Vector3 pos = new Vector3(playpos.x, playpos.y, 0).add(vector3); int nopassId = 0; TileSet set = map.tileSets.get(map.tileSets.size() - 1); int masSize = set.firstgid + layer.tiles.length; for (int i = 0; i < masSize; i++) { if ("False".equals(map.getTileProperty(i, "Pass"))) { nopassId = i; Gdx.app.log("Find!", i + " "); break; } } int xid = MathUtils.ceilPositive(pos.x / map.tileWidth); int yid = MathUtils.ceilPositive(pos.y / map.tileWidth); if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) { return true; } else { return false; } } @Override public void resize(int width, int height) { // TODO Auto-generated method stub } @Override public void resume() { // TODO Auto-generated method stub } @Override public boolean keyDown(int keycode) { return false; } @Override public boolean keyTyped(char character) { // TODO Auto-generated method stub return false; } @Override public boolean keyUp(int keycode) { // TODO Auto-generated method stub return false; } @Override public boolean scrolled(int amount) { // TODO Auto-generated method stub return false; } private void ChangeDirect(int typeId) { switch (typeId) { case 1: moveVector.set(0, 1, 0); Gdx.app.log("方向变动", "向上"); break; case 2: moveVector.set(0, -1, 0); Gdx.app.log("方向变动", "向下"); break; case 3: moveVector.set(-1, 0, 0); Gdx.app.log("方向变动", "向左"); break; case 4: moveVector.set(1, 0, 0); Gdx.app.log("方向变动", "向右"); break; } } @Override public boolean touchDown(int x, int y, int pointer, int button) { Vector3 tmp = new Vector3(x, y, 0); stage.getCamera().unproject(tmp); float newx = tmp.x - player.x; float newy = tmp.y - player.y; if (newx > 0 && newy > 0) { if (newx > newy) { ChangeDirect(4); } else { ChangeDirect(1); } } else if (newx > 0 && newy < 0) { if (newx > -newy) { ChangeDirect(4); } else { ChangeDirect(2); } } else if (newx < 0 && newy > 0) { if (-newx > newy) { ChangeDirect(3); } else { ChangeDirect(1); } } else { if (-newx > -newy) { ChangeDirect(3); } else { ChangeDirect(2); } } isPress = true; return false; } @Override public boolean touchDragged(int x, int y, int pointer) { // TODO Auto-generated method stub return false; } @Override public boolean touchMoved(int x, int y) { // TODO Auto-generated method stub return false; } @Override public boolean touchUp(int x, int y, int pointer, int button) { isPress = false; Gdx.app.log("Info", "touchUp: x:" + x + " y: " + y + " pointer: " + pointer + " button: " + button); return false; } }最终效果:
写在最后:
1.调试好了就要在绘制前把障碍层删除。
2.注意各种坐标转化。
3.如果需要变化地图,直接操作Layer里面的那个二维数组。
本文用的检测方法只是一种可行方案而已,也可以直接看角色占的块数。
相关文章推荐
- android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理
- android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理
- android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理
- Android游戏引擎libgdx使用教程8:相机和观察点
- 【Android LibGDX游戏引擎开发教程】第06期:图形图像的绘制(下)图片整合工具的使用
- Android游戏引擎libgdx使用教程16:使用TexturePacker工具加快开发速度
- Android游戏引擎libgdx使用教程4:舞台和演员的游戏实例
- Android游戏引擎libgdx使用教程10:双舞台
- Android游戏引擎libgdx使用教程12:如何使用TiledMap地图
- 【Android LibGDX游戏引擎开发教程】第09期:动画类的使用
- Android游戏引擎libgdx使用教程1:搭建libgdx环境
- Android游戏引擎libgdx使用教程7:引擎框架总观
- Android游戏引擎libgdx使用教程3:绘制汉字与显示中文
- Android游戏引擎libgdx使用教程5:常用UI类与舞台
- Android游戏引擎libgdx使用教程11:如何使用Skin和UI配置文件
- Android游戏引擎libgdx使用教程15:TWL布局
- Android游戏引擎libgdx使用教程6:演员与演出
- Android游戏引擎libgdx使用教程9:libgdx中Box2d的用法
- Android游戏引擎libgdx使用教程2:如何绘制图形
- 【Android LibGDX游戏引擎开发教程】第05期:图形图像的绘制(上)基础类的使用和详解