转载自://03/23/tiledmap/
要做游戏地图,在libgdx中我们使用到的工具是TiledMap Editor,官方网址是:链接
Tiled 地图编辑器是一种用于通用目的的编辑器。特点是比较好用,风格类似mini版的photoshop。它可以用于制作多种类型的游戏引擎需要,而且支持使用插件读写map、增加用于引擎的map格式。
地图编辑 :
使用图层、图块以及对象层的方式进行地图的编辑。以“.tmx”格式保存。
————————————————————————————-
地图:
所谓的地图,特别是使用 Tiledmap 制作的地图,其实就是一张背景图片,
加上tiledmap地图编辑起来的一些图块所构成,这也是许多安卓和苹果应用所使用的技巧,
这样不仅可以节省游戏资源同时可以方便代码的实现。
————————————————————————————-
地图图块:
负责做一些碰撞检测等的而背景中的蓝天、白云这些其实就是一张JPG的图片,就是为了是游戏更加好看、美观。
————————————————————————————-
1.TiledMap软件的使用
TiledMap编辑器中,可以分为3个部分,第一个是图层、第二个是图块、第三个是对象层
(1) 图层介绍
游戏中一般要使用许多个图层,根据功能的不同,这里我将图层分为2种,
一种是作为碰撞检测的图层(即检测图层),另一种是作为美观装饰的图层(即装饰图层)。
检测图层:
一个作为碰撞检测的图层,就是负责游戏中不能穿过的图层。一般常作为地面、砖墙、障碍物等方面,游戏中必须使用到的。
装饰图层:
一个作为美观装饰的图层,就是负责装饰的,在地图中不作为碰撞检测,仅仅起到美观的作用,
可有可无,但是为了游戏体验,一般还是加入一个美观的图层,也可以被背景图片替代。
对象层:
一个作为创建对象而是用的图层,该层和其他2种不同,他是负责创建对象而是用的。
例如,超级玛丽中玛丽在地图的哪个点出生,金币在哪个点出生,怪物的设置点说的通俗点就是游戏中可以动的元素
————————————————————————————–
层的高低
对象层装饰层障碍层(全透明)背景层(地板层)
————————————————————————————-
地形
瓦片地图中有许多方式来实现瓦片与瓦片之间地形的过渡,
但Tiled里的地形工具支持对地形的四角完好定义的瓦片图素过渡,这也是最常见的方式。
这个图块有4个不同地形种类。传统的编辑地图方式意味着你不得不小心地连接正确的过渡,来避免把接缝边缘玩坏掉。
但是我们现在会从图块中定义地形信息,地形工具会自动识别和放置地形间正确的过渡。
方法:
1 在图块上点击小按钮“编辑地形信息”
2 出现对话框,并允许你标记属于各地形种类的瓦片衔接处的拐角。
我们现在开始。先添加4种地形种类。最快的方法是,在最准确代表某一地形的瓦片上右键点击,
选择“增加地形类型”。这将自动设置这瓦片作为此地形的图片。
3 起一个合适的名字。然后在图块上标记这个地形应当有的所有拐角。
4如果中途涂错了,可以撤销或用清除工具擦掉错误的拐角。如此对4种地形每个都进行定义。
最终你标记完所有每个特定地形的所有瓦片部件。
5 关闭对话框,可以开始试着使用地形工具啦。使用地形工具编辑
6 切换到地形窗口。你会见到4种地形。点击沙地开始画,你会立即发现并没有什么特别的事情发生。
这是因为此时还没有其他瓦片在地图上,所以地形工具不知道如何提供帮助(因为我们图块种没有与“空”地形的过渡)
既然这样,那么此时我们最好先把整个地图用沙地填满。先暂时切回“图块”窗口,选择沙地瓦片然后使用“填充”工具。
7 切回地形窗口,来画一些地形。现在你可以看到效果啦!
在绘图的时候按住Command键,会减小绘图区域(默认是九宫格,减小后绘制4格)
到最接近光标位置的一个角落,可用来进行细微调整工作。
8 你会发现,当在“尘土”地形里面画鹅卵石的时候,因为没有“尘土”地形与“鹅卵石”地形的直接过渡,地形工具在两者之间插入了沙地地形过渡。很棒。
9 写在最后
现在你应该对地形工具在你自己项目中的运用有了很好的想法。这里还有几件事需要铭记在心:
– 目前这个工具需要所有地形种类的各个瓦片在同一个图块里。你可以有许多带地形的图块,但该工具不能自动跨图块提供地形的过渡。这意味着通常你需要把几个图块合并到一张图里。
– 目前通过Tiled的“添加外部图块”加入的图块包含的地形信息是不可编辑的。如需编辑,可以先导入图块,编辑后再重新导出。
– 地形工具同样适用于等角地图(不能用于交错等角地图),但是,“编辑地形信息”对话框目前对其瓦片不能正确渲染覆盖指示。需要脑补:左上角会应用到上,右上角应用到右,依此类推。有疑问可参考example目录下的“isometric_grass_and_water.tmx”文件。
– 该工具会处理任意数量的地形种类,且每个瓦片拐角都可以有不同种类的地形。针对此工具不能处理的过渡,仍然还有其他方法来解决。还有,同时编辑多图层是不可行的。要寻求关于自动放置瓦片的更加灵活且却更复杂的方法,请查阅“Automapping”。
Tiled在上维护着一些瓦片地图图块,它们包含过渡且与此工具兼容。
以上内容英文原始资料来自《Using the Terrain tool》
————————————————————————————-
地图使用 :
————————————————————————————-
步骤简述:
1 读取地图2 设置地图渲染3 新建相机4 设置相机5 图层处理5.1 遍历图层5.2 遍历图层对象5.3 读取图层对象属性5.4 设置障碍标识6 设置舞台6.1 新建演员6.2 添加监听6.3 添加音乐7 渲染7.1 清屏7.2 舞台渲染 7.2.1 相机更新 7.2.2 地图渲染关联相机 7.2.3 地图渲染 7.2.4 舞台行动 7.2.5 舞台绘制 7.2.6 其他画板绘制7.3 舞台监听重置8销毁资源
地图渲染 OrthogonalTiledMapRenderer 类
OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map,1/20f);map 是 map = new TmxMapLoader().load("map/map1.tmx")
1/20f 是焦距
renderer.setView(camera)
绘制指定图层。传的是数组,例如绘制0,2图层:
int[] layers = {0,2};render(layers);render(OrthographicCamera cam)方法:渲染是传入相机。
相机 OrthographicCamera 类
OrthographicCamera camera = new OrthographicCamera();camera.setToOrtho(false, 40, 24);camera.update();renderer.setView(camera);renderer.render();//相机监听CameraInputController cameraController = new CameraInputController(camera);Gdx.input.setInputProcessor(cameraController);
例子代码:
package com.qsuron11.barriers;import com.badlogic.gdx.ApplicationListener;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.audio.Music;import com.badlogic.gdx.audio.Sound;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.maps.MapLayer;import com.badlogic.gdx.maps.MapLayers;import com.badlogic.gdx.maps.MapObject;import com.badlogic.gdx.maps.MapObjects;import com.badlogic.gdx.maps.objects.RectangleMapObject;import com.badlogic.gdx.maps.tiled.TiledMap;import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;import com.badlogic.gdx.maps.tiled.TmxMapLoader;import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;import com.badlogic.gdx.math.MathUtils;import com.badlogic.gdx.scenes.scene2d.InputEvent;import com.badlogic.gdx.scenes.scene2d.InputListener;import com.badlogic.gdx.scenes.scene2d.Stage;import com.badlogic.gdx.scenes.scene2d.ui.Image;import com.qsuron11.barriers.Person;public class MyDemo extends Gdx implements ApplicationListener {public static final int WIDTH = 800;//窗口宽度public static final int HEIGHT = 480;//窗口高度public static final int MAP_PIC = 20;//瓷砖大小public static final int MAP_WIDTH = 40;//瓷砖横向个数public static final int MAP_HEIGHT = 24;//瓷砖纵向个数private static boolean GameIsRunning;//主游戏界面是否运行private static boolean ShopIsRunning;//商店界面是否运行private static boolean SuccessIsRunning;//购买成功界面是否运行private static int[][] barriers;//障碍标识(24行40列)private TiledMap map;//地图private BitmapFont font;//标题字体private SpriteBatch batch;//画板private OrthographicCamera camera;//相机private OrthogonalTiledMapRenderer renderer;//图层渲染private float p_x , p_y;//人物坐标private Image image_openStage_background,//开始游戏背景image_openStage_btn_newGame,//新游戏按钮image_openStage_btn_shop,//商店按钮image_shopStage_background,//商店背景image_shopStage_btn_heart,//商店商品hreatimage_shopStage_btn_coin,//商店商品coinimage_shopStage_btn_close,//商店关闭按钮image_successStage_success;//购买成功private Stage stage,//游戏舞台openStage, //开始舞台shopStage, //商店舞台successStage; //购买成功舞台private Sound sound_openStage_btn_newGame; //新游戏按钮声音private Music music_openStage_background,//开始游戏音乐music_stage_background;//主游戏音乐static{barriers = new int[MAP_HEIGHT][MAP_WIDTH];GameIsRunning = false;ShopIsRunning = false;SuccessIsRunning = false;}@Overridepublic void create() {batch = new SpriteBatch();stage = new Stage(WIDTH, HEIGHT, false, batch);openStage = new Stage(WIDTH, HEIGHT, false, batch);shopStage = new Stage(WIDTH, HEIGHT, false, batch);successStage = new Stage(WIDTH, HEIGHT, false, batch);//地图map = new TmxMapLoader().load("map/map1.tmx");renderer = new OrthogonalTiledMapRenderer(map,1f/MAP_PIC);//相机camera = new OrthographicCamera();camera.setToOrtho(false, MAP_WIDTH, MAP_HEIGHT);//图层处理this.setLayer();//设置舞台this.setStage();//设置监听this.setListener();//设置音乐this.setSound();}//判断是否能够移动public static boolean passEnable(float x, float y){//+0.5使四舍五入int col = MathUtils.round(y/MAP_PIC);int row = MathUtils.round(x/MAP_PIC);boolean flag = barriers[col][row] == 0;System.out.println("往↑第" + col + "行,往→第" + row + "列 --> " + flag);return flag;}//判断是否能够下掉public static boolean downEnable(float x, float y) {//+0.5使四舍五入boolean flag = barriers[(int) ((y/MAP_PIC-0.1f))][(int) (x/MAP_PIC+0.5f)] == 0;//System.out.println("往↑第" + ((y/MAP_PIC-0.1f)) + "行,往→第" + (x/MAP_PIC+0.5f) + "列 --> " + flag);return flag;}//图层处理private void setLayer(){MapLayers layers = map.getLayers();for(int i=0;i<layers.getCount();i++){System.out.println("第" + i + "层:" + layers.get(i).getName());}for(MapLayer layer : layers){//遍历图层if(layer.getName().equals("object")){//找到对象层MapObjects objs = layer.getObjects();for(MapObject obj : objs){//遍历图层内容if(obj.getName().equals("person")){//找到人物图块RectangleMapObject rmobj = (RectangleMapObject) obj;p_x = rmobj.getRectangle().x;p_y = rmobj.getRectangle().y;break;}}}if(layer.getName().equals("barriers")){//找到障碍层if(layer instanceof TiledMapTileLayer){//类型转换判断TiledMapTileLayer tlayer = (TiledMapTileLayer) layer;for(int x=0;x<MAP_HEIGHT;x++){for(int y=0;y<MAP_WIDTH;y++){if(tlayer.getCell(y, x) != null){barriers[x][y] = 1;}}}}}}for(int x=0;x<MAP_HEIGHT;x++){for(int y=0;y<MAP_WIDTH;y++){System.out.print(barriers[x][y]+" ");}System.out.println("");}}//设置舞台private void setStage(){//开始游戏舞台Texture texture_open = new Texture(Gdx.files.internal("data/open.png"));Texture texture_button = new Texture(Gdx.files.internal("data/btn_newgame.png"));Texture texture_button2 = new Texture(Gdx.files.internal("data/btn_shop.png"));image_openStage_background = new Image(new TextureRegion(texture_open,798,480));image_openStage_background.setPosition(0,0);image_openStage_btn_newGame = new Image(texture_button);image_openStage_btn_newGame.setPosition(80,380);image_openStage_btn_shop = new Image(texture_button2);image_openStage_btn_shop.setPosition(80,80);openStage.addActor(image_openStage_background);openStage.addActor(image_openStage_btn_newGame);openStage.addActor(image_openStage_btn_shop);//商店舞台Texture texture_shop = new Texture(Gdx.files.internal("data/shop.png"));image_shopStage_background = new Image(new TextureRegion(texture_shop,0,85,512,350));image_shopStage_btn_heart = new Image(new TextureRegion(texture_shop,0,0,102,85));image_shopStage_btn_coin = new Image(new TextureRegion(texture_shop,102,0,102,85));image_shopStage_btn_close = new Image(new TextureRegion(texture_shop,300,10,50,40));image_shopStage_background.setPosition(0, 0);image_shopStage_background.setSize(480,320);image_shopStage_btn_heart.setPosition(190,50);image_shopStage_btn_coin.setPosition(50,50);image_shopStage_btn_close.setPosition(400,275);shopStage.addActor(image_shopStage_background);shopStage.addActor(image_shopStage_btn_heart);shopStage.addActor(image_shopStage_btn_coin);shopStage.addActor(image_shopStage_btn_close);//购买舞台image_successStage_success = new Image(new TextureRegion(texture_shop,512,0,255,255));image_successStage_success.setPosition(100,100);successStage.addActor(image_successStage_success);//游戏舞台//背景//image_stage_background = new Image(new Texture(Gdx.files.internal("data/background.jpg")));//标题font = new BitmapFont(Gdx.files.internal("font/title.fnt"),Gdx.files.internal("font/title.png"),false);font.setColor(Color.BLACK);Gdx.input.setInputProcessor(stage);Person p = new Person(p_x,p_y);stage.addActor(p);stage.addActor(p.btn_L);stage.addActor(p.btn_R);}//设置声音private void setSound() {sound_openStage_btn_newGame = Gdx.audio.newSound(Gdx.files.internal("music/openStage_btn_newGame.ogg"));music_openStage_background = Gdx.audio.newMusic(Gdx.files.internal("music/stage.ogg"));music_stage_background = Gdx.audio.newMusic(Gdx.files.internal("music/openStage.ogg"));music_openStage_background.setLooping(true);music_stage_background.setLooping(true);//播放主页面音乐music_openStage_background.play();}//设置监听private void setListener() {image_openStage_btn_newGame.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {GameIsRunning = true;music_openStage_background.stop();music_stage_background.play();return true;}});image_openStage_btn_shop.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {ShopIsRunning = true;return true;}});image_shopStage_btn_heart.addListener(new InputListener() {@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {SuccessIsRunning = true;sound_openStage_btn_newGame.play(1);return true;}});image_shopStage_btn_coin.addListener(new InputListener() {@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {SuccessIsRunning = true;sound_openStage_btn_newGame.play();return true;}});image_shopStage_btn_close.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {ShopIsRunning = false;return true;}});image_successStage_success.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {SuccessIsRunning = false;return true;}});}//重置舞台监听private void updateStage(){if(GameIsRunning){Gdx.input.setInputProcessor(stage);}else{Gdx.input.setInputProcessor(openStage);if(ShopIsRunning){Gdx.input.setInputProcessor(shopStage);if(SuccessIsRunning){Gdx.input.setInputProcessor(successStage);}}}}//重置舞台绘制private void stageRender(){if(GameIsRunning){camera.update();renderer.setView(camera);renderer.render();stage.act();stage.draw();batch.begin();font.draw(batch,"小树LibGDX游戏引擎开发", 240, 450);//普通绘制batch.end();}else{openStage.act();openStage.draw();if(ShopIsRunning){shopStage.act();shopStage.draw();if(SuccessIsRunning){successStage.act();successStage.draw();}}}} @Overridepublic void render() {Gdx.gl.glClearColor(0,0,0, 0);Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);this.stageRender();this.updateStage();}@Overridepublic void dispose() {batch.dispose();stage.dispose();openStage.dispose();shopStage.dispose();successStage.dispose();map.dispose();}@Overridepublic void resize(int width, int height) {}@Overridepublic void pause() {}@Overridepublic void resume() {}}
第二个类
package com.qsuron11.barriers;import com.badlogic.gdx.Gdx;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.TextureRegion;import com.badlogic.gdx.scenes.scene2d.Actor;import com.badlogic.gdx.scenes.scene2d.InputEvent;import com.badlogic.gdx.scenes.scene2d.InputListener;import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;public class Person extends Actor {public static final int MAP_PIC = MyDemo.MAP_PIC;public float x;public float y;public float statetime;//按钮ImageButton btn_L;ImageButton btn_R;//人物当前状态(枚举类)STATE state;//对应的动画Animation animation_left;Animation animation_right;Animation animation_idle;//实时动画TextureRegion currentFrame;public Person(float x, float y) {super();this.x = x;this.y = y;this.statetime = 0;this.show();state = STATE.I;}public void show() {//分割动画Texture animation_file = new Texture(Gdx.files.internal("data/animation2-2.png"));TextureRegion[][] animation_split_right = TextureRegion.split(animation_file,42,51);TextureRegion[][] animation_split_left = TextureRegion.split(animation_file,42,51);Texture animation_file2 = new Texture(Gdx.files.internal("data/idle.png"));TextureRegion[][] animation_split_idle = TextureRegion.split(animation_file2,42,51);//实例化动画图片组TextureRegion[] textureRegion_right = new TextureRegion[30];TextureRegion[] textureRegion_left = new TextureRegion[30];TextureRegion[] textureRegion_idle = new TextureRegion[1];//向右动画图片组int i = 0;for(TextureRegion[] animation_split_right_split:animation_split_right){for(TextureRegion animation_pic:animation_split_right_split){textureRegion_right[i] = animation_pic;i++;}}//向左动画图片组i = 0;for(TextureRegion[] animation_split_left_split:animation_split_left){for(TextureRegion animation_pic:animation_split_left_split){textureRegion_left[i] = animation_pic;textureRegion_left[i].flip(true,false);i++;}}//原地动画图片组textureRegion_idle[0] = animation_split_idle[0][0];//合成动画animation_right = new Animation(0.05f,textureRegion_right);animation_right.setPlayMode(Animation.LOOP);animation_left = new Animation(0.05f,textureRegion_left);animation_left.setPlayMode(Animation.LOOP);animation_idle = new Animation(1f,textureRegion_idle);animation_idle.setPlayMode(Animation.LOOP);//按钮图片读取Texture button_file = new Texture(Gdx.files.internal("data/button.png"));TextureRegion[][] button_split = TextureRegion.split(button_file,100,100);//按钮实例化btn_R = new ImageButton(new TextureRegionDrawable(button_split[0][0]),new TextureRegionDrawable(button_split[0][1]));btn_L = new ImageButton(new TextureRegionDrawable(button_split[0][2]),new TextureRegionDrawable(button_split[0][3]));//按钮位置btn_R.setPosition(150,20);btn_L.setPosition(20,20);//按钮监听btn_R.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {state = STATE.R;return true;}@Overridepublic void touchUp(InputEvent event, float x, float y,int pointer, int button) {state = STATE.I;super.touchUp(event, x, y, pointer, button);}});btn_L.addListener(new InputListener(){@Overridepublic boolean touchDown(InputEvent event, float x, float y,int pointer, int button) {state = STATE.L;return true;}@Overridepublic void touchUp(InputEvent event, float x, float y,int pointer, int button) {state = STATE.I;super.touchUp(event, x, y, pointer, button);}});}//状态控制与动画选择public void check() {if(this.state==STATE.L){currentFrame = animation_left.getKeyFrame(statetime,true);}else if(this.state==STATE.R){currentFrame = animation_right.getKeyFrame(statetime,true);}else if(this.state==STATE.I){currentFrame = animation_idle.getKeyFrame(statetime,true);}}//人物移动public void update(){//验证间隔一个瓷砖(x,y是左端点,因此偏移多半块瓷砖)if(state==STATE.L && MyDemo.passEnable(this.x - MAP_PIC*0.5f, this.y)){this.x -= 2f;}else if(state==STATE.R && MyDemo.passEnable(this.x + MAP_PIC*1.5f, this.y)){this.x += 2f;}if(MyDemo.downEnable(this.x, this.y)){this.y -= 2f;}}@Overridepublic void act(float delta) {statetime += delta;this.update();this.check();super.act(delta);}@Overridepublic void draw(SpriteBatch batch, float parentAlpha) {batch.draw(currentFrame, x, y);}enum STATE {L,R,I;};}