spine 动画层 动态权重

news2025/1/23 9:27:02

 前奏.业务背景

这边想实现一个功能,项目中有 一只猫 猫的头会盯着逗猫棒移动。因为素材还没到所以这里使用了 spine 自带的猫头鹰。他的动画 刚好挺有针对性:(关联上篇)icon-default.png?t=O83Ahttps://blog.csdn.net/nicepainkiller/article/details/144113214

一共有六组动画:

        idel  空闲动画全身都会动 多帧动画

        blink 眨眼睛动画 头不动 多帧动画

        其余几个动画只有有头会动 单帧动画 只有一帧

  •     blink  眼睛在中间的动画 会眨眼睛
  •     down 眼睛朝下看 单帧
  •     left     眼睛朝左看 单帧
  •     right   眼睛朝右看 单帧
  •     up      眼睛朝上看 单帧
  •     idel    空闲动画 全身动

 猫头鹰眼睛可以盯着 屏幕物体移动,实现效果如下 :

最初想的实现方式是:类似 Unity3D 有一个二位动画权重的东西。但是找遍 cocos creator  API 发现没找到,

那么只能纯手撸了:

        思路就是动态控制 动画层的权重来显示不同动画头上,头右,头下,头左)的播放权重。

实操1.逗猫棒位置计算

首先就确定一个屏幕可以拖动的元素,并且计算出一个区域,用来确定 向量 Vec2:

  1. 屏幕触点 转 屏幕坐标
  2. 根据屏幕坐标计算出 用来表示范围的  一个 向量
核心代码:
//获取 触点坐标位置
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;

    }

}


工程下载

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

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

相关文章

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程&#xff0c;Executor 的内存管理建立在 JVM(最小为六十四分之一&#xff0c;最大为四分之一)的内存管理之上&#xff0c;此外spark还引入了堆外内存&#xff08;不在JVM中的内存&#xff09;&#xff0c;在spark中是指不…

Vision Transformer(vit)的主干

图解&#xff1a; 代码&#xff1a; class VisionTransformer(nn.Module):def __init__(self, img_size224, patch_size16, in_c3, num_classes1000,embed_dim768, depth12, num_heads12, mlp_ratio4.0, qkv_biasTrue,qk_scaleNone, representation_sizeNone, distilledFalse,…

mongodb配置ssl连接

mongodb5.0.9 centos7.6x86 1、正常启动mongod -f mongodb.conf 2、生成所需要的ssl证书 服务端ssl配置&#xff1a; 2.1生成ca.pem证书 #-x509&#xff1a; 用于生成自签证书&#xff0c;如果不是自签证书则不需要此项 #-days: 证书的有效期限&…

Linux 中的 ls 命令:从使用到源码解析

ls 命令是 Linux 系统中最常用和最基本的命令之一。下面将深入探讨 ls 命令的使用方法、工作原理、源码解析以及实际应用场景。 1. ls 命令的使用** ls 命令用于列出目录内容&#xff0c;显示文件和目录的详细信息。 1.1 基本用法 ls [选项] [文件或目录]例如&#xff1a; …

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo)

Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 目录 Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 一、简单介绍 二、PyTorch 三、CNN 1、神经网络 2、卷…

【C语言】结构体(二)

一&#xff0c;结构体的初始化 和其它类型变量一样&#xff0c;对结构体变量可以在定义时指定初始值 #include <stdio.h> #include <stdlib.h> struct books // 结构体类型 {char title[50];char author[50]; //结构体成员char subject[100];int book_id; }…

C++(4个类型转换)

1. C语言中的类型转换 1. 隐式 类型转换&#xff1a; 具有相近的类型才能进行互相转换&#xff0c;如&#xff1a;int,char,double都表示数值。 2. 强制类型转换&#xff1a;能隐式类型转换就能强制类型转换&#xff0c;隐式类型之间的转换类型强相关&#xff0c;强制类型转换…

Windows下从命令行(Powershell/CMD)发送内容到系统通知中心

Windows下从命令行&#xff08;Powershell/CMD&#xff09;发送内容到系统通知中心 01 前言 在平时写脚本的时候&#xff0c;将日志等信息直接输出到控制台固然是最直接的&#xff0c;而如果是一些后台执行的任务&#xff0c;不需要时刻关注运行细节但是又想知道一些大致的情…

四、初识C语言(4)

一、作业&#xff1a;static修饰局部变量 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> //作业&#xff1a;static修饰局部变量 int sum (int a) {int c 0;static int b 3;c 1;b 2;return (abc); } int main() {int i 0;int a …

基于深度学习的甲状腺结节影像自动化诊断系统(PyQt5界面+数据集+训练代码)

随着医学影像技术的发展&#xff0c;计算机辅助诊断在甲状腺结节的早期筛查中发挥着重要作用。甲状腺结节的良恶性鉴别对临床治疗具有重要意义&#xff0c;但传统的诊断方法依赖于医生的经验和影像学特征&#xff0c;存在一定的主观性和局限性。为了解决这一问题&#xff0c;本…

VLC 播放的音视频数据处理流水线搭建

VLC 播放的音视频数据处理流水线搭建 音视频流播放处理循环音频输出处理流水线VLC 用 input_thread_t 对象直接或间接管理音视频播放有关的各种资源,包括 Access, Demux, Decode, Output, Filter 等,这个类型定义 (位于 vlc-3.0.16/include/vlc_input.h) 如下: s…

Android 12系统源码_RRO机制(一)Runtime Resource Overlay机制实践

前言 Android的RRO&#xff08;Runtime Resource Overlay&#xff09;机制允许开发者在运行时替换或重写系统资源&#xff0c;例如布局、图标、字符串等。这个机制的目标是为了支持设备定制和主题化&#xff0c;特别是在不修改系统源代码的情况下。RRO通过在系统的资源上叠加一…

Tomcat新手成长之路:安装部署优化全解析(下)

接上篇《Tomcat新手成长之路&#xff1a;安装部署优化全解析&#xff08;上&#xff09;》: link 文章目录 7.应用部署7.1.上下文7.2.启动时进行部署7.3.动态应用部署 8.Tomcat 类加载机制8.1.简介8.2.类加载器定义8.3.XML解析器和 Java 9.JMS监控9.1.简介9.2.启用 JMX 远程监…

动态代理如何加强安全性

在当今这个信息爆炸、网络无孔不入的时代&#xff0c;我们的每一次点击、每一次浏览都可能留下痕迹&#xff0c;成为潜在的安全隐患。如何在享受网络便利的同时&#xff0c;有效保护自己的隐私和信息安全&#xff0c;成为了每位网络使用者必须面对的重要课题。动态代理服务器&a…

python---面向对象-python中的实践(2)

如何定义一个类&#xff1f; class 类名:pass怎样通过类&#xff0c;创建出一个对象&#xff1f; 根据类创建对象one Money() 执行流程1. 类的定义2. 根据类&#xff0c;创建出一个对象3. 将对象的唯一标识返回class Money:passprint(Money.__name__) xxx Money print(xxx.…

数据结构-散列函数的构造方法

一.数字关键词 关键词存储应该尽可能的离散 直接定址法:利用线性函数,例如上面的例子,h(key)key-1990,key1990&#xff0c;这个就被存放在0的位置 数字分析法:关键字可能有很到位组成,每一位变化可能都不一样&#xff0c;有的位是不变的,就是说不同的对象这一位都是一样的,有的…

单点登录解决方案 CAS(Central Authentication Service)详解

目录 CAS 的工作原理 票据&#xff08;Ticket&#xff09;详解 CAS 的优势 CAS 的应用场景 小结 参考资料 Central Authentication Service&#xff08;中央认证服务&#xff0c;简称 CAS&#xff09;是一个开源的企业级单点登录&#xff08;Single Sign-On, SSO&#xf…

输入json 达到预览效果

下载 npm i vue-json-pretty2.4.0 <template><div class"newBranchesDialog"><t-base-dialogv-if"addDialogShow"title"Json数据配置"closeDialog"closeDialog":dialogVisible"addDialogShow":center"…

U盘文件夹变打不开的文件:深度解析、恢复策略与预防之道

一、U盘文件夹变打不开的文件现象解析 在日常使用U盘的过程中&#xff0c;我们时常会遇到这样的困扰&#xff1a;原本存储有序、可以轻松访问的文件夹&#xff0c;突然之间变成了无法打开的文件。这些文件通常以未知图标或乱码形式显示&#xff0c;双击或右键尝试打开时&#…

2025年软考-网络工程师新旧教程及考纲变化对比!

2025网工教材改版基本确认&#xff01;网络工程师一直是软考中级的热门科目。最近&#xff0c;官方发布了官方第六版的网工教材&#xff0c;本次出版在原有第五版的基础上做了大量的删减&#xff0c;并新增了部分新内容。明年的软考考纲大概率会根据这次的新版教材进行修改&…