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

品牌网站建设毛尖2企业文化是什么

品牌网站建设毛尖2,企业文化是什么,html5 单页 响应式 网站模板,南通网站建设方案开发文章目录简介变量说明实现动画准备动画状态机State 状态NoneStand To CoverIs CoveringCover To Stand高度适配高度检测脚部IK简介 本文介绍如何在Unity中实现一个Avatar角色的智能掩体系统,效果如图所示: 初版1.0.0代码已上传至SKFramework框架Package…

文章目录

  • 简介
    • 变量说明
  • 实现
    • 动画准备
    • 动画状态机
    • State 状态
      • None
      • Stand To Cover
      • Is Covering
      • Cover To Stand
    • 高度适配
      • 高度检测
      • 脚部IK


简介

本文介绍如何在Unity中实现一个Avatar角色的智能掩体系统,效果如图所示:

效果图(一)

效果图(二)

效果图(三)

效果图(四)

初版1.0.0代码已上传至SKFramework框架Package Manager中:

SKFramework PackageManager

变量说明

Avatar Cover System

  • Cover Layer Mask:掩体物体的Layer层级
  • Shortcut Key:进入、退出掩体状态的快捷键
  • Box Cast Size:寻找掩体所用物理检测的Box大小
  • Box Cast Num:寻找掩体所用物理检测的Box数量(maxDistance = boxCastSize * boxCastNum)
  • Stand 2 Cover Speed:切换至掩体状态的移动速度
  • Cover 2 Stand Speed:退出掩体状态的移动速度
  • Stand 2 Cover Time:切换至掩体状态的时长(动画时长决定)
  • Cover 2 Stand Time:退出掩体状态的时长(动画时长决定)
  • Sneak Speed:掩体状态下移动的速度
  • Direction Lerp Speed:左右方向的插值速度
  • Head Radius:头部的半径 用于物理检测 未检测到碰撞时身体高度向下调整 并启用脚部IK
  • Head Down Cast Count Limit:头部下方物理检测的次数限制(每次下降一个半径的单位进行检测)
  • Ground Layer Mask:地面的Layer层级 用于脚部IK检测地面
  • Body Position Lerp Speed:身体高度插值的速度
  • Foot Position Lerp Speed:脚部IK插值的速度
  • Raycast Distance:脚部IK检测用的距离
  • Raycast Origin Height:脚部IK检测的高度

实现

动画准备

  • Mixamo:动作文件全部是在Mixamo网站上下载的:

Mixamo

  • Humanoid:Animation Type设为Humanoid人形动画:

Animation Type:Humanoid

  • Animation:调整相关设置:

Animation Settings

Root Transform Rotation Offset:此处设为-180,目的为了调整朝向,使其与Stand2Cover、Cover2Stand等动画连贯。

动画状态机

  • Animator Parameters:添加相关参数:
    • Stand2Cover:bool类型,用于进入、退出掩体状态;
    • Cover Direction:float类型,用于控制左右方向的混合树;
    • Cover Sneak:float类型,用于控制移动的混合树。
  • Sub-State Machine:创建一个子状态机,用于处于Cover相关状态:

Sub-State Machine

Cover子状态机中添加Stand2CoverCover2Stand动画状态及Cover Direction混合树:

Cover State Machine

Cover Direction混合树:包含Cover LeftCover Right子混合树,两个子混合树又分别包含其对应方向的IdleSneak动画。Cover Direction参数用于控制进入Cover Left还是Cover Right,Cover Sneak参数用于控制Idle和Sneak之间的混合:

Cover Direction

  • IK Pass:启用对应层级的IK Pass通道,计算脚部IK所需:

IK Pass

State 状态

定义相关状态:

  • None:未在任何状态;
  • Stand2Cover:正在切换至掩体状态(切换过程)
  • IsCovering:正处于掩体状态
  • Cover2Stand:正在退出掩体状态(切换过程)
public enum State
{None, //未在任何状态Stand2Cover, //正在切换至掩体状态IsCovering, //正处于掩体状态Cover2Stand, //正在退出掩体状态
}
//当前状态
private State state = State.None;/// <summary>
/// 当前状态
/// </summary>
public State CurrentState
{get{return state;}
}

None

未处于任何状态时,向身体前方进行BoxCast物理检测寻找掩体,当检测到掩体时,按下指定快捷键则进入Stand2Cover切换过程:

//未处于掩体状态
case State.None:{//Box检测的中心点Vector3 boxCastCenter = transform.position + transform.up;//最大检测距离float maxDistance = boxCastSize.z * boxCastNum;//向身体前方进行Box检测 寻找掩体 castResult = Physics.BoxCast(boxCastCenter, boxCastSize * .5f, transform.forward, out hit, transform.rotation, maxDistance, coverLayerMask);//调试:法线方向Debug.DrawLine(hit.point, hit.point + hit.normal, Color.magenta);//检测到掩体if (castResult){//按下快捷键 进入掩体状态if (Input.GetKeyDown(shortcutKey)){//正在切换至掩体状态state = State.Stand2Cover;//播放动画animator.SetBool(AnimParam.Stand2Cover, true);//禁用其他人物控制系统GetComponent<AvatarController>().enabled = false;//默认右方(动画Stand2Cover默认右方)targetCoverDirection = 1f;//启用脚部IKenableFootIk = true;bodyYOffset = 0.04f;}}
}
break;

Stand To Cover

切换至掩体状态的过程中,向RaycastHit中的法线反方向移动,移动到掩体前方:

case State.Stand2Cover:
{//计时stand2CoverTimer += Time.deltaTime;if (stand2CoverTimer < stand2CoverTime){//向法线反方向移动 到掩体前cc.Move(-hit.normal * Time.deltaTime * stand2CoverSpeed);//朝向 面向法线方向transform.forward = Vector3.Lerp(transform.forward, -hit.normal, Time.deltaTime * stand2CoverSpeed);}else{//重置计时器stand2CoverTimer = 0f;//切换完成 进入掩体状态state = State.IsCovering;bodyYOffset = 0.02f;}
}
break;

Stand To Cover

Is Covering

在掩体状态时,获取用户Horizontal水平方向上的输入,通过输入控制Avatar转向左侧或右侧并进行Sneak移动:

//获取水平方向输入
float horizontal = Input.GetAxis("Horizontal");
//目标方向 输入为负取-1 为正取1
if (horizontal != 0f)
{targetCoverDirection = horizontal < 0f ? -1f : 1f;castResult = Physics.BoxCast(transform.position + transform.up, boxCastSize * .5f, transform.forward, out hit, Quaternion.identity, boxCastSize.z * boxCastNum, coverLayerMask);Debug.DrawLine(hit.point, hit.point + hit.normal, Color.magenta);cc.Move(-hit.normal * sneakSpeed * Time.deltaTime);transform.forward = Vector3.Lerp(transform.forward, -hit.normal, Time.deltaTime * stand2CoverSpeed);
}
//方向插值运算
coverDirection = Mathf.Lerp(coverDirection, targetCoverDirection, Time.deltaTime * directionLerpSpeed);
//动画 方向
animator.SetFloat(AnimParam.CoverDirection, coverDirection);
//动画 掩体状态行走
animator.SetFloat(AnimParam.CoverSneak, Mathf.Abs(horizontal));
//通过输入控制移动
cc.Move(horizontal * sneakSpeed * Time.deltaTime * transform.right);

Cover Left/Right

按下快捷键时,退出掩体状态:

animator.SetBool(AnimParam.Stand2Cover, false);
state = State.Cover2Stand;

Cover To Stand

退出掩体状态的过程中,向身体后方移动:

//计时
cover2StandTimer += Time.deltaTime;
cover2StandTimer = Mathf.Clamp(cover2StandTimer, 0f, cover2StandTime);
if (cover2StandTimer < cover2StandTime)
{//后移cc.Move(cover2StandSpeed * Time.deltaTime * -transform.forward);
}
else
{//重置计时器cover2StandTimer = 0f;state = State.None;//启用其他人物控制脚本GetComponent<AvatarController>().enabled = true;
}

Cover To Stand

高度适配

如图所示,当掩体的高度降低时,角色会逐渐下蹲调整高度,实现该功能一方面需要在头部进行物理检测,另一方面需要启用脚部的IK。

高度检测

高度检测

高度检测贯穿于Stand2CoverIsCovering状态中,注意观察下图中红色球的变动,当SphereCast球形检测在初始高度未检测到掩体时,会下降一个球半径的单位再次进行检测,如果在限制次数中都未检测到掩体,则退出掩体状态,如果检测到掩体,则获取碰撞点和初始高度的delta差值,该差值就是身体要下降的高度:

高度检测

//头部物理检测的初始点
headSphereCastOrigin = transform.position + Vector3.up * headOriginPosY + transform.right * targetCoverDirection * headRadius * 2f;
//向前方进行球形检测(掩体状态下前方就是后脑勺的方向)
headCastResult = Physics.SphereCast(headSphereCastOrigin, headRadius, transform.forward, out RaycastHit headHit, coverLayerMask);
int i = 0;
if (!headCastResult)
{for (i = 0; i < headDownCastCountLimit; i++){//每次下降一个半径的单位进行检测headSphereCastOrigin -= Vector3.up * headRadius;headCastResult = Physics.SphereCast(headSphereCastOrigin, headRadius, transform.forward, out headHit, coverLayerMask);if (headCastResult) break;}
}
if (headCastResult)
{Debug.DrawLine(headSphereCastOrigin, headHit.point, Color.green);float delta = headOriginPosY - headHit.point.y;targetBodyPositionY = originBodyPositionY - delta - headRadius;Debug.DrawLine(headSphereCastOrigin, headSphereCastOrigin - Vector3.up * (delta + i * headRadius), Color.red);
}

检测的位置受Cover Direction方向影响,当处于Cover Left时,会在头部左侧一定单位进行检测,相反,处于Cover Right时,会在头部右侧一定单位进行检测:

高度检测的左右方向

获取到身体要下降的高度后,在OnAnimatorIK函数中调整Animator组件的bodyPosition属性:

Vector3 bodyPosition = animator.bodyPosition;
bodyPosition.y = Mathf.Lerp(lastBodyPositionY, targetBodyPositionY, bodyPositionLerpSpeed);
animator.bodyPosition = bodyPosition;
lastBodyPositionY = animator.bodyPosition.y;

脚部IK

单纯的下调身体高度会导致脚穿模到地面以下,因此需要启用脚部IK,不断调整脚的位置,脚部IK在前面的文章中有介绍,这里不再详细说明,代码如下:

private void FixedUpdate()
{//未启用FootIK or 动画组件为空if (!enableFootIk || animator == null) return;#region 计算左脚IK//左脚坐标leftFootPosition = animator.GetBoneTransform(HumanBodyBones.LeftFoot).position;leftFootPosition.y = transform.position.y + raycastOriginHeight;//左脚 射线检测leftFootRaycast = Physics.Raycast(leftFootPosition, Vector3.down, out RaycastHit hit, raycastDistance + raycastOriginHeight, groundLayerMask);if (leftFootRaycast){leftFootIkPosition = leftFootPosition;leftFootIkPosition.y = hit.point.y + bodyYOffset;leftFootIkRotation = Quaternion.FromToRotation(transform.up, hit.normal);
#if UNITY_EDITOR//射线Debug.DrawLine(leftFootPosition, leftFootPosition + Vector3.down * (raycastDistance + raycastOriginHeight), Color.yellow);//法线Debug.DrawLine(hit.point, hit.point + hit.normal * .5f, Color.cyan);
#endif}else{leftFootIkPosition = Vector3.zero;}#endregion#region 计算右脚IK//右脚坐标rightFootPosition = animator.GetBoneTransform(HumanBodyBones.RightFoot).position;rightFootPosition.y = transform.position.y + raycastOriginHeight;//右脚 射线检测rightFootRaycast = Physics.Raycast(rightFootPosition, Vector3.down, out hit, raycastDistance + raycastOriginHeight, groundLayerMask);if (rightFootRaycast){rightFootIkPosition = rightFootPosition;rightFootIkPosition.y = hit.point.y + bodyYOffset;rightFootIkRotation = Quaternion.FromToRotation(transform.up, hit.normal);#if UNITY_EDITOR//射线Debug.DrawLine(rightFootPosition, rightFootPosition + Vector3.down * (raycastDistance + raycastOriginHeight), Color.yellow);//法线Debug.DrawLine(hit.point, hit.point + hit.normal * .5f, Color.cyan);
#endif}else{rightFootIkPosition = Vector3.zero;}#endregion
}
#region 应用左脚IK
//权重
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1f);
animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1f);Vector3 targetIkPosition = animator.GetIKPosition(AvatarIKGoal.LeftFoot);
if (leftFootRaycast)
{//转局部坐标targetIkPosition = transform.InverseTransformPoint(targetIkPosition);Vector3 world2Local = transform.InverseTransformPoint(leftFootIkPosition);//插值计算float y = Mathf.Lerp(lastLeftFootPositionY, world2Local.y, footPositionLerpSpeed);targetIkPosition.y += y;lastLeftFootPositionY = y;//转全局坐标targetIkPosition = transform.TransformPoint(targetIkPosition);//当前旋转Quaternion currRotation = animator.GetIKRotation(AvatarIKGoal.LeftFoot);//目标旋转Quaternion nextRotation = leftFootIkRotation * currRotation;animator.SetIKRotation(AvatarIKGoal.LeftFoot, nextRotation);
}
animator.SetIKPosition(AvatarIKGoal.LeftFoot, targetIkPosition);
#endregion#region 应用右脚IK
//权重
animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, 1f);
animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1f);
targetIkPosition = animator.GetIKPosition(AvatarIKGoal.RightFoot);
if (rightFootRaycast)
{//转局部坐标targetIkPosition = transform.InverseTransformPoint(targetIkPosition);Vector3 world2Local = transform.InverseTransformPoint(rightFootIkPosition);//插值计算float y = Mathf.Lerp(lastRightFootPositionY, world2Local.y, footPositionLerpSpeed);targetIkPosition.y += y;lastRightFootPositionY = y;//转全局坐标targetIkPosition = transform.TransformPoint(targetIkPosition);//当前旋转Quaternion currRotation = animator.GetIKRotation(AvatarIKGoal.RightFoot);//目标旋转Quaternion nextRotation = rightFootIkRotation * currRotation;animator.SetIKRotation(AvatarIKGoal.RightFoot, nextRotation);
}
animator.SetIKPosition(AvatarIKGoal.RightFoot, targetIkPosition);
#endregion
http://www.shuangfujiaoyu.com/news/36615.html

相关文章:

  • 郑州品牌网站建设高权重外链
  • 济南做网站优化app网络推广公司
  • wordpress 4.8.1漏洞昆明优化网站公司
  • 个人微网站怎么做b站推广2024mmm已更新
  • 网站开发vs2015是什么百度最新版app下载安装
  • 郑州企业网站开发今天的新闻摘抄
  • 网站建设广告百度导航下载2022最新版官网
  • 做校园后勤管理网站得重点难点网络搜索引擎优化
  • 个人站长网站需要注册公司吗广州做网站的公司哪家好
  • 自己做本地视频网站今日热榜
  • 餐饮美食网站源码百度手机版网址
  • 网上申报税务流程沈阳seo网站关键词优化
  • 丹徒网站建设信息alexa排名查询统计
  • 郑州做网站哪家便宜云建站
  • 定制网站制作哪家好免费网站seo排名优化
  • wordpress 上传文件名网站seo优化检测
  • wordpress怎么开启seo外包服务公司
  • 安徽网站建设百度竞价系统
  • wordpress 阿里云 邮件注册内蒙古seo
  • 那个网做网站便宜自媒体营销模式有哪些
  • 做网站视频赚钱吗宁波seo外包服务商
  • 域名网站如何做成人用品网店进货渠道
  • 政府环保部门网站建设的意义百度网盘客户端下载
  • 汽车网站建设工作室推广引流图片
  • 台州建设工程信息网站vi设计
  • 网站设计要求有哪些seo排名优化服务
  • 浙江做网站公司seo怎么快速提高排名
  • 三合一网站制作公司百度用户服务中心电话
  • 网站备案查询工信部制作网站
  • 怎么做网页个人信息网站优化推广排名