当前位置: 首页 > news >正文

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

  大家好,我是阿赵。
  在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;
}

相关文章:

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

大家好&#xff0c;我是阿赵。   在Unity引擎里面用shader播放序列图&#xff0c;估计很多人都有用到了&#xff0c;我自己而已写过好几个版本。这里大概介绍一下。 一、原理 先说目的&#xff0c;我现在有一张这样的图片&#xff1a; 这张图片上面&#xff0c;有9个格子&a…...

无涯教程-Android - List fragments函数

框架的ListFragment的静态库支持版本&#xff0c;用于编写在Android 3.0之前的平台上运行的应用程序&#xff0c;在Android 3.0或更高版本上运行时,仍使用此实现。 List fragment 的基本实现是用于创建fragment中的项目列表 List in Fragments 示例 本示例将向您说明如何基于…...

【图解RabbitMQ-3】消息队列RabbitMQ介绍及核心流程

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表&#xff0c;业余独自创办智源恩创网络科技工作室。会点点Java相关技术栈、帆软报表、低代码平台快速开…...

jmeter源码二次开发

本文以jmeter5.5为例&#xff0c;扩展“TCP Socket支持定长的返回字节流”功能。 一、 源码本地编译运行 1、在jmeter官网下载源码&#xff1a;jmeter各版本源码地址 2、在idea中用gradle导入jmeter源码&#xff0c;idea中要配置jdk&#xff0c;gradle&#xff0c;我用的是j…...

深入实现 MyBatis 底层机制的任务阶段4 - 开发 Mapper 接口和 Mapper.xml

&#x1f600;前言 在我们的自定义 MyBatis 底层机制实现过程中&#xff0c;我们已经深入研究了多个任务阶段&#xff0c;包括配置文件的读取、数据库连接的建立、执行器的编写&#xff0c;以及 SqlSession 的封装。每个任务阶段都为我们揭示了 MyBatis 内部工作原理的一部分&a…...

分享一下在微信上有哪些微信活动可以做

微信营销活动是吸引更多用户和提高品牌知名度的有效策略。下面是一些微信营销活动的做法&#xff1a; 抽奖活动&#xff1a;通过设置奖品和参与条件&#xff0c;吸引用户参与抽奖活动。例如&#xff0c;可以设置关注公众号、转发活动页面等条件&#xff0c;吸引更多用户参与抽奖…...

视频监控/视频汇聚/视频云存储EasyCVR平台接入国标GB协议后出现断流情况,该如何解决?

视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。安防监控平台EasyCVR既具备传统安防视频监控的能…...

没有软件怎么管理固定资产

在当今数字化的世界中&#xff0c;我们已经习惯了使用各种软件来管理我们的日常生活和工作。然而&#xff0c;当我们面临一个看似简单的问题——如何管理固定资产时&#xff0c;我们可能会感到困惑。那么&#xff0c;如果没有软件&#xff0c;我们该如何进行资产管理呢&#xf…...

几种反序列化漏洞

1.PHP魔术方法 <?php class c {private $name hacker;function __construct() { // 构造方法&#xff0c;new时调用echo construct<br>;}function __serialize() { // 序列化时调用echo serialize<br>;return [hack];}function __unserialize($data) { // 反…...

华为OD机试 - 找出符合要求的字符串子串(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》…...

深度学习环境搭建笔记(二):mmdetection-CPU安装和训练

文章目录 第一步&#xff1a;安装anaconda第二步&#xff1a;安装虚拟环境第三步&#xff1a;安装torch和torchvision第四步: 安装mmcv-full第五步: 安装mmdetection第六步&#xff1a;测试环境第七步&#xff1a;训练-目标检测7.1 准备数据集7.2 检查数据集7.3 训练网络 第一步…...

Ubuntu18.04安装cuDNN

注册账号 https://developer.nvidia.com/rdp/cudnn-archive 该网站下载安装包需要先进行注册。登录成功后&#xff0c;找到与CUDA对应的版本。 选择Linux版本进行下载。 下载后的格式为.tar.xz 解压 tar xvJf cudnn-linux-x86_64-8.9.3.28_cuda12-archive.tar.xz配置环境 su…...

Java逻辑控制

目录 一、顺序结构 二、分支结构 1、if语句 &#xff08;1&#xff09; 语法格式1​编辑 &#xff08;2&#xff09;语法格式2​编辑 &#xff08;3&#xff09;语法格式3 2、switch 语句 三、循环结构 1、while循环 2、break 3、continue 4、for 循环 5、do whil…...

easyExcel合并单元格导出

一、导入maven依赖 &#xff08;很多旧项目自定义了一套Excel导出工具&#xff0c;poi版本可能不兼容&#xff0c;一般poi新旧版本不兼容分界线在3.17&#xff0c;选择3.17版本不会发生代码不兼容情况&#xff09; <dependency><groupId>com.alibaba</groupId&…...

SpringBoot项目--电脑商城【用户注册】

1.创建数据表 1.1 创建t_user表 CREATE TABLE t_user (uid INT AUTO_INCREMENT COMMENT 用户id,username VARCHAR(20) NOT NULL UNIQUE COMMENT 用户名,password CHAR(32) NOT NULL COMMENT 密码,salt CHAR(36) COMMENT 盐值,phone VARCHAR(20) COMMENT 电话号码,email VARCH…...

HCIP学习-IPv6

目录 前置学习内容 IPv6解决的一些IPv4的缺陷 无限的地址 层次化的地址结构 即插即用 简化报文头部 IPv4和IPv6报头比较 端到端的网络罗完整性 安全性增强 挣钱QoS特性 IPv6地址介绍 格式 首选格式 压缩格式 内嵌IPv4地址格式的IPv6地址格式 IPv6的网络前缀和接…...

golang高精度十进制数扩展包decimal用法

在Go语言中&#xff0c;没有内置的十进制数&#xff08;decimal&#xff09;类型或相关的标准库。然而&#xff0c;有一些第三方包可用于处理十进制数&#xff0c;其中比较常用的是decimal包。 decimal包提供了一个big.Float的子类型decimal.Decimal&#xff0c;可以用于表示和…...

STM32F4X RNG随机数发生器

STM32F4X RNG随机数发生器 随机数的作用STM32F4X 随机数发生器RNG控制寄存器RNG状态寄存器RNG数据寄存器RNG数据步骤RNG例程 随机数的作用 随机数顾名思义就是随机产生的数字&#xff0c;这种数字最大的特点就是其不确定性&#xff0c;你不知道它下一次产生的数字是什么。随机…...

5、QT中SQLite数据库的操作

一、QT中的SQLite数据库 1、添加头文件和模块 Header: #include <QSqlDatabase> qmake: QT sql//pro文件添加sql模块执行数据库操作的类&#xff1a; Header: #include <QSqlQuery> qmake: QT sql2、C语言中的SQLite增删减查 SQLite3的基础教程 3、SQLite的…...

git回退到某个提交

git是一个分布式版本控制软件&#xff0c;分布式版本库的做法使源代码的发布和交流都极为方便&#xff0c;因此有不少用户都在使用git。最近小编也正在学习git这款软件&#xff0c;发现要想熟练运用git&#xff0c;学会git中的一些命令是很重要的&#xff0c;如果我们要回滚到某…...

对可再生能源和微电网集成研究的新控制技术和保护算法进行基线和测试及静态、时域和频率分析研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Full authentication is required to access this resource解决办法

我们在使用postman调接口时候&#xff0c;有的时候需要权限才可以访问&#xff0c;否则可能会报下面这个错误 {"success": false,"message": "Full authentication is required to access this resource","code": 401,"result&q…...

Jetty:使用上下文文件部署离线瓦片.md

说明 介绍利用jetty在任意位置如桌面的资源进行发布。比如下载的离线瓦片数据&#xff0c;如果放到jetty的webapps目录下&#xff0c;则启动时间会比较久&#xff0c;可以通过本文的步骤进行配置&#xff0c;也免去了拷贝过程的耗时。 关键字&#xff1a;自定义路径、Jetty、…...

Docker实战:docker compose 搭建Rocketmq

1、配置文件准备 1.1、 新建目录&#xff1a;/home/docker/data/rocketmq/conf mkdir /home/docker/data/rocketmq/conf1.2、 在上面目录下新建文件broker.conf文件&#xff0c;内容如下 brokerClusterName DefaultCluster brokerName broker-a brokerId 0 deleteWhen 0…...

STL常用容器 (C++核心基础教程之STL容器详解)String的API

在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;有多种容器可供使用。以下是一些常见的容器类型&#xff1a; 序列容器&#xff08;Sequential Containers&#xff09;&#xff1a; std::vector&#xff1a;动态数组&#xff0c;支持快速随机访问。 std::list&…...

《人生苦短,我学Python》——条件判断->(if-elif-else)多向选择 条件嵌套

今天&#xff0c;我们来学习多向选择&#xff01;if--elif--else if 后的语句是当 if 判断条件成立时&#xff0c;执行的操作。elif 后的语句是当 if 判断不成立时&#xff0c;再判断一次&#xff0c;如果成立&#xff0c;执行的操作。else 后的语句是当以上所有判断条件都不成…...

MongoDB 数据库性能优化技巧

原文&#xff1a;MongoDB 数据库性能优化技巧 (techdatafuture.com) MongoDB 是一款灵活且可扩展的NoSQL数据库&#xff0c;为了提高其性能&#xff0c;我们可以采取一些优化技巧。本文将介绍一些MongoDB性能优化的关键点&#xff0c;包括索引的使用、查询优化、数据模型设计和…...

网络安全人才缺口超百万,如今的就业情况怎样?

网络安全人才缺口超百万&#xff0c;如今的就业情况怎样&#xff1f; 2022年9月7日&#xff0c;国家网络安全宣传周准时开始。本次网络安全宣传周和以前一样&#xff0c;主要目的都是为了普及网络安全知识&#xff0c;提高网络安全的防护技能&#xff0c;提升对电信网络诈骗的…...

「MySQL」MySQL面试题全解析:常见问题与高级技巧详解

MySQL面试题全解析&#xff1a;常见问题与高级技巧详解 1. 什么是数据库&#xff1f;2. 什么是MySQL&#xff1f;3. 什么是SQL&#xff1f;4. 什么是主键&#xff1f;5. 什么是外键&#xff1f;6. 请解释索引是什么以及为什么使用索引&#xff1f;7. 什么是事务&#xff1f;8. …...

【USRP】产品型号、参数、架构全解析系列 6:N320 / N321

一、USRP 简介 通用软件无线电外设( USRP ) 是由 Ettus Research 及其母公司National Instruments设计和销售的一系列软件定义无线电。USRP 产品系列由Matt Ettus领导的团队开发&#xff0c;被研究实验室、大学和业余爱好者广泛使用。 大多数 USRP 通过以太网线连接到主机&am…...