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

14 2月

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2013/02/14/libgdx-game-1/

写这几篇文章主要是看了这个系列的文章: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;

publicclass DartsShaSha extends ApplicationAdapter {
Stage stage;
@Override
publicvoidcreate() {
stage =new Stage(480,320,true);
}

@Override
publicvoiddispose() {
stage.dispose();
}

@Override
publicvoidrender() {
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;

publicclass TargetGroup extends Group {
publicTargetGroup(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;
}elseif (tempY < tempActor.getY()) {// 如果生成的Y值小于当前怪兽的Y值,则判断生成的Y值加上高度后是否合适
if ((tempY + region.getRegionHeight()) >= tempActor
.getY()) {
flag =true;
break;
}
}else {// 如果生成的Y值大于当前怪兽的Y值,则判断当前怪兽的Y值加上高度后是否合适
if (tempY

在主类的create方法中添加

TargetGroup group =new TargetGroup(atlas.findRegion("Target"));
stage.addActor(group);

效果如下:

12_thumb1

目前怪兽还不能移动,这里需要一个简单的动画效果,libgdx中的Action可以办到。

考虑到怪兽是水平移动,即Y值不变,X值变小。

所以添加一个方法

publicvoidAddMove(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来处理。

首先是飞镖的创建,和怪兽群一样的原因,我还是希望一个专门的组来管理。

创建一个专门的方法来创建飞镖

publicstatic ImagecreateProjectile(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;
}

在主类进行一些修改以便其可以获取屏幕的触摸。

首先修改类声明为

publicclass DartsShaSha extends InputAdapter implements ApplicationListener

其实具体也可以两个都实现接口,主要是我觉得看着不舒服。

重写touchDown方法为

publicbooleantouchDown(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));// 添加新飞镖到飞镖组
returntrue;
}

在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,我们可以断定飞镖只是在旋转而已经到达目的地了。这个时候就可以把它删除了。

添加一个专门的方法来判断飞镖是否应该移除了

publicstatic BooleancheckAlive(Actor projectile) {
if (projectile.getActions().size ==1) {
returnfalse;
}
returntrue;
}

在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);

现在一切正常了。

碰撞检测和杀敌

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

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

创建方法attackAlive

publicstatic BooleanattackAlive(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。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2013/02/14/libgdx-game-1/

One Reply to “如何使用libgdx编写一个简单的游戏(一)— 雏形”

发表评论

您的电子邮箱地址不会被公开。