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

VUE2+THREE.JS 按照行动轨迹移动人物模型并相机视角跟随人物

按照行动轨迹移动人物模型并相机视角跟随人物

  • 1. 初始化加载模型
  • 2. 开始移动模型
  • 3. 人物模型启动
  • 4. 暂停模型移动
  • 5. 重置模型位置
  • 6. 切换区域动画
  • 7. 摄像机追踪模型
  • 8. 移动模型位置
  • 9.动画执行

人物按照上一篇博客所设定的关键点位置,匀速移动

1. 初始化加载模型

// 加载巡航人物模型 callback 动作完成的回调函数
initWalkPerson(callback) {fbxloader("walk").then((obj) => {obj.scale.set(2.5, 2.5, 2.5);obj.name = "person";person = obj;scene.add(obj);//有回调函数 就执行回调函数callback && callback();});
},

2. 开始移动模型

// 开始移动模型
startAnimation() {if (isAnimating) return this.elMessage("当前巡航已开始,请勿多次操作", "error");isAnimating = true;//说明模型已加载完成,无需重复加载,直接执行动画效果if (person) {this.personPositionStart();} else {//人物模型加载完毕后在执行this.initWalkPerson(() => {this.personPositionStart();});}
},

3. 人物模型启动

//人物动画启动
personPositionStart() {personMixer = new THREE.AnimationMixer(person);let AnimationAction = personMixer.clipAction(person.animations[0]);AnimationAction.play();person.position.set(pointArr[0]);scene.getObjectByName("path").material.visible = false; //隐藏行动轨迹动画scene.getObjectByName("person").visible = true;tweenHandlers = [];// 定义速度(单位:单位长度/秒)const speed = 300; // 你可以根据需要调整这个速度值let prevTween = null;let startPos = new THREE.Vector3(...pointArr[0]);for (let i = 1; i < pointArr.length; i++) {// 每次循环设置下一个目标点const endPos = new THREE.Vector3(...pointArr[i]);const newTween = this.createTween(startPos.clone(), endPos, speed);tweenHandlers.push(newTween);if (prevTween) {prevTween.chain(newTween);} else {// 如果是序列中的第一个tween,立即开始动画newTween.start();}// 将此tween存储为下一个tween的'prevTween'prevTween = newTween;// 更新起始点为当前结束点,为下一个循环准备startPos.copy(endPos);}// 开始第一个tween动画if (tweenHandlers.length > 0) {currentTween = tweenHandlers[0];currentTween.start();isAnimating = true;}// 在最后一个Tween结束后执行的动作prevTween.onComplete(() => {// 在动画被标记为完成时才重置位置this.resetPosition();});
},

4. 暂停模型移动

// 暂停模型移动
pauseAnimation() {if (!isAnimating) {this.elMessage("当前巡航未开始", "warning");return;}if (this.isPaused) {// 恢复摄像机状态camera.position.copy(savedCameraPosition);controls.target.copy(savedCameraTarget);controls.update();// 恢复动画tweenHandlers.forEach((tween) => tween.resume());personMixer.timeScale = 1;this.isPaused = false; //设置this.isPaused为falseisAnimating = true;this.elMessage("动画已恢复", "success");this.updateCameraPosition(person, camera, new THREE.Vector3(0, 250, 200));} else {// 保存当前摄像机状态savedCameraPosition = camera.position.clone();savedCameraTarget = controls.target.clone();// 暂停动画tweenHandlers.forEach((tween) => tween.pause());personMixer.timeScale = 0;this.isPaused = true; //设置this.isPaused为truethis.elMessage("动画已暂停", "success");}
},

5. 重置模型位置

// 重置模型位置
resetPosition() {isAnimating = false;this.pauseAnimation();// 将模型从场景中移除scene.getObjectByName("person").visible = false;// 清理动画混合器if (personMixer) {personMixer.uncacheClip(personMixer._actions[0]._clip);personMixer = null;}tweenHandlers.forEach((item) => item.stop());tweenHandlers = [];// 重置动画状态this.isPaused = false;this.tweenArea({ x: -5000, y: 7000, z: 16000 }, { x: 0, y: 0, z: 1 });//显示行动轨迹scene.getObjectByName("path").material.visible = true;
},

6. 切换区域动画

// 切换区域动画
tweenArea(Position, controlsTarget) {// 传递任意目标位置,从当前位置运动到目标位置const p1 = {// 定义相机位置是目标位置到中心点距离的2.2倍x: camera.position.x,y: camera.position.y,z: camera.position.z,};const p2 = {x: Position.x,y: Position.y,z: Position.z,};changeAreaTween = new TWEEN.Tween(p1).to(p2, 1200); // 第一段动画const update = function (object) {camera.rotation.y = (90 * Math.PI) / 180;camera.position.set(object.x, object.y, object.z);controls.target = new THREE.Vector3(controlsTarget.x, controlsTarget.y, controlsTarget.z);// camera.lookAt(lookAt); // 保证动画执行时,相机焦距在中心点controls.enabled = false;controls.update();};changeAreaTween.onUpdate(update);//  动画完成后的执行函数changeAreaTween.onComplete(() => {controls.enabled = true; // 执行完成后开启控制});changeAreaTween.easing(TWEEN.Easing.Quadratic.InOut);changeAreaTween.start();
},

7. 摄像机追踪模型

// 摄像机追踪模型
updateCameraPosition(model, camera, offset) {if (!this.isPaused && isAnimating) {// 添加条件判断const desiredPosition = new THREE.Vector3().copy(model.position).add(offset);camera.position.lerp(desiredPosition, 0.05);camera.lookAt(model.position);}
},

8. 移动模型位置

// 移动模型位置
createTween(startPosition, endPosition, speed) {// 计算起点到终点的距离const distance = startPosition.distanceTo(endPosition);// 使用距离除以速度来计算持续时间const duration = (distance / speed) * 1000; // 持续时间(以毫秒为单位)// 创建并返回一个新的Tween动画return new TWEEN.Tween(startPosition).to({ x: endPosition.x, y: endPosition.y, z: endPosition.z }, duration).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(() => {//相机的相对偏移量,z=-400 在人物模型的后面const relativeCameraOffset = new THREE.Vector3(0, 100, -400);const targetCameraPosition = relativeCameraOffset.applyMatrix4(person.matrixWorld);camera.position.set(targetCameraPosition.x, targetCameraPosition.y, targetCameraPosition.z);//更新控制器的目标为Person的位置const walkerPosition = person.position.clone();controls.target = new THREE.Vector3(walkerPosition.x, 100, walkerPosition.z);// 确保控制器的变更生效controls.update();// 更新模型位置person.position.copy(startPosition);person.lookAt(endPosition);}).onComplete(() => {// 动画完成时,确保模型位置与结束位置相匹配person.position.copy(endPosition);});
},

9.动画执行

全局定义的参数:

let personMixer = null; // 巡航混合器变量
let personClock = new THREE.Clock(); // 巡航计时工具
// 获取巡航时间差
const personDelta = personClock.getDelta();if (personMixer && isAnimating) {personMixer.update(personDelta);
}
TWEEN.update();

相关文章:

VUE2+THREE.JS 按照行动轨迹移动人物模型并相机视角跟随人物

按照行动轨迹移动人物模型并相机视角跟随人物 1. 初始化加载模型2. 开始移动模型3. 人物模型启动4. 暂停模型移动5. 重置模型位置6. 切换区域动画7. 摄像机追踪模型8. 移动模型位置9.动画执行 人物按照上一篇博客所设定的关键点位置&#xff0c;匀速移动 1. 初始化加载模型 //…...

Hadoop YARN组件

1. 请解释Yarn的基本架构和工作原理。 YARN&#xff0c;也被称为"Yet Another Resource Negotiator"&#xff0c;是Apache HadoopYARN&#xff0c;也被称为"Yet Another Resource Negotiator"&#xff0c;是Apache Hadoop的一部分&#xff0c;它被设计为一…...

Java架构师技术架构路线

目录 1 概论2 如何规划短中长期的技术架构路线图3 如何规划面向未来的架构4 如何修订路线图执行过程中的偏差5 如何落地路线图-阿里系糙快猛之下的敏捷模式想学习架构师构建流程请跳转:Java架构师系统架构设计 1 概论 首先,规划一个短中长期的技术路线图是非常重要的。短中…...

guacamole docker一键部署脚本

前言 在我学习guacamole的过程中发现全网大致有两种方式安装guacamole的方式&#xff1a; 1. 直接安装&#xff08;下载java环境/mysql/, 修改配置&#xff09; 2. docker安装&#xff08;和直接安装类似&#xff0c;需要下载相关环境&#xff0c;然后做配置&#xff09; 然…...

蓝桥杯算法心得——想吃冰淇淋和蛋糕(dp)

大家好&#xff0c;我是晴天学长&#xff0c;dp题&#xff0c;怎么设计状态很重要&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .想吃冰淇淋和蛋糕 想吃冰淇淋与蛋糕 输入格式 第一行输入一个整数n。…...

LLM之RAG实战(二):使用LlamaIndex + Metaphor实现知识工作自动化

最先进的大型语言模型&#xff08;LLM&#xff09;&#xff0c;如ChatGPT、GPT-4、Claude 2&#xff0c;具有令人难以置信的推理能力&#xff0c;可以解锁各种用例——从洞察力提取到问答&#xff0c;再到通用工作流自动化。然而&#xff0c;他们检索上下文相关信息的能力有限。…...

【容器】Docker打包Linux操作系统迁移

0x0 场景 因老服务器操作系统文centos6.5&#xff0c;现要迁移至uos v20 1050a&#xff08;底层centos8&#xff09;&#xff0c;其中需要迁移的应用组件有&#xff1a; mysql 、tomcat、apachehttpd&#xff0c;因版本跨越太大&#xff0c;导致centos8直接安装无法完全恢复原…...

redis基本数据结构

Redis入门&#xff1a;五大数据类型 文章目录 Redis入门&#xff1a;五大数据类型一.概述二.Redis的基本了解三.Redis五大数据类型1.String (字符串)2.List(列表)3.Set集合(元素唯一不重复)4.Hash集合5.zSet(有序集合) 一.概述 什么是Redis Redis&#xff08;Remote Dictiona…...

Learning Normal Dynamics in Videos with Meta Prototype Network 论文阅读

文章信息&#xff1a;发表在cvpr2021 原文链接&#xff1a; Learning Normal Dynamics in Videos with Meta Prototype Network 摘要1.介绍2.相关工作3.方法3.1. Dynamic Prototype Unit3.2. 视频异常检测的目标函数3.3. 少样本视频异常检测中的元学习 4.实验5.总结代码复现&a…...

Unity 关于SpriteRenderer 和正交相机缩放

float oldWidth 750f;float oldHeight 1334f;float newWidth Screen.width;float newHeight Screen.height;float oldAspect oldWidth / oldHeight;float newAspect newWidth / newHeight;//水平方向缩放float horizontalCompressionRatio newAspect / oldAspect;//垂直…...

HarmonyOS应用开发者基础认证考试(98分答案)

基于最近大家都在考这个应用开发者基础认证考试&#xff0c;因此出了一期&#xff0c;一样复制word里面搜索做&#xff0c;很快&#xff0c;当然good luck 判断题 Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。 正确(Tr…...

Ubuntu20.04 Kimera Semantic运行记录

Ubuntu20.04 Kimera Semantic 官方bag运行记录 以下基本为官方教程&#xff0c;有部分修改 依赖 sudo apt-get install python3-wstool python3-catkin-tools protobuf-compiler autoconf sudo apt-get install ros-noetic-cmake-modulessudo apt-get install ros-noetic-i…...

服务器RAID系统的常见故障,结合应用场景谈谈常规的维修处理流程

常见的服务器RAID系统故障包括硬盘故障、控制器故障、电源故障、写入错误和热插拔错误。下面结合这些故障的应用场景和常规维修处理流程来详细讨论&#xff1a; 硬盘故障&#xff1a; 应用场景&#xff1a;在服务器RAID系统中&#xff0c;硬盘故障是最常见的问题之一。硬盘可能…...

计算机网络——数据链路层-封装成帧(帧定界、透明传输-字节填充,比特填充、MTU)

目录 介绍 帧定界 PPP帧 以太网帧 透明传输 字节填充&#xff08;字符填充&#xff09; 比特填充 比特填充习题 MTU 介绍 所谓封装成帧&#xff0c;就是指数据链路层给上层交付下来的协议数据单元添加帧头和帧尾&#xff0c;使之成为帧。 例如下图所示&#xff1a; …...

MySQL笔记-第03章_基本的SELECT语句

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第03章_基本的SELECT语句1. SQL概述1.1 SQL背景知识1.2 SQL语言排行榜1.3 SQL 分类 2. SQL语言的规则与规范2.1 基本规则2.2 SQL大小写规范 …...

FTP服务文件上传失败,错误码553的排故过程

本文主要记录文件上传失败&#xff0c;错误码553的排故过程。 1 背景 树莓派通过FTP给嵌入式板卡传输文件&#xff0c;好几套设备&#xff0c;发现有的能传输成功&#xff0c;有的传输不成功。树莓派和嵌入式板卡都一样的&#xff0c;出现问题时感觉很懵。 2 逐项对比 2.1 自…...

音频录制软件哪个好?帮助你找到最合适的一款

音频录制软件是日常工作、学习和创作中不可或缺的一部分。选择一个适合自己需求的录音软件对于确保音频质量和提高工作效率至关重要。可是您知道音频录制软件哪个好吗&#xff1f;本文将深入探讨两种常见的音频录制软件&#xff0c;通过详细的步骤指南&#xff0c;帮助您了解它…...

9.Unity搭建HTTP服务器

搭建HTTP服务器的几种方式 //1.使用别人做好的HTTP服务器软件&#xff0c;一般作为资源服务器时使用该方式&#xff08;学习阶段建议使用&#xff09; //2.自己编写HTTP服务器应用程序&#xff0c;一般作为Web服务器 或者 短链接游戏服务器时 使用该方式 使用别人做好的HTTP服…...

C# 热键注册工具类

写在前面 介绍一个验证过的热键注册工具类&#xff0c;使用系统类库user32.dll中的RegisterHotkey函数来实现全局热键的注册。 代码实现 [Flags]public enum KeyModifiers{Alt 1,Control 2,Shift 4,Windows 8,NoRepeat 0x4000}public static class HotKeyHelper{[DllImp…...

nodejs微信小程序+python+PHP天天网站书城管理系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...