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

【airsimunity】添加人物与行走动画

首先,在unity assets获取喜欢的角色和动画


选择好后,保存到自己的assets,在unity编辑器界面选择window/package manager进行下载和导入。如此,这些资源会在你的项目文件夹中,可以随意使用

其次,把人物放在场景中,并添加人物动画,通过键盘控制其前后左右移动

再导入的npc文件夹中找到prefabs选择一个人物放在你的unity场景内。人物放进去后,可以通过unity修改其外观颜色,但是衣服发型等只能用你下载好的资源。但是这个时候人物是没办法受控制移动的。

要想人物自然移动,还需要添加人物的动画animator controller,否则的话人物只会在场景“漂移”。

首先选择你需要修改的人物,在其inspector界面添加组件animator。这时候的animator controller显示为none,需要自己搭建一个自己需要的controller。

在animator界面,拖入下载的动画资源里的人物动作。

我先拖入了idle和walk_forward进行测试查看效果

拖入后,使idle为默认状态,walk和idle直接用双箭头链接起来(右键选择make transition链接),并添加一个参数为bool IsWalking。把该animator挂载在需要控制的人物上。最后写一个控制脚本,挂载在被控人物上。【可借助ai完成】


测试通过后,我希望通过键盘控制人物前后左右的移动,不再简单的只能向前。

这时候在animator新建一个状态,选择blend tree,命名为locomotion。在animator添加变量float MoverX 和float MoverZ。

将entry连接到locomotion,idle链接到locomotion。
进入blend tree,按照一下图像进行设置。motion选择的都是下载的人物动作资源。

人物挂载的控制脚本,实现了键盘wasd控制人物在环境中的“行走”:

using UnityEngine; public class NPCMove : MonoBehaviour { public float speed = 2f; private Animator animator; void Start() { animator = GetComponent<Animator>(); } void Update() { // 获取输入 float moveX = Input.GetAxis("Horizontal"); // A/D 或 左/右 float moveZ = Input.GetAxis("Vertical"); // W/S 或 前/后 // 传入 Animator animator.SetFloat("MoveX", moveX); animator.SetFloat("MoveZ", moveZ); // 移动人物 Vector3 move = new Vector3(moveX, 0, moveZ); if (move.magnitude > 0.01f) // 有输入才移动 { transform.Translate(move * speed * Time.deltaTime, Space.World); // 让人物朝移动方向旋转 transform.forward = move; } else { // 没有输入,人物静止 // Animator 会自动播放 PosX=0, PosY=0 的 Idle 动画 } } }

最后,实现人物的轨迹移动。

在之前的小球轨迹控制移动的基础上,增加人物控制脚本:
注意要对被控人物增加waypoint,同样可以通过手动拖动waypoint修改曲线形状,即运动的轨迹。

using System.Collections; using System.Collections.Generic; using UnityEngine; public class CharacterBezierMove : MonoBehaviour { public Transform wayPoint1; public Transform wayPoint2; public Transform wayPoint3; public float moveSpeed = 2f; // 人物移动速度 public float lifeTime = 5f; // 走完整条曲线的时间 private float t = 0f; private Vector3 position0; private Vector3 position1; private Vector3 position2; private Vector3 position3; private Animator animator; void Start() { animator = GetComponent<Animator>(); position0 = transform.position; position1 = wayPoint1.position; if (wayPoint2 != null) position2 = wayPoint2.position; if (wayPoint3 != null) position3 = wayPoint3.position; } void Update() { MoveAlongBezier(); } void MoveAlongBezier() { if (t >= 1f) { animator.SetFloat("Speed", 0); return; } Vector3 currentPos = transform.position; Vector3 nextPos; // 🔥 根据不同阶数计算曲线 if (wayPoint2 == null) { nextPos = BezierCurve.Point(position0, position1, t); } else if (wayPoint3 == null) { nextPos = BezierCurve.Point(position0, position1, position2, t); } else { nextPos = BezierCurve.Point(position0, position1, position2, position3, t); } // ✅ 计算方向(关键:让人物朝前) Vector3 direction = (nextPos - currentPos).normalized; // ✅ 移动(比直接赋值更自然) transform.position = Vector3.MoveTowards(currentPos, nextPos, moveSpeed * Time.deltaTime); // ✅ 朝向 if (direction != Vector3.zero) { transform.forward = direction; } // ✅ 动画控制 float speedPercent = moveSpeed; animator.SetFloat("Speed", speedPercent); // ✅ 推进t t += Time.deltaTime / lifeTime; } private void OnDrawGizmos() { wayPoint1 = transform.Find("WayPoint1"); wayPoint2 = transform.Find("WayPoint2"); wayPoint3 = transform.Find("WayPoint3"); float deltaT = 0.02f; Gizmos.color = Color.green; // 人物路径用绿色区分 for (float t = 0; t < 1; t+=deltaT) { if (wayPoint2 == null) { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, t), 0.05f); } else if (wayPoint3 == null) { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, wayPoint2.position, t), 0.05f); } else { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, wayPoint2.position, wayPoint3.position, t), 0.05f); } } } }

这时候人物还是飘着的,并没有行走的动画。

新创建了一个animator,添加blend tree


修改代码为:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class CharacterBezierMove : MonoBehaviour { public Transform wayPoint1; public Transform wayPoint2; public Transform wayPoint3; public float moveSpeed = 2f; // 人物移动速度 public float lifeTime = 5f; // 走完整条曲线的时间 private float t = 0f; private Vector3 position0; private Vector3 position1; private Vector3 position2; private Vector3 position3; private Animator animator; void Start() { animator = GetComponent<Animator>(); position0 = transform.position; position1 = wayPoint1.position; if (wayPoint2 != null) position2 = wayPoint2.position; if (wayPoint3 != null) position3 = wayPoint3.position; } void Update() { MoveAlongBezier(); } void MoveAlongBezier() { if (t >= 1f) { animator.SetFloat("SpeedX", 0); animator.SetFloat("SpeedZ", 0); return; } Vector3 currentPos = transform.position; Vector3 nextPos; if (wayPoint2 == null) nextPos = BezierCurve.Point(position0, position1, t); else if (wayPoint3 == null) nextPos = BezierCurve.Point(position0, position1, position2, t); else nextPos = BezierCurve.Point(position0, position1, position2, position3, t); Vector3 direction = (nextPos - currentPos).normalized; // 移动人物 transform.position = Vector3.MoveTowards(currentPos, nextPos, moveSpeed * Time.deltaTime); // 朝向 if (direction != Vector3.zero) transform.forward = direction; // 根据方向计算Animator参数 Vector3 localDir = transform.InverseTransformDirection(direction); animator.SetFloat("SpeedX", localDir.x); animator.SetFloat("SpeedZ", localDir.z); // 推进t t += Time.deltaTime / lifeTime; } private void OnDrawGizmos() { wayPoint1 = transform.Find("WayPoint1"); wayPoint2 = transform.Find("WayPoint2"); wayPoint3 = transform.Find("WayPoint3"); float deltaT = 0.02f; Gizmos.color = Color.green; // 人物路径用绿色区分 for (float t = 0; t < 1; t+=deltaT) { if (wayPoint2 == null) { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, t), 0.05f); } else if (wayPoint3 == null) { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, wayPoint2.position, t), 0.05f); } else { Gizmos.DrawSphere(BezierCurve.Point(transform.position, wayPoint1.position, wayPoint2.position, wayPoint3.position, t), 0.05f); } } } }
http://www.jsqmd.com/news/564100/

相关文章:

  • (转)mybatis拦截器
  • 2019~2026年更新大众点评数据,商家店铺,电话,评分,营业时间,名称地址经纬度,消费价格,支持外卖,收录时间等字段~不指定年份的话,默认报价是2026年。默认发2026年的
  • C++ 中this的秘密
  • 数字孪生通信层开发:C#实现OPC UA到Unity3D的实时数据映射(2026年工业级实战指南)
  • 开源大模型实战案例:Pixel Epic如何用AgentCPM-Report写行业分析报告
  • 手把手教你:在纯CPU的Linux服务器上离线部署Ollama和Qwen2-0.5B模型
  • JavaSE从0到1-DAY4.1-多态实战(ii)
  • Seurat与DoubletFinder联用:构建自动化双胞过滤流水线
  • Matlab闪退弹窗stopped working and needs to close
  • WinDiskWriter:Mac用户制作Windows启动盘的零门槛开源工具
  • PP-DocLayoutV3教育场景:教材/试卷图片中竖排文本+图表+公式同步解析
  • Lingbot-Depth-Pretrain-Vitl-14 保姆级教程:Ubuntu 20.04 系统环境配置
  • 华为OD机考双机位C卷 - 最左侧冗余覆盖子串 (Java)
  • 弦音墨影保姆级教程:解决‘视频加载失败’‘墨迹不跟随目标’等10类高频问题
  • 忍者像素绘卷Z-Image-Turbo模型优化原理:线条锐化与色彩分层技术
  • 2026年防爆门厂家选择:我的实践案例与避坑分享
  • Loop窗口管理工具:Mac多任务处理的终极解决方案
  • ComfyUI节点连接报错?一文搞懂‘条件’与‘文本’数据类型的区别与转换
  • DDColor效果展示:同一张黑白照,不同语义引导下的5种风格化着色结果
  • 完全离线语音处理:基于AnythingLLM的本地化语音转文字开源方案
  • Qwen3-ASR-0.6B部署教程:Ubuntu 22.04 + NVIDIA驱动 + Docker全链路
  • 依然似故人_孙珍妮文生图模型教程:Z-Image-Turbo LoRA提示词中英文混合写法技巧
  • 复古像素UI设计哲学:像素极光引擎大气/明亮/交互三原则技术实现
  • 2026年口碑好的电子级无水乙醇/工业级无水乙醇制造厂家推荐 - 行业平台推荐
  • StructBERT效果实测:错别字容错能力惊人,相似度计算准确率高
  • Z-Image-Turbo-rinaiqiao-huiyewunv入门指南:Streamlit会话状态管理避免多用户并发冲突
  • Qwen-Image-2512-Pixel-Art-LoRA 结合YOLOv8:智能识别并生成场景像素画
  • CLIP-GmP-ViT-L-14保姆级教程:日志分析+性能压测+异常恢复全链路运维指南
  • 3分钟上手Fast-F1:Python赛车数据分析实战指南
  • Edge浏览器批量下载GLASS数据集全攻略:DownThemAll插件+Python脚本双保险