spine 动画层 动态权重
前奏.业务背景
这边想实现一个功能,项目中有 一只猫 猫的头会盯着逗猫棒移动。因为素材还没到所以这里使用了 spine 自带的猫头鹰。他的动画 刚好挺有针对性:(关联上篇)https://blog.csdn.net/nicepainkiller/article/details/144113214
一共有六组动画:
idel 空闲动画全身都会动 多帧动画,
blink 眨眼睛动画 头不动 多帧动画
其余几个动画只有有头会动 单帧动画 只有一帧
- blink 眼睛在中间的动画 会眨眼睛
- down 眼睛朝下看 单帧
- left 眼睛朝左看 单帧
- right 眼睛朝右看 单帧
- up 眼睛朝上看 单帧
- idel 空闲动画 全身动
猫头鹰眼睛可以盯着 屏幕物体移动,实现效果如下 :
最初想的实现方式是:类似 Unity3D 有一个二位动画权重的东西。但是找遍 cocos creator API 发现没找到,
那么只能纯手撸了:
思路就是动态控制 动画层的权重,来显示不同动画(头上,头右,头下,头左)的播放权重。
实操1.逗猫棒位置计算
首先就确定一个屏幕可以拖动的元素,并且计算出一个区域,用来确定 向量 Vec2:
- 屏幕触点 转 屏幕坐标
- 根据屏幕坐标计算出 用来表示范围的 一个 向量
核心代码:
//获取 触点坐标位置 touchMove(event: EventTouch) { //更新 拖拽元素位置 console.log('touchMove'); this.potion.x = event.getLocationX(); this.potion.y = event.getLocationY(); //触点坐标转 屏幕坐标 this.worldPostion = this.camera.screenToWorld(this.potion); //屏幕坐标转 局部坐标 this.node.position = this.nodeParent.getComponent(UITransform).convertToNodeSpaceAR(this.worldPostion); //console.log("this.node.position:", this.node.position); //计算向量 this.localPostion.x = clamp(this.node.position.x, -this.range, this.range) / this.range; this.localPostion.y = clamp(this.node.position.y, -this.range, this.range) / this.range; //console.log("this.localPostion:", this.localPostion); this.spinMerge1.updateAnimation(this.localPostion); }
功能完整代码:
import { _decorator, Component, Node, EventTouch, Camera, Vec3, UITransform, Vec2, clamp, director, view } from 'cc'; import { SpinMerge1 } from './SpinMerge1'; const { ccclass, property } = _decorator; @ccclass('TouchMove') export class TouchMove extends Component { @property({ type: Node }) private nodeParent: Node; @property({ type: Camera }) private camera: Camera; //控制 Spine 动画的脚步 @property({ type: SpinMerge1 }) private spinMerge1: SpinMerge1; private potion: Vec3 = new Vec3(); private worldPostion: Vec3 = new Vec3(); private localPostion: Vec2 = new Vec2(); private range: number; onLoad() { this.node.on(Node.EventType.TOUCH_START, this.touchStart, this); this.node.on(Node.EventType.TOUCH_MOVE, this.touchMove, this); this.node.on(Node.EventType.TOUCH_END, this.touchEnd, this) this.node.on(Node.EventType.TOUCH_CANCEL, this.touchCancel, this) } start() { this.range = view.getVisibleSize().width / 2; } //展示只有 在拖拽情况下才显示的 元素 equipmentFly touchStart(event: EventTouch) { //Log.trace('touchStart'); console.log('touchStart'); } //获取 触点坐标位置 touchMove(event: EventTouch) { //更新 拖拽元素位置 console.log('touchMove'); this.potion.x = event.getLocationX(); this.potion.y = event.getLocationY(); //触点坐标转 屏幕坐标 this.worldPostion = this.camera.screenToWorld(this.potion); //屏幕坐标转 局部坐标 this.node.position = this.nodeParent.getComponent(UITransform).convertToNodeSpaceAR(this.worldPostion); //console.log("this.node.position:", this.node.position); //计算向量 this.localPostion.x = clamp(this.node.position.x, -this.range, this.range) / this.range; this.localPostion.y = clamp(this.node.position.y, -this.range, this.range) / this.range; //console.log("this.localPostion:", this.localPostion); this.spinMerge1.updateAnimation(this.localPostion); } //结束拖拽 隐藏拖拽元素 touchCancel(event: EventTouch) { console.log('touchCancel'); this.node.position = new Vec3(); this.spinMerge1.resetAnimation(); } //结束拖拽 touchEnd(event: EventTouch) { console.log('touchEnd'); this.node.position = new Vec3(); this.spinMerge1.resetAnimation(); } }
实操2.猫头鹰盯着逗猫棒
猫头随着物体移动的核心就是:
通过动画层混合 分别控制 头上,头右,头下,头左 动画权重,做到动态控制展示的权重。
核心代码:
public updateAnimation(vec2: Vec2) { //console.log('updateAnimation:', vec2); this.ver2Normal.x = Math.abs(vec2.x); this.ver2Normal.y = Math.abs(vec2.y); //右上 if (vec2.x > 0 && vec2.y > 0) { this.trackleft.alpha = 0.0; this.trackdown.alpha = 0.0; this.trackright.alpha = this.ver2Normal.x; this.trackUp.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //右下 } else if (vec2.x > 0 && vec2.y < 0) { this.trackUp.alpha = 0; this.trackleft.alpha = 0; this.trackright.alpha = this.ver2Normal.x; this.trackdown.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //左下 } else if (vec2.x < 0 && vec2.y < 0) { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackleft.alpha = this.ver2Normal.x; this.trackdown.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //左上 } else if (vec2.x < 0 && vec2.y > 0) { this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = this.ver2Normal.x; this.trackUp.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); } else { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } }
完整代码:
import { _decorator, Component, Node, sp, Button, Vec2, clamp } from 'cc'; const { ccclass, property } = _decorator; @ccclass('SpinMerge1') export class SpinMerge1 extends Component { @property({ type: sp.Skeleton }) private spinAnimation: sp.Skeleton; private trackUp: sp.spine.TrackEntry; private trackright: sp.spine.TrackEntry; private trackdown: sp.spine.TrackEntry; private trackleft: sp.spine.TrackEntry; private ver2Normal: Vec2 = new Vec2(); start() { this.spinAnimation.setAnimation(0, 'idle', true); //猫头鹰的动画名 left right 反了 //所以我们这里的顺序是: 上 -> 右 -> 下 -> 左 this.trackUp = this.spinAnimation.setAnimation(1, 'up', true); this.trackright = this.spinAnimation.setAnimation(4, 'left', true); this.trackdown = this.spinAnimation.setAnimation(3, 'down', true); this.trackleft = this.spinAnimation.setAnimation(2, 'right', true); //眨眼睛动画 this.spinAnimation.addAnimation(5, 'blink', true, 2); this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } onBtnTop() { this.trackUp.alpha = 1; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } onBtnRight() { this.trackUp.alpha = 0; this.trackright.alpha = 1; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } onBtnBottom() { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 1; this.trackleft.alpha = 0; } onBtnleft() { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 1; } onBtnRightTop() { this.trackleft.alpha = 0.0; this.trackdown.alpha = 0.0; this.trackright.alpha = 0.5; this.trackUp.alpha = 0.5; } onBtnRightBottom() { this.trackUp.alpha = 0; this.trackleft.alpha = 0; this.trackright.alpha = 0.5; this.trackdown.alpha = 0.5; } onBtnleftBottom() { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackleft.alpha = 0.5; this.trackdown.alpha = 0.5; } onBtnleftTop() { this.trackdown.alpha = 0; this.trackright.alpha = 0; this.trackleft.alpha = 0.5; this.trackUp.alpha = 0.5; } public updateAnimation(vec2: Vec2) { //console.log('updateAnimation:', vec2); this.ver2Normal.x = Math.abs(vec2.x); this.ver2Normal.y = Math.abs(vec2.y); //右上 if (vec2.x > 0 && vec2.y > 0) { this.trackleft.alpha = 0.0; this.trackdown.alpha = 0.0; this.trackright.alpha = this.ver2Normal.x; this.trackUp.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //右下 } else if (vec2.x > 0 && vec2.y < 0) { this.trackUp.alpha = 0; this.trackleft.alpha = 0; this.trackright.alpha = this.ver2Normal.x; this.trackdown.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //左下 } else if (vec2.x < 0 && vec2.y < 0) { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackleft.alpha = this.ver2Normal.x; this.trackdown.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); //左上 } else if (vec2.x < 0 && vec2.y > 0) { this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = this.ver2Normal.x; this.trackUp.alpha = clamp(this.ver2Normal.y, 0, 1 - this.ver2Normal.x); } else { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } } public resetAnimation() { this.trackUp.alpha = 0; this.trackright.alpha = 0; this.trackdown.alpha = 0; this.trackleft.alpha = 0; } }
工程下载