SAIMA/Assets//脚本/Horse.cs
Roman e1fa077241 任务:编写玩法框架
1.视野移动和死亡重开系统
(1.相机以一个可指定的、热更新的速度始终往右移动
(2.相机带有一个子物体,子物体带有触发器,触发器位于镜头最左端
(3.给马头添加碰撞体,用于判断镜头卡死死亡
(*.创建类:GameController,作为各种中介者使用
(4.当相机子物体的触发器检测到马头,说明马被卡死,给中介者发送信息触发死亡的一系列事件
((1.马死后,修改马状态至死亡
((2.相机不再移动
((3.关闭马的碰撞体,给一个右上的速度和旋转,马模型掉出地图
(5.设置按键以调试重开,目前是按R

DONE
2022-07-28 15:58:55 +08:00

607 lines
20 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;
public enum FootType { Front, Back }
/// <summary>
/// 马状态Stand代表无输入Move为有输入着地行走Jump为跳跃当两对足均并拢时进入
/// </summary>
public enum HorseState { Stand, Move, Jump, Dead}
/// <summary>
/// 单次跳跃过程中的状态量,Ready表示还没开始累计最终力Charge表示已经开始蓄力jump表示已经触发跳跃
/// </summary>
public enum JumpState { Ready, Charging, Jump }
/// <summary>
/// 跳跃判定阈值,只有输入的变化大于该值才会触发跳跃的蓄力
/// </summary>
[BoxGroup("马的基本运动信息")][Header("马跳跃蓄力阈值")]
public float jumpChargeThreshold;
/// <summary>
/// 马跳跃力度
/// </summary>
[BoxGroup("马的基本运动信息")][Header("马跳跃力度")]
public float jumpForce;
[BoxGroup("马的其他信息")][Header("马被相机挤死后飞出有多大的力")]
public float deadFlySpeed;
// _____ _ _
// | __ \ (_) | |
// | |__) | __ ___ ____ _| |_ ___
// | ___/ '__| \ \ / / _` | __/ _ \
// | | | | | |\ 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>
/// 前一帧的前马脚输入向量
/// </summary>
private Vector2 previousInputFrontVector;
/// <summary>
/// 前一帧的后马脚输入向量
/// </summary>
private Vector2 previousInputBackVector;
/// <summary>
/// 原重力尺度在Start中通过随意一条腿的刚体获取
/// </summary>
private float oriGravityScale;
/// <summary>
/// 马蹄碰撞体数组
/// </summary>
private BoxCollider2D[] footBoxColliders;
/// <summary>
/// 马的刚体组件
/// </summary>
private Rigidbody2D horseRig;
/// <summary>
/// 四条腿分别对输入偏差多少,但是是原始值,不会改变,用于还原并拢的脚
/// </summary>
private Vector2[] oriFootOffestOfInput;
/// <summary>
/// 马此时的状态
/// </summary>
private HorseState horseState;
/// <summary>
/// 此时前腿并拢着吗?
/// </summary>
private bool isFrontFootMerged = false;
/// <summary>
/// 此时后腿并拢着吗?
/// </summary>
private bool isBackFootMerged = false;
/// <summary>
/// 此时的跳跃状态
/// </summary>
private JumpState jumpState = JumpState.Ready;
/// <summary>
/// 跳跃蓄力的速度
/// </summary>
private Vector2 jumpChargeVelocity;
/// <summary>
/// 死亡后的旋转中心
/// </summary>
private Transform rotateCenter;
void Start()
{
//初始化
Init();
//找到必要的物体和组件
FindSth();
//计算必要的量
CaculateSth();
}
void Update()
{
//更新一些数据
UpdateDataEarly();
if(horseState != HorseState.Dead)
{
//计算马脚目标位置
CaculateTargetPosition();
//让马脚朝着目标移动
MoveFoot();
//让马整体运动
MoveHorse();
//跳跃检测和物理实现
if(horseState == HorseState.Jump) CheckJump();
}
//更新一些数据
UpdateDataLate();
}
public void Death(GameController.Death deathInfo)
{
//修改状态至死亡
horseState = HorseState.Dead;
//关闭马蹄的碰撞体,并给一个右上角的速度和旋转,让马的模型掉出地图
foreach(foot foot in foots){
//foot.footBoxCollider2D.enabled = false;
//马蹄刚体也需要给速度,否则会黏在地上飞不起来
foot.footRigidbody2D.velocity = Vector2.one * deadFlySpeed * 1.5f;
}
//马身体的碰撞体也要关
List<BoxCollider2D> temp = new List<BoxCollider2D>();
GetComponentsInChildren<BoxCollider2D>(temp);
foreach(BoxCollider2D box in temp)
box.enabled = false;
horseRig.velocity = Vector2.one * deadFlySpeed;
horseRig.AddTorque(deadFlySpeed * 2f);
}
private void Jump()
{
jumpState = JumpState.Jump;
Vector2 jumpVelocity = jumpChargeVelocity * jumpForce * -1 * new Vector2(1.5f,1.5f);
if(jumpVelocity.magnitude > 30f) jumpVelocity *= 0.1f;
Debug.Log("Jump Velocity: " + jumpVelocity);
horseRig.velocity += jumpVelocity;
jumpChargeVelocity = Vector2.zero;
}
/// <summary>
/// 在跳跃状态时每帧执行,用于检测跳跃和实现跳跃的物理
/// </summary>
private void CheckJump()
{
//通过本帧输入向量减上一帧输入向量得到一个瞬时速度
Vector2 frontInstantaneousSpeed = inputFrontVector - previousInputFrontVector;
Vector2 backInstantaneousSpeed = inputBackVector - previousInputBackVector;
//如果任意一组的y大于0说明在上升不考虑它的跳跃
if(frontInstantaneousSpeed.y > 0 || backInstantaneousSpeed.y > 0) return;
else
{
//此时双腿在下降,开始检测跳跃蓄力
//首先看前后腿输入变化的瞬时速度的模长和是否大于蓄力标准
if(frontInstantaneousSpeed.magnitude + backInstantaneousSpeed.magnitude > jumpChargeThreshold)
{
//若输入的瞬时速度大于蓄力标准时,看马的跳跃状态,若处于准备态,则转移至蓄力态
if(jumpState == JumpState.Ready) jumpState = JumpState.Charging;
//若已处于蓄力态,累计力大小
if(jumpState == JumpState.Charging)
{
jumpChargeVelocity += (frontInstantaneousSpeed + backInstantaneousSpeed);
}
}
else
{
//若输入的瞬时速度小于蓄力标准时,看马的跳跃状态,若处于蓄力态,则触发跳跃
if(jumpState == JumpState.Charging) Jump();
}
}
}
/// <summary>
/// 在Update之前更新一些数据
/// </summary>
private void UpdateDataEarly()
{
//判断是否两腿均并拢若是进入跳跃状态否则设置为Stand状态
if(horseState != HorseState.Dead)
{
if (isFrontFootMerged && isBackFootMerged) horseState = HorseState.Jump;
else horseState = HorseState.Stand;
}
//Debug.Log("jumpState = " + jumpState);
}
/// <summary>
/// 使马并拢输入的双腿,
/// </summary>
public void MergeFoot(FootType type)
{
//根据输入的组类型,判断需要更改偏移值的是那一个索引下的脚
int index = type == FootType.Front ? 1 : 3;
footOffestOfInput[index] = Vector2.zero;
if(type == FootType.Front) isFrontFootMerged = true;
else isBackFootMerged = true;
}
/// <summary>
/// 恢复输入的并拢的双腿至有偏移状态
/// </summary>
/// <param name="type">需要恢复的是哪一组腿</param>
public void RecoverFoot(FootType type)
{
//根据输入的组类型,判断需要更改偏移值的是那一个索引下的脚
int index = type == FootType.Front ? 1 : 3;
footOffestOfInput[index] = oriFootOffestOfInput[index];
if(type == FootType.Front) isFrontFootMerged = false;
else isBackFootMerged = false;
}
/// <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 UpdateDataLate()
{
//更新四足的上帧位置
for(int i = 0; i < 4; i++)
foots[i].footPreviousPosition = foots[i].footRealTransform.position;
//更新上帧输入
previousInputFrontVector = inputFrontVector;
previousInputBackVector = inputBackVector;
}
void Init()
{
foots = new foot[4];
oriFootOffestOfInput = new Vector2[4];
footBoxColliders = new BoxCollider2D[4];
for(int i = 0; i < 4; i++) foots[i].footPreviousPosition = transform.position;
for(int i = 0; i < 4; i++) oriFootOffestOfInput[i] = footOffestOfInput[i];
horseState = HorseState.Stand;
}
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>();
//
rotateCenter = transform.Find("马死后的旋转中心");
}
/// <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;
//撞击地面后,修改跳跃状态
void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Ground")
{
jumpState = JumpState.Ready;
}
}
}