Libgdx粒子效果介绍与使用心得

  • Libgdx粒子效果介绍与使用心得
    • 概述
      • 前言
      • 基本概念
    • 粒子效果ParticleEffect
      • 粒子效果编辑器
    • 常用属性设置
    • 将粒子效果加入游戏
    • 使用心得
      • 粒子效果生命周期
      • 实现稍复杂的动画效果

Libgdx粒子效果介绍与使用心得

我也将这篇文章发表在了知乎。
Libgdx粒子效果介绍与使用心得

概述

前言

我在本学期的面向对象编程课程中,参与到Devotees战棋游戏项目开发中,主要承担美术工作。我在绘制所有的图片素材的同时,编写了一些与视觉效果有关的代码,如粒子特效、分辨率调整,以及部分UI的布局和响应。我们使用的Libgdx不同于游戏引擎,它更接近于底层一些,许多东西不能满足我们的需求,还需要自己“造轮子”,加上没有可视化开发界面,对于视觉效果的调整更是困难重重。在此分享我对于Libgdx粒子效果的一些心得或踩过的坑。

基本概念

在阅读本篇文章前,你需要了解:

Libgdx是免费开源的Java游戏框架,提供了若干用于游戏开发的类;

Actor是Libgdx中的一个类,是游戏场景中的基本元素;

ParticleEffect是Libgdx中的一个类,用于构建粒子效果;

Stage是Libgdx中的一个类,用于展示多个Actor,能够处理视口和事件响应等;

Batch是Libgdx中的一个类,用于统一在屏幕上绘制图像。

粒子效果ParticleEffect

粒子效果在游戏中展示闪光、火焰、爆炸、烟雾等效果时很有用,在Devotees战棋游戏中我将粒子效果用于射弹的飞行以及爆炸效果、近战的刀光(也是以射弹实现的)、还有结算界面的光效。在这里介绍我使用粒子效果的方法:用可视化粒子效果编辑器创建粒子文件、在代码中加载并进一步设置、播放和结束。图1是Devotees中角色“混沌执事”和其射弹爆炸时的样子。


图1 混沌执事的一个粒子效果

粒子效果编辑器

Libgdx提供了可运行的可视化粒子效果编辑器Particle Editor,它的界面如图2所示。


图2 Particle Editor

粒子效果包括一个或若干个粒子发射器Emitter,粒子发射器将在一段时间内发射粒子,从左上角的Effect Emitters面板可以管理发射器,如新建、复制、删除、调整上下层级等;保存和打开操作是针对整个粒子效果文件的。

左下角的面板可以预览目前的粒子效果。

右上角的Editor Properties面板用于调整预览面板的缩放、背景等属性;建议将背景颜色调整为与游戏背景接近的颜色,这样能够更好地估计当前粒子效果在游戏中的实际表现。

右下角的Emitter Properties面板最为重要,在其中可以调整当前选中的发射器的属性,来改变整个粒子效果的外观。

常用属性设置

粒子发射器ParticleEmitter的常用属性如表1所示。

表1 发射器常用属性

属性名 说明
Image 每个粒子的材质,在图3中的粒子效果中,多个发射器均使用了一张由5个像素点组成的图片为材质
Count 发射器的最大、最小粒子数
Duration 发射器的持续时间,单位为ms
Emission 发射器每秒发射的粒子数
Life 粒子的持续时间
X Size 粒子的大小,如果Y Size属性未被激活则等比例缩放
Velocity 粒子运动速度
Angle 粒子发射角度
Rotation 粒子自转角度
Tint 粒子颜色
Transparency 粒子不透明度
Continuous 粒子发射器是否持续发射,默认为false,如果为true则Duration为无穷大,且isComplete()永远为false
Attatched 粒子是否对齐于发射器,默认为false,如果为true则粒子随发射器移动而移动


图3 以十字为材质的粒子效果

大多数属性都可以设置HighMin、HignMax、LowMin、LowMax四个值,以及一条变化曲线,发射器大致遵循这样的规则:在产生一个粒子时,在HighMin-HighMax中随机选择一个值h作为变化曲线的上限,在LowMin-LowMax中随机选择一个值l作为变化曲线的下限,并根据曲线在粒子的生命Life中根据变化曲线在上下限之间进行变化,得到某一时刻该粒子的这项属性值。

注意Attatched这个属性。Libgdx中的粒子效果默认参照屏幕坐标系,即每个粒子在生成后参照屏幕进行运动,而不管发射器如何运动;在Devotees中,我们有时需要拖动整个地图,如果地图拖到了一边,而粒子却纹丝不动,这无疑是非常奇怪的,加上我只做了几个直线发射的射弹,不需要太复杂的运动,因此在我的项目中勾选了这一项,并设置发射器跟随地图移动,这样粒子也就以地图为参照系运动了。

在添加发射器和材质、完成属性设置后点击保存,我们就得到了一个粒子效果文件,之后将把它加载到游戏中。

考虑到许多属性需要动态设置,如弹道需要根据游戏内的发射位置和目标位置设置其方向,因此利用代码而不是编辑器来设置属性也是必要的,一般来讲设置代码像是这样的形式:

explode.getEmitters().get(i).getAngle().setHighMax(dir+EXPLODEANGLE);

通过getEmitters().get(i)得到该粒子特效的第i个发射器,getAngle()得到发射器的Angle属性,setHighMax()将设置这一属性的HighMax值为dir+EXPLODEANGLE。

将粒子效果加入游戏

总地来说,将粒子效果加入游戏步骤包括:在一个Actor类中创建ParticleEffect类的实例,并重写draw方法,使粒子效果显示出来,再在Stage中加入这个Actor的实例。

这里需要用到的方法如表2所示,除了构造方法外其他方法返回类型均为void。

表2 绘制粒子效果相关方法

方法名 参数 说明
ParticleEffect() ParticleEffect的构造方法,创建一个空的粒子效果
ParticleEffect.load(FileHandle effectFile, FileHandle imagesDir) effectFile是粒子效果文件,而imagesDir是粒子材质所在地址(由于同一个粒子效果可能有多种材质) 从文件中加载粒子效果
ParticleEffect.Start() 开始播放粒子效果
ParticleEffect.setPosition(float x, float y) x,y是横纵坐标 设置粒子效果的位置
ParticleEffect.draw(Batch batch, float deltaTime) batch,delta是距离上一次绘制经过的时间 绘制粒子效果

注意start()这个方法的使用,应当确保其在整个粒子效果的使用过程中仅调用一次,因为似乎执行的结果是将粒子效果接下来的待运行时间加上Duration,如果多次调用则会使其实际上的发射时间为n*Duration,导致意料之外的情况发生。

以Devotees中Bullet类的代码为例进行说明。

第一部分:

public class Bullet extends Actor {...protected ParticleEffect effect;protected ParticleEffect explode;.../**** 生成射弹* @param unit 产生射弹的单位种类* @param from 发射方块* @param to 目标方块* @param atk 伤害*/public Bullet(UnitCategory unit, Block from, Block to,int atk){...effect=new ParticleEffect();explode=new ParticleEffect();String indx=unit.toString();effect.load(Gdx.files.internal(indx+"/Bullet.p"),Gdx.files.internal(indx+"/"));explode.load(Gdx.files.internal(indx+"/Explode.p"),Gdx.files.internal(indx+"/"));...}
}

第一部分包括了定义成员,我们用到的两个粒子效果effect和explode分别是飞行中效果和击中后爆炸效果。注意这里需要实现代码复用,因为游戏中多个角色的不同射弹都可以以Bullet类为基础制作,所以Bullet类的大部分成员可见性都为protected,便于继承时更改;定义成员后在Bullet类构造方法中创建Particle实例并加载文件,这里文件的地址和参数unit有关,实际上写成相对于assets文件夹的路径即可。

第二部分:

public class Bullet extends Actor {.../**** 初始化设置粒子效果* effect为射弹效果* explode为击中后爆炸效果*/protected void particleInit(){float zoomline[]={0,1,0};int top=effect.getEmitters().size;for(int i=0;i<top;i++){effect.getEmitters().get(i).setMaxParticleCount(MAXEFFECTPTC);...effect.getEmitters().get(i).getRotation().setHigh(360);effect.getEmitters().get(i).getRotation().setLowMax(720);effect.getEmitters().get(i).getRotation().setLowMin(0);}top=explode.getEmitters().size;for(int i=0;i<top;i++){explode.getEmitters().get(i).setMaxParticleCount(MAXEXPLODEPTC);...explode.getEmitters().get(i).getRotation().setLowMax(360);explode.getEmitters().get(i).getRotation().setLowMin(0);}effect.start();     //使effect开始播放}.../**** 绘制射弹* @param batch* @param parentAlpha*/public void draw(Batch batch, float parentAlpha) {if(lifetime<=0&&!end){end=true;explode.setPosition(getX()+effectOffset.x,getY()+effectOffset.y);EXPLODEOFFSETX=getX()+effectOffset.x-to.getX();EXPLODEOFFSETY=getY()+effectOffset.y-to.getY();endBehavior();}...effect.setPosition(getX()+effectOffset.x,getY()+effectOffset.y);explode.setPosition(to.getX()+EXPLODEOFFSETX,to.getY()+EXPLODEOFFSETY);if(!end) {batch.draw(new TextureRegion(bulletTexture), getX(), getY(), getOriginX(), getOriginY(),getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());}else {if(!hit&&effect.isComplete()) {         //判断粒子效果是否结束getStage().getRoot().removeActor(this);if(!effectDone)missFinalBehavior();effectDone=true;}if(hit)explode.draw(batch,Gdx.graphics.getDeltaTime()); //绘制explodeif(explode.isComplete()&&effect.isComplete()) {          //判断粒子效果是否结束if(!effectDone)hitFinalBehavior();effectDone=true;getStage().getRoot().removeActor(this);}}if(!effectDone)effect.draw(batch,Gdx.graphics.getDeltaTime());      //绘制effect}
}

第二部分首先通过particleInit()方法批量对粒子效果进行设置,由于一些参数需要经过子类计算再实现,这使得子类构造方法中应当先调用父类构造方法,再计算需要的参数,最后对粒子效果进行设置,因此这部分代码不应写在Bullet的构造方法中;再重写draw方法,在适当的时机绘制粒子效果。

当然添加粒子效果有其他方法,但是ParticleEffect和Actor的draw方法涉及Batch等问题,以上方法能够方便地获取Batch,比较方便,也避免了多个Batch可能导致的预料之外的问题。

使用心得

以下是我在使用粒子效果时的一些心得,其中有主观猜测的成分,但当我这样理解、这样写的时候它确实达到了我想要的效果,因此如果有理解或者实践上的错误,欢迎指正。

粒子效果生命周期

如何判断粒子效果是否结束?我们需要将包含粒子效果的演员Actor在使用后删除,在这个时候,如果粒子效果还在产生或已经产生的粒子还未消散,直接将Actor移除会十分生硬,因此需要判断粒子效果是否结束,而完成这一判断的是方法isComplete(),它返回粒子效果是否已经完成,仅在所有粒子发射器不再发射粒子、最后一个粒子生命结束后返回true,否则返回false。

和粒子效果生命周期有关的两个参数是Duration和Life,假设在你的设置中,粒子发射器直到Duration的最后一刻粒子总数仍未到达上限,或者先前产生的粒子有所消散,即仍在产生新的粒子,那么这种情况下从粒子效果start()到isComplete()==true的时间约为Duration+Life,如图4所示。

图4 粒子效果生命周期

了解这一点有什么用呢?如果如果游戏中的射弹在击中后,空中仍然留着尾迹,岂不是会更帅?Devotees中的射弹Bullet就能够实现的设计,其生命周期图如图5所示。

图5 Bullet的生命周期

射弹在effect的Duration结束后总会执行endBehavior(),其中包括了诸如对单位造成伤害、爆炸效果explode.start()等方法。射弹有两种情况,命中和未命中,未命中时不会播放explode;参考之前的代码可知,一旦两个粒子效果均已结束,那么射弹Bullet将被从舞台Stage移除。Bullet的命中效果如图6所示,可以看到即使射弹命中后仍留有微弱的尾迹。

图6 Bullet命中前后

实现稍复杂的动画效果

在上一部分你应该注意到一些之前没怎么提到的方法:endBehavior()、missFinalBehavior()以及hitFinalBehavior(),它们与Bullet的生命周期高度相关,能够在特定时机执行一些动作,根据不同的子类而变化,比如与角色互动、生成另一个射弹。

这里我给单位“混沌执事”增加了一个引导的动作ChantMagic,他需要蓄力一小段时间才会发出火球。这一过程也用弹道来实现,但在Libgdx上实现这一效果似乎并非易事,要知道,Libgdx的粒子效果不像Unity那样可以倒放,而且即使发射器形状设为一定的形状,其本质还是多个相对独立的点状发射器,不能根据发射位置来调整方向,也就没法使得其聚到某一点上,那么如何实现这一效果呢?

考虑如下设置:首先将发射角度设为0-360°,这样粒子将会向四面八方发射,将速度的High和Low设为相反数,并将曲线设置成从线性降低,这样粒子表现为发射后又向中心聚集,由于我们只想要粒子聚集的效果,因此将粒子不透明度Transparency生命周期的前半段设置为0,即透明,这样只会显示其生命周期的后半部分,即聚集阶段。相关设置如图7所示,注意其他曲线的变化也要考虑其实际显示仅有生命周期的后半部分。


图7 聚集粒子效果设置

这样得到的粒子效果在聚集的时候会有一个加速度,表现出来的效果仿佛中心确实有什么在吸引着它们;但实际上这一方案有一个比较明显的缺点,那就是显示会有延迟,当对单位下达命令后,需要等待一下,相当于粒子生命周期一半的时间后,第一个向中心聚集的粒子才会显示到屏幕上,然而我暂时没有想出更好的方法。实际游戏中这一粒子效果的表现如图8所示。

图8 ChantMagic效果

接下来,将ChantMagic设为hit=false,在ChantMagic的missFinalBehavior()方法中发射“混沌执事”的攻击射弹MagicBullet,还记得这个方法吗?在hit==false的情况下,他将在ChantMagic的最后一个粒子消散,即被“混沌执事”暴风吸入之后执行,具体代码如下。

package team.devoteens.game.unit.behaviors.bullets;import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import team.devoteens.game.UI.Resolution;
import team.devoteens.game.board.Block;
import team.devoteens.game.unit.UnitCategory;
import team.devoteens.game.unit.UnitData;public class ChantMagic extends Bullet{...public ChantMagic(Block from, Block to){super(UnitCategory.Wiz, from, to,UnitData.getInstance().getAtkOfUnitCategory(UnitCategory.Wiz));String indx=UnitCategory.Wiz.toString();effect.load(Gdx.files.internal(indx+"/Chant.p"), //设置新的粒子效果Gdx.files.internal(indx+"/"));bulletTexture= new Texture(Gdx.files.internal(indx+"/Empty.png"));       //设置射弹材质为空...hit=false;...}@Overrideprotected void missFinalBehavior() {to.getStage().addActor(new MagicBullet(from,to)); //发射MagicBullet}
}

注意我们并不希望这个特殊的“射弹”真的发射什么东西,因此将其射弹材质设置为Empty.png,这个图片中只包含一个透明像素。

Libgdx粒子效果介绍与使用心得相关推荐

  1. iOS动画开发之五——炫酷的粒子效果

    iOS动画开发之五--炫酷的粒子效果 在上几篇博客中,我们对UIView层的动画以及iOS的核心动画做了介绍,基本已经可以满足iOS应用项目中所有的动画需求,如果你觉得那些都还不够炫酷,亦或是你灵光一 ...

  2. 【Cocosd2d实例教程七】Cocos2d实现超炫的粒子效果!!

    (转载请注明出处:http://blog.csdn.net/buptgshengod) 1.介绍      你想拥有炫酷的效果么,你想让你的应用亮瞎别人的狗眼么!!不要急,这里告诉大家怎么实现绚丽的粒 ...

  3. Unity特效基础:粒子效果面板

    如何在Unity中实现粒子效果? 首先,右键点击 Hierarchy栏,选择effects->Particle System,这样你就新建了一个粒子系统,如图所示: 下面来介绍右侧监视器(Ins ...

  4. 粒子效果动画使用总结

    我们常见的一些像下雪.下雨.火苗这类的动画,都是可以使用粒子效果来实现.主要使用了类CAEmitterLayer和CAEmitterCell来实现.下面我们将通过实现一个下雪的效果来说明该类的使用方法 ...

  5. 三维电子无人机倾斜摄影数字沙盘开发第38课 实现简单的粒子效果

    三维电子无人机倾斜摄影数字沙盘开发第38课 实现简单的粒子效果 设置system.ini 如下内容 Server=122.112.229.220 user=GisTest Password=china ...

  6. 超好看的粒子效果文字动画特效HTML5源码

    介绍: 一款基于HTML5 Canvas的文字特效原作者(Shape Shifter),输入框中输入想要展示的文字,回车后即可在canvas上绘制出粒子效果的文字动画,相当酷的动画效果.喜欢的可以下载 ...

  7. 融入动画技术的粒子效果文字动画交互应用

    写在前面 本次实现的交互系统是基于粒子系统的粒子文本效果.本次课程设计主要参考代码本色一书中的内容,系统应用中运用了 <代码本色> 第一章 向量.第二章 力.第四章 粒子系统等章节的动画技 ...

  8. 【前端】particle.js页面粒子效果

    一.效果 二.使用 GitHub地址:https://github.com/VincentGarreau/particles.js html中引入: <div id="particle ...

  9. AE472 美丽动感金银粒子散发背景漏光组合几何图形扩展喷发粒子效果标题幻灯片ae片头模板

    AE472 美丽动感金银粒子散发背景漏光组合几何图形扩展喷发粒子效果标题幻灯片ae片头模板 [AE模板介绍] 模板用途:干净,商业,公司,金色,简介,字幕,现代,开场白,演示,银色,文本,标题,婚礼 ...

最新文章

  1. hive启用本地模式
  2. oracle 取系统当前年份_Oracle中如何获取系统当前时间
  3. 汇编语言(六)之输出字符的前导后字符
  4. STL源码剖析 第八章 配接器
  5. 微软发布人工智能教育与学习共建社区
  6. netty的零拷贝、架构设计、ByteBuf扩容机制详解
  7. Eclipse错误:Syntax error on tokens, delete these tokens问题解决
  8. 微博java版_新浪微博JAVA通用版
  9. HBuilderX App开发环境搭建
  10. JavaScript开发环境准备
  11. 通过TXT文件批量制作ITF-14条码
  12. 计算机开题报告课题来源,开题报告课题来源该怎么写
  13. Docker私有化部署RSSHub全记录
  14. React 优化:懒惰加载(lazy loading)
  15. linux c 获取usb vid,Linux如何使用libudev获取USB设备VID及PID
  16. 牛逼,一个开源,高隐私,自架自用的聚合搜索引擎
  17. Python3.x+迅雷x 自动下载高分电影
  18. GMM-EM in Mnist
  19. win10尘埃4点击开始游戏自动关闭没反应|dirt4.exe进程消失的解决方法
  20. .Net微服务架构:API网关

热门文章

  1. GNU Bison 2.1 中文手册
  2. 重视六大职场面试礼仪
  3. OpenStack 云主机的创建
  4. 【设计模式05】单例模式
  5. 企业微信裂变获客,粉丝增长的优势在哪?
  6. Body estimation 论文阅读笔记(3):Unipose:Unified Human Pose Estimation in Single Images and Videos Bruno Ar
  7. 捕鱼问题 matlab,鱼捕捞问题(数学建模).docx
  8. uniapp小程序唤醒微信支付
  9. Java - MyBatis中的动态SQL是什么意思?
  10. 数学不好大学可以学计算机吗,高中数学不好的人大学计算机系能学好吗