Unity通过偏移UV播放序列帧动画

news2024/11/18 3:21:38

  大家好,我是阿赵。
  在Unity引擎里面用shader播放序列图,估计很多人都有用到了,我自己而已写过好几个版本。这里大概介绍一下。

一、原理

  先说目的,我现在有一张这样的图片:
在这里插入图片描述

  这张图片上面,有9个格子,可以理解成是一个动画的9个序列帧,接下来,通过写一个简单的Shader,按照顺序逐个的显示出来,形成一个循环的动画:
在这里插入图片描述

ASE里面直接就有这样一个播放序列帧动画的节点,叫做Flipbool UV Animation节点:
在这里插入图片描述

从节点可以看出,做这个UV动画,需要的参数有这些:
1、原始的UV坐标
2、序列图的行列数,比如我刚才那张图就是3x3的行列式
3、播放速度
4、第几帧开始播放
5、当前播放的时间。
然后返回的结果,是一个新的UV坐标
  所以从原理上来说,这个序列帧播放其实是根据当前时间,算出当前需要播放第几帧,然后通过行列数,算出截取第几帧所在的图片的UV坐标,然后返回。

二、实现的代码

  这个UV序列帧动画的代码,我也写过好几个版本,但感觉还是ASE的看起来比较标准一点,所以我就参考ASE的Flipbool UV Animation节点,把它翻译成一个方法:

float2 GetSequenceAnimUV(float2 uv,float cols,float rows,float speed,float startFrame)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float speedVal = _Time.y * speed;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = round(fmod(speedVal + startFrame, totalTiles));
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv*offsetTiling +offsetXY;
	return result;
}

  使用的时候,传入uv、行列数、速度、开始帧这几个参数之后,就可以返回一个当前帧的UV,然后拿这个UV去采样整张图片就可以了;

三、扩展应用

1、自己控制时间流逝

从上面的代码可以看出,这个序列帧动画会自己播放,是因为使用了_Time.y,这是一个时间,代表了从加载场景完成到当前的时间,是会自己增加的。
如果想不用这个系统的时间,而是由自己来控制时间,有2个办法:

1.改变speed参数

speed可以使正数、负数或者是0。当speed越大时,播放得越快,当speed为负数时,动画就是倒着播放。当速度为0时,动画播放就停止了。
不过我觉得速度参数只是一个控制播放快慢的手段,不是控制时间的方式。

2.自己控制time

  这个方法是,不使用_Time.y,而是自己传入timeVal参数。这样,我们需要在其他脚本比如C#里面维护一个时间变量。
  这样做的好处是,在不改变播放正常速度时,我们可以任意的跳转到某一个时间点。比如现在需要做一个时光回退的效果,突然间整个世界都回溯到之前几秒钟,通过统一传入某个时间戳,所有动画都可以一起回退到之前的状态。
  当然,用time参数也可以实现加速减速和暂停。
自己控制time参数的方法如下:

float2 GetSequenceAnimUVByTime(float2 uv, float cols, float rows, float speed, float startFrame,float timeVal)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float speedVal = timeVal * speed;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = round(fmod(speedVal + startFrame, totalTiles));
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv * offsetTiling + offsetXY;
	return result;
}

2、自己控制播放指定帧

  有时候做一些比较特殊的序列帧动画,需要根据情况播放特定很准确的某一帧,或者在某几帧之间重复。
  这样的情况如果使用Time作为控制,似乎就比较的不适合了。所以,根据实际情况改一下,可以把传入的速度参数和时间参数都去掉,变成传入想播放第几帧:

float2 GetSequenceAnimUVByIndex(float2 uv, float cols, float rows, float currentFrame)
{
	float totalTiles = cols * rows;

	float colsOffset = 1.0f / cols;
	float rowsOffset = 1.0f / rows;
	float2 offsetTiling = float2(colsOffset, rowsOffset);
	float currentIndex = currentFrame;
	currentIndex += (currentIndex < 0) ? totalTiles : 0;
	float lineNum = round(fmod(currentIndex, cols));
	float offsetX = lineNum * colsOffset;
	float rowCount = round(fmod((currentIndex - lineNum) / cols, rows));
	rowCount = (int)(rows - 1) - rowCount;
	float offsetY = rowCount * rowsOffset;
	float2 offsetXY = float2(offsetX, offsetY);
	float2 result = uv * offsetTiling + offsetXY;
	return result;
}

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

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

相关文章

Shell自动化日志维护脚本

简介&#xff1a; 系统日志对于了解操作系统的运行状况、故障排除和性能分析至关重要。然而&#xff0c;长期积累的日志文件可能变得庞大&#xff0c;影响系统性能。在这篇文章中&#xff0c;我们将介绍一个自动化的解决方案&#xff0c;使用 Bash 脚本来监控和维护系统日志文件…

管理类联考——逻辑——形式逻辑——汇总篇——知识点突破——形式逻辑——联言选言假言——等价

角度 角度——汇总 性质 &#xff08;1&#xff09; 有的 S 是 P 有的 S → P &#xff1b;换位&#xff1a;有的 S 是 P 有的 P 是 S &#xff1b;不可逆否 有的S是P有的S→P&#xff1b;换位&#xff1a;有的S是P有的P是S&#xff1b;不可逆否 有的S是P有的S→P&#xff1…

嵌入式面试笔试刷题(day14)

文章目录 前言一、进程控制块1.PCB控制块的作用2.PCB的存储位置 二、进程的三级映射三、return , exit, pthread_exit四、pthread_join作用五、互斥锁和信号量的区别六、怎么判断链表是否有环总结 前言 本篇文章继续我们的刷题之路。 一、进程控制块 这里只讲解进程的PCB控制…

北京APP外包开发需要注意的问题

开发APP的过程中&#xff0c;由于开发APP需要投入大量的时间、精力和资源&#xff0c;所以在开始前一定要做好充足的准备和规划。您需要注意以下重点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1…

3D路径,控件

1控件拖入画板&#xff1a; 2属性配置&#xff1a; 1轨迹颜色 2 3 4

本地环境安装了node.js,vscode运行js代码出现乱码

本地环境安装了node.js,vscode运行js代码出现乱码 问题描述—vscode运行js代码出现乱码 报错信息&#xff1a; 问题原因 node.js的下载安排了&#xff0c;环境变量vscode没有识别导致的乱码 解决方法 安装Node.js&#xff0c;软件会自动将其配置到环境变量中&#xff0c;然…

Kotlin inline、noinline、crossinline 深入解析

主要内容&#xff1a; inline 高价函数的原理分析Non-local returns noinlinecrossinline inline 如果有C语言基础的&#xff0c;inline 修饰一个函数表示该函数是一个内联函数。编译时&#xff0c;编译器会将内联函数的函数体拷贝到调用的地方。我们先看下在一个普通的 kot…

题目有点太简单了,不知道怎么选了

有个公司给了下面一个题目&#xff0c;看了下太简单了&#xff0c;都怕选错了。 后来拿着程序跑了下&#xff0c;就是这个意思嘛。 结论 程序跑出来的结果就是对输入的列表进行倒序排列。 public void testGetPut() throws Exception {List<Integer> numbers List.of(…

【基于交叉注意力的泛锐化深度展开迭代网络】

CADUI: Cross-Attention-Based Depth Unfolding Iteration Network for Pansharpening Remote Sensing Images &#xff08;CADUI&#xff1a;基于交叉注意力的泛锐化深度展开迭代网络&#xff09; 全色锐化是遥感成像系统获取高分辨率多光谱图像的重要技术。它主要通过融合低…

异或和大小比较类问题——抓住最高位:CF1863F

https://codeforces.com/contest/1863/problem/F 因为有等于&#xff0c;所以考虑异或和为0的合法区间&#xff0c;它可以随意切现在考虑切开后左边大于右边&#xff0c;可以发现左右边最高位可以互相抵消&#xff0c;似乎不太可做&#xff1f;此时可以换个考虑&#xff0c;考…

Jetpack业务架构—四件套(Lifecycle、ViewModel、LiveData、DataBinding)

Jetpack 是一个由多个库组成的套件&#xff0c;可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码&#xff0c;让开发者可将精力集中于真正重要的编码工作。 Android Jetpack组件的优势&#xff1a; Jetpack推出的主要目的是为了能够…

【LeetCode75】第四十四题 省份数量

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一个二维数组&#xff0c;表示城市之间的连通情况&#xff0c;连在一起的城市为一个省份&#xff0c;问我们一共有多少个省份。 这…

Python实现多子图绘制系统

文章目录 修改DrawTypeDrawType的调用逻辑绘图逻辑源代码 Python绘图系统&#xff1a; &#x1f4c8;从0开始的3D绘图系统&#x1f4c9;一个3D坐标系&#xff0c;多个函数图表类型和风格&#xff1a;&#x1f4c9;极坐标绘图&#x1f4ca;散点图和条形图&#x1f4ca;混合类型…

推进数据要素化,数据云为何是“加速器”?

数据要素化&#xff0c;一个世界性难题。 相比于传统生产要素&#xff0c;数据要素具有获得非竞争性、使用非排他性等独有特征&#xff0c;在流通、产权、安全和使用等方面需要法规制度与基础设施的双重保障。 我国无疑是最早探索数据要素化的国家之一。从早期成立的各种大数…

win10下的CLion控制台中文乱码终极解决方案

win10下的CLion控制台中文乱码终极解决方案 如果你也是&#xff0c;用Clion时候&#xff0c;CPP文件中有中文&#xff0c;然后终端运行会有乱码&#xff0c;改了设置发现&#xff0c;项目中的中文乱码了&#xff0c;但是终端又不乱码了&#xff0c;&#xff0c;&#xff0c;&am…

使用java代码给Excel加水印,保真,新鲜出炉

首先&#xff0c;往表格里贴透明图片&#xff0c;这个很智障&#xff0c;会严重干扰正常阅读和操作 设置文件背景图&#xff1b; 其次&#xff0c;其实就是给excel加一个背景图&#xff0c;但是问题就麻烦在java中基本没有这么干过的&#xff0c;可用方案很少&#xff0c;有spi…

基于java+springboot+vue的置换网站-lw

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql Webstorm Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; ssm mybatis Maven mysql5.7或8.0等等组成&#xff0c;B/S模式 Maven管理等等。 环境需要 1.…

Java-HashMap中put()方法是如何实现的,内含详细流程图

文章目录 Java中的HashMap什么是HashMap&#xff1f;对比其他Map中put()方法HashMap中put()方法使用示例 HashMap中put()源码解析手绘流程图实现原理源码探究&#xff08;JDK 1.8&#xff09; 设计put()的意义总结 Java中的HashMap 什么是HashMap&#xff1f; HashMap是Java中…

ARP欺骗

ARP欺骗定义 ARP欺骗&#xff08;英语&#xff1a;ARP spoofing&#xff09;&#xff0c;又称ARP毒化&#xff08;ARP poisoning&#xff0c;网络上多译为ARP病毒&#xff09;或ARP攻击&#xff0c;是针对以太网地址解析协议&#xff08;ARP&#xff09;的一种攻击技术&#x…

Qt —UDP通信QUdpSocket 简介 +案例

1. UDP通信概述 UDP是无连接、不可靠、面向数据报&#xff08;datagram&#xff09;的协议&#xff0c;可以应用于对可靠性要求不高的场合。与TCP通信不同&#xff0c;UDP通信无需预先建立持久的socket连接&#xff0c;UDP每次发送数据报都需要指定目标地址和端口。 QUdpSocket…