Android粒子系统库——DroidParticle
来源:互联网 发布:无人机源码 编辑:程序博客网 时间:2024/05/18 03:43
Android粒子系统库——DroidParticle
今天给大家介绍一款粒子系统库,并简要介绍下粒子系统的工作原理。
首先这款名为DroidParticle的库其实就是我自己没事做的,因为以前看过HGE的C++的粒子系统,觉得很有趣,现在从事Android开发工作就模仿着做了一个,希望对大家有用处。
先给大家看一下效果:
源代码下载地址:
https://github.com/sunty2016/DroidParticle
下面言归正传。
1. 何谓粒子系统
做视觉效果的时候,有时会用到火焰、云雾、光影,而如果用一般贴图的方式制做,则效果比较差。
后来有人便根据这些事物的视觉特性发明了一种方法来模拟:将同一张很简单的图片不断地贴到画布上,每次贴上去的图片都只存在一小段时间,而在这段时间内它的位置,大小,颜色,透明度会发生变化。当大量的贴图诞生、变化、死亡,画布上便会呈现出与源图片完全不同的效果。比如说上面第二张火焰图完全是由一张画着字母“Z”的图片贴图而成的,读者们能看出来么?
粒子系统便是这种方法的软件实现,其中每个贴到画布进行变化的贴图叫做粒子。
目前许多绘图、建模工具支持粒子系统的制作。而本文介绍的DroidParticle则是用编程的方式在Android应用中直接实现的粒子系统。
2. DroidParticle的实现
要实现粒子系统,则“粒子系统”和“粒子”是两个很好识别出的类
其中表示粒子的类代码如下:
public class Particle { public static class Inter { public float get(float v0, float v1, float t) { return v0 + (v1 - v0) * t; } } public static class Var { public Var() { inter = new Inter(); } public void set(float v0, float v1) { this.v0 = v0; this.v1 = v1; this.vt = v0; } public void set(Var other) { this.v0 = other.v0; this.v1 = other.v1; this.vt = other.vt; } public void update(float t) { this.vt = inter.get(v0, v1, t); } public float v0; public float v1; public float vt; public Inter inter; } public Particle() { velo__ = new Var(); bias__ = new Var(); spin__ = new Var(); scale__ = new Var(); alpha__ = new Var(); red__ = new Var(); green__ = new Var(); blue__ = new Var(); matrix = new Matrix(); colorMatrix = new ColorMatrix(); colorFilter = new ColorMatrixColorFilter(colorMatrix); } public long durInMillis; public long timeInMillis; public long startTimeInMillis; public float x, y; // pixel public float theta; // degree public float rot; // degeee public float scale; // 1.0 public float alpha; // 1.0 public float red; // 1.0 public float green; // 1.0 public float blue; // 1.0 public Bitmap bitmap; public Matrix matrix; public ColorMatrix colorMatrix; public ColorMatrixColorFilter colorFilter; public boolean deleteMark; public Var velo__; // pixel per second public Var bias__; // degree per second public Var spin__; // degree per second public Var scale__; // 1.0 public Var alpha__; // 1.0 public Var red__; // 1.0 public Var green__; // 1.0 public Var blue__; // 1.0 static private final double PI_D_180 = Math.PI / 180.0; public void config(Bitmap image, int x, int y, ParticleSystemConfig config, Random random) { this.bitmap = image; this.x = x; this.y = y; config.setParticle(random, this); } public void reset(long timeInMillis) { this.startTimeInMillis = timeInMillis; this.timeInMillis = timeInMillis; this.deleteMark = false; } public boolean update(long timeInMillis) { matrix.reset(); matrix.postTranslate(-bitmap.getWidth() / 2, -bitmap.getHeight() / 2); matrix.postScale(scale, scale); matrix.postRotate(rot); matrix.postTranslate(x, y); colorMatrix.reset(); colorMatrix.setScale(red, green, blue, alpha); ColorMatrixFilterHelper.setFilterMatrix( colorFilter, colorMatrix); if (this.timeInMillis - this.startTimeInMillis >= this.durInMillis) { return true; } double dtInSeconds = (double)(timeInMillis - this.timeInMillis) * 0.001; double rad = theta * PI_D_180; double dd = velo__.vt * dtInSeconds; //Log.i("STY", String.format("velo__.vt %f, dt %f, dd %f", velo__.vt, dt, dd)); x += dd * Math.cos(rad); y += dd * Math.sin(rad); theta += bias__.vt * dtInSeconds; rot += spin__.vt * dtInSeconds; scale = scale__.vt; alpha = alpha__.vt; red = red__.vt; green = green__.vt; blue = blue__.vt; this.timeInMillis = timeInMillis; float t = (float)(this.timeInMillis - this.startTimeInMillis) / (float)this.durInMillis; velo__.update(t); bias__.update(t); spin__.update(t); scale__.update(t); alpha__.update(t); red__.update(t); green__.update(t); blue__.update(t); return false; } public void draw(Canvas canvas, Paint paint) { paint.setColorFilter(colorFilter); canvas.drawBitmap(bitmap, matrix, paint); }}Particle类中,Var类型的成员表示在粒子生命周期中需要变化的参数,Var的v0表示起始值,v1表示结束值,vt表示当前值:
- velo__: 移动速度大小
- bias__: 前进路线的偏移(角)速度(比如开始让粒子沿X轴前进,1秒后沿Y轴前进,则该值设为90)
- spin__: 自旋角速度
- scale__: 缩放
- alpha__: 透明度
- red__, green__, blue__: RGB三色占比
- x, y: 位置
- theta: 移动方向(角度)
- rot: 自旋角度
- scale,alpha,red,green,blue: 参考Var成员
当一个Particle对象先被调用config来设置这些参数,然后没隔一小段时间调一次update和draw。在update中先由当前状态变量更新matrix和colorMatrix,再由Var变量更新当前状态变量;在draw中则应用matrix和colorMatrix最终绘图。
实现粒子系统的类中主要有两处重要的地方,一个是粒子的更新相关代码,另一个是生产粒子的代码:
final private Runnable mUpdatePost = new Runnable() { @Override public void run() { mSimpleTimer.mark(); if (mState != STATE_BUSY && mState != STATE_STOPPING) { return; } long time = System.currentTimeMillis(); ++mFrameCount; synchronized (mParticles) { updatePtc(time); if (mState == STATE_STOPPING && mParticles.size() == 0) { mState = STATE_READY; if (mOnStateChangeListener != null) { mOnStateChangeListener.onStateChanged(STATE_READY, STATE_STOPPING); } } } mParticleSystemView.postInvalidate(); long cost = mSimpleTimer.mark(); mHandler.postDelayed(this, mDpf - cost); } }; private void updatePtc(long time) { for (int i = 0; i < mParticles.size(); ++i) { Particle ptc = mParticles.removeFirst(); if (ptc.deleteMark) { ParticlePool.get().recycle(ptc); } else { ptc.deleteMark = ptc.update(time); mParticles.addLast(ptc); } } if (mNewBlend != mBlend) { mBlend = mNewBlend; if (mBlend == 0) { mPaint.setXfermode(null); } else if (mBlend == 1) { mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); } } } final private Runnable mSpawnPost = new Runnable() { @Override public void run() { mSimpleTimer.mark(); if (mState != STATE_BUSY) { return; } long time = System.currentTimeMillis(); if (mPtcImage != null) { ++mPtcCount; Particle ptc = ParticlePool.get().obtain(); ptc.config(mPtcImage, mX, mY, mConfig, mRandom); ptc.reset(time); ptc.update(time); synchronized (mParticles) { mParticles.push(ptc); } } long cost = mSimpleTimer.mark(); mHandler.postDelayed(this, mDpp - cost); } };这里的实现主要注意了以下几个问题:
- 粒子更新的速度和粒子产生的速度并不一致。mDpf表示delay per frame,而mDpp表示delay per particle。
- 由于在此处代码要被大量地调用,根据Google的性能建议,这里应该尽量少new新对象,以避免GC被频繁调用。因此对Particle采取obtain/recycle的模式进行复用。相信Android自己的Parcel和MotionEvent等也是出于相同的考虑。
- 基于同样的原因,内部的循环不使用“for (Particle ptc : mParticles) {}”这类写法,以避免产生大量隐蔽的enumerator对象
- 这里mParticles被同步保护的原因是,View会在主线程中访问mParticles并将其中的粒子逐个画出,而这里的mHandler是另外一个HandlerThread的Handler。
除Particle类和ParticleSystem类外,ParticleSystemView类也是重要的类,正是它使粒子系统被画在Android应用里。
ParticleSystemView直接继承自View,可用在layout的资源文件中。
3. DroidParticle的使用
使用方式非常简单,直接给代码:
public class MainActivity extends Activity { ParticleSystemView mPtcSysView; ParticleSystem mPtcSys1; ParticleSystem mPtcSys2; ParticleSystem mPtcSys3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPtcSysView = (ParticleSystemView) findViewById(R.id.canvas); mPtcSys1 = mPtcSysView.createParticleSystem(); mPtcSys2 = mPtcSysView.createParticleSystem(); mPtcSys3 = mPtcSysView.createParticleSystem(); BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ptc16); Bitmap img = drawable.getBitmap(); mPtcSys1.setPtcBlend(1); mPtcSys1.setFps(40); mPtcSys1.setPps(30); mPtcSys1.setPtcImage(img); mPtcSys1.setConfig(createConfig(1)); mPtcSys2.setPtcBlend(1); mPtcSys2.setFps(40); mPtcSys2.setPps(30); mPtcSys2.setPtcImage(img); mPtcSys2.setConfig(createConfig(2)); mPtcSys3.setPtcBlend(1); mPtcSys3.setFps(40); mPtcSys3.setPps(30); mPtcSys3.setPtcImage(img); mPtcSys3.setConfig(createConfig(3)); mPtcSysView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { v.removeOnLayoutChangeListener(this); mPtcSys1.setPtcPosition(v.getWidth() / 6, v.getHeight() / 2); mPtcSys2.setPtcPosition(v.getWidth() / 2, v.getHeight() / 2); mPtcSys3.setPtcPosition(v.getWidth() * 5 / 6, v.getHeight() / 2); mPtcSys1.start(); mPtcSys2.start(); mPtcSys3.start(); } }); } @Override protected void onDestroy() { super.onDestroy(); mPtcSysView.releaseParticleSystem(mPtcSys1); mPtcSysView.releaseParticleSystem(mPtcSys2); mPtcSysView.releaseParticleSystem(mPtcSys3); } static private ParticleSystemConfig createConfig(int id) { ParticleSystemConfig config = new ParticleSystemConfig(); config.duration.set(1000, 0); config.theta.set(270, 15); config.startVelocity.set(400, 0); config.endVelocity.set(400, 0); config.startAngularRate.set(0, 0); config.endAngularRate.set(0, 0); config.startSpinRate.set(360, 0); config.endSpinRate.set(360, 0); config.startScale.set(1, 0); config.endScale.set(1.5f, 0); config.startAlpha.set(1, 0); config.endAlpha.set(0.75f, 0); if (id == 1) { config.startRed.set(1, 0); config.endRed.set(1, 0); config.startGreen.set(0, 0); config.endGreen.set(1, 0); config.startBlue.set(0, 0); config.endBlue.set(0, 0); } else if (id == 2) { config.startRed.set(0, 0); config.endRed.set(0, 0); config.startGreen.set(1, 0); config.endGreen.set(1, 0); config.startBlue.set(0, 0); config.endBlue.set(1, 0); } else if (id == 3) { config.startRed.set(0, 0); config.endRed.set(1, 0); config.startGreen.set(0, 0); config.endGreen.set(0, 0); config.startBlue.set(1, 0); config.endBlue.set(1, 0); } return config; }}可以看出同一个ParticleSystemView中可以同时建立多个particle system。但不建议建立太多system,会影响性能。
总结
一般粒子系统的实现都会基于图形库的支持,比如OpenGL等,但是DroidParticle完全是基于Android标准API。这样做的好处是实现、使用起来比较方便,也很轻量级,但缺点就是一来被束缚在2D上,无法扩展为三维粒子库,二来性能上也会差一些。不过就一般的效果而言DroidParticle是完全够用的。
另外GitHub源代码中含有Demo程序,可以帮助读者理解粒子各个参数的作用:
https://github.com/sunty2016/DroidParticle
0 0
- Android粒子系统库——DroidParticle
- OpenGL——粒子系统
- Unity3D——粒子系统
- android-粒子系统
- android粒子系统
- Android粒子系统
- Cult3D基础教程——4.粒子系统
- Cocos-js——粒子系统
- 摸爬滚打DirectX11_day_11——三维粒子系统
- 总体性能——粒子系统性能
- Q&A——粒子系统
- 总体性能——粒子系统性能
- 群体智能——粒子仿真系统
- 粒子系统—雪花粉飞
- Cocos2d-x—粒子系统的实现
- unity学习笔记—一部分粒子系统
- android opengl es 粒子系统
- android游戏物理引擎开发——粒子系统(二)
- java 字母数字和下划线 正则表达式
- QTP的退出函数
- SSMTP—让Linux系统从Office 365发送邮件
- clipse中使用maven插件的时候,运行run as maven build的时候报错:
- 图片处理(二)之亮度调整
- Android粒子系统库——DroidParticle
- leetcode_c++:哈希:Substring with Concatenation of All Words(030)
- Swift入门基础语法
- Css文本框样式
- Chrome中字体设置小于12px后无法显示解决办法
- J2EE开发:struts2 文件上传(单个文件与多个文件)
- iOS工程中创建库工程
- Guava Futures异步回调机制源码解析
- [2016/06/25] LeetCode / Java - Day 3 -