android 粒子爆炸特效

最近有个需求,需要在view爆炸的动画,搜索了一下,已经有人做到了类似的动画,我参考了下,改进出自己的爆炸效果。

参考文章:blog.csdn.net/crazy__chen/article/details/50149619/

框架基本没变主要是:每个粒子携带自身的运动信息,valueAnimation的getAnimatedValue()来获得每次绘制的时间点,根据时间点来计算当前粒子应该在的位置然后进行绘制。

粒子的类:

package com.example.administrator.viewexplosion.particle;

import android.graphics.*;
import com.example.administrator.viewexplosion.math.Parabola;

import java.util.Random;

/**
 * Created by TouTouch on 2016/9/5.
 */
public class RecfParticeNew extends Particle {
    static Random random = new Random();
    public float alpha;
    public Parabola parabola;
    public float u0x = 0;//初始速度都设为﹢ 不管左右
    public float u0y = 0;//初始速度都设为﹢
    public float p0x = 0;//初始X坐标
    public float p0y = 0;//初始Y坐标
    public boolean isLeft = false;//用来区分左右飞
    public float length = 0;//边长
    public float life;
    public int type = 0;
    public float one,two,three,four;

    /**
     * @param color 颜色
     * @param x     X坐标
     * @param y     Y坐标
     */
    public RecfParticeNew(int color, float x, float y) {
        super(color, x, y);
    }

    public void init(int color, float x, float y){
        this.color = color;
        cx = x;
        cy = y;
    }
    public void setParabola(boolean isLeftt ,float ux ,float uy){
        parabola = new Parabola(cx , cy , isLeftt , ux , uy);
    }
    public void getData(){
        one = (random.nextFloat() - 0.5f ) * length * 5;
        two = (random.nextFloat() - 0.5f) * length * 5;
        three = (random.nextFloat() - 0.5f) * length * 5 ;
        four = (random.nextFloat() - 0.5f) * length * 5;
    }

    @Override
    protected void draw(Canvas canvas, Paint paint) {
        paint.setColor(color);
        paint.setAlpha((int) (Color.alpha(color) * alpha)); //这样透明颜色就不是黑色了
        if(type == 0){
            canvas.drawRect(new RectF(cx , cy , cx + length, cy + length), paint); //画正方形
        }else if(type == 1){
            canvas.drawRect(new RectF(cx , cy , cx + length, cy + length  * 1.5f), paint); //画长方形
        }else if(type == 2){
            Path path = new Path();
            path.moveTo(cx ,cy);
            path.lineTo(cx + one ,cy + two);
            path.lineTo(cx + three, cy + four);
            path.close();
            canvas.drawPath(path , paint);
        }


    }

    @Override
    protected void caculate(float factor) {

        alpha = 1f - factor;
        float[] pp = parabola.getNewPosition(factor * 20);
        cx = pp[0];
        cy = pp[1];
    }
}

生产粒子

package com.example.administrator.viewexplosion.factory;

import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.Log;
import com.example.administrator.viewexplosion.particle.Particle;
import com.example.administrator.viewexplosion.particle.RecfParticeNew;

import java.util.Random;

/**
 * Created by TouTouch on 2016/9/5.
 */
public class RecfParticleNewFactory extends ParticleFactory{
    private static final String TAG = RecfParticleNewFactory.class.getSimpleName();
    public static final int PART_WH = 30; //默认宽高
    private Rect mBound;
    public static final float END_VALUE = 1.4f;
    @Override
    public Particle[][] generateParticles(Bitmap bitmap, Rect bound) {
        mBound = new Rect(bound);
        int w = bound.width();
        int h = bound.height();
        Random random = new Random();
        int partW_Count = w / PART_WH; //横向个数
        int partH_Count = h / PART_WH; //竖向个数

        int bitmap_part_w = bitmap.getWidth() / partW_Count;
        int bitmap_part_h = bitmap.getHeight() / partH_Count;
//        Log.v(TAG ,"mBound.centerX:" + String.valueOf(mBound.centerX()));
        Particle[][] particles = new Particle[partH_Count][partW_Count];
        for (int row = 0; row < partH_Count; row ++) { //行
            for (int column = 0; column < partW_Count; column ++) { //列
                //取得当前粒子所在位置的颜色
                int color = bitmap.getPixel(column * bitmap_part_w, row * bitmap_part_h);

                float x = bound.left + PART_WH * column;
//                Log.v(TAG ,"x:" + String.valueOf(x));
                float y = bound.top + PART_WH * row;

                particles[row][column] = generateParticle(color,random , x , y );
            }
        }
        return particles;
    }

    public RecfParticeNew generateParticle(int color , Random random , float x ,float y ){
        RecfParticeNew particeNew = new RecfParticeNew(color , x , y);
        boolean isLeft = true;
        if(x + 15 >= (mBound.centerX() - (mBound.width() % PART_WH) / 2)){
            isLeft = false;
        }
        float u0x = 40 * Math.abs(mBound.centerX() - x - 15) / (mBound.width() >> 2) + (random.nextFloat() - 0.5f) * 20;
        float u0y = (random.nextFloat() - 0.5f) * 40 + (80 - u0x);
        Log.v(TAG,"x: " + u0x + " y: " + u0y);
        particeNew.length = random.nextFloat() * 40;
        particeNew.setParabola(isLeft, u0x, u0y);
        particeNew.alpha = 1f;
        particeNew.life = END_VALUE / 10 * random.nextFloat();
        particeNew.type = random.nextInt(3);
        particeNew.getData();
        return particeNew;
    }
}

最后,因为在动画太多的事导致APP卡顿,修改了下承载View,改为在SurfaceView上绘制。
package com.example.administrator.viewexplosion;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.*;
import com.example.administrator.viewexplosion.factory.ParticleFactory;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Created by TouTouch on 2016/9/20.
 */
public class ExplosionFieldSurfaceView  extends SurfaceView implements SurfaceHolder.Callback{
    private static final String TAG = ExplosionFieldSurfaceView.class.getSimpleName();

    private ArrayList<ExplosionAnimator> explosionAnimators;
    private HashMap<View,ExplosionAnimator> explosionAnimatorsMap;
    private OnClickListener onClickListener;
    private ParticleFactory mParticleFactory;
    protected SurfaceHolder holder;
    private HandlerThread myThread;
    public Handler  drawHandle;

    public ExplosionFieldSurfaceView(Context context ,ParticleFactory particleFactory) {
        super(context);
        init(particleFactory);
    }

    public ExplosionFieldSurfaceView(Context context, AttributeSet attrs,ParticleFactory particleFactory) {
        super(context, attrs);
        init(particleFactory);
    }

    public ExplosionFieldSurfaceView(Context context, AttributeSet attrs, int defStyleAttr,ParticleFactory particleFactory) {
        super(context, attrs, defStyleAttr);
        init(particleFactory);
    }

    public void init(ParticleFactory particleFactory){
        setZOrderOnTop(true);
        getHolder().setFormat(PixelFormat.TRANSLUCENT);
        //使得surfaceView透明,并清除
        explosionAnimators = new ArrayList<ExplosionAnimator>();
        explosionAnimatorsMap = new HashMap<View,ExplosionAnimator>();
        mParticleFactory = particleFactory;
        attach2Activity((Activity) getContext());
          holder = getHolder();
          holder.addCallback(this);

          myThread = new HandlerThread("draw surfaceView");
          myThread.start();
          drawHandle = new Handler(myThread.getLooper()){
              @Override
              public void handleMessage(Message msg) {
//                  super.handleMessage(msg);
                  Canvas canvas = null;
                  switch (msg.what){
                      case 0://更新动画
                          try{
                              canvas = holder.lockCanvas();
                              canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                              for (ExplosionAnimator animator : explosionAnimators) {
                                  animator.draw(canvas);
                              }
                          }catch(Exception e){
                              Log.v(TAG ,"绘制出错");
                          }finally {
                              holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
                          }
                          break;
                      case 1://清除屏幕
                          try{
                              canvas = holder.lockCanvas();
                              canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

                          }catch(Exception e){
                              Log.v(TAG ,"绘制出错");
                          }finally {
                              holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
                          }
                          break;
                  }
                  Log.v(TAG ,"收到绘制消息");



              }

          };
    }


    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            myThread.quitSafely();
        }else{
            myThread.quit();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
//        myThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    /**
     * 爆破
     * @param view 使得该view爆破
     */
    public void explode(final View view) {
        //防止重复点击
        if(explosionAnimatorsMap.get(view)!=null&&explosionAnimatorsMap.get(view).isStarted()){
            return;
        }
        if(view.getVisibility()!=View.VISIBLE||view.getAlpha()==0){
            return;
        }

        final Rect rect = new Rect();
        view.getGlobalVisibleRect(rect); //得到view相对于整个屏幕的坐标
        int contentTop = ((ViewGroup)getParent()).getTop();
        Rect frame = new Rect();
        //getWindowVisibleDisplayFrame方法可以获取到程序显示的区域,包括标题栏,但不包括状态栏。
        ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;
        rect.offset(0, -contentTop - statusBarHeight);//去掉状态栏高度和标题栏高度
        if(rect.width()==0||rect.height()==0){
            return;
        }

        //震动动画
//        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);
//        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//
//            Random random = new Random();
//
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f);
//                view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f);
//            }
//        });
//        animator.addListener(new AnimatorListenerAdapter() {
//            @Override
//            public void onAnimationEnd(Animator animation) {
//                super.onAnimationEnd(animation);
//                explode(view, rect);
//            }
//        });
        explode(view, rect);
//        animator.start();
    }

    private void explode(final View view,Rect rect) {
        final ExplosionAnimator animator = new ExplosionAnimator(this, Utils.createBitmapFromView(view), rect,mParticleFactory);
        explosionAnimators.add(animator);
        explosionAnimatorsMap.put(view, animator);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                //缩小,透明动画
                view.animate().setDuration(150).scaleX(0f).scaleY(0f).alpha(0f).start();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                view.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).start();
//                view.setVisibility(INVISIBLE);
                drawHandle.sendEmptyMessageDelayed(1 ,0);
                //动画结束时从动画集中移除
                explosionAnimators.remove(animation);
                explosionAnimatorsMap.remove(view);
                animation = null;
            }
        });
        animator.start();
    }

    /**
     * 给Activity加上全屏覆盖的ExplosionField
     */
    private void attach2Activity(Activity activity) {
        ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);

        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        rootView.addView(this, lp);
    }


    /**
     * 希望谁有破碎效果,就给谁加Listener
     * @param view 可以是ViewGroup
     */
    public void addListener(View view) {
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            int count = viewGroup.getChildCount();
            for (int i = 0 ; i < count; i++) {
                addListener(viewGroup.getChildAt(i));
            }
        } else {
            view.setClickable(true);
            view.setOnClickListener(getOnClickListener());
        }
    }


    private OnClickListener getOnClickListener() {
        if (null == onClickListener) {
            onClickListener = new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ExplosionFieldSurfaceView.this.explode(v);
                }
            };
        }
        return onClickListener;
    }


}


全部代码在这里,代码下载

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity粒子特效爆炸可以通过使用特效包来实现。例如,可以使用Unity3D特效粒子中的墙体爆破爆炸特效包或者烟雾气特效来创建爆炸效果。另外,还可以使用绚丽魔法阵系列特效包来创建一个有魔法感的爆炸效果。 在实现爆炸效果时,可以在当前粒子系统下面创建一个新的粒子系统,并取消循环效果,以获得更加真实的爆炸效果。另外,可以通过调整粒子的大小随着生命周期的变化以及使用网格平面和圆形材质来实现冲击波效果。 通过结合这些方法和特效包,你可以创建出逼真的Unity粒子特效爆炸。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [10种Unity粒子特效包资源](https://download.csdn.net/download/huczyt/10387643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Unity3D 粒子系统实现一个简单的爆炸效果](https://blog.csdn.net/six_sex/article/details/72857295)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Unity粒子特效系列-爆炸冲击波](https://blog.csdn.net/qq_27489007/article/details/122853415)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值