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

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

上一篇介绍游戏雏形的编写,这一篇将完善部分逻辑并添加更多效果。

例子代码在 https://github.com/htynkn/DartsShaSha,如有需要请自行在tag中下载对应部分。

完善飞镖逻辑

现在的飞镖可以旋转可以飞行了,但是有一个问题却没有解决。

首先飞镖的速度,如果用户触摸位置很靠近左侧,那么飞镖的速度就很慢了。

其次,如果用户触摸中间位置,默认情况下飞镖应该是朝那个方向飞行,而不是飞到触摸位置就消失了。

这里的处理办法很简单,就是根据用户触摸位置,算出一个X为480的值,这样飞镖就可以飞到最右侧,同时保持相当的速度。

在createProjectile方法中添加

float r = (target.y - image.getY()) / (target.x - image.getX()); //获取斜率
float detY = r * 480; //获取Y的变动量
image.addAction(Actions.moveTo(480 + image.getX(), detY + image.getY(),
2f)); // 设置飞镖的移动

这样基本就解决了问题。

接下来来思考飞镖的数量和相应位置。

首先飞镖的速度一定要得到限制,不然满屏幕飞镖有什么意思。这里限制飞镖的数量为5个。

在touchDown的最开始添加

if (projectiles.getChildren().size >= 5) { //限制飞镖的数量为5个
return false;
}

 

这样当屏幕上的飞镖数量大于等于5时就不会产生新的飞镖了。

还有就是触摸的位置,如果触摸的位置太靠右的话,会出现飞镖倒飞或者速度过快的问题,所以当触摸位置太靠近左侧的时候就不释放飞镖。

在touchDown方法中添加

if (vector3.x < man.getX() + 5) { //如果触摸太靠近左侧就不响应
return false;
}

 

这里的5是我随便写的,仅仅表示个意思。测试一下,觉得5还是太小了,最后我改成10了。

更带感的对手

说实话,现在的对手一动不动,只会匀速平移。我们先改进它的外貌吧。

我从 http://untamed.wild-refuge.net/rmxpresources.php?characters获取到如下图片

scythe

打包以后放入assets文件夹中。

因为libgdx只有默认Animation类,但是没法办法直接在stage中使用,所以新建一个Scythe类并继承Actor类。

public Scythe(AtlasRegion atlasRegion) {
super();
this.setWidth(titleWidth); //设置高度
this.setHeight(titleHeight); //设置宽度
TextureRegion[][] temp = atlasRegion.split(titleWidth, titleHeight); //分割图块
walkFrames = new TextureRegion[4]; //获取第二行的4帧
for (int i = 0; i < 4; i++) {
walkFrames[i] = temp[1][i];
}
animation = new Animation(0.1f, walkFrames); //创建动画,帧间隔0.1
}

因为原图宽200,高192,一共16张图,所以每一块的宽就是50,高48。使用Animation类需要手动提供相关帧,并通过Animation和当前时间获取的帧。

重写draw方法如下

@Override
public void draw(SpriteBatch batch, float parentAlpha) {
stateTime += Gdx.graphics.getDeltaTime(); //获取总时间
currentFrame = animation.getKeyFrame(stateTime, true); //获取当前关键帧
batch.draw(currentFrame, this.getX(), this.getY(), this.getWidth(),
this.getHeight()); //绘制
}

 

修改TargetGroup中有关region.getRegionHeight()的部分,全部除以4。同时修改Image类Scythe类。

最后完整的Scythe如下

package com.cnblogs.htynkn;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class Scythe extends Actor {
TextureRegion[] walkFrames; // 保存每一帧
Animation animation; // 动画类
float stateTime; // 总时间
TextureRegion currentFrame; // 当前帧
int titleWidth = 50; // 声明块宽度
int titleHeight = 48; // 声明块高度

public Scythe(AtlasRegion atlasRegion) {
super();
this.setWidth(titleWidth); // 设置高度
this.setHeight(titleHeight); // 设置宽度
TextureRegion[][] temp = atlasRegion.split(titleWidth, titleHeight); // 分割图块
walkFrames = new TextureRegion[4]; // 获取第二行的4帧
for (int i = 0; i < 4; i++) {
walkFrames[i] = temp[1][i];
}
animation = new Animation(0.1f, walkFrames); // 创建动画,帧间隔0.1
}

@Override
public void draw(SpriteBatch batch, float parentAlpha) {
stateTime += Gdx.graphics.getDeltaTime(); // 获取总时间
currentFrame = animation.getKeyFrame(stateTime, true); // 获取当前关键帧
batch.draw(currentFrame, this.getX(), this.getY(), this.getWidth(),
this.getHeight()); // 绘制
}
}

 

效果如下:

01

添加血条

我们来试试在给予怪兽血量,即有些怪兽可以承受两次伤害。这里我们将用到比较基本的东西。

首先是血条的位置,一般来看应该在怪兽正上方,以红色显示。

绘制可以用很多种方法,我不怎么习惯mesh那套,所以这里我使用Pixmap类。

先在Sythe类中添加两个变量

int margin = 2; // 血条和人物之间的间隔
int pixHeight = 5; // 血条高度

然后在绘制方法中添加

Pixmap pixmap = new Pixmap(64, 8, Format.RGBA8888); //生成一张64*8的图片
pixmap.setColor(Color.BLACK); //设置颜色为黑色
pixmap.drawRectangle(0, 0, titleWidth, pixHeight); //绘制边框
Texture pixmaptex = new Texture(pixmap); //生成图片
TextureRegion pix = new TextureRegion(pixmaptex, titleWidth, pixHeight); //切割图片
batch.draw(pix, this.getX(), this.getY() + this.titleHeight
+ this.margin); //绘制

这样我们就有了一个黑色的边框了

02

然后就是血量的填充了,在添加两个变量以记录总血量和当前血量

int maxHp; // 总血量
int currentHp; // 当前血量

绘制血条的代码添加到绘制完黑框的语句后面

pixmap.setColor(Color.RED); // 设置颜色为红色
pixmap.fillRectangle(0, 1, titleWidth * currentHp / maxHp,
pixHeight - 2); // 绘制血条

最后一定要释放掉pixmap

pixmap.dispose();

这是设置总血量为2,当前血量为1的效果

03

控制转换

增加了血量以后我们的代码也需要修改了,在主类DartsShaSha中修改相关的逻辑。

为了方便起见我们将游戏的逻辑赋给控制器。

先新建一个IController

package com.cnblogs.htynkn.controller;

import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.Stage;

public abstract class IController extends Group {
public abstract void update(Stage stage);
}

 

然后新建TargetController类,重写update方法,在这个方法中我们处理相关逻辑,然后在主类中只需要调用方法就可以。

首先将中已有的代码拷贝过来,然后在Sythe中添加两个方法来处理受到伤害和死亡判断。

public void beAttacked(int damage) {
if (this.currentHp > damage) { // 如果血量大于伤害就扣除响应数值
currentHp = currentHp - damage;
} else if (this.currentHp > 0) { // 如果血量小于伤害但是仍有血量就让血量归零
currentHp = 0;
}
}

public Boolean isAlive() {
return this.currentHp > 0;
}

 

然后在TargetController中添加update的相关代码

 

public void update(Stage stage) {
Group projectiles = (Group) stage.getRoot().findActor("projectiles"); // 获取飞镖所在Group
Actor[] projectile = projectiles.getChildren().begin();
Actor[] targets = this.getChildren().begin();
for (int i = 0; i < projectiles.getChildren().size; i++) {
Actor actor = projectile[i];
for (int j = 0; j < this.getChildren().size; j++) {
Actor target = targets[j];
if (ProjectileFactory.attackAlive(target, actor)) {
Scythe scythe = (Scythe) target;
scythe.beAttacked(1);
projectiles.removeActor(actor);
if (!scythe.isAlive()) {
this.removeActor(target);
}
break;
}
}
}
}

 

然后在主类中修改相关的实例化代码,在render中调用update方法。

targetController.update(this.stage); //调用update方法,处理怪兽的逻辑

 

效果如下:

04 

飞镖的杀伤力和手势识别

上面的代码中每一个飞镖的杀伤力是1,每一个怪兽的血量是2。

现在我们来修改一下飞镖的控制,让飞镖也采用控制器来处理。

在这里设定为触摸一下屏幕是发射一般的飞镖,杀伤力为1。而长按以后的杀伤力是2。

libgdx中提供了一个手势识别的接口,实现它就可以了。这里我选择继承GestureAdapter。

public class DartsListener extends GestureAdapter

同时修改飞镖的控制为控制器模式,新建控制器DartsController。

代码如下:

package com.cnblogs.htynkn.controller;

import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.cnblogs.htynkn.elements.Dart;

public class DartsController extends IController {

AtlasRegion region;

@Override
public void update(Stage stage) {
// 如果飞镖已经飞到则刪除
Actor[] projectile = this.getChildren().begin();
for (int j = 0; j < this.getChildren().size; j++) {
Actor actor = projectile[j];
if (!this.checkAlive(actor)) {
this.removeActor(actor);
}
}
}

public DartsController(AtlasRegion region) {
this.region = region;
}

public void AddDarts(Dart dart) {
if (this.getChildren().size >= 5) { //如果飞镖数量大于等于5个就结束
return;
}
float r = (dart.getTarget().y - dart.getY())
/ (dart.getTarget().x - dart.getX()); // 获取斜率
float detY = r * 480; // 获取Y的变动量
dart.addAction(Actions.moveTo(480 + dart.getX(), detY + dart.getY(), 2f)); // 设置飞镖的移动
this.addActor(dart);
}

public 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); // 判断是否在矩阵中,即是否击中
}

public Boolean checkAlive(Actor projectile) {
if (projectile.getActions().size == 1) {
return false;
}
return true;
}

public Dart createDart() {
return new Dart(region);
}
}

其中Dart类代码如下:

package com.cnblogs.htynkn.elements;

import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

public class Dart extends Image {

Vector2 target;

public Dart(AtlasRegion region) {
super(region);
this.setOrigin(getWidth() / 2, getHeight() / 2);
this.addAction(Actions.repeat(50, Actions.rotateBy(360, 0.5f)));
}

public void setTarget(Vector2 target) {
this.target = target;
}

public void setTarget(float x, float y) {
this.target = new Vector2(x, y);
}

public Vector2 getTarget() {
return target;
}
}

 

因为我们的输入接受另外有类DartsListener来处理,所以修改主类的继承如下:

public class DartsShaSha implements ApplicationListener

 

在multiplexer中添加我们新的手势识别器

GestureDetector gestureDetector = new GestureDetector(
new DartsListener(this.stage));
multiplexer.addProcessor(gestureDetector); // 添加手势识别

目前DartsListener中代码如下

 

package com.cnblogs.htynkn.listener;

import com.badlogic.gdx.input.GestureDetector.GestureAdapter;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.cnblogs.htynkn.controller.DartsController;
import com.cnblogs.htynkn.elements.Dart;

public class DartsListener extends GestureAdapter {

Stage stage;

public DartsListener(Stage stage) {
this.stage = stage;
}

@Override
public boolean touchDown(float x, float y, int pointer, int button) {
DartsController dartsController = (DartsController) stage.getRoot()
.findActor("dartsController");
if (dartsController.getChildren().size >= 5) { // 限制飞镖的数量为5个
return false;
}
Vector3 vector3 = new Vector3(x, y, 0);
stage.getCamera().unproject(vector3); // 坐标转化
Actor man = stage.getRoot().findActor("player");
if (vector3.x < man.getX() + 10) { // 如果触摸太靠近左侧就不响应
return false;
}
Dart dart = dartsController.createDart();
dart.setX(man.getX() + man.getWidth() / 2);
dart.setY(man.getY() + man.getHeight() / 2);
dart.setTarget(vector3.x, vector3.y);
dartsController.AddDarts(dart);
return true;
}

@Override
public boolean longPress(float x, float y) {
return true;
}
}

可能还有其他细节的修改,详细的请参考代码。

目前我们只能算是重构了一下,游戏效果并没有改变。现在来设置飞镖的杀伤力和长按的处理。

在Dart中添加属性

int power;

在实例化中添加

power = 1; //默认杀伤力为1

将TargetController中的

scythe.beAttacked(1);

 

修改为

scythe.beAttacked(dart.getPower());

 

而中的longPress方法基本和touchDown相同,只是增加了

dart.setPower(2); //设置杀伤力为2
dart.setColor(Color.RED); //设置成红色

来思考一下处理流程,用户触摸屏幕,首先会触发tap事件,然后是touchDown,最后才是longPress。

也就是目前我们的游戏长按一下会发出一个普通的飞镖,然后才是我们的红色飞镖。

要处理这个问题,我们添加一个DartsDetector类,继承GestureDetector类。

因为事件的触发顺序是tap->touchdown->longpress->touchup。

所以我们的事件逻辑全部转移到touchup中,如果是longpress事件就发出红色飞镖,如果是touchdown就发出普通飞镖。

由于我们的DartsListener已经不处理任何逻辑了,所以删除其中所有代码。

GestureDetector中的代码如下

package com.cnblogs.htynkn.listener;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.cnblogs.htynkn.controller.DartsController;
import com.cnblogs.htynkn.elements.Dart;

public class DartsDetector extends GestureDetector {

Stage stage;

public DartsDetector(Stage stage, GestureListener listener) {
super(listener);
this.stage = stage;
}
@Override
public boolean touchUp(float x, float y, int pointer, int button) {
DartsController dartsController = (DartsController) stage.getRoot()
.findActor("dartsController");
if (dartsController.getChildren().size >= 5) { // 限制飞镖的数量为5个
return false;
}
Vector3 vector3 = new Vector3(x, y, 0);
stage.getCamera().unproject(vector3); // 坐标转化
Actor man = stage.getRoot().findActor("player");
if (vector3.x < man.getX() + 10) { // 如果触摸太靠近左侧就不响应
return super.touchUp(x, y, pointer, button);
}
Dart dart = dartsController.createDart();
dart.setX(man.getX() + man.getWidth() / 2);
dart.setY(man.getY() + man.getHeight() / 2);
dart.setTarget(vector3.x, vector3.y);
if (this.isLongPressed()) { //如果是长按就变成红色飞镖
dart.setPower(2); // 设置杀伤力为2
dart.setColor(Color.RED); // 设置成红色
}
dartsController.AddDarts(dart);
return super.touchUp(x, y, pointer, button);
}
}

 

效果如下:

05

写在最后

这一篇修改了很多细节,可能部分很小但却关键的修改没有在文中标明。比如为Actor设置名称以便通过findActor方法获取。

如果直接复制有问题,可以从git库获取 https://github.com/htynkn/DartsShaSha,对应的tag为page2。

apk地址是 http://pan.baidu.com/share/link?shareid=331455&uk=4127624209

下一篇将会添加一些声音效果和资源加载,然后会添加一个统计功能。

本文链接

相关 [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.