SAIMA/Assets//脚本/Horse.cs
Roman 718deb06d1 任务:实现基本玩法
1.策划数学模型
2.搬迁工具类如单例模式模板
3.创建玩家类,用于接受和发出指令、记录玩家信息等
4.创建马类,用来接受信息、物理计算、控制马的碰撞等模拟等
5.实装数学模型,实现马脚的控制
6.探索物理实现方式,给控制点加上碰撞体和刚体,似乎可行
--NG,分离的刚体无法作为整体传递力,脚的反作用力传不到身体上
7.把操控分组,左摇杆控制后两条腿、右摇杆控制前两条腿
8.探索物理的实现方式,尝试仅在主物体上挂载仅一个刚体,探索碰撞体组的使用方式/
((1.给马添加四个碰撞体代表马足
((2.每帧刷新碰撞体的偏移,使其对准真实马蹄位置,所以需要找准offset和世界空间下位置的对应关系
((3.如何让马前进?目前发现无法使用真实物理的反作用力,只能根据输入或其他信息算出这个反作用力
(((1.最终应该通过原速度 + 计算出输入速度,不能使用力,因为使用力涉及的参数极多,很难修改
(((2.应该通过马足真实位置计算输入速度,不能使用控制点位置
(((3.每只足应该记住自己上一帧的位置,并每帧更新
((4.本帧位置减上帧位置 = 运动速度,一个二维向量
((5.并非一直有效,当本帧马足位置低于圆心 + 调整值时,才有效,否则该足贡献的速度为0.同时,需要有输入才有效,若无输入,同样贡献速度为0
((6.NG,主要在判断速度是否有效上出了问题,试试每帧给足和地面测距,小于阈值的才给过,然后才加速度
((7.GoodJob!通过上述方法和其他细微调整,已经使得马可以行走,并且得到不错的效果。但是肯定还有问题,需要后期更进一步调整

目前马能够受到控制,并且可以走动。同样支持键盘游玩,但是体验极差
2022-07-24 21:15:22 +08:00

397 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = 根位置 - V0L - 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;
}