本文链接: http://blog.csdn.net/xietansheng/article/details/50187485
LibGDX 基础教程(总目录)
动作(Action)是附加在演员身上的在指定时间内随着时间推移而被执行的一些任务逻辑。前面章节介绍的演员都是静态不会动的,给演员附加上动作可以让演员自己“动”起来,相当于 Android 中给 View 添加的动画(Animation)。
LibGDX 中所有的动作均继承自 Action 抽象类,所有的动作实现均在 com.badlogic.gdx.scenes.scene2d.actions 包中。常用的动作可分为 特效类动作 和 控制类动作 两种。
(1)特效类动作
特效类动作会改变演员的状态(位置,尺寸,旋转角度,缩放比,透明度等),还可细分为 绝对动作 和 相对动作。
绝对动作(从 当前状态 过渡到 指定状态 的动作,结果位置确定 并与原位置无关):
相对动作(在 当前状态 基础上给 某一状态的值 增加一个增量,结果位置相对于原位置):
(2)控制类动作
控制类动作本身一般不会改变演员的状态,只是用来控制动作的执行(对动作的包装,本身也是一个动作),是控制动作的动作。
特别注意: 动作需要附加在演员身上执行(本质上其实就是动态改变演员的状态属性值),所以演示动作的使用,首先需要有一个自定义的演员,这里不能直接引用前面章节自定义的演员,因为前面章节的演员绘制时只处理了 位置,尺寸,缩放比 和 旋转角度(分别对应 Move,Size,Scale,Rotate 这四种动作所需要改变的目标状态属性)。这里还有一个 AlphaAction 动作,AlphaAction 所需要改变的目标属性是演员的 alpha 属性,所以要对前面章节自定义的演员进行 增强封装 ,在绘制时 处理 color/alpha 属性 。如下代码所示:
自定义演员(绘制时处理 位置,尺寸,缩放比,旋转角度 和 color/alpha 等属性):
package com.libgdx.test;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
/** * 自定义演员(绘制时处理 位置,尺寸,缩放比,旋转角度 和 color/alpha 等属性) */
public class MyActor extends Actor {
private TextureRegion region;
public MyActor(TextureRegion region) {
super();
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
public TextureRegion getRegion() {
return region;
}
public void setRegion(TextureRegion region) {
this.region = region;
setSize(this.region.getRegionWidth(), this.region.getRegionHeight());
}
@Override
public void act(float delta) {
super.act(delta);
}
@Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
if (region == null || !isVisible()) {
return;
}
/* * 先把 batch 原本的 color 保存起来, 因为 batch 是从外部传进来的, 最好不要改变它原本的状态, * 但在这里需要重新设置 batch 的 color, 所以先保存起来, 等当前方法执行完时再将 batch 原本的 color 设置回去。 */
Color tempBatchColor = batch.getColor();
/* * 实际上演员并没有单独的 alpha 属性, alpha 包含在颜色(color)属性中, rgba color 中的 a 表示 alpha; * 演员有 alpha 值, 而父节点(舞台/演员组)中也有 alpha 值(parentAlpha)。 由于最终在演员节点中才真正把纹理 * 绘制在屏幕上, 才是真正绘制的地方, 而父节点一般用于组织演员, 不会直接绘制任何纹理, 透明度 alpha 值只有在绘制 * 时才能体现出来, 所以父节点无法体现自己的 alpha 值, 因此父节点会将自己的 alpha 值(就是draw方法中的参数 parentAlpha) * 传递给它自己的所有子节点,即最终直接绘制纹理的演员, 让演员结合自身的 alpha 值在绘制时综合体现。 */
// 获取演员的 color 属性
Color color = getColor();
/* * 处理 color/alpha 属性, 即将演员的 rgba color 设置到纹理画布 batch。 * 其中的 alpha 需要结合演员和父节点的 alpha, 即演员的 alpha 与父节点的 alpha 相乘, * 例如父节点的 alpha 为 0.5, 演员的 alpha 为 0.5, 那么最终的显示效果就是 0.5 * 0.5 = 0.25 */
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
// 处理 位置, 缩放和旋转的支点, 尺寸, 缩放比, 旋转角度
batch.draw(
region,
getX(), getY(),
getOriginX(), getOriginY(),
getWidth(), getHeight(),
getScaleX(), getScaleY(),
getRotation()
);
// 将 batch 原本的 color 设置回去
batch.setColor(tempBatchColor);
}
}
游戏主程序的启动入口类(包含所有动作的演示):
package com.libgdx.test;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.AfterAction;
import com.badlogic.gdx.scenes.scene2d.actions.AlphaAction;
import com.badlogic.gdx.scenes.scene2d.actions.DelayAction;
import com.badlogic.gdx.scenes.scene2d.actions.MoveByAction;
import com.badlogic.gdx.scenes.scene2d.actions.MoveToAction;
import com.badlogic.gdx.scenes.scene2d.actions.ParallelAction;
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
import com.badlogic.gdx.scenes.scene2d.actions.RotateByAction;
import com.badlogic.gdx.scenes.scene2d.actions.RotateToAction;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.badlogic.gdx.scenes.scene2d.actions.ScaleByAction;
import com.badlogic.gdx.scenes.scene2d.actions.ScaleToAction;
import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction;
import com.badlogic.gdx.scenes.scene2d.actions.SizeByAction;
import com.badlogic.gdx.scenes.scene2d.actions.SizeToAction;
import com.badlogic.gdx.utils.viewport.StretchViewport;
/** * 游戏程序的启动入口类 */
public class MainGame extends ApplicationAdapter {
private static final String TAG = MainGame.class.getSimpleName();
// 视口世界的宽高统使用 480 * 800, 并统一使用伸展视口(StretchViewport)
public static final float WORLD_WIDTH = 480;
public static final float WORLD_HEIGHT = 800;
private Texture texture;
private Stage stage;
private MyActor actor;
@Override
public void create() {
// 设置 log 输出级别
Gdx.app.setLogLevel(Application.LOG_DEBUG);
// 创建纹理, badlogic.jpg 图片的宽高为 256 * 256
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
// 使用伸展视口(StretchViewport)创建舞台
stage = new Stage(new StretchViewport(WORLD_WIDTH, WORLD_HEIGHT));
// 创建演员
actor = new MyActor(new TextureRegion(texture));
// 添加演员到舞台
stage.addActor(actor);
/* * 动作演示 * * 有些时候动作需要频繁使用, 如果每次要使用动作都重新创建一个对象, 会消耗性能。 * LibGDX 中对动作实例使用了对象池进行了缓存, 使用完一个动作对象后不会直接让系统回收对象, * 而是放到对象池中缓存待下次重复使用。LibGDX 中所有动作实例的获取均通过 Actions 工具类 * 中的静态方法获取(Actions 中实现了对象池缓存)。 * * 获取到一个动作对象后附加在演员身上执行(addAction), 动作执行完后会自动从演员身上移除 * 并放回对象池中缓存。 当然在动作还没有执行完时也可在中途手动移除(removeAction)。 * * 下面对每一种动作的演示放在单独方法中, 按顺序打开相应测试方法的注释进行测试(打开一个方法的 * 注释后必须要注释掉其他测试方法, 否则多个测试同时执行看不出效果)。 */
// 1. 移动动作
testMoveToAction();
// 2. 移动动作(相对)
// testMoveByAction();
// 3. 旋转动作
// testRotateToAction();
// 4. 旋转动作(相对)
// testRotateByAction();
// 5. 缩放动作
// testScaleToAction();
// 6. 缩放动作(相对)
// testScaleByAction();
// 7. 尺寸改变动作
// testSizeToAction();
// 8. 尺寸改变动作(相对)
// testSizeByAction();
// 9. 透明度动作
// testAlphaAction();
// 10. 并行动作
// testParallelAction();
// 11. 顺序动作(包含了延时动作的演示)
// testSequenceAction();
// 12. 重复动作
// testRepeatAction();
// 13. Runnable 动作
// testRunnableAction();
// 14. After 动作
// testAfterAction();
}
/** * 1. 移动动作 */
private void testMoveToAction() {
// 设置演员初始化位置
actor.setPosition(0, 0);
// 获取一个 MoveTo 动作, 3 秒内移动到 (150, 300) 的位置
MoveToAction action = Actions.moveTo(150, 300, 3.0F);
// 将动作附加在演员身上, 执行动作
actor.addAction(action);
/* * 动作执行原理(查看 Actor 和相应 Action 的源码): * * 实际上动作添加到演员身上的后, 动作被存放到一个数组中, 然后在更新演员逻辑的 actor.act()方法中遍历存放动作的数组, * 对每一个动作根据时间步 delta 改变演员相应的状态属性值。然后在绘制演员的 actor.draw() 方法中绘制演员时使用新的 * 状态属性值绘制, 和上一帧相比, 就显的演员被“动”起来了。 */
}
/** * 2. 移动动作(相对) */
private void testMoveByAction() {
// 演员初始化位置设置显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 获取一个 MoveBy 动作
// 2 秒内, 在演员在原位置基础上, 水平方向移动 100, 竖直方向移动 -200
MoveByAction action = Actions.moveBy(100, -200, 2.0F);
// 将动作附近在演员身上, 执行动作
actor.addAction(action);
}
/** * 3. 旋转动作 */
private void testRotateToAction() {
// 设置演员显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 设置演员的初始角度为 -90 度(逆时针为正, 顺时针为负)
actor.setRotation(-90);
// 获取一个 RotateTo 动作, 2 秒内 从原角度 旋转到 -270 度(最终角度为 -270 度)
RotateToAction action = Actions.rotateTo(-270, 2.0F);
// 执行动作
actor.addAction(action);
}
/** * 4. 旋转动作(相对) */
private void testRotateByAction() {
// 设置演员显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 设置演员的初始角度为 -90 度(逆时针为正, 顺时针为负)
actor.setRotation(-90);
// 获取一个 RotateBy 动作, 2 秒内 从原角度基础上增加45度(最终角度为 -90 + 45 = -45 度)
RotateByAction action = Actions.rotateBy(45, 2.0F);
// 执行动作
actor.addAction(action);
}
/** * 5. 缩放动作 */
private void testScaleToAction() {
// 设置演员显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 设置演员的水平和竖直方向初始缩放比分别为 0.5 和 2.0
actor.setScale(0.5F, 2.0F);
// 获取一个 ScaleTo 动作, 2 秒内水平和竖直方向缩放比从 原缩放比 分别缩放到 1.0 和 1.0,
// 最终水平和竖直方向缩放比分别为 1.0 和 1.0
ScaleToAction action = Actions.scaleTo(1.0F, 1.0F, 2.0F);
// 执行动作
actor.addAction(action);
}
/** * 6. 缩放动作(相对) */
private void testScaleByAction() {
// 设置演员显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 设置演员的水平和竖直方向初始缩放比分别为 0.5 和 0.5
actor.setScale(0.5F, 0.5F);
// 获取一个 ScaleBy 动作, 2 秒内水平和竖直方向缩放比从 原缩放比基础上 分别加上 0.5 和 0.5(正数表示增加, 负数表示减少),
// ScaleBy 是对原缩放值单纯的数值相加, 最终水平和竖直方向的缩放比均为: 0.5 + 0.5 = 1.0
ScaleByAction action = Actions.scaleBy(0.5F, 0.5F, 2.0F);
// 执行动作
actor.addAction(action);
}
/** * 7. 尺寸改变动作 */
private void testSizeToAction() {
// 设置演员初始化位置
actor.setPosition(0, 0);
// 获取一个 SizeTo 动作, 2 秒内从原来尺寸变到宽为 150, 高为 300 (最终宽高为 150 * 300)
SizeToAction action = Actions.sizeTo(150, 300, 2.0F);
// 执行动作
actor.addAction(action);
/* * Scale 和 Size 的区别: * * Size 指的是演员的宽高, Scale 指的是演员相对于 Size 的缩放比。 * 如果 Scale 为 0.5, * 当 Size 变为 256 时, 绘制到屏幕上的大小变为 256 * 0.5 = 128, * 当 Size 变为 512 时, 绘制到屏幕上的大小变为 512 * 0.5 = 256, */
}
/** * 8. 尺寸改变动作(相对) */
private void testSizeByAction() {
// 设置演员初始化位置
actor.setPosition(0, 0);
// 演员初始宽高为图片的宽高 256 * 256
// 获取一个 SizeTo 动作, 2 秒内宽高从原来基础上分别增加150, 300
// 最终宽度为: 256 + 150 = 406
// 最终高度为: 256 + 300 = 556
SizeByAction action = Actions.sizeBy(150, 300, 2.0F);
// 执行动作
actor.addAction(action);
}
/** * 9. 透明度动作 */
private void testAlphaAction() {
// 设置演员初始化位置
actor.setPosition(0, 0);
// 设置演员初始 alpha 值为 1(完全不透明, 默认就是为 1)
actor.getColor().a = 1.0F;
// 获取一个 Alpha 动作, 5 秒内 alpha 变为 0(完全透明)
AlphaAction action = Actions.alpha(0.0F, 5.0F);
// 执行动作
actor.addAction(action);
}
/** * 10. 并行动作: 同时 移动, 缩放, 旋转 */
private void testParallelAction() {
// 设置演员初始化状态
actor.setPosition(0, 0);
actor.setScale(0.5F, 0.5F);
actor.setRotation(0);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 移动动作
MoveToAction moveTo = Actions.moveTo(150, 500, 3.0F);
// 缩放动作
ScaleToAction scaleTo = Actions.scaleTo(1.0F, 1.0F, 3.0F);
// 旋转动作
RotateByAction rotateBy = Actions.rotateBy(360.0F, 3.0F);
// 并行动作, 包含 moveTo, scaleTo, rotateBy
ParallelAction parallelAction = Actions.parallel(moveTo, scaleTo, rotateBy);
// 执行并行动作
actor.addAction(parallelAction);
}
/** * 11. 顺序动作(包含了延时动作的演示): 先延时, 然后移动, 再旋转并缩放 */
private void testSequenceAction() {
// 设置演员初始化状态
actor.setPosition(0, 0);
actor.setScale(1.0F, 1.0F);
actor.setRotation(0);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 1. 延时动作, 延时 3 秒
DelayAction delay = Actions.delay(3.0F);
// 2. 移动动作
MoveToAction moveTo = Actions.moveTo(150, 500, 3.0F);
// 3. 并行动作, 包含 缩放 和 旋转 两个动作
ParallelAction parallel = Actions.parallel(
Actions.scaleTo(0.5F, 0.5F, 3.0F),
Actions.rotateBy(360.0F, 3.0F)
);
// 顺序动作, 包含 delay, moveTo, parallel
SequenceAction sequenceAction = Actions.sequence(delay, moveTo, parallel);
// 执行顺序动作
actor.addAction(sequenceAction);
}
/** * 12. 重复动作: 重复 缩小, 放大 */
private void testRepeatAction() {
// 设置演员显示到舞台中心
actor.setPosition(
actor.getStage().getWidth() / 2 - actor.getWidth() / 2,
actor.getStage().getHeight() / 2 - actor.getHeight() / 2
);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 设置演员初始缩放比
actor.setScale(1.0F, 1.0F);
// 顺序动作: 先缩小到 0.5, 再放大到 1.0
SequenceAction sequence = Actions.sequence(
Actions.scaleTo(0.5F, 0.5F, 2.0F),
Actions.scaleTo(1.0F, 1.0F, 2.0F)
);
// 重复动作: 重复执行 sequence
RepeatAction repeatAction = Actions.forever(sequence);
// 执行重复动作
actor.addAction(repeatAction);
}
/** * 13. Runnable 动作: 适当时机执行自己的代码, 与顺序动作一起使用可用于监听某个动作的完成 */
private void testRunnableAction() {
// 设置演员初始化状态
actor.setPosition(0, 0);
// 移动动作
MoveToAction moveTo = Actions.moveTo(150, 300, 3.0F);
// Runnable 动作
RunnableAction runnable = Actions.run(new Runnable() {
@Override
public void run() {
// 打印一句 log 表示动作已执行
Gdx.app.log(TAG, "The runnable action has been running.");
}
});
// 顺序动作: 在 moveTo 动作执行完后执行 runnable 动作
SequenceAction sequence = Actions.sequence(moveTo, runnable);
// 执行顺序动作
actor.addAction(sequence);
}
/** * 14. After 动作: 可用于监听演员动作的执行完成 */
private void testAfterAction() {
// 设置演员初始化状态
actor.setPosition(0, 0);
actor.setRotation(0);
// 缩放和旋转支点设置到演员中心
actor.setOrigin(actor.getWidth() / 2, actor.getHeight() / 2);
// 移动动作, 移动 3 秒
MoveToAction moveTo = Actions.moveTo(150, 300, 3.0F);
// 旋转动作, 旋转 2 秒
RotateByAction rotateBy = Actions.rotateBy(360.0F, 2.0F);
// Runnable 动作
RunnableAction runnable = Actions.run(new Runnable() {
@Override
public void run() {
// 打印一句 log 表示动作已执行
Gdx.app.log(TAG, "演员的其他所有动作都已经执行完了");
}
});
// After 动作, 包含一个 runnable 动作
AfterAction afterAction = Actions.after(runnable);
// 同时添加多个动作到演员: 将立即执行 moveTo 和 rotateBy, 都执行完后执行 afterAction
actor.addAction(moveTo);
actor.addAction(rotateBy);
actor.addAction(afterAction);
}
@Override
public void render() {
// 使用淡蓝色清屏
Gdx.gl.glClearColor(0.75F, 1, 0.98F, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// 更新舞台逻辑
stage.act();
// 绘制舞台
stage.draw();
}
@Override
public void dispose() {
// 释放资源
if (stage != null) {
stage.dispose();
}
if (texture != null) {
texture.dispose();
}
}
}
动作演示较多,结果也较复杂,这里不再展示运行结果。