Android 粒子喷泉动效

news2025/1/16 14:06:44

一、前言:

在学习open gl es实现动效的时候,打算回顾了一下用普通的2D坐标系实现粒子效果和 open gl 3d 坐标系的区别,以及难易程度,因此本篇以Canvas 2D坐标系实现了一个简单的demo。

粒子动效原理:

粒子动效本质上是一种知道起点和各个坐标轴方向速度的无规则运动,这种动效的实现的算法确实有规则的。

我们以物理学公式为例,本质上是一种匀加速矢量方程,至于为什么忽快忽慢,也是从该类方程延伸出来的新算法。

x = startX  +  (Vx*t + 1/2*aX*t * t)

y = startY + (Vy * t + 1/2*aY*t * t)

t: 时间  ,vY,vX 各个方向的速度,aX,aY各个方向的加速度

当然,用向量解释就是向量A到向量B各个分量的运动学公式。

粒子动效的特点:

  1. 具备起点位置

  2. 需要计算出速度和运动角度,当然,难点也是速度的计算和定义。

  3. 符合运动学方程,但与现实规律有区别,因为在手机中使用的单位和重力加速度都是有一定区别的。

二、代码实现

2.1 构建粒子对象,在open gl中由于没有对象化的概念,绘制时通过数组的偏移实现,当然后果是代码可读性差一些。

public class Particle {
    private float speedZ = 0;
    private float x;
    private float y;

    private float speedX;
    private float speedY;

    int color;
    long startTime;
    private float radius = 10;

    public Particle(float x, float y, float speedX, float speedY, int color,float speedZ,long clockTime) {
        this.x = x;
        this.y = y;
        this.speedX = speedX;
        this.speedY = speedY;
        this.speedZ = speedZ;
        this.color = color;
        this.startTime = clockTime;
    }

    public void draw(Canvas canvas, long clockTime, Paint paint) {
        long costTime = (clockTime - startTime)/2;
        float gravityY = costTime * costTime / 3000f;  //重力加速度
        float dx = costTime * speedX;
        float dy = costTime * speedY + gravityY;
        float v = costTime / 500f;

        float ty = y + dy;  // vt + t*t/2*g
        float tx = x + dx;

        int paintColor = paint.getColor();
        if(v > 1f && speedZ != 1) {
           //非z轴正半轴的降低透明度
            int argb = argb((int) (Color.alpha(color) /v), Color.red(color), Color.green(color), Color.blue(color));
            paint.setColor(argb);
        }else {
            paint.setColor(color);
        }

        float tRadius = radius;
      //这只Blend叠加效果,这个api版本较高        paint.setBlendMode(BlendMode.DIFFERENCE);  

        canvas.drawCircle(tx,ty,tRadius,paint);
        paint.setColor(paintColor);

        if(ty > radius){
            reset(clockTime);
        }
    }
    private void reset(long clockTime) {
        startTime = clockTime;
    }
    public static int argb(
            @IntRange(from = 0, to = 255) int alpha,
            @IntRange(from = 0, to = 255) int red,
            @IntRange(from = 0, to = 255) int green,
            @IntRange(from = 0, to = 255) int blue) {
        return (alpha << 24) | (red << 16) | (green << 8) | blue;
    }
} 

 

2.2 构建粒子系统

public class CanvasParticleSystem {
    private Particle[] particles;
    private int maxParticleCount = 500;
    private Random random = new Random();
    private final float angle = 30f;  //x轴的活动范围
    private int index = 0;
    private float radius = 60;  //x轴和y轴不能超过的边界

    public void addParticle(float centerX,float centerY,float maxWidth,float maxHeight,long clockTime){

        if(particles == null){
            particles = new Particle[maxParticleCount];
        }
        if(index >= particles.length) {
            return;
        }

        float degree = (float) Math.toRadians((270 - angle) + 2f * angle * random.nextFloat());
        float dx = (float) (radius * Math.cos(degree)) * 2f;  //计算初目标位置x的随机点
        float dy = -(float) ((maxHeight * 1f / 2 - radius * 2f) * random.nextFloat()) - maxHeight / 2f;
        //计算目标y的随机点

         float dt = 1000;  //时间按1s计算
    //    dx = speedx * dt + centerX;
    //    dy = speedy * dt + centerY;

        float sx = (dx - centerX) / dt;  // x轴方向的速度
        float sy = (dy - centerY) / dt;   //y轴方向的速度

        int num = (int) (random.nextFloat() * 100);
        float sz = 0;
        if(num % 5 == 0) {
            sz = random.nextBoolean() ? -1 : 1;
        }

        int argb = argb(random.nextFloat(), random.nextFloat(), random.nextFloat());
       // argb = argb(210, 110, 80);
        Particle p = new Particle(centerX,centerY,sx,sy, argb,sz,clockTime);

        particles[index++] = p;
    }

    public void drawFrame(Canvas canvas, Paint paint,long clockTime) {
        for (int i = 0; i < particles.length;i++) {
            Particle particle = particles[i];
            if(particle == null) continue;
            particle.draw(canvas,clockTime,paint);
        }
    }

    public  int argb( float red, float green, float blue) {
        return ((int) (1 * 255.0f + 0.5f) << 24) |
                ((int) (red   * 255.0f + 0.5f) << 16) |
                ((int) (green * 255.0f + 0.5f) <<  8) |
                (int) (blue  * 255.0f + 0.5f);
    }
}

2.3 粒子View实现

public class PracticeView extends View {

    Paint paint;
    CanvasParticleSystem particleSystem;
    private long clockTime = 0L; //自定义时钟,防止粒子堆积
    long startTimeout = 0;  

    public PracticeView(Context context) {
        super(context);
        init();
    }

    public PracticeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PracticeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void init(){
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(false);
        paint.setStrokeWidth(2f);
        particleSystem = new CanvasParticleSystem();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        if (width <= 10 || height <= 10) {
            return;
        }
        int save = canvas.save();
        canvas.translate(width/2,height);

        fillParticles(5,width, height);
        particleSystem.drawFrame(canvas,paint,getClockTime());
        canvas.restoreToCount(save);
        clockTime += 32;
        postInvalidateDelayed(16);
    }


    private void fillParticles(int size,int width, int height) {
        if(SystemClock.uptimeMillis() - startTimeout > 60) {
            for (int i = 0; i < size; i++) {
                particleSystem.addParticle(0, 0, width, height,getClockTime());
            }
            startTimeout = SystemClock.uptimeMillis();
        }
    }
    private long getClockTime() {
        return clockTime;
    }
}

三、总结

总体上使用Canvas 绘制高帧率的粒子动效,其对比open gl肯定有很多差距,甚至有一些天然缺陷比如Z轴的处理。当然,易用性肯定是Canvas 2D的优势了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1442382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

504. Base 7(七进制数)

题目描述 给定一个整数 num&#xff0c;将其转化为 7 进制&#xff0c;并以字符串形式输出。 问题分析 按照二进制转换的方式进行转换即可 代码 char* convertToBase7(int num) {int count 0;char *x (char *)malloc(sizeof(char)*32);char *y (char *)malloc(sizeof(c…

【Redis】深入理解 Redis 常用数据类型源码及底层实现(3.详解String数据结构)

【Redis】深入理解 Redis 常用数据类型源码及底层实现&#xff08;1.结构与源码概述&#xff09;-CSDN博客 【Redis】深入理解 Redis 常用数据类型源码及底层实现(2.版本区别dictEntry & redisObject详解)-CSDN博客 紧接着前两篇的总体介绍&#xff0c;从这篇开始&#x…

windows安装sqlite

windows安装sqlite比linux麻烦很多 1.下载 下载链接&#xff1a;链接 下载dll的zip文件 2.解压并放到文件夹 将压缩包内的文件解压&#xff0c;放到C://sqlite下 3.编辑环境变量 添加到系统变量的path中 4.验证 打开命令提示符&#xff0c;输入 sqlite3有结果就行

JavaWeb02-MyBatis

目录 一、MyBatis 1.概述 2.JavaEE三层架构简单介绍 &#xff08;1&#xff09;表现层 &#xff08;2&#xff09;业务层 &#xff08;3&#xff09;持久层 3.框架 4.优势 &#xff08;1&#xff09;JDBC的劣势 &#xff08;2&#xff09;MyBatis优化 5.使用 &#…

Golang的for循环变量和goroutine的陷阱,1.22版本的更新

先来看一段golang 1.22版本之前的for循环的代码 package mainimport "fmt"func main() {done : make(chan bool)values : []string{"chen", "hai", "feng"}for _, v : range values {fmt.Println("start")go func() {fmt.P…

Gemini 下一章节即将拉开帷幕

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

树与二叉树---数据结构

树作为一种逻辑结构&#xff0c;同时也是一种分层结构&#xff0c;具有以下两个特点&#xff1a; 1&#xff09;树的根结点没有前驱&#xff0c;除根结点外的所有结点有 且只有一个前驱。 2&#xff09;树中所有结点可以有零个或多个后继。 树结点数据结构 满二叉树和完全二…

洛谷使用指南

详细看——洛谷的规则 1.注册账号 1.打开洛谷首页 这样就对了&#xff01;&#xff01;&#xff01; 2.点击注册 当显示以上页面时表示进入了注册页面。 3.登录 当注册好后就可以登陆了。 当显示以上页面时表示进入了登录页面。 2.题库使用 当单击题库后&#xff0c;会出…

算法练习-二叉搜索树中的搜索(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;二叉树 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨…

二、OpenAI开发者快速入门

启动并运行OpenAI API OpenAI API 为开发者提供一个简单的接口&#xff0c;使其能够在他们的应用中创建一个智能层&#xff0c;由OpenAI最先进的模型提供支持。聊天补全端点为ChatGPT提示支持&#xff0c;一种简单的方法是&#xff1a;输入文本&#xff0c;使用GPT-4模型输出。…

在JSP中实现JAVABEAN

在JSP中实现JAVABEAN 问题陈述 创建Web应用程序以连接数据库并检索作者名、地址、城市、州及邮政编码等与作者的详细信息。JavaBean组件应接受作者ID、驱动程序名及URL作为参数。信息要从authors表中检索。 解决方案 要解决上述问题,需要执行以下任务: 创建Web应用程序。创…

macbookair怎么清理内存 ?如何利用 CleanMyMac X 进行系统清理

macbookair怎么清理内存 清理MacBook Air的内存可以通过以下几种方法&#xff1a; 优化储存空间。在MacBook Air上&#xff0c;可以通过“优化储存空间”来释放空间。这包括将文件储存在iCloud中&#xff0c;如桌面、文稿和iCloud信息&#xff0c;以及自动移除在iCloud中观看…

Lombok 高级说明

优质博文&#xff1a;IT-BLOG-CN 一、痛点 【1】代码臃肿&#xff1a;POJO中的getter/setter/equals/hashcode/toString等&#xff1b; 【2】样板式代码&#xff1a;I/O流的关闭操作等&#xff1b; Lombok是一个可以通过注解简化Java代码开发的工具&#xff0c;能够在我们编…

2.8日学习打卡----初学RabbitMQ(三)

2.8日学习打卡 一.springboot整合RabbitMQ 之前我们使用原生JAVA操作RabbitMQ较为繁琐&#xff0c;接下来我们使用 SpringBoot整合RabbitMQ&#xff0c;简化代码编写 创建SpringBoot项目&#xff0c;引入RabbitMQ起步依赖 <!-- RabbitMQ起步依赖 --> <dependency&g…

关节点检测

https://www.bilibili.com/video/BV19g4y1777q/?p2&spm_id_frompageDriver 关节点检测全流程 YOLO:单阶段&#xff0c;快&#xff1b; MMPose&#xff1a;双阶段&#xff0c;准&#xff1b; 标注工具Labelme 用Labelme标注样本数据集

利用Pybind11封装Python版的WiringPi!

原版的WiringPi是一个用于树莓派的GPIO库&#xff0c;用C语言开发&#xff0c;仓库地址&#xff1a;https://github.com/WiringPi/WiringPi。该库允许用户以编程方式访问和控制树莓派的GPIO引脚。而随着Python在嵌入式设备上的快速发展&#xff0c;其对底层引脚的操作也变得越来…

OpenAI给DALL-E 3来了个新动作,加入了全新水印技术

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

老胡的周刊(第128期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 coze-discord-proxy[2] 代理 Discord-Bot 对…

018 Linux

文章目录 操作系统定义分类Linux系统构成 Linux文件系统Linux常用命令基础操作命令文件操作压缩解压权限管理显示展示命令其他命令 vi编译器操作使用 添加用户基本概念用户管理命令 ubuntu软件安装ssh服务终端启动Python服务 操作系统 定义 操作系统是管理计算机硬件与软件资…

【超高效!保护隐私的新方法】针对图像到图像(l2l)生成模型遗忘学习:超高效且不需要重新训练就能从生成模型中移除特定数据

针对图像到图像生成模型遗忘学习&#xff1a;超高效且不需要重新训练就能从生成模型中移除特定数据 提出背景如何在不重训练模型的情况下从I2I生成模型中移除特定数据&#xff1f; 超高效的机器遗忘方法子问题1: 如何在图像到图像&#xff08;I2I&#xff09;生成模型中进行高效…