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

标签: libgdx 游戏 | 发表时间:2013-02-14 15:39 | 作者:夜明的孤行灯
出处:http://www.cnblogs.com/

写这几篇文章主要是看了这个系列的文章: http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial

这个系列主要讲述了如何使用Cocos2D编写简单的游戏。稍微读读感觉不错,所以想写个libgdx版本的。

本篇文章主要讲述基本内容的编写,包括显示人物、怪兽和飞镖。

最终效果如下图:

01_thumb1

获取libgdx

你可以从libgdx的 官网下载打包好的代码,我下载的是0.98版本。

02_thumb4

 

当然,你也可以从git代码仓库获取最新的版本的,或者你习惯使用的以前版本,比如0.97。

创建项目

libgdx项目的创建可以有多种方式,我推荐使用setup-ui。方便易用还可以省去很多麻烦,特别是ADT升级以后的ClassNotFound问题。

如果是下载打包好的,那么就默认包含了gdx-setup-ui,双击就可以打开。

03_thumb1

填写一些基本信息,然后选中你下载的0.98.zip那个压缩文件。这里我只生成一个桌面项目和Android项目。

桌面项目是方便调试,而Android项目是最后发布的。在整个开发中我始终用桌面项目调试,因为速度快,容易排错。同时周期性的在Android真机上测试。

04_thumb1

点击生成项目,然后在Eclipse中导入。

05_thumb1

一般导入进去以后Android项目会有一些问题,修改project.properties文件和AndroidManifest.xml配置文件。

运行效果如下:

06_thumb1

准备工作

本例子中用到的图片如下:

Player_thumb Projectile_thumb Target_thumb

用gdx-texturepacker打包成一张大图。

07_thumb1

我整个例子都是用的是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();
}
}

这时候运行效果是一个白茫茫的画面。

09_thumb1

注意一下这句

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值以保证显示位置正确性

 

效果如下:

10_thumb1

添加忍者

现在来添加我们的主角,我希望主角显示在屏幕左侧中央。所以它的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); //将主角添加到舞台

效果如下:

11_thumb1

添加怪兽

然后我们来添加几只怪兽。怪兽应该是随机从屏幕右侧出现,并直线移动到屏幕左侧。

同时我们还要检测怪兽的生命值什么的,或者其他效果,所以为了方便处理,我们专门建立一个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);

效果如下:

12_thumb1

目前怪兽还不能移动,这里需要一个简单的动画效果,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); //添加到组中

效果如下:

13_thumb1

添加武器

我们的主角自然不能赤手空拳和怪兽进行搏斗,现在添加一些飞镖。

假定用户触摸屏幕以后,主角就向触摸位置发射一个飞镖。

因为飞镖的数量不一定,所以我这里创建一个专门的类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); //设置多输入接收器为接收器

 

效果如下:

14_thumb1

更完善的飞镖

飞镖虽然添加出来了,但是飞镖没有转动…而且飞镖没有在到达目的地后自动消失。

现在先来添加旋转效果,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);
}
}

 

效果如下:

15_thumb1

现在飞镖可以自动消失了,并且也在旋转了。不过旋转效果很奇怪,它并不是沿中心旋转,而是沿着左下角旋转的。

重新设置中心

image.setOrigin(image.getWidth() / 2, image.getHeight() / 2);

现在一切正常了。

碰撞检测和杀敌

当然,发出飞镖的目的自然是杀敌,现在马上来添加这个功能。

我们可以把怪兽看着一个矩形,即飞镖击中任何位置都算作有效。而飞镖就以其中心为代表。

创建方法attackAlive

public 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);
}
}

效果如下:

16_thumb1

写在最后

虽然实现了个大概,但是仔细看看其实问题还是很多的,后面的文章会提到进一步的修改。包括逻辑上的完善,声音效果,预加载,背景绘制,集成第三方社交和广告等等。

这片文章对应demo可以从这里下载来试试。

http://pan.baidu.com/share/link?shareid=328840&uk=4127624209

我用的2.2的sdk编译的,低版本没有测试。我的手机是ZTE V880,fps50上下。

ps:testin的测试结果是通过率  100.00%

ps: 代码上传到github上了,地址  https://github.com/htynkn/DartsShaSha

文章对应的代码的tag为page 1。

本文链接

相关 [libgdx 游戏] 推荐:

libGDX 1.0 正式发布,Android 游戏引擎

- - 开源中国社区最新新闻
libGDX 1.0 发布,此版本现已提供 下载,主要更新内容如下:. 基于 Gradle 项目的安装 ,不需要下载二进制包,不需要更多的 jars,支持所有平台的简单打包. 所有文档 的格式都是视频和 Wiki 文章. OpenGL ES 1.x 的支持,支持 OpenGL ES 3.0. 移除了大量 Android 后端的集群,要求最低 Android 版本是.

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

- - 博客园_首页
写这几篇文章主要是看了这个系列的文章: http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial. 这个系列主要讲述了如何使用Cocos2D编写简单的游戏. 稍微读读感觉不错,所以想写个libgdx版本的.

如何使用libgdx编写一个简单的游戏(二)— 完善

- - 博客园_首页
上一篇介绍游戏雏形的编写,这一篇将完善部分逻辑并添加更多效果. 例子代码在 https://github.com/htynkn/DartsShaSha,如有需要请自行在tag中下载对应部分. 现在的飞镖可以旋转可以飞行了,但是有一个问题却没有解决. 首先飞镖的速度,如果用户触摸位置很靠近左侧,那么飞镖的速度就很慢了.

libgdx 概述

- - CSDN博客推荐文章
libgdx 是一个跨平台的2D/3D的游戏开发框架,由Java/C/C++语言编写而成,基于  Apache License 2.0 协议,对商业使用和非商业使用均免费,代码托管于 github. libgdx兼容大多数平台,采用标准JavaSE实现,能运行在Mac、Linux、Windows等系统,与Android平台(Android1.5以上即可使用,Android2.1以上可满功率发挥).

libgdx 环境搭建

- - CSDN博客推荐文章
1) libgdx 开发包下载: google code(最新 libgdx-0.9.7.zip    2012.11.12). 2) libgdx 主干源码下载: github tags. libgdx的android开发包主要有 gdx.jar, gdx-backend-android.jar,以及 armeabi 和 armeabi-v7a ( 区别).

(libgdx小结)图形绘制

- - CSDN博客推荐文章
    在这一小节的图形绘制中所涉及到的类主要有4个:Texture 、TextureRegion、SpriteBatch、Sprite. Texture:图片的容器. TextureRegion:用于截取Texture. SpriteBatch:相当于画笔. Sprite:其实就是加强版的TextureRegion和SpriteBatch.

[译]Libgdx Developer&#39;s Guide(Libgdx开发者手册)-1

- - bob007abc的专栏
Libgdx 是一个跨平台的游戏对象开发框架. 目前支持的开发平台有:Windows, Linux, Mac OS X, Android, iOS 和 HTML5. Libgdx 允许一次编码无需修改即可发布到多个平台. 与其等待最新的修改被布置至设备或者编译为HTML5语言,你可以在桌面环境开发你的应用,主要受益于一个极快的迭代周期.

游戏指南

- Blacat - 韩寒
这是一个复杂的国度,人们并不是那么渴望文人范畴里的自由,如果你上街问问,大家都觉得自己过的挺自由. 人们已经习惯了在台上台下的两种话语,你只要不冲进他......>>点击查看新浪博客原文.

游戏开发商开源HTML5游戏

- - Solidot
游戏工作室Wooga开源了其开发的HTML5游戏Pocket Island,源代码托管在GitHub上,该公司在官方博客上介绍了他们的开发经验,认为HTML5游戏有潜力,但尚未做好准备,开源的意图将是让其他人了解他们的工作,学习和改进. Wooga认为,2012年也许不是HTML5的黄金时代,但它的黄金时代即将到来.

JS游戏引擎

- 米随随 - HTML5研究小组
If you don’t have anything better to do and want to help fellow redditors interested in JS game dev out, feel free to fork the list and modify it as you like.