using System.Collections; using System.Collections.Generic; using UnityEngine; using Sirenix.OdinInspector; /// /// 马类,用于接受来自玩家的指令、进行物理模拟 /// public class Horse : MonoBehaviour { // _____ _ _ _ // | __ \ | | | (_) // | |__) | _| |__ | |_ ___ // | ___/ | | | '_ \| | |/ __| // | | | |_| | |_) | | | (__ // |_| \__,_|_.__/|_|_|\___| /// /// 马的前腿根部 /// [BoxGroup("必须绑定的物体")][Header("马的前腿根部")] public Transform frontLegRoot; /// /// 马的后腿根部 /// [BoxGroup("必须绑定的物体")][Header("马的后腿根部")] public Transform backLegRoot; /// /// 马伸直腿后前脚底部 /// [BoxGroup("必须绑定的物体")][Header("马前脚伸直后的最深位置")] public Transform frontFoot; /// /// 马伸直腿后后脚底部 /// [BoxGroup("必须绑定的物体")][Header("马后脚伸直后的最深位置")] public Transform backFoot; /// /// 马脚Transform /// [BoxGroup("必须绑定的物体")][Header("马脚Transform,必须按照左前、右前、左后、右后的顺序绑定")] public Transform[] footsTransform; /// /// 腿做圆周运动的半径 /// [BoxGroup("马的基本运动信息")][Header("马脚圆周运动半径")] public float footMoveRadius; /// /// 四条腿分别对输入偏差多少 /// [ListDrawerSettings] [BoxGroup("马的基本运动信息")][Header("四条腿分别对输入偏差多少")] public Vector2[] footOffestOfInput; /// /// 马足力量 /// [BoxGroup("马的基本运动信息")][Header("马足力量")] public float footForce; /// /// 马蹄推动马身的力度调整值 /// [BoxGroup("马的基本运动信息")][Header("马前进的力度")] public float strength; /// /// 马蹄供力有效范围 /// [BoxGroup("马的基本运动信息")][Header("马蹄供力有效范围")] public float effectiveDistance; // _____ _ _ // | __ \ (_) | | // | |__) | __ ___ ____ _| |_ ___ // | ___/ '__| \ \ / / _` | __/ _ \ // | | | | | |\ V / (_| | || __/ // |_| |_| |_| \_/ \__,_|\__\___| struct foot { /// /// 腿对输入偏差多少 /// public Vector2 footOffestOfInput; /// /// 马脚控制点,通过transform找到 /// public Transform footControlPoint; /// /// 真实的马脚的Transform /// public Transform footRealTransform; /// /// 马脚此时目标位置,每帧计算 /// public Vector3 footTargetPosition; /// /// 马蹄刚体组件 /// public Rigidbody2D footRigidbody2D; /// /// 马蹄的碰撞体,但是不绑在马蹄上,而是为了物理计算绑在马身上 /// public BoxCollider2D footBoxCollider2D; /// /// 马蹄前一帧的位置 /// public Vector3 footPreviousPosition; } private foot[] foots; /// /// 马前腿长度 /// private float frontLegLength; /// /// 马后腿长度 /// private float backLegLength; /// /// 从前腿根骨骼到马脚运动圆心的距离,用来推算最终马脚位置 /// private float frontRootDistanceToCenterOfCircle; /// /// 从后腿根骨骼到马脚运动圆心的距离,用来推算最终马脚位置 /// private float backRootDistanceToCenterOfCircle; /// /// 输入的马前脚信息 /// private Vector2 inputFrontVector; /// /// 输入的马后脚信息 /// private Vector2 inputBackVector; /// /// 原重力尺度,在Start中通过随意一条腿的刚体获取 /// private float oriGravityScale; /// /// 马蹄碰撞体数组 /// private BoxCollider2D[] footBoxColliders; /// /// 马的刚体组件 /// private Rigidbody2D horseRig; void Start() { //初始化 Init(); //找到必要的物体和组件 FindSth(); //计算必要的量 CaculateSth(); } void Update() { //计算马脚目标位置 CaculateTargetPosition(); //让马脚朝着目标移动 MoveFoot(); //让马整体运动 MoveHorse(); //更新一些数据 UpdateData(); } /// /// 移动马身体 /// private void MoveHorse() { Vector2 v = new Vector2(); for(int i = 0; i < 4; i++) { //仅在有输入的情况下,会考虑给速度 //看此时该脚是否有输入 bool hasInput = i < 2 ? inputFrontVector.magnitude > 0.1f : inputBackVector.magnitude > 0.1f; if(!hasInput) continue; //从足底向正下方发射射线,获取碰撞到的物体 RaycastHit2D hit = Physics2D.Raycast( foots[i].footRealTransform.position, Vector2.down, 1000f, LayerMask.NameToLayer("foot") ); //如果击中物体的距离已经超过了有效范围,则不贡献速度 if(hit.distance > effectiveDistance) continue; //判断本帧中,马足位置是否低于圆心+调整值,仅低于时,需要计算影响 //根位置 Vector3 rootPosition = i < 2 ? frontLegRoot.position : backLegRoot.position; //L float L = i < 2 ? frontLegLength : backLegLength; //圆心y坐标 float centerY = L - footMoveRadius; centerY += effectiveDistance; //仅低于时,需要计算影响 if(foots[i].footRealTransform.position.y >= centerY) continue; //本帧位置减上帧位置 Vector3 trans = foots[i].footRealTransform.position - foots[i].footPreviousPosition; Vector2 v_ = trans; //把本足的影响加到速度中 v += v_; } //给速度乘以力量,得到最终理论速度 v *= strength; //取反,因为是模拟反作用力 v*=-1; //减弱y轴的影响,不要让马在平地上起飞 v *= new Vector2(1,0.2f); Debug.Log(v); //如果模长超过5,那必是触发了什么逆天Bug,会导致马起飞,需要避免这些错误 if(v.magnitude > 10f) v = Vector2.zero; //把速度加给马的刚体 horseRig.velocity += v; } private void UpdateData() { //更新四足的上帧位置 for(int i = 0; i < 4; i++) foots[i].footPreviousPosition = foots[i].footRealTransform.position; } void Init() { foots = new foot[4]; footBoxColliders = new BoxCollider2D[4]; for(int i = 0; i < 4; i++) foots[i].footPreviousPosition = transform.position; } void FindSth() { //把四只脚的目标点找到,0:左前 1:右前 2:左后 3:右后 foots[0].footControlPoint = transform.Find("左前脚").GetChild(0); foots[1].footControlPoint = transform.Find("右前脚").GetChild(0); foots[2].footControlPoint = transform.Find("左后脚").GetChild(0); foots[3].footControlPoint = transform.Find("右后脚").GetChild(0); //同步四只脚的输入偏差 for (int i = 0; i < 4; i++) foots[i].footOffestOfInput = footOffestOfInput[i]; //找到马蹄刚体组件 for(int i = 0; i < 4; i++) foots[i].footRigidbody2D = foots[i].footControlPoint.GetComponent(); //找到马蹄的真实Transform for(int i = 0; i < 4; i++) foots[i].footRealTransform = footsTransform[i]; //找到马蹄的碰撞体 footBoxColliders = GetComponents(); //给foots的碰撞体赋值 for (int i = 0; i < 4; i++) { foots[i].footBoxCollider2D = footBoxColliders[i]; footBoxColliders[i].offset = foots[i].footControlPoint.localPosition ; } //找到马的刚体 horseRig = GetComponent(); } /// /// 计算一些不会变的必要的量 /// void CaculateSth() { //计算前后腿马腿长度 frontLegLength = Vector3.Distance(frontLegRoot.position, frontFoot.position); backLegLength = Vector3.Distance(backLegRoot.position, backFoot.position); //计算从腿根骨骼到马脚运动圆心的距离 frontRootDistanceToCenterOfCircle = frontLegLength - footMoveRadius; backRootDistanceToCenterOfCircle = backLegLength - footMoveRadius; //取得原始重力尺度 oriGravityScale = foots[0].footRigidbody2D.gravityScale; } /// /// 根据记录的输入向量计算马脚本帧的目标位置,保存到footTargetPoints数组中 /// private void CaculateTargetPosition() { //循环4次 for(int i = 0; i < 4; i++) { //整体公式为:Target = 根位置 - V(0,L - r) + inputdir * offest * r //根位置 Vector3 rootPosition = i < 2 ? frontLegRoot.position : backLegRoot.position; //L float L = i < 2 ? frontLegLength : backLegLength; //中间向量 Vector3 middleVector = new Vector3(0,L - footMoveRadius,0); //inputdir Vector2 inputVector = i < 2 ? inputFrontVector : inputBackVector; //inputTrans Vector3 inputTrans = new Vector3 ( inputVector.x * ( footOffestOfInput[i].x + 1), inputVector.y * ( footOffestOfInput[i].y + 1), 0 ) * footMoveRadius; //最终计算 //footTargetPoints[i] = rootPosition - middleVector + inputTrans; foots[i].footTargetPosition = rootPosition - middleVector + inputTrans; } } /// /// 把马脚朝着算好的目标点移动 /// private void MoveFoot() { //循环4次 for(int i = 0; i < 4; i++) { //看此时该脚是否有输入 bool hasInput = i < 2 ? inputFrontVector.magnitude > 0.1f : inputBackVector.magnitude > 0.1f; //如果有输入,则把马脚朝着目标移动 // if(hasInput) if(true) { //若无输入,则把目标定在可以使马腿伸直的地方 Vector3 targetPosition = hasInput ? foots[i].footTargetPosition : foots[i].footTargetPosition - new Vector3(0,footMoveRadius + 0.5f,0); //给刚体以target位置-自身位置作为力的方向 Vector2 Dir = targetPosition - foots[i].footControlPoint.position; //如果已经很接近目标点了,则不再给速度,同时解除重力,防止出现抖动问题 if(Dir.magnitude < 0.1f) { foots[i].footRigidbody2D.gravityScale = 0; foots[i].footRigidbody2D.velocity = Vector2.zero; continue; } //标准化马脚移动方向 Dir.Normalize(); //最终计算应有的速度 Vector2 force = Dir * footForce * (horseRig.velocity.magnitude * 0.5f + 1); //赋予脚的刚体以速度 foots[i].footRigidbody2D.velocity = foots[i].footRigidbody2D.velocity * 0.8f + force * 0.2f; //foots[i].footRigidbody2D.velocity = force; //foots[i].footRigidbody2D.AddForce(force); } // // 强制把马脚移到目标点 //foots[i].footControlPoint.position = foots[i].footTargetPosition; // 恢复重力 foots[i].footRigidbody2D.gravityScale = oriGravityScale; // 刷新碰撞体位置 footBoxColliders[i].offset = foots[i].footRealTransform.position - transform.position; } } public void SetInputFrontVector(Vector2 input) => inputFrontVector = input; public void SetInputBackVector(Vector2 input) => inputBackVector = input; }