/** * @file MoveUV.ts * @description UV 滚动动画组件(视觉效果层) * * 职责: * 1. 通过每帧修改 Sprite 的 spriteFrame.rect 偏移量, * 实现纹理的 **UV 滚动效果**(如流水、云层、传送带等)。 * 2. 支持 X / Y 轴独立的滚动速度控制。 * 3. 支持动态切换纹理包裹模式(WrapMode)。 * * 关键设计: * - 使用 spriteFrame.rect 的 x / y 偏移模拟 UV 滚动, * 纹理 WrapMode 必须设为 REPEAT 才能看到循环滚动效果。 * - packable=false:禁止图集自动打包,确保纹理独立加载以支持 REPEAT 模式。 * - 每帧调用 markForUpdateRenderData() 强制刷新渲染数据, * 确保 rect 变化能即时反映到渲染管线。 * * 使用方式: * 挂载在带有 Sprite 的节点上,设置 moveSpeedX / moveSpeedY 和 wrapMode。 * * 依赖: * - cc.Sprite —— 读写 spriteFrame.rect * - cc.Texture2D.WrapMode —— 控制纹理采样的循环方式 */ import { Texture2D } from 'cc'; import { Enum } from 'cc'; import { rect } from 'cc'; import { Sprite } from 'cc'; import { _decorator, Component } from 'cc'; const { ccclass, property } = _decorator; /** * MoveUV —— UV 滚动动画组件 * * 通过修改 Sprite 的 rect 偏移实现纹理循环滚动。 * 适用于水面、云层、传送带等需要连续滚动的视觉效果。 */ @ccclass('MoveUV') export class MoveUV extends Component { /** X 轴滚动速度(像素/秒,正值向右,负值向左) */ @property moveSpeedX = 0; /** Y 轴滚动速度(像素/秒,正值向上,负值向下) */ @property moveSpeedY = 0; /** 纹理包裹模式(默认 REPEAT,循环滚动必须使用此模式) */ @property({ type: Enum(Texture2D.WrapMode) }) wrapMode = Texture2D.WrapMode.REPEAT; /** Sprite 组件引用 */ private _sprite: Sprite; /** 当前 rect 偏移量(修改 x / y 实现滚动) */ private _rect = rect(); /** 上一次应用的 WrapMode(用于检测变化) */ private _currentWrapMode: number; /** 获取当前 rect(供外部读取偏移状态) */ getRect() { return this._rect; } /** * 初始化: * 1. 获取 Sprite 组件(不存在则抛出异常)。 * 2. 记录初始 rect 尺寸。 * 3. 禁止自动打包(packable=false),确保纹理独立以支持 REPEAT。 */ onLoad() { this._sprite = this.getComponent(Sprite); if (!this._sprite) { let tempStr = "获取Sprite失败"; throw new Error(tempStr); } this._rect.set(this._sprite.spriteFrame.rect); this._sprite.spriteFrame.packable = false; } /** * 帧更新: * 1. 检测 WrapMode 是否变化,变化则重新设置纹理参数。 * 2. 按速度累加 rect 的 x / y 偏移。 * 3. 将新 rect 赋回 spriteFrame 并标记渲染数据更新。 */ update(dt: number) { // 动态切换 WrapMode if (this._currentWrapMode !== this.wrapMode) { this._currentWrapMode = this.wrapMode; this._sprite.spriteFrame.texture.setWrapMode(this.wrapMode, this.wrapMode); } // 累加 UV 偏移 this._rect.x += this.moveSpeedX * dt; this._rect.y += this.moveSpeedY * dt; // 应用新 rect 并强制刷新渲染 this._sprite.spriteFrame.rect = this._rect; this._sprite.markForUpdateRenderData(); } }