分类: libGDX

  • android游戏开发框架libgdx的使用(十九)—使用自定义配置改进AVG游戏开发

    android游戏开发框架libgdx的使用(十九)—使用自定义配置改进AVG游戏开发

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考。

    先说明一下上一篇文章我使用了多张hiero图的字体绘制,因为我对源码进行了一些修改,本来想这次发出来的,但是我仔细调试了一下,发现对于多图的支持还是有问题,有些字会出现偏移。

    这个只有继续尝试了…大家可以考虑使用ttf字库。

    然后继续说上一篇,虽然实现了一个简单的效果,但是目前有很多不足。我把AVG游戏需要的资源全部提取出来,放在一个个文件夹中,然后通过配置文件加载这些数据。

    libgdx的工具库

    com.badlogic.gdx.utils就是libgdx的工具库,支持两种格式xml和json。

    我最先倾向于使用json格式,但是反复想了想,虽然json的大小可能要小点,但是没有xml直观好读。

    所以还是选择使用xml格式,读取xml文件使用XmlReader。

    XmlReader reader =new XmlReader();
    try {
    Element config = reader.parse(Gdx.files.internal("data/config.xml"));
    config.get("name");//获取属性值,如果没有属性值则返回同名child的值
    config.getAttribute("name");//获取属性值
    }catch (Exception e) {
    e.printStackTrace();
    }

    配置文件格式分析

    配置文件需要配置的内容其实就是上一篇我们硬编码进去的东西,有背景、边框、对话等。

    我设计的比较随意,不一定是最好的,大家可以参考参考。

    <?xml version="1.0" encoding="UTF-8"?>
    <scene>
    <packfile>pack</packfile>
    <background>bg1</background>
    <border>
    <border-name>border</border-name>
    <border-left>26</border-left>
    <border-right>26</border-right>
    <border-top>31</border-top>
    <border-bottom>31</border-bottom>
    </border>
    <dialogues>
    <dialogue>
    <name>人物1</name>
    <data>这是对话1</data>
    </dialogue>
    <dialogue>
    <name>人物1</name>
    <data>这是对话2</data>
    </dialogue>
    </dialogues>
    </scene>

    如果对话比较长还可以继续添加<dialogue>节点。border-*那4个值就是NinePatch的左边距、右边距、上边距、下边距。

    这里还有一个地方需要注意,如果我们需要切换场景怎么办?切换的场景可能是一个AVG场景,也可能是个一般的Screen。我本来想做个简单的IOC容器的,但后来想想也没有那么复杂。

    在配置文件中加上一句

    如果是一般Screen就是

    <nextscreentype="standard">com.cnblogs.htynkn.screen.GameOver</nextscreen>

    如果是AVG场景就是

    <nextscreentype="avg">data/scene2/config.xml</nextscreen>

    使用libgdx实现相关效果

    其实整个原理和上一篇原理是一样的,唯一多的一个就是解析配置文件和场景跳转。

    XmlReader xmlReader =new XmlReader();
    try {
    Element config = xmlReader.parse(Gdx.files.internal(this.configFileName));//加载配置文件
    atlas =new TextureAtlas(Gdx.files.internal(configFileName).parent() +"/" + config.get("packfile"));//获取pack配置文件
    background = atlas.findRegion(config.get("background"));//创建背景图
    Element borderConfig = config.getChildByName("border");//获取border配置
    border =new NinePatch(atlas.findRegion(borderConfig.getChild(0).getText()), Integer.parseInt(borderConfig.getChild(1).getText()), Integer.parseInt(borderConfig.getChild(2).getText()), Integer.parseInt(borderConfig.getChild(3).getText()), Integer.parseInt(borderConfig.getChild(4).getText()));//实例化border
    dialogues =new ArrayList<string[]>();
    Element dialoguesConfig = config.getChildByName("dialogues");//开始处理对话
    for (int i =0; i < dialoguesConfig.getChildCount(); i++) {
    dialogues.add(new String[]{
    dialoguesConfig.getChild(i).get("name"),
    dialoguesConfig.getChild(i).get("data")});
    }
    Element screenConfig = config.getChildByName("nextscreen");//处理场景
    if (screenConfig.getAttribute("type").equals("avg")) {
    nextScreen =new AVGScreen(this.game, screenConfig.getText());
    }else {
    try {
    nextScreen = (Screen) Class.forName(screenConfig.getText()).newInstance();
    }catch (Exception e) {
    e.printStackTrace();
    }
    }
    }catch (IOException e) {
    e.printStackTrace();
    }

    读取完成后构建舞台,然后绘制。唯一需要修改的就是场景跳转。

    borderImage.setClickListener(new ClickListener() {
    @Override
    publicvoidclick(Actor actor,float x,float y) {
    if (currentSize < dialogues.size() -1) {
    currentSize++;
    }else {
    if (nextScreen !=null) {
    game.setScreen(nextScreen);
    }
    }
    }
    });

    完整代码:

    import com.badlogic.gdx.Screen;
    import com.badlogic.gdx.scenes.scene2d.Stage;
    
    publicclass AVGScreen implements Screen {
    rivate NinePatch
    private String configFileName;
    private Stage stage;// 舞台
    private List<string[]> dialogues;// 对话
    private BitmapFont bitmapFont;// 文字 p border; // 边框
    privateint currentSize;// 当前对话序号
    private TextureAtlas atlas;
    private TextureRegion background;
    private Screen nextScreen;
    private Game game;
    
    publicAVGScreen(Game game, String configFileName) {
    this.game = game;
    bitmapFont =new BitmapFont(Gdx.files.internal("font/chinese.fnt"),false);
    this.configFileName = configFileName;
    XmlReader xmlReader =new XmlReader();
    try {
    Element config = xmlReader.parse(Gdx.files.internal(this.configFileName));
    atlas =new TextureAtlas(Gdx.files.internal(configFileName).parent() +"/" + config.get("packfile"));
    background = atlas.findRegion(config.get("background"));
    Element borderConfig = config.getChildByName("border");
    border =new NinePatch(atlas.findRegion(borderConfig.getChild(0).getText()), Integer.parseInt(borderConfig.getChild(1).getText()), Integer.parseInt(borderConfig.getChild(2).getText()), Integer.parseInt(borderConfig.getChild(3).getText()), Integer.parseInt(borderConfig.getChild(4).getText()));
    dialogues =new ArrayList<string[]>();
    Element dialoguesConfig = config.getChildByName("dialogues");
    for (int i =0; i < dialoguesConfig.getChildCount(); i++) {
    dialogues.add(new String[]{dialoguesConfig.getChild(i).get("name"), dialoguesConfig.getChild(i).get("data")});
    }
    Element screenConfig = config.getChildByName("nextscreen");
    if (screenConfig.getAttribute("type").equals("avg")) {
    nextScreen =new AVGScreen(this.game, screenConfig.getText());
    }else {
    try {
    nextScreen = (Screen) Class.forName(screenConfig.getText()).newInstance();
    }catch (Exception e) {
    e.printStackTrace();
    }
    }
    }catch (IOException e) {
    e.printStackTrace();
    }
    }
    
    @Override
    publicvoiddispose() {
    bitmapFont.dispose();
    stage.dispose();
    }
    
    @Override
    publicvoidhide() {
    // TODO Auto-generated method stub
    }
    
    @Override
    publicvoidpause() {
    // TODO Auto-generated method stub
    }
    
    @Override
    publicvoidrender(float delta) {
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    ((Label) stage.findActor("label")).setText(dialogues.get(currentSize)[0] +" : " + dialogues.get(currentSize)[1]);
    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
    }
    
    @Override
    publicvoidresize(int width,int height) {
    // TODO Auto-generated method stub
    }
    
    @Override
    publicvoidresume() {
    // TODO Auto-generated method stub
    }
    
    @Override
    publicvoidshow() {
    currentSize =0;
    stage =new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),false); Image backgroundImage =new Image(background); backgroundImage.x = backgroundImage.y =0; backgroundImage.setFillParent(true); Image borderImage =new Image(border); borderImage.x = borderImage.y =0; borderImage.width = Gdx.graphics.getWidth(); borderImage.height = Gdx.graphics.getHeight() /4; borderImage.setClickListener(new ClickListener() {@Overridepublicvoidclick(Actor actor,float x,float y) {if (currentSize < dialogues.size() -1) { currentSize++; }else {if (nextScreen !=null) { game.setScreen(nextScreen); } } } }); LabelStyle labelStyle =new LabelStyle(bitmapFont, Color.WHITE); Label label =new Label("", labelStyle,"label"); label.x = border.getLeftWidth() +10; label.y = borderImage.height - border.getTopHeight() -10; stage.addActor(backgroundImage); stage.addActor(borderImage); stage.addActor(label);
    Gdx.input.setInputProcessor(stage);
    }
    }

    最后贴上效果图,这是我正在做的一个东西:

    demo1

    demo2

  • android游戏开发框架libgdx的使用(十八)—简单的AVG游戏效果实现

    android游戏开发框架libgdx的使用(十八)—简单的AVG游戏效果实现

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考。

    好久没有写libgdx的东西了,主要是最近迷上了各种算法…

    文章是关于实现简单的AVG游戏效果,可能会有好几篇。

    想用libgdx做点AVG效果主要是因为目前Android上运行的AVG游戏(主要是恋爱模拟类型)的基本基于NScripter引擎移植出来的,我的2B中兴机子跑着巨卡,所以想自己做个。

     

    何为AVG游戏

    冒险类游戏AVG (Adventure Game)通常是玩家控制角色进行虚拟冒险的游戏,其故事情节往往是以完成某个任务或是解开一个谜题的形式出现的。

    我这里说的AVG主要是指日式AVG,就是在最初的文字冒险游戏的基础上利用精美的CG图片和动人的音响效果加以强化,靠优秀的文字和剧情打动人心的一种游戏形式。

    比如夜明前的琉璃色

    20100317110151412

     

    简单分析

    AVG游戏主要是由对话,CG图和音效组成。

    拆分下来其实很简单,如下图

    thinking

    再看一张:

    thinking2

    就不举其他例子了,每个场景基本由背景(场景图),对话,人物组成。要是以前玩过的话就很清楚了,背景切换比较少,人物的变化也比较少,文字是点击或者触摸就切换到下一句去。

    我是选用的libgdx的Stage,背景和人物做成Image,对话由Label实现,对话边框由NinePatch实现,然后触摸对话框区域就切换到下一句去。

     

    文字处理

    AVG游戏的对话占据了很大部分,中文的处理很重要。

    libgdx支持中文有两种方法,一是使用Hiero制作,二是使用ttf字库(好像目前还有点问题,暂时不推荐使用)。

    最全的汉字字库有文字9万余个,但是常用的字并不多(相对于所有文字)。

    对比中国大陆、中国台湾和香港特区各自的常用字标准,可以得到3500个常用字,全部做成libgdx用的字体文件。

    总共生成12张图,共1.90M

    chinese

    还是有点大就是了。我比较偏向于先使用这个常用字开发,完成后在重新根据实际使用制作字库。

     

    libgdx实现

    我选用的是Stage,游戏由Game和Screen控制。新建一个类AVGScreen,实现Screen接口。

    private Stage stage;//舞台
    private TextureRegion background;//背景
    private List<string[]> dialogues;//对话
    private BitmapFont bitmapFont;//文字
    private NinePatch border;//边框
    privateint currentSize;//当前对话序号

    在show方法中,先实例化Stage,然后添加背景,添加边框,在render方法中修改文字。

    chinese

    背景是铺满就行了,对话的边框宽度是100%,高度是25%左右。

    文字绘制是在边框内部。边框我选用的是

    border

    具体的拆分如下:

    border

    label定位的代码:

    label.x = border.getLeftWidth() +10;
    label.y = borderImage.height - border.getTopHeight() -10;

    至于触摸切换到下一句在边框添加一个ClickListener就行了。

    borderImage.setClickListener(new ClickListener() {
    @Override
    publicvoidclick(Actor actor,float x,float y) {
    System.out.println("click");
    if (currentSize < dialogues.size() -1) {
    currentSize++;
    }
    }
    });

    而List<String[]> dialogues中的数据就是对话数据,由【人物名称】和【对话】组成。

    如:

    List<string[]> list =new ArrayList<string[]>();
    for (int i =0; i <10; i++) {
    list.add(new String[] {"人物","这是对话" + i });
    }

    AVGScreen完整代码:

    package com.cnblogs.htynkn.ui;import java.util.List;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.Screen;import com.badlogic.gdx.graphics.Color;import com.badlogic.gdx.graphics.GL10;import com.badlogic.gdx.graphics.g2d.BitmapFont;import com.badlogic.gdx.graphics.g2d.NinePatch;import com.badlogic.gdx.graphics.g2d.TextureRegion;import com.badlogic.gdx.scenes.scene2d.Actor;import com.badlogic.gdx.scenes.scene2d.Stage;import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;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;publicclass AVGScreen implements Screen {private Stage stage;//舞台 private TextureRegion background; //背景 private List<string[]> dialogues; //对话 private BitmapFont bitmapFont; //文字 private NinePatch border; //边框 private int currentSize; //当前对话序号 public AVGScreen(TextureRegion background, NinePatch border, List<string[]> dialogues) { this.background = background; this.dialogues = dialogues; this.border = border; bitmapFont = new BitmapFont(); } public AVGScreen(TextureRegion background, NinePatch border, List<string[]> dialogues,BitmapFont bitmapFont) { this.background = background; this.dialogues = dialogues; this.border = border; this.bitmapFont = bitmapFont; } @Override public void dispose() { // TODO Auto-generated method stub } @Override public void hide() { // TODO Auto-generated method stub } @Override public void pause() { // TODO Auto-generated method stub } @Override public void render(float delta) { Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); ((Label) stage.findActor("label")) .setText(dialogues.get(currentSize)[0] + " : " + dialogues.get(currentSize)[1]); stage.act(Gdx.graphics.getDeltaTime()); stage.draw(); } @Override public void resize(int width, int height) { // TODO Auto-generated method stub } @Override public void resume() { // TODO Auto-generated method stub } @Override public void show() { currentSize = 0; stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false); Image backgroundImage = new Image(background); backgroundImage.x = backgroundImage.y = 0; backgroundImage.setFillParent(true); Image borderImage = new Image(border); borderImage.x = borderImage.y = 0; borderImage.width = Gdx.graphics.getWidth(); borderImage.height = Gdx.graphics.getHeight() / 4; borderImage.setClickListener(new ClickListener() { @Override public void click(Actor actor, float x, float y) { System.out.println("click"); if (currentSize < dialogues.size() - 1) { currentSize++; } } }); LabelStyle labelStyle = new LabelStyle(bitmapFont, Color.BLACK); Label label = new Label("", labelStyle, "label"); label.x = border.getLeftWidth() + 10; label.y = borderImage.height - border.getTopHeight() - 10; stage.addActor(backgroundImage); stage.addActor(borderImage); stage.addActor(label); Gdx.input.setInputProcessor(stage); } }

    演示效果:

    demo

    可以看出基本效果是实现了,但是还有一些问题,比如人物的添加还有对话框的美观,而且这段代码是在很烂,不易扩展,没有权责分离。

    下一篇文章会介绍人物的添加还有使用配置文件保存对话等数据。

    最后来一个黎明前的琉璃色的效果:

    demo2

  • android游戏开发框架libgdx的使用(十七)—TiledMap中角色的行动路径

    android游戏开发框架libgdx的使用(十七)—TiledMap中角色的行动路径

    分享了一些素材,有兴趣的朋友可以看看:http://www.cnblogs.com/htynkn/archive/2012/01/19/game_resource.html

    前些日子的文章介绍了tiledmap的主角出现和移动等等问题。相对于主角游戏自然还应该有敌人(?)。

    与主角不同的是,这些元素的移动时程序控制的,一般有3种。

    1.随主角的移动变化,靠近主角或远离主角

    2.按照固定路线移动

    3.不动

    第一种的话完全是看你的游戏逻辑决定,和tiledmap关系不大。第二种的话我们可以避免硬编码(把移动路径写入程序代码中),而采用tiledmap实现,下面我们来看看具体过程。

    还是新建一张地图,我选用的大小是5030,块大小:3232。

    towntown1

    然后绘制地图:

    adancedmap

    我们假定敌人从地图中间的那条路走到左边的角上。路径如下:

    adancedmap

    现在新建一个对象层,命名为wayPoints。在几个关键的地方标注上对象,命名为wayPoint1,wayPoint2…

    adancedmap

    处理好地图后拷贝到项目中。

    advancelibgdx

    现在新建一个Enemy类,继承Image。

    现在来整理一下思路,首先我们要得到所有的wayPoint.而第一个wayPoint就是角色的初始化点。那么Enemy类首先需要一个Vector2列表,然后继承Image需要一个TextureRegion。

    所以构造函数为

    publicEnemy(List<vector2> vector2s, TextureRegion region) {super(region);this.vector2s = vector2s; currentIndex =0;this.x = vector2s.get(currentIndex).x;this.y = vector2s.get(currentIndex).y; }

    初始点有了,如何移动呢?我们先来看一下坐标

    advancelibgdx2

    我们现在在点1位置,将要移动到点2位置。只需计算x,y,z长度,然后求出对应的moveX和moveY就可以了。

    float x = Math.abs(v1.x - v2.x);float y = Math.abs(v1.y - v2.y);float z = (float) MathUtil.distanceBetweenTwoPoints(v1, v2);float moveX =0f;float moveY =0f; moveX = (x / z) * stepLength; moveY = (y / z) * stepLength;if (this.x < v2.x) {this.x += moveX; }else {this.x -= moveX; }if (this.y < v2.y) {this.y += moveY; }else {this.y -= moveY; }

    distanceBetweenTwoPoints是我自己写的方法,计算两点距离。

    现在我们的Enemy类就可以很正常的移动到下一个点了。

    但当它接近下一个点的时候可以发现它在不停的颤抖。这是因为我们没有处理当Enemy到达下一个点时对点序列的更新。

    当它和下一个点的距离很小时我们认定它到达下一个点,更新序列以保证它继续向下一个点移动。

    int nextIndex = currentIndex +1 >= vector2s.size() -1 ? vector2s .size() -1 : currentIndex +1; Vector2 v1 = vector2s.get(currentIndex); Vector2 v2 = vector2s.get(nextIndex);if (MathUtil.distanceBetweenTwoPoints(new Vector2(this.x,this.y), v2) <1) { currentIndex = currentIndex +1 < vector2s.size() -1 ? currentIndex +1 : vector2s.size() -1; nextIndex = currentIndex +1 >= vector2s.size() -1 ? vector2s .size() -1 : currentIndex +1; v1 = vector2s.get(currentIndex); v2 = vector2s.get(nextIndex); }

    基本没有问题了,我们看一下效果:

    advancelibgdx3

    因为手机不好截图,所以用的java桌面项目。

    Enemy用的图片是这张

    Enemy

    用TextureRegion[][] regions = TextureRegion.split(texture, 25, 33);切分,去2行3列。

    完整代码:

    package com.cnblogs.htynkn.game;import java.util.ArrayList;import java.util.List;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.TextureAtlas;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.MathUtil;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;import com.cnblogs.htynkn.actors.Enemy;publicclass MapDemo implements ApplicationListener, InputProcessor { Stage stage;float width;float height;private TiledMap map;private TileAtlas atlas;private TileMapRenderer tileMapRenderer; Vector3 camDirection =new Vector3(1,1,0); Vector2 maxCamPosition =new Vector2(0,0); Vector3 moveVector =new Vector3(0,0,0); Enemy enemy;int i =0;@Overridepublicvoidcreate() {final String path ="map/";final String mapname ="adancedmap"; FileHandle mapHandle = Gdx.files.internal(path + mapname +".tmx"); map = TiledLoader.createMap(mapHandle); 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); List<vector2> list =new ArrayList<vector2>();//获取所有wayPoints for (TiledObjectGroup group : map.objectGroups) { for (TiledObject object : group.objects) { if (object.name.startsWith("wayPoint")) { System.out.println(object.name + " X:" + object.x + " Y:" + object.y); list .add(new Vector2(object.x, maxCamPosition.y - object.y)); } } } TextureAtlas region = new TextureAtlas(Gdx.files.internal("imgs/pack")); Texture texture = region.findRegion("Enemy").getTexture(); TextureRegion[][] regions = TextureRegion.split(texture, 25, 33); enemy = new Enemy(list, regions[1][2]); stage.addActor(enemy); 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(); c.position.set(enemy.x, enemy.y, 0); stage.act(Gdx.graphics.getDeltaTime()); tileMapRenderer.render(c); stage.draw(); } @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; } @Override public boolean touchDown(int x, int y, int pointer, int button) { 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) { Gdx.app.log("Info", "touchUp: x:" + x + " y: " + y + " pointer: " + pointer + " button: " + button); return false; } }

    分割线=====================================分割线

    package com.cnblogs.htynkn.actors;import java.util.ArrayList;import java.util.List;import com.badlogic.gdx.graphics.g2d.SpriteBatch;import com.badlogic.gdx.graphics.g2d.TextureRegion;import com.badlogic.gdx.math.MathUtil;import com.badlogic.gdx.math.Vector2;import com.badlogic.gdx.scenes.scene2d.Actor;import com.badlogic.gdx.scenes.scene2d.ui.Image;publicclass Enemy extends Image { List<vector2> vector2s =new ArrayList<vector2>();int currentIndex;float stepLength =1f;publicEnemy(List<vector2> vector2s, TextureRegion region) {super(region);this.vector2s = vector2s; currentIndex =0;this.x = vector2s.get(currentIndex).x;this.y = vector2s.get(currentIndex).y; }@Overridepublicvoiddraw(SpriteBatch batch,float parentAlpha) {super.draw(batch, parentAlpha); }@Overridepublic Actorhit(float x,float y) {returnnull; }@Overridepublicvoidact(float delta) {int nextIndex = currentIndex +1 >= vector2s.size() -1 ? vector2s .size() -1 : currentIndex +1; Vector2 v1 = vector2s.get(currentIndex); Vector2 v2 = vector2s.get(nextIndex);if (MathUtil.distanceBetweenTwoPoints(new Vector2(this.x,this.y), v2) <1) { currentIndex = currentIndex +1 < vector2s.size() -1 ? currentIndex +1 : vector2s.size() -1; nextIndex = currentIndex +1 >= vector2s.size() -1 ? vector2s .size() -1 : currentIndex +1; v1 = vector2s.get(currentIndex); v2 = vector2s.get(nextIndex); }float x = Math.abs(v1.x - v2.x);float y = Math.abs(v1.y - v2.y);float z = (float) MathUtil.distanceBetweenTwoPoints(v1, v2);float moveX =0f;float moveY =0f; moveX = (x / z) * stepLength; moveY = (y / z) * stepLength;if (this.x < v2.x) {this.x += moveX; }else {this.x -= moveX; }if (this.y < v2.y) {this.y += moveY; }else {this.y -= moveY; } System.out.println("pos: " +this.x +"," +this.y +" v1:" + v1.toString() +" v2:" + v2.toString() +" d:" + z +" move:" + moveX +" , " + moveY);super.act(delta); } }

    文章中用到的地图文件和相关资源:http://www.ctdisk.com/file/4279808

  • 分享从网上收集的一些游戏资源,以RPG类为主

    分享从网上收集的一些游戏资源,以RPG类为主

    最近一直在研究libgdx引擎的使用,自然需要一些图片资源,一边学习一边从网上搬运各种用的上的资源,估摸可能有人用的上,特意分享在此。

    下载地址:点击此处

    收集的资源主要分5个大类,我每一类传几张预览图吧:

    1.tiles

    主要是tiledmap用的。

    TileA1

     

    TileC

    2.场景

    002-Woods01

     

    005-Beach01

    3.过渡

    000007-9S-Transition07

     

    000012-9S-Transition12

    4.角色

    这个的角色资源包含脸部特写,也包含人物走动的图,还有一张情绪图。

    chara01_a

     

    face_m02c

    5.游戏结束

    000016-9S-GameOver16

     

    000026-9S-GameOver26

    反正东西也不大,我就不分开打包了。大家要是觉得有用就下载吧。

    下载地址:

    诚通网盘:http://www.ctdisk.com/file/4279436

     

    这个网站也不错哈~素材丰富 http://www.66rpg.com/category.php?c=3

  • android游戏开发框架libgdx的使用(十六)—使用TexturePacker工具加快开发速度

    android游戏开发框架libgdx的使用(十六)—使用TexturePacker工具加快开发速度

    libgdx绝对是一个相当不错的游戏引擎,最近一段时间的学习我感觉收获很多。但是我一直对于libgdx加载的图片大小必须是2的次方感到很纠结。

    如果是一张一般的图片想在libgdx中使用的话就需要自己去用ps什么的把图像拉一下,一两张没关系,但是数量大了还是有点烦。

    而且拉大的图片还需要TextureRegion来切割一下,实在麻烦。仔细看了一下libgdx的tool包,发现早有解决方案了。

    具体的类是imagepacker,在com.badlogic.gdx.tools.imagepacker包之中。它可以将多张图片合并在一张之中。同时可以通过原有文件的文件名获得图片资源。

    这里有几张图片:

    111415xpic2766

    将它们放在一个文件夹中(这个文件夹没有其他东西了)。引用gdx-tool.jar。

    代码如下:

    Settings settings =new Settings(); settings.alias =true; TexturePacker.process(settings,"D:img","D:imgoutput");

    在output文件夹中可以找到两个文件,其中一个是合成好的图片

    img1

    另外一个名为pack,其中记录了源文件的位置,大小和名称。

    将图片和pack文件拷贝到项目目录。这里说明一下,pack文件的文件名可以随便改,合成的图片如果需要改名的话就需要改一下pack文件里面的内容了。

    使用时的具体类是TextureAtlas。

    代码:

    package com.cnblogs.htynkn.game;import com.badlogic.gdx.ApplicationListener;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.graphics.GL10;import com.badlogic.gdx.graphics.g2d.TextureAtlas;import com.badlogic.gdx.scenes.scene2d.Stage;import com.badlogic.gdx.scenes.scene2d.ui.Image;publicclass TexturePackerDemo implements ApplicationListener { Stage stage;@Overridepublicvoidcreate() { stage=new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),false); TextureAtlas atlas=new TextureAtlas(Gdx.files.internal("imgs/pack"));//根据pack文件获取所有图片 Image image1=new Image(atlas.findRegion("14")); //获取名为14的图片,并创建一个Image对象 image1.scaleX=image1.scaleY=0.2f; image1.x=image1.y=0; Image image2=new Image(atlas.findRegion("xpic2766")); image2.x=image2.y=40; image2.scaleX=image2.scaleY=0.5f; stage.addActor(image1); stage.addActor(image2); } @Override public void dispose() { stage.dispose(); } @Override public void pause() { // TODO Auto-generated method stub } @Override public void render() { Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); stage.act(Gdx.graphics.getDeltaTime()); stage.draw(); } @Override public void resize(int width, int height) { // TODO Auto-generated method stub } @Override public void resume() { // TODO Auto-generated method stub }}

    效果:

    texturepacker1

    写在最后:

    1.使用TexturePacker的最大好处就是方便,我觉得从根本让加载图片大小的问题对开发者透明了。

    2.使用TexturePacker提升了效率,比起一张一张图片加载,合并图无疑是个很好的选择。

    3.使用TexturePacker应该注意不要盲目合并,将在一个场景中使用的图片合并在一起,其他场景或暂时用不到的图片合并到其他中去。我个人认为一张图片的大小不超过1024*1024为佳。

    4.TexturePacker工具用着不怎么顺手。我觉得每次新建一个java项目,然后填写路径很麻烦。热心的网友做了GUI版本的。

    texturepacker2

    下载地址:http://www.ctdisk.com/file/4279795

    项目地址:http://code.google.com/p/libgdx-texturepacker-gui/

  • android游戏开发框架libgdx的使用(十五)—TWL布局

    android游戏开发框架libgdx的使用(十五)—TWL布局

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考。

    TWL是一个基于OpenGL的图形化用户界面库,它提供了一套非常丰富的窗口小部件,比如标签,编辑框,表格等等。不同的布局方式配合使用可以创建出非常高级的用户界面。

    TWL我原来接触过,但是觉得配置文件太难写了,今天逛论坛发现了官方出了Theme编辑器,马上来试试。

    官网:http://twl.l33tlabs.org/

    Editor下载地址:http://twl.l33tlabs.org/themer/themer.jnlp

    双击等下就可以运行了,界面如下。

    twl1

    先新建一个项目。

    twl2

    来看看默认的项目有什么。

    twl3

    第一项是字体,默认字体只支持英文。

    第二项是输入映射,定义了诸如全选,剪切等操作的快捷键。

    第三项是一个名为-defaults的主题样式,这个样式指明了字体使用第一项声明的那个,同时规定了文字靠左。

    第四项是一个名为label的主题样式。

    我们先让其支持中文。我们仍然可以使用hiero制作文字(注意格式上转化),然后将做好的文件拷贝到项目文件夹中。

    新建一个字体定义。

    twl4

    将新定义拖到最上面,改名为normal。

    twl5

    注意:保持修改要点右边的小勾。

    label其实就是为标签设计的样式。TWL Theme Editor提供了很方便的开发环境。

    twl6

    选择Label,选择Theme name为label,测试效果如下:

    twl7

    现在来新建一个Button的样式,先建一个Image,图片用下面那张:

    widgets

    新建Image节点

    twl8

    新建一个Aera节点,命名为buttonBackground。

    twl9

    将no center,spilt x,spilt y选上。

    twl10

    图像范围的选择可以直接用鼠标,这也是我最喜欢的地方…不用自己数像素了。

    twl11

    新建一个样式,名为button。字体选成normal,background选成buttonBackground,测试一下。

    twl13

    可以看出按钮有问题,新建一个button,将L和R都设成10.

    然后需要按钮有按下的效果怎么办?新建一个select节点,在其中添加两个alias节点。

    其中if(pressed)的效果设置成inset 2(就是变小一点),颜色改成黄色。

    twl16

    将button样式的background指定成buttons。保存项目,将项目文件拷贝到android项目的资源文件夹中。

    TWL Theme Editor的更多使用可以参考:http://wiki.l33tlabs.org/bin/view/TWL/Using+the+Theme+Editor

    使用TWL需要gdx-twl.jar。绘制使用twlrender。

    代码如下:

    package com.cnblogs.htynkn.game;import com.badlogic.gdx.ApplicationListener;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.Files.FileType;import com.badlogic.gdx.graphics.GL10;import com.badlogic.gdx.graphics.g2d.SpriteBatch;import com.badlogic.gdx.twl.Layout;import com.badlogic.gdx.twl.TWL;import de.matthiasmann.twl.Button;publicclass TwlDemo implements ApplicationListener {private TWL twl;private SpriteBatch batch;@Overridepublicvoidcreate() { batch =new SpriteBatch(); Button button=new Button("Button"); button.setTheme("button"); Layout layout =new Layout(); layout.add(button); twl=new TWL(batch,"data/twldemo.xml", FileType.Internal, layout); }@Overridepublicvoiddispose() {// 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); twl.render(); } @Override public void resize(int width, int height) { // TODO Auto-generated method stub } @Override public void resume() { // TODO Auto-generated method stub }}

    效果:

    finally

    写在最后:

    1.TWL真的很不错,样式的声明和CSS感觉很像,有继承有覆盖。相关工具也很完整,功能强大。

    2.TWL在android使用是比较方便的,但是在libgdx的话还是有点瑕疵的,比如汉字的处理,图片大小等,使用时请注意。

  • android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理

    android游戏开发框架libgdx的使用(十四)—TiledMap中视角完善和障碍物处理

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考。

    本文紧跟上文:http://www.cnblogs.com/htynkn/archive/2012/01/13/libgdx_13.html

    上文说到绘制了Map,然后我们的主角也可以四处活动了,但是仍有一些不完善的地方。

    1.地图的边界没有控制。Camera的位置其实是viewport的位置,不是屏幕边界,所以如果直接按照上文的做法做的话主角走到屏幕边缘的时候就有问题了。

    zuobiao

    2.没有障碍,主角的行动没有约束。

    现在先来解决第一个问题。

    解决方案很简单,我们时刻注意viewport的位置,根据viewport计算Screen的边界,让其不超过地图。

    代码如下:

    privatevoidCameraMove(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;
    }
    }

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

    13-1

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

    13-2

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

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

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

    metatiles

    大小还是32*32.

    13-3

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

    tilemap

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

    13-4

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

    在移动时先判断一下,如果会达到不能到达的块就直接退出

    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;
    
    publicclass 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
    publicvoidcreate() {
    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
    publicvoiddispose() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidpause() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidrender() {
    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();
    }
    
    privatevoidCameraMove(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;
    }
    }
    
    privatebooleanCheckMoveable(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) {
    returntrue;
    }else {
    returnfalse;
    }
    }
    
    @Override
    publicvoidresize(int width,int height) {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidresume() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicbooleankeyDown(int keycode) {
    
    returnfalse;
    }
    
    @Override
    publicbooleankeyTyped(char character) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleankeyUp(int keycode) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleanscrolled(int amount) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    privatevoidChangeDirect(int typeId) {
    switch (typeId) {
    case1:
    moveVector.set(0,1,0);
    Gdx.app.log("方向变动","向上");
    break;
    case2:
    moveVector.set(0, -1,0);
    Gdx.app.log("方向变动","向下");
    break;
    case3:
    moveVector.set(-1,0,0);
    Gdx.app.log("方向变动","向左");
    break;
    case4:
    moveVector.set(1,0,0);
    Gdx.app.log("方向变动","向右");
    break;
    }
    }
    
    @Override
    publicbooleantouchDown(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);
    }
    }elseif (newx >0 && newy <0) {
    if (newx > -newy) {
    ChangeDirect(4);
    }else {
    ChangeDirect(2);
    }
    }elseif (newx <0 && newy >0) {
    if (-newx > newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(1);
    }
    }else {
    if (-newx > -newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(2);
    }
    }
    isPress =true;
    returnfalse;
    }
    
    @Override
    publicbooleantouchDragged(int x,int y,int pointer) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleantouchMoved(int x,int y) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleantouchUp(int x,int y,int pointer,int button) {
    isPress =false;
    Gdx.app.log("Info","touchUp: x:" + x +" y: " + y +" pointer: "
    + pointer +" button: " + button);
    returnfalse;
    }
    }

    最终效果:

    13-5

    写在最后:

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

    • 注意各种坐标转化。

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

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

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

  • android游戏开发框架libgdx的使用(十三)—TiledMap中的角色和角色移动

    android游戏开发框架libgdx的使用(十三)—TiledMap中的角色和角色移动

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考

    本文紧跟上文,[android游戏开发框架libgdx的使用(十二)TiledMap地图的使用]

    地图我们创建好了接下来就是主角的出现。其实上文介绍了如何TiledMap和Stage的结合,角色的处理就简单了。

    可以继承Actor类创建主角类,我就偷个懒,用Image代替。

    编辑我们的TMX文件,添加一个对象层。

    在主角要出现的地方加个形状

    取名为play1

    我们的主角是:

    思路是我们遍历map中的所有Object,如果名字和我们设定的play1一致,那么就实例化一个Image,位置和Object一致,添加到舞台。

    关键代码:

    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);
    }
    }
    }
    复制代码

    效果如下:

    然后现在来试试让主角动起来。

    首先是我们如何控制,android设备的话优先选用触控。如果我们按住前方不放,主角向前。按住上方不放,主角向上。

    那么如何确定我们按住的是哪个方向呢?

    如图所示,黄色的是Stage,粉红的边框是整个Map,有部分显示,有一部分没有显示。右下角的绿色点是主角的位置,我们假定红色的点是我们的触碰点。

    认定红色的触碰点为向前,我在提供一个方案,但是方法不唯一哈,我这样确定方向也不一定是最符合用户体验的。

    以主角的位置为原点重现建立坐标系,得到触碰点的新坐标x,y.

    确定了在新坐标系下的触碰点的象限,在判断x,y的大小就可以知道方向了。

    代码如下:

    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);
    }
    }elseif (newx >0 && newy <0) {
    if (newx > -newy) {
    ChangeDirect(4);
    }else {
    ChangeDirect(2);
    }
    }elseif (newx <0 && newy >0) {
    if (-newx > newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(1);
    }
    }else {
    if (-newx > -newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(2);
    }
    }

    直接移动Camera位置可以移动地图,但是我们的主角却从地图上消失了…处理办法是将你希望仍然显示在地图上的Actor的坐标随着Camera一起移动。

    代码如下:

    privatevoidCameraMove(Vector3 vector3) {
    stage.getCamera().position.add(vector3);
    for (Actor actor : stage.getActors()) {
    actor.x += vector3.x;
    actor.y += vector3.y;
    }
    }

    完整代码:

    package com.cnblogs.htynkn.game;
    
    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.TextureRegion;
    import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas;
    import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
    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.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;
    
    publicclass firstGame 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;
    
    // Image image;
    
    @Override
    publicvoidcreate() {
    final String path ="map/";
    final String mapname ="tilemap";
    FileHandle mapHandle = Gdx.files.internal(path + mapname +".tmx");
    map = TiledLoader.createMap(mapHandle);
    atlas =new TileAtlas(map, Gdx.files.internal("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
    publicvoiddispose() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidpause() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidrender() {
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    OrthographicCamera c = (OrthographicCamera) stage.getCamera();
    if (isPress) {
    CameraMove(moveVector);
    }
    ((Label) stage.findActor("fpsLabel")).setText("FPS: "
    + Gdx.graphics.getFramesPerSecond());
    stage.act(Gdx.graphics.getDeltaTime());
    tileMapRenderer.render(c);
    stage.draw();
    }
    
    privatevoidCameraMove(Vector3 vector3) {
    stage.getCamera().position.add(vector3);
    for (Actor actor : stage.getActors()) {
    actor.x += vector3.x;
    actor.y += vector3.y;
    }
    }
    
    @Override
    publicvoidresize(int width,int height) {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidresume() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicbooleankeyDown(int keycode) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleankeyTyped(char character) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleankeyUp(int keycode) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleanscrolled(int amount) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    privatevoidChangeDirect(int typeId) {
    switch (typeId) {
    case1:
    moveVector.set(0,1,0);
    Gdx.app.log("方向变动","向上");
    break;
    case2:
    moveVector.set(0, -1,0);
    Gdx.app.log("方向变动","向下");
    break;
    case3:
    moveVector.set(-1,0,0);
    Gdx.app.log("方向变动","向左");
    break;
    case4:
    moveVector.set(1,0,0);
    Gdx.app.log("方向变动","向右");
    break;
    }
    }
    
    @Override
    publicbooleantouchDown(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);
    }
    }elseif (newx >0 && newy <0) {
    if (newx > -newy) {
    ChangeDirect(4);
    }else {
    ChangeDirect(2);
    }
    }elseif (newx <0 && newy >0) {
    if (-newx > newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(1);
    }
    }else {
    if (-newx > -newy) {
    ChangeDirect(3);
    }else {
    ChangeDirect(2);
    }
    }
    isPress =true;
    returnfalse;
    }
    
    @Override
    publicbooleantouchDragged(int x,int y,int pointer) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleantouchMoved(int x,int y) {
    // TODO Auto-generated method stub
    returnfalse;
    }
    
    @Override
    publicbooleantouchUp(int x,int y,int pointer,int button) {
    isPress =false;
    Gdx.app.log("Info","touchUp: x:" + x +" y: " + y +" pointer: "
    + pointer +" button: " + button);
    returnfalse;
    }
    }

    最终效果:(图像加载可能有点慢)

    (…传不上来)无语中

    我不知道怎么录制手机屏幕,所以只有用模拟机演示,但是真机(中兴V880)速度很流畅,完全没问题。

    如果有多个角色,方法是一样的,多建几个Object就行了。可以很明显看出,我们的忍者水平很高…行走地图完全没有障碍,而且如果你一直走的话会发现地图会消失一部分,这些问题接下的文章会慢慢解决的。

  • android游戏开发框架libgdx的使用(十二)—TiledMap地图的使用

    android游戏开发框架libgdx的使用(十二)—TiledMap地图的使用

    提示:本文记录的是本人的操作流程和心得,各位操作时可能由于版本和其它问题会出现点障碍,请参考本文后面的评论和回复。

    虽说可以用Image什么的当个背景,但是要是做个RPG类的游戏就有点复杂了。为了追求效率一般可以使用libgdx的SpriteCache,但是如果习惯于TiledMap的话libgdx也是支持的。

    相关的类是TiledMap,TileAtlas,TileMapRenderer,都在com.badlogic.gdx.graphics.g2d.tiled之中。

    现在我们从头来看看TiledMap的使用。

    1.制作TiledMap。

    我使用的是Tile Map editor,下载地址:http://www.mapeditor.org/

    先新建一个地图,大小5030,每个块的大小是3232。

    tiledmap1

    然后我使用的图块资源是从网上下的,具体出处不知了,对于资源奉献者表示感谢~

    map

    添加一个新图块

    tiledmap2

    块的高宽都是32.边距和间距都是1px,这些数值是取决你的图块资源本身的。效果如下:

    tiledmap3

    然后发挥想象吧,画一幅地图。

    tiledmap

    保存tmx文件。然后用gdx-tiled-preprocessor处理一下。

    处理完成后会多三个文件,覆盖原来的同名文件:

    tiledmap4

    tilest1.png文件:

    tileset1

    终于可以开始绘制了

    map = TiledLoader.createMap(mapHandle);
    atlas =new TileAtlas(map,new FileHandle("map/"));
    tileMapRenderer =new TileMapRenderer(map, atlas,10,10)

    最后在render用tileMapRenderer.render(cam);绘制

    而在TileMapRenderer(map, atlas, 10, 10)这句中后面两个10是缓冲的块数,可以酌情调整。我是随意写的。

    tiledmap5

    我个人比较喜欢舞台,其实tiledmap一样可以在舞台中使用。

    我在舞台绘制一个标签作为例子。

    其实原理很简单,从Stage获取Camera,然后给Render用,最后在Stage绘制。

    关键代码:

    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    OrthographicCamera c = (OrthographicCamera) stage.getCamera();
    ((Label) stage.findActor("fpsLabel")).setText("FPS: "
    + Gdx.graphics.getFramesPerSecond());
    stage.act(Gdx.graphics.getDeltaTime());
    tileMapRenderer.render(c);
    stage.draw();

    完整代码:

    package com.cnblogs.htynkn.game;
    
    import com.badlogic.gdx.ApplicationListener;
    import com.badlogic.gdx.Gdx;
    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.g2d.BitmapFont;
    import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas;
    import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
    import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader;
    import com.badlogic.gdx.graphics.g2d.tiled.TiledMap;
    import com.badlogic.gdx.math.Vector2;
    import com.badlogic.gdx.math.Vector3;
    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;
    
    publicclass JavaGame implements ApplicationListener {
    
    Stage stage;
    float width;
    float height;
    private TiledMap map;
    private TileAtlas atlas;
    private TileMapRenderer tileMapRenderer;
    
    Vector3 camDirection =new Vector3(1,1,0);
    Vector2 maxCamPosition =new Vector2(0,0);
    
    Image image;
    
    @Override
    publicvoidcreate() {
    final String path ="map/";
    final String mapname ="tilemap";
    FileHandle mapHandle = Gdx.files.internal(path + mapname +".tmx");
    map = TiledLoader.createMap(mapHandle);
    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.BLACK),
    "fpsLabel");
    stage.addActor(label);
    Gdx.input.setInputProcessor(stage);
    }
    
    @Override
    publicvoiddispose() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidpause() {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidrender() {
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    OrthographicCamera c = (OrthographicCamera) stage.getCamera();
    ((Label) stage.findActor("fpsLabel")).setText("FPS: "
    + Gdx.graphics.getFramesPerSecond());
    stage.act(Gdx.graphics.getDeltaTime());
    tileMapRenderer.render(c);
    stage.draw();
    }
    
    @Override
    publicvoidresize(int width,int height) {
    // TODO Auto-generated method stub
    
    }
    
    @Override
    publicvoidresume() {
    // TODO Auto-generated method stub
    
    }
    }

    tiledmap6

    效果不是很明显,左下角有个Label。

    虽说我们绘制出来了Map,但是还没有角色,而且还没有设置墙壁等障碍,接下来的几篇文章会说说这些。

    写在最后:

    1.做好的TMX文件一定要处理以后再用,用TiledMapPacker处理,不是TexturePacker。我起初弄混淆了,结果把这一块的代码读懂了才反应过来…

    2.Stage和TiledMap混用时,一定是stage的绘制在后。

    3.Stage的相机可以移动,移动时会产生地图移动效果(比如主角向前走),但是一定要更新Stage的Actors的位置。

    这篇博文写了一天半,主要是我粗心把TiledMapPacker和TexturePacker搞错,仔细看了看TexturePacker,觉得TexturePacker也是很有用的东西。算法很有趣,而且还有热心网友做的一个GUI界面,用这个就不用自己辛苦拉图了。

    算法介绍:http://www.blackpawn.com/texts/lightmaps/default.html

    GUI界面的TexturePacker:http://code.google.com/p/libgdx-texturepacker-gui/

    特别说明:

    我操作的时候没有任何问题…要是各位遇到问题,请细心调试,可以参考一下:http://www.cnblogs.com/SkyD/archive/2012/04/19/2457237.html

    这里还有博友破木吉他关于tilemap生成的文章:http://bluthmatter.blog.163.com/blog/static/18429405920124205458401/

  • android游戏开发框架libgdx的使用(十一)—Skin和UI配置文件的使用

    android游戏开发框架libgdx的使用(十一)—Skin和UI配置文件的使用

    本文使用的libgdx是0.92版本,和现在的最新版可能有一些不一样的地方。全文内容仅供参考。

    libgdx的UI改进很大,原来各种稀奇古怪的问题都已经解决了,而且UI的类型也基本上完全了。推荐大家下载最近的版本使用。

    UI的使用我觉得唯一复杂的就是各种样式的制定,比如TextButton:

    public TextButtonStyle (NinePatch down, NinePatch up, NinePatch checked,float pressedOffsetX,float pressedOffsetY,float unpressedOffsetX,float unpressedOffsetY, BitmapFontfont,Color fontColor,Color downFontColor,Color checkedFontColor)

    再看看List:

    public ListStyle (BitmapFontfont,Color fontColorSelected,Color fontColorUnselected, NinePatch selectedPatch)

    每次使用都需要实例化若干个Texture,NinePatch什么的还是有点麻烦,还好libgdx给出了一个解决方案:Skin。

    Skin保存了UI的样式和相关的资源,定义使用的是Json或者Json-like。API地址:http://libgdx.l33tlabs.org/docs/api/com/badlogic/gdx/scenes/scene2d/ui/Skin.html

    先看看基本格式:

    {
    resources: {
    className: {
    name: value,
    ...
    },
    ...
    },
    styles: {
    className: {
    name: value,
    ...
    },
    ...
    }
    }

    由两个大块定义:资源和样式。

    先做个fnt文件以便支持中文,保持为chinese.fnt和chinese.png。再做张图:

    全部拷贝到项目文件中(我是新建了一个UI文件夹)。

    我们先写个Label试试,定义需要的NinePath:

    com.badlogic.gdx.graphics.g2d.NinePatch: {
    default-rect-red: [
    { width: 2, height: 1, x: 1, y: 43 },
    { width: 1, height: 1, x: 2, y: 43 },
    { width: 2, height: 1, x: 3, y: 43 },
    { width: 2, height: 1, x: 1, y: 45 },
    { width: 1, height: 1, x: 2, y: 45 },
    { width: 2, height: 1, x: 3, y: 45 },
    { width: 2, height: 1, x: 1, y: 43 },
    { width: 1, height: 1, x: 2, y: 43 },
    { width: 2, height: 1, x: 3, y: 43 }
    ],
    default-rect-yellow: [
    { width: 2, height: 1, x: 1, y: 54 },
    { width: 1, height: 1, x: 2, y: 54 },
    { width: 2, height: 1, x: 3, y: 54 },
    { width: 2, height: 1, x: 1, y: 55 },
    { width: 1, height: 1, x: 2, y: 55 },
    { width: 2, height: 1, x: 3, y: 55 },
    { width: 2, height: 1, x: 1, y: 54 },
    { width: 1, height: 1, x: 2, y: 54 },
    { width: 2, height: 1, x: 3, y: 54 }
    ]
    }

    再定义一个白色:

    com.badlogic.gdx.graphics.Color: {
    white: { a: 1, b: 1, g: 1, r: 1 }
    }

    然后我们的字体:(提示:如果提示没有file字段,请直接使用default-font: default.fnt试试,这是版本问题而已。感谢阳光游子的提醒)

    com.badlogic.gdx.graphics.g2d.BitmapFont: {
    default-font: { file: chinese.fnt }
    }

    然后在样式中声明一个Label样式:

    com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: {
    default: { font: default-font, fontColor: white}
    }

    使用时

    Skin skin =new Skin(Gdx.files.internal("ui/uiskin.json"), Gdx.files.internal("ui/ui.jpg"));//加载样式final Label label = new Label("FPS:", skin.getStyle("default",LabelStyle.class), "fpsLabel"); //获取Label样式

    效果:

    image2

    然后再来试试TextButton:

    com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: {
    default: { down: default-rect-red, up: default-rect-yellow,fontColor: white, font: default-font}
    }

    调用:

    final TextButton textButton =new TextButton("确认", skin.getStyle("default", TextButtonStyle.class),"okButton");

    效果:

    image2

    按下去的时候会变黄(没截起图)…

    Skin真的用着很方便,特别是你大量使用了libgdx的UI的时候。

    写在最后:

    1.Skin支不支持xml?

    说实话我也很想知道,因为gdx-tests里面skin 的配置文件有两个,一个是json格式,另外一个是xml格式。但是我试了一下直接加载xml会报错。

    其实我比较推荐用xml格式,因为Json格式书写时候容易写错(少个逗号或者括号什么的),而且eclipse的自动缩进没有发挥作用(难道是配置问题?)。

    2.Skin支持不一个配置文件多个图片文件,这个情况推荐将图片合并或者干脆用多个Skin就行了。

    3.Skin的图片定义的原点是左上角。