bgd

阅读 / 问答 / 标签

如何使用libgdx编写一个简单的游戏

获取libgdx 你可以从libgdx的 官网 下载打包好的代码,我下载的是0.98版本。当然,你也可以从git代码仓库获取最新的版本的,或者你习惯使用的以前版本,比如0.97。创建项目libgdx项目的创建可以有多种方式,我推荐使用setup-ui。方便易用还可以省去很多麻烦,特别是ADT升级以后的ClassNotFound问题。如果是下载打包好的,那么就默认包含了gdx-setup-ui,双击就可以打开。填写一些基本信息,然后选中你下载的0.98.zip那个压缩文件。这里我只生成一个桌面项目和Android项目。桌面项目是方便调试,而Android项目是最后发布的。在整个开发中我始终用桌面项目调试,因为速度快,容易排错。同时周期性的在Android真机上测试。点击生成项目,然后在Eclipse中导入。一般导入进去以后Android项目会有一些问题,修改project.properties文件和AndroidManifest.xml配置文件。运行效果如下:准备工作本例子中用到的图片如下:用gdx-texturepacker打包成一张大图。我整个例子都是用的是Stage模式。所以它的坐标原点在左下角,如果是一般用Spirte直接绘制,那么原点在右上角。首先将打包好的图册复制到assets中新建的pack文件夹。然后我们开始动工了,首先删除setup-ui生成的多余代码,整理DartsShaSha.java文件如下:package com.cnblogs.htynkn;import com.badlogic.gdx.ApplicationAdapter;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.graphics.GL10;import com.badlogic.gdx.scenes.scene2d.Stage;public class DartsShaSha extends ApplicationAdapter { Stage stage; @Override public void create() { stage = new Stage(480, 320, true); } @Override public void dispose() { stage.dispose(); } @Override public void render() { Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); stage.act(); stage.draw(); }}这时候运行效果是一个白茫茫的画面。注意一下这句stage = new Stage(480, 320, true);因为我希望屏幕的自适应有Stage自动完成,所以坐标基本可以写死。先不着急开工,我们先添加一个现实FPS的标签。我希望这个标签显示在屏幕右下角。在create方法中添加LabelStyle labelStyle = new LabelStyle(new BitmapFont(), Color.BLACK); //创建一个Label样式,使用默认黑色字体 Label label = new Label("FPS:", labelStyle); //创建标签,显示的文字是FPS: label.setName("fpsLabel"); //设置标签名称为fpsLabel label.setY(0); //设置Y为0,即显示在最下面 label.setX(480 - label.getTextBounds().width); //设置X值,显示为最后一个字紧靠屏幕最右侧 stage.addActor(label); //将标签添加到舞台在render方法中更新fps的值Label label = (Label) stage.getRoot().findActor("fpsLabel"); //获取名为fpsLabel的标签 label.setText("FPS:" + Gdx.graphics.getFramesPerSecond()); label.setX(480 - label.getTextBounds().width); //更新X值以保证显示位置正确性效果如下:添加忍者现在来添加我们的主角,我希望主角显示在屏幕左侧中央。所以它的x值必然是0,但是它的y值并不是320的一半,而是160减去图片高度的一半。因为我们指定的x、y值其实相对图片的左下角的。所以要补上多余或者不足的部分。主角其实就是一张图片,并没有太多特别的效果,所以我使用Image类。首先获取图册TextureAtlas atlas = new TextureAtlas("pack/default.pack");在从图册中获取Player.png并创建Image对象。Image man = new Image(atlas.findRegion("Player")); //获取图册中的Player.png并创建image对象 man.setX(0); man.setY(160 - man.getHeight() / 2); //设置Y值,以让图片在中间显示 stage.addActor(man); //将主角添加到舞台效果如下:添加怪兽然后我们来添加几只怪兽。怪兽应该是随机从屏幕右侧出现,并直线移动到屏幕左侧。同时我们还要检测怪兽的生命值什么的,或者其他效果,所以为了方便处理,我们专门建立一个Group来管理怪兽。新建类TargetGroup,并集成Group类。package com.cnblogs.htynkn;import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;import com.badlogic.gdx.scenes.scene2d.Group;import com.badlogic.gdx.scenes.scene2d.ui.Image;public class TargetGroup extends Group { public TargetGroup(AtlasRegion region) { super(); }}因为还需要传入怪兽的图片,所以我们的创建方法保留参数AtlasRegion region。怪兽的Y值因为是随机的,但是又不能超出屏幕。所以用随机数来生成。libgdx的MathUtils提供了相关方法。int minY = 0; int maxY = (int) (320 - region.getRegionHeight()); int tempY = MathUtils.random(minY, maxY);这里还有一个问题需要注意,就是怪兽之间不应该出现遮挡,所以对于生成的Y值还需要进行判断。假设我们要生成3只怪兽,那么代码应该如下:int tempY = 0; for (int i = 0; i < 3; i++) { Image image = new Image(region); image.setX(480 - image.getWidth()); // 开始判断Y值是否符合要求 boolean flag = false; do { flag = false; tempY = MathUtils.random(minY, maxY); // 生成Y值 Actor[] actors = this.getChildren().begin(); // 获取当前已有的怪兽对象 for (int j = 0; j < this.getChildren().size; j++) { Actor tempActor = actors[j]; if (tempY == tempActor.getY()) { // 如果Y值相等,比如重合,所以舍弃,重新生成 flag = true; break; } else if (tempY < tempActor.getY()) { // 如果生成的Y值小于当前怪兽的Y值,则判断生成的Y值加上高度后是否合适 if ((tempY + region.getRegionHeight()) >= tempActor .getY()) { flag = true; break; } } else { // 如果生成的Y值大于当前怪兽的Y值,则判断当前怪兽的Y值加上高度后是否合适 if (tempY <= (tempActor.getY() + region .getRegionHeight())) { flag = true; break; } } } } while (flag); image.setY(tempY); this.addActor(image); //添加到组中在主类的create方法中添加TargetGroup group = new TargetGroup(atlas.findRegion("Target")); stage.addActor(group);效果如下:目前怪兽还不能移动,这里需要一个简单的动画效果,libgdx中的Action可以办到。考虑到怪兽是水平移动,即Y值不变,X值变小。所以添加一个方法public void AddMove(Actor actor, float time) { actor.addAction(Actions.moveTo(0, actor.getY(), time)); }怪兽的移动速度也随机一下,代码如下image.setY(tempY);this.AddMove(image, MathUtils.random(3f, 8f)); //怪兽移动效果this.addActor(image); //添加到组中效果如下:添加武器我们的主角自然不能赤手空拳和怪兽进行搏斗,现在添加一些飞镖。假定用户触摸屏幕以后,主角就向触摸位置发射一个飞镖。因为飞镖的数量不一定,所以我这里创建一个专门的类ProjectileFactory来处理。首先是飞镖的创建,和怪兽群一样的原因,我还是希望一个专门的组来管理。创建一个专门的方法来创建飞镖public static Image createProjectile(AtlasRegion region, Actor man, Vector3 target) { Image image = new Image(region); image.setX(man.getX() + man.getWidth() / 2); image.setY(man.getY() + man.getHeight() / 2); image.addAction(Actions.moveTo(target.x, target.y, 2f)); //设置飞镖的移动 return image; }在主类进行一些修改以便其可以获取屏幕的触摸。首先修改类声明为public class DartsShaSha extends InputAdapter implements ApplicationListener其实具体也可以两个都实现接口,主要是我觉得看着不舒服。重写touchDown方法为public boolean touchDown(int screenX, int screenY, int pointer, int button) { Vector3 vector3 = new Vector3(screenX, screenY, 0); stage.getCamera().unproject(vector3); // 坐标转化 projectiles.addActor(ProjectileFactory.createProjectile( atlas.findRegion("Projectile"), man, vector3)); // 添加新飞镖到飞镖组 return true; }在create方法中添加新的Group并设置Input响应。stage.addActor(projectiles); //添加飞镖组到舞台 InputMultiplexer multiplexer = new InputMultiplexer(); //多输入接收器 multiplexer.addProcessor(this); //添加自身作为接收 multiplexer.addProcessor(stage); //添加舞台 Gdx.input.setInputProcessor(multiplexer); //设置多输入接收器为接收器效果如下:更完善的飞镖飞镖虽然添加出来了,但是飞镖没有转动…而且飞镖没有在到达目的地后自动消失。现在先来添加旋转效果,libgdx提供了rotateBy方法。在创建飞镖的createProjectile方法中添加image.addAction(Actions.repeat(50, Actions.rotateBy(360, 0.5f))); //设置飞镖的旋转这个不方便截图,就不展示效果了。现在来考虑如何让飞镖到达目的后消失。首先来看看我们的Image对象,它包含了两个Action,一个是旋转Action,另外一个移动Action。我们可以检测Action的数量,如果只有一个Action,我们可以断定飞镖只是在旋转而已经到达目的地了。这个时候就可以把它删除了。添加一个专门的方法来判断飞镖是否应该移除了public static Boolean checkAlive(Actor projectile) { if (projectile.getActions().size == 1) { return false; } return true; }在render方法中添加处理代码// 飞镖的移除判 Actor[] projectile = projectiles.getChildren().begin(); //获取Actor数组 for (int j = 0; j < projectiles.getChildren().size; j++) { //移除判断 Actor actor = projectile[j]; if (!ProjectileFactory.checkAlive(actor)) { projectiles.removeActor(actor); } }效果如下:现在飞镖可以自动消失了,并且也在旋转了。不过旋转效果很奇怪,它并不是沿中心旋转,而是沿着左下角旋转的。重新设置中心image.setOrigin(image.getWidth() / 2, image.getHeight() / 2);现在一切正常了。碰撞检测和杀敌当然,发出飞镖的目的自然是杀敌,现在马上来添加这个功能。我们可以把怪兽看着一个矩形,即飞镖击中任何位置都算作有效。而飞镖就以其中心为代表。创建方法attackAlivepublic static Boolean attackAlive(Actor target, Actor projectile) { Rectangle rectangle = new Rectangle(target.getX(), target.getY(), target.getWidth(), target.getHeight()); // 创建一个矩形 return rectangle.contains( projectile.getX() + projectile.getWidth() / 2, projectile.getY() + projectile.getHeight() / 2); //判断是否在矩阵中,即是否击中 }在render方法中修改// 开始处理飞镖 Actor[] projectile = projectiles.getChildren().begin(); Actor[] targets = targetGroup.getChildren().begin(); for (int i = 0; i < projectiles.getChildren().size; i++) { Actor actor = projectile[i]; for (int j = 0; j < targetGroup.getChildren().size; j++) { Actor target = targets[j]; if (ProjectileFactory.attackAlive(target, actor)) { targetGroup.removeActor(target); projectiles.removeActor(actor); break; } } } // 如果飞镖已经飞到则删除 projectile = projectiles.getChildren().begin(); for (int j = 0; j < projectiles.getChildren().size; j++) { Actor actor = projectile[j]; if (!ProjectileFactory.checkAlive(actor)) { projectiles.removeActor(actor); } }效果如下: