位置: IT常识 - 正文

3D 沙盒游戏之人物的点击行走移动(3d沙盒游戏推荐)

编辑:rootadmin
前言 在 3D 游戏中,都会有一个主人公。我们可以通过点击游戏中的其他位置,使游戏主人公向点击处移动。 那当我们想要实现一个“点击地面,人物移动到点击处”的功能,需要什么前置条件,并且具体怎么实现呢?本文带大家一步步实现人物行走移动,同时进行状态改变的功能。 一、骨骼动画 骨骼动画(Skeleton ... 前言

推荐整理分享3D 沙盒游戏之人物的点击行走移动(3d沙盒游戏推荐),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:沙盒3d无限金币,3d沙盒游戏是什么意思,3d沙盒游戏大全,3d沙盒游戏大全,3d沙盒游戏是什么意思,沙盒3d无限金币,沙盒3d无限金币,人气沙盒游戏,内容如对您有帮助,希望把文章链接给更多的朋友!

在 3D 游戏中,都会有一个主人公。我们可以通过点击游戏中的其他位置,使游戏主人公向点击处移动。

那当我们想要实现一个“点击地面,人物移动到点击处”的功能,需要什么前置条件,并且具体怎么实现呢?本文带大家一步步实现人物行走移动,同时进行状态改变的功能。

一、骨骼动画

骨骼动画(Skeleton animation 又称骨架动画,是一种计算机动画技术,它将三维模型分为两部分:用于绘制模型的蒙皮(Skin),以及用于控制动作的骨架。

一般在 3D 游戏中的主人公,它的跑步、走路、站立的动作,都是模型文件的自带骨骼动画。

骨骼动画权重改变骨骼动画的权重,可以使得动画间的过渡更为自然。比如体测时,当你到达终点后,会逐渐减慢速度,跑步动作的幅度越来越小,然后变成走路,最后停止。

让我们看看一个俩动作权重渐变的例子:

这个例子中,从休闲变到走路,休闲动画的权重从1到0递减,同时走路动画的权重从0到1递增。可以的点击? 这个网站中 > Crossfading > from idle to walk 体验一下。

在本次 3D 沙盒游戏中,人物状态改变,主要是鼠标点击地面后,人物从休闲状态转为跑步状态,当人物到达目的地后,又变为休闲状态。我们先来看看这些状态改变是如何实现的。

首先,我们需要设计师提供一个拥有骨骼动画的模型,它有两个骨骼动画,一个为休闲(idle)状态,一个为跑步(run)状态。

1.1 思路

1.2 动画初始化

先让我们将骨骼动画、动画名称、权重放到一个对象中存储起来,

idleAnimConfig = { name: string; anim: AnimationGroup; weight: number;}

那么如何判断是否正在行走呢?就需要一个当前动画的 flag,初始化时将 idle 设为当前动画

currentAnimConfig = idleAnimConfig

1.3 动画权重改变

如图,我们在人物状态改变时,需要将当前状态的动画权重递增,另一状态的动画权重递减(注意,权重值需要限制在[0, 1])。让我们看下伪代码,假设 deltaWeight 为正数

changeAnimWeight() { // 当前动画 -> 递增 if (currentAnimConfig) { setAnimationWeight(currentAnimConfig, deltaWeight) } // 其他动画 -> 递减,如站立动作切换到走路 if (currentAnimConfig !== idleAnimConfig) { setAnimationWeight(idleAnimConfig, -deltaWeight) } // 其他动画 -> 递减,如走路动作切换到站立 if (currentAnimConfig !== runAnimConfig) { setAnimationWeight(runAnimConfig, -deltaWeight) }}

然后在 render 的时候,进行状态切换

onRender() { if (准备到达目的地) { setCurAnimation(runAnimConfig) } else { setCurAnimation(idleAnimConfig) } changeAnimWeight()}

1.4 缺少动画

如果 animationGroup 里只有一个 run 动画怎么办呢?答案还是一样的,只要将 idle 动画的骨骼动画设为 null 即可,像这样:

idleAnimConfig = { name: string; anim: null; weight: number;}

这么做即使后来更换了具有两个动画的人物模型,也能复用。

动画状态切换实现效果

二、行走移动

当我们平常写动画时,会用到 rAF 并递归调用渲染函数,实现一个逐帧渲染动画。当人物行走在平地上时,也可以利用逐帧移动,来实现一个位移的动画。例如 Babylon 已经封装好了 render 的事件 API,只要我们将渲染动画绑定 render 事件,就可以使用了。

让我们看看具体思路:

2.1 移动

由上面的思路可以看出,我们移动的时候需要用到几个变量:

距终点的距离(distance)移动的方向(direction)

那么就需要在点击的时候,获取到这些变量。distance 可以利用矩阵对应坐标相加减计算,direction 就是目标位置减初始位置的法向量

directToPath() { // 将人物的位置设为初始位置 initVec = this.player.position // 计算初始位置与终点的距离 distance = Distance(targetVec, initVec) // 将终点位置与初始位置相减 targetVec = targetVec.subtract(initVec) // 使用法向量计算出与终点的朝向 direction = Normalize(targetVec) player.lookAt(targetVec)}onClick() { // ... directToPath()}

在 render 的时候进行位移

onRender() { if (distance > READY_ARRIVE) { distance -= SPEED // 人物朝 direction 方向移动 SPEED 距离 player.translate(direction, SPEED, Space.WORLD) }}

位移实现效果

2.2 结合动画

当我们的移动结合模型的骨骼动画

让我们看看伪代码:

onRender() { if (distance > READY_ARRIVE) { distance -= SPEED // 人物朝 direction 方向移动 SPEED 距离 player.translate(direction, SPEED, Space.WORLD) setCurAnimation(runAnimConfig) } else { setCurAnimation(idleAnimConfig) } changeAnimWeight()}

位移及状态变化实现效果

三、人物避障3.1 思路3D 沙盒游戏之人物的点击行走移动(3d沙盒游戏推荐)

人物行走避障,实际上就是从起点到终点,在这之中添加了中间点。如图

所以我们只要记录下当前起点到终点这个路径数组,每次都朝数组的第N个点行走,就能做到转向。下面我们来根据思路及伪代码进行步骤细化。

(1) 记录路径和初始化当前的路径索引

path = getPath(targetVec)prePathIdx = 0

(2) 当到达当前中间点时,切换到下一个中间点。当走到最后一个,则停止

onRender() { if (distance > READY_ARRIVE) { // ...移动及动画权重切换... } else { switchPath() // ... } // ...}switchPath() { prePathIdx += 1 directToPath()}directToPath() { const curPath = path[prePathIdx] if (!curPath) return // ...人物移动及转向...}

3.2 接入实际避障算法

由 3.1 得知,人物的行走移动要接入避障算法,需要利用到该算法提供的路径规划数组。实际应用中,我们只需要把,伪代码里的getPath()方法,换成算法计算道路的方法即可。

3.2.1 RecastJSPlugin

下面我们使用 Babylon 自带的 Recast 插件 ,来具体说明一下如何接入避障算法。

方法 1

在 recast 中,可以通过 computePath 获取路径:

const closestPoint = this.navigationPlugin.getClosestPoint(pickedPoint)const path = this.navigationPlugin.computePath( this._crowd.getAgentPosition(0), closestPoint)

然后利用 3.1 的思路,通过路径索引切换进行移动。

方法 2

recast 首先会创建一个导航网格,然后通过添加 agent 让它们约束在这个导航网格中,而这些 agent 的集合,称为 crowd。

并且 recast 自带了移动的 API —— agentGoto,此时可以不需要再去计算距离和方向,并且也不需要手动切换移动路径,让我们看看具体是怎么做的。

(1) 初始化插件,并设置 Web Worker 来获取网格数据以优化性能

initNav() { navigationPlugin = new RecastJSPlugin() // 设置Web Worker,在里面获取网格数据 navigationPlugin.setWorkerURL(WORKER_URL) // 创建导航mesh navigationPlugin.createNavMesh([ ground, ...obstacleList, // 障碍物列表mesh ], NAV_MESH_CONFIG, (navMeshData) => { navigationPlugin.buildFromNavmeshData(navMeshData) } this.navigationPlugin = navigationPlugin}

(2) 初始化 crowd(crowd:约束在导航网格中 agent 的集合)

initCrowd() { this.crowd = this.navigationPlugin.createCrowd(1, MAX_AGENT_RADIUS, this.scene) const transform = new TransformNode('playerTrans') this.crowd.addAgent(this.player.position, AGENTS_CONFIG, transform)}

(3) 点击时利用 agentGoto API进行移动,pickedPoint 为点击点的三维坐标,由于 crowd 里只有一个对象,所以索引是 0

const closestPoint = this.navigationPlugin.getClosestPoint(pickedPoint)this.crowd.agentGoto(0, closestPoint)

(4) 判断是否停止,未停止则改变人物朝向

那么如何改变人物的朝向呢,我们需要下一个中间点的位置,让人物看向它即可。所以回到之前初始化的地方,创建一个 navigator。

initCrowd() { // ... const navigator = MeshBuilder.CreateBox('navBall', { size: 0.1, height: 0.1, }, this.scene) navigator.isVisible = false this.navigator = navigator // ...}

在 render 的时候,人物是否停止,可以通过当前 agent 的移动速度来进行判断。而改变方向,则是通过将 navigator 移动到下一个 path 的中间点,让人物看向它。

onRender () { // 第一个agent对象的移动速度 const velocity = this.crowd.getAgentVelocity(0) // 移动人物到agent的位置 this.player.position = this.crowd.getAgentPosition(0) // 将navigator的位置移到下一个点 this.crowd.getAgentNextTargetPathToRef(0, this.navigator.position) if (velocity.length() > 0) { this.player.lookAt(this.navigator.position) // ... } else { // ... } // ...}

4. 避障实现效果

让我们看看最后的效果

5. 遇到的问题

整个开发过程中,其实也不是非常顺利,总结了一些遇到的问题,可以给大家参考一下。

(1) 年兽移动时,有时会“无法刹车”,导致在终点时反复来回停不下来;这是因为在这一帧里,由于年兽的加速度较小,无法使得短时间内将速度降为0。所以只能“走过头”再“走回来”直到速度降为0之后,停止在终点。此时,只需要 hack 一下,将 agent 的 maxAcceleration 设为极大,让其有种匀速行走并立马停下的感觉。

export const AGENTS_CONFIG: IAgentParameters = { maxAcceleration: 1000 // ...}

(2) 障碍物的动态添加与移除

如果障碍物在该场景初始化后,位置发生了改变,此时再去销毁创建一次 navMesh 是很消耗性能的。

于是我们通过查找文档,看到还有动态添加障碍物的 API。再立马调了下文档中的Playground,发现是可以用的。但是当我们把障碍物放大了之后,穿模了? 看看这里。

于是在 Babylon 的论坛上提了这个问题,20分钟后就得到了 reply,这个速度?。

原来是需要调整 NavMeshParameters 的 ch / cs / tileSize 参数,对项目做适配。

那如果想要自己实现避障,创建更快的navMesh,我们应该怎么做呢?可以看看这篇文章:3D 沙盒游戏之避障踩坑和实现之旅

总结

这篇文章,我们从骨骼动画的介绍及使用、模型的移动及状态改变、路径规划的适配三个方面,讲解了3D沙盒游戏中实现人物行走移动并进行状态改变的思路及步骤,希望新人阅读结束之后,能更快上手这个功能。

当然,本篇文章介绍的实现方式还仍有不足之处,比如移动可以加上加速度,让动作与移动速度匹配得更自然等。

如果还有什么合适的建议,也欢迎大家积极留言交流。

参考资料骨骼动画 - 维基百科,自由的百科全书Grouping Animations | Babylon.js DocumentationAdvanced Animation Methods | Babylon.js DocumentationVector3 | Babylon.js DocumentationCrowd Navigation System | Babylon.js DocumentationWeb Workers APIMake crowd agent move at constant speed - Questions - Babylon.js

欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章:

本文链接地址:https://www.jiuchutong.com/zhishi/303304.html 转载请保留说明!

上一篇:在iPhone14和iPad上进行音频和视频编辑的最佳应用app-OK教程(ios14.5ipad)

下一篇:libev & libevent简介 IntelIOT 博客园

  • 为什么苹果手机不能下载软件(为什么苹果手机连不上wifi)

    为什么苹果手机不能下载软件(为什么苹果手机连不上wifi)

  • 苹果平板连不上校园网(苹果平板连不上蓝牙耳机怎么回事)

    苹果平板连不上校园网(苹果平板连不上蓝牙耳机怎么回事)

  • 域名结构有几层(域名的结构有哪些类型)

    域名结构有几层(域名的结构有哪些类型)

  • 俩个开关控制一个灯怎么接线(俩开关控制一个灯怎么接线)

    俩个开关控制一个灯怎么接线(俩开关控制一个灯怎么接线)

  • 微信两个安卓手机怎么定位(两个微信 安卓)

    微信两个安卓手机怎么定位(两个微信 安卓)

  • 小米手环3显示请先绑定怎么办(小米手环3显示已充满电就是开不了屏)

    小米手环3显示请先绑定怎么办(小米手环3显示已充满电就是开不了屏)

  • 青绿色共享单车叫什么(青色的共享单车是什么牌子)

    青绿色共享单车叫什么(青色的共享单车是什么牌子)

  • 微信最近联系人怎么查(微信最近联系人怎么查找)

    微信最近联系人怎么查(微信最近联系人怎么查找)

  • 冯诺依曼计算机的基本构件(冯诺依曼计算机的特点)

    冯诺依曼计算机的基本构件(冯诺依曼计算机的特点)

  • 微信违规行为有哪些(微信出现违规行为有影响吗)

    微信违规行为有哪些(微信出现违规行为有影响吗)

  • ps怎么加粗线条(怎样在ps中加粗线条)

    ps怎么加粗线条(怎样在ps中加粗线条)

  • 手机移动数据打不开怎么办(手机移动数据打不开电话也打不出去)

    手机移动数据打不开怎么办(手机移动数据打不开电话也打不出去)

  • ios系统怎么清理缓存(怎样清理苹果系统)

    ios系统怎么清理缓存(怎样清理苹果系统)

  • 抖音抖币怎么兑换人民币(抖音抖币怎么兑换提现)

    抖音抖币怎么兑换人民币(抖音抖币怎么兑换提现)

  • ps4内存不足怎么办(ps4内存不足怎么清理)

    ps4内存不足怎么办(ps4内存不足怎么清理)

  • 华为p30和p30pro拍照对比(华为p30拍照对比p30 pro)

    华为p30和p30pro拍照对比(华为p30拍照对比p30 pro)

  • 怎么弄到花呗12期免息(怎么弄到花呗24期免息券)

    怎么弄到花呗12期免息(怎么弄到花呗24期免息券)

  • 微信语音怎么开启扩音(微信语音怎么开变声器?)

    微信语音怎么开启扩音(微信语音怎么开变声器?)

  • xs max多长厘米(苹果xs max几厘米长)

    xs max多长厘米(苹果xs max几厘米长)

  • qq留言怎么隐藏(qq留言怎么隐藏手机型号)

    qq留言怎么隐藏(qq留言怎么隐藏手机型号)

  • word文档保存快捷键(word文档保存快捷键用不了)

    word文档保存快捷键(word文档保存快捷键用不了)

  • 【运筹优化】带时间窗约束的车辆路径规划问题(VRPTW)详解 + Python 调用 Gurobi 建模求解(运筹最优化方法有哪些)

    【运筹优化】带时间窗约束的车辆路径规划问题(VRPTW)详解 + Python 调用 Gurobi 建模求解(运筹最优化方法有哪些)

  • starter包~mica-auto自动生成spring.factories等元文件(micro start)

    starter包~mica-auto自动生成spring.factories等元文件(micro start)

  • 刚打完发票可以注销税控盘吗
  • 转租价格由谁决定
  • 记账人和复核人是一个人吗
  • 购买金蝶软件多少钱
  • 个人转让住房要交哪些税费?
  • 临时设施费怎么结算
  • 季度预缴所得税可以不交吗
  • 进口货物关税计入成本吗
  • 抵债资产处置账务实例
  • 发微信验证消息
  • 应交税费待抵扣进项税额
  • 现金支票作废需要签字吗
  • 医院计提坏账准备分录
  • 发票抵税是指哪些内容
  • 发票开什么明目列福利费
  • 2018年个体工商户增值税起征点
  • 非居民纳税人怎么纳税
  • 增值税普通发票几个点
  • 土地在会计中怎么处理
  • 销售方开具的红字信息表
  • swimsuitnetwork.exe - swimsuitnetwork是什么进程 有何作用
  • 矿山机械设备租赁有限公司
  • 发票收款人和复核人空白
  • win10分辨率调整
  • w11系统黑屏
  • bellzee.exe是什么
  • 销项负数发票怎么处理
  • php设计模式及使用场景
  • 福利企业增值税优惠政策
  • 销售自己使用过的固定资产
  • 初学者安装visual studio
  • 其他债权投资如何进行投资收益的确认的核算?
  • javascript高阶
  • 商品流通企业库存商品的核算方法主要有
  • 销货退回与折让的关系
  • 企业收到工程款会计分录
  • python复制文件的代码
  • sqlserver2008误操作数据
  • 资本性支出与资本性支出(基本建设)的区别
  • 净营业周期为负数
  • 消费税是指怎样的收入
  • 已认证发票是否已抵扣
  • 预收账款退款的会计处理
  • 短期借款财务处理
  • 冲估价入库怎么冲成本
  • 建筑业清包工合同范本
  • 表彰奖励奖金规定
  • 现金流量表现金及现金等价物净增加额
  • 高价卖股份犯法么
  • 员工预支工资现金流量
  • 提供加工劳务计入什么科目
  • 企业注销其他应付款怎么处理账务
  • 建账的过程包括哪些内容
  • 在幻灯片母版中
  • linux用
  • win8分盘怎么分
  • win8 开始
  • 主板设置uefi模式
  • winxp显示设置
  • directx?
  • mac使用vim
  • win10在哪里更改用户名
  • centos8 redhat
  • linux内核架构
  • win7系统电脑开不了机
  • win8远程桌面连接设置
  • linux中复制目录到另一个目录的命令
  • recyclerview addview
  • node的express
  • socket怎么用
  • Python爬取微信视频号
  • [置顶]bilinovel
  • python tcp编程
  • jquery做下拉
  • android数据存储与访问方式
  • adb命令ls
  • 公司自有房屋装修费用入什么料目
  • 2018年小微企业所得税优惠政策
  • 乌市沙区医保局电话是多少
  • 国家税务总局2011年34号公告
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设