397 lines
13 KiB
C#
397 lines
13 KiB
C#
![]() |
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using Sirenix.OdinInspector;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马类,用于接受来自玩家的指令、进行物理模拟
|
|||
|
/// </summary>
|
|||
|
public class Horse : MonoBehaviour
|
|||
|
{
|
|||
|
// _____ _ _ _
|
|||
|
// | __ \ | | | (_)
|
|||
|
// | |__) | _| |__ | |_ ___
|
|||
|
// | ___/ | | | '_ \| | |/ __|
|
|||
|
// | | | |_| | |_) | | | (__
|
|||
|
// |_| \__,_|_.__/|_|_|\___|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马的前腿根部
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("必须绑定的物体")][Header("马的前腿根部")]
|
|||
|
public Transform frontLegRoot;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马的后腿根部
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("必须绑定的物体")][Header("马的后腿根部")]
|
|||
|
public Transform backLegRoot;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马伸直腿后前脚底部
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("必须绑定的物体")][Header("马前脚伸直后的最深位置")]
|
|||
|
public Transform frontFoot;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马伸直腿后后脚底部
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("必须绑定的物体")][Header("马后脚伸直后的最深位置")]
|
|||
|
public Transform backFoot;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马脚Transform
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("必须绑定的物体")][Header("马脚Transform,必须按照左前、右前、左后、右后的顺序绑定")]
|
|||
|
public Transform[] footsTransform;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 腿做圆周运动的半径
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("马的基本运动信息")][Header("马脚圆周运动半径")]
|
|||
|
public float footMoveRadius;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 四条腿分别对输入偏差多少
|
|||
|
/// </summary>
|
|||
|
[ListDrawerSettings] [BoxGroup("马的基本运动信息")][Header("四条腿分别对输入偏差多少")]
|
|||
|
public Vector2[] footOffestOfInput;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马足力量
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("马的基本运动信息")][Header("马足力量")]
|
|||
|
public float footForce;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄推动马身的力度调整值
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("马的基本运动信息")][Header("马前进的力度")]
|
|||
|
public float strength;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄供力有效范围
|
|||
|
/// </summary>
|
|||
|
[BoxGroup("马的基本运动信息")][Header("马蹄供力有效范围")]
|
|||
|
public float effectiveDistance;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// _____ _ _
|
|||
|
// | __ \ (_) | |
|
|||
|
// | |__) | __ ___ ____ _| |_ ___
|
|||
|
// | ___/ '__| \ \ / / _` | __/ _ \
|
|||
|
// | | | | | |\ V / (_| | || __/
|
|||
|
// |_| |_| |_| \_/ \__,_|\__\___|
|
|||
|
|
|||
|
struct foot
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// 腿对输入偏差多少
|
|||
|
/// </summary>
|
|||
|
public Vector2 footOffestOfInput;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马脚控制点,通过transform找到
|
|||
|
/// </summary>
|
|||
|
public Transform footControlPoint;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 真实的马脚的Transform
|
|||
|
/// </summary>
|
|||
|
public Transform footRealTransform;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马脚此时目标位置,每帧计算
|
|||
|
/// </summary>
|
|||
|
public Vector3 footTargetPosition;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄刚体组件
|
|||
|
/// </summary>
|
|||
|
public Rigidbody2D footRigidbody2D;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄的碰撞体,但是不绑在马蹄上,而是为了物理计算绑在马身上
|
|||
|
/// </summary>
|
|||
|
public BoxCollider2D footBoxCollider2D;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄前一帧的位置
|
|||
|
/// </summary>
|
|||
|
public Vector3 footPreviousPosition;
|
|||
|
}
|
|||
|
|
|||
|
private foot[] foots;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马前腿长度
|
|||
|
/// </summary>
|
|||
|
private float frontLegLength;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马后腿长度
|
|||
|
/// </summary>
|
|||
|
private float backLegLength;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 从前腿根骨骼到马脚运动圆心的距离,用来推算最终马脚位置
|
|||
|
/// </summary>
|
|||
|
private float frontRootDistanceToCenterOfCircle;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 从后腿根骨骼到马脚运动圆心的距离,用来推算最终马脚位置
|
|||
|
/// </summary>
|
|||
|
private float backRootDistanceToCenterOfCircle;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 输入的马前脚信息
|
|||
|
/// </summary>
|
|||
|
private Vector2 inputFrontVector;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 输入的马后脚信息
|
|||
|
/// </summary>
|
|||
|
private Vector2 inputBackVector;
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 原重力尺度,在Start中通过随意一条腿的刚体获取
|
|||
|
/// </summary>
|
|||
|
private float oriGravityScale;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马蹄碰撞体数组
|
|||
|
/// </summary>
|
|||
|
private BoxCollider2D[] footBoxColliders;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 马的刚体组件
|
|||
|
/// </summary>
|
|||
|
private Rigidbody2D horseRig;
|
|||
|
|
|||
|
|
|||
|
void Start()
|
|||
|
{
|
|||
|
//初始化
|
|||
|
Init();
|
|||
|
//找到必要的物体和组件
|
|||
|
FindSth();
|
|||
|
//计算必要的量
|
|||
|
CaculateSth();
|
|||
|
}
|
|||
|
|
|||
|
void Update()
|
|||
|
{
|
|||
|
//计算马脚目标位置
|
|||
|
CaculateTargetPosition();
|
|||
|
//让马脚朝着目标移动
|
|||
|
MoveFoot();
|
|||
|
//让马整体运动
|
|||
|
MoveHorse();
|
|||
|
//更新一些数据
|
|||
|
UpdateData();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 移动马身体
|
|||
|
/// </summary>
|
|||
|
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<Rigidbody2D>();
|
|||
|
|
|||
|
//找到马蹄的真实Transform
|
|||
|
for(int i = 0; i < 4; i++) foots[i].footRealTransform = footsTransform[i];
|
|||
|
|
|||
|
//找到马蹄的碰撞体
|
|||
|
footBoxColliders = GetComponents<BoxCollider2D>();
|
|||
|
|
|||
|
//给foots的碰撞体赋值
|
|||
|
for (int i = 0; i < 4; i++)
|
|||
|
{
|
|||
|
foots[i].footBoxCollider2D = footBoxColliders[i];
|
|||
|
footBoxColliders[i].offset = foots[i].footControlPoint.localPosition ;
|
|||
|
}
|
|||
|
|
|||
|
//找到马的刚体
|
|||
|
horseRig = GetComponent<Rigidbody2D>();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 计算一些不会变的必要的量
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 根据记录的输入向量计算马脚本帧的目标位置,保存到footTargetPoints数组中
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 把马脚朝着算好的目标点移动
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
|
|||
|
|
|||
|
}
|