GrassTE 626f792f11 Squashed commit of the following:
commit 9559cc8882161192a43be22670a8ff69db1ea13a
Author: Roman <1325980292@qq.com>
Date:   Sat Aug 27 19:22:38 2022 +0800

    短学期Log

    任务:替换和实装美术素材
    1.在教程场景布置图标和文字提示内容
    DONE
    2.制作GamePlay中背景元素的动画并实装
    DONE
    3.替换拆迁障碍物美术素材
    DONE

    任务:实装音乐和音效
    1.为两个场景添加音乐,并且在GamePlay场景不重开音乐。
    DONE
    2.实装人摔地音效
    DONE
    3.实装人跑动音效
    效果不好
    DONE
    4.实装人马分离音效
    DONE
    *.补充实装人降落到马上的音效
    DONE
    5.实装创烂障碍物音效
    DONE
    6.实装圣火点燃音效
    DONE
    7.实装马磕磕碰碰音效
    DONE
    8.实装马似音效
    DONE
    9.实装马着地音效
    DONE
    10.实装马起跳音效
    DONE
    11.实装马蹄点地音效
    效果不好
    DONE

    任务:实装特效
    1.实装后处理
    DONE
    2.实装日夜更替
    DONE
    3.实装树叶飘落
    DONE
    4.实装圣火粒子
    效果不好
    DONE
    5.实装破碎灰尘
    效果不好
    DONE
    6.实装马蹄灰尘
    DONE
    7.实装红黑闪特效
    DONE

    任务:完善没写的零散逻辑
    1.在教程场景,若人马分离障碍处没接到人,过一个转场然后复位人和马
    **:仍然存在问题,当如此,人的动画失效。这个问题现在不好修,等待人的死亡动画实装后再修
    已修复
    DONE

    2.在教程场景,点燃圣火仅有进入触发器后加载转场然后转移场景的功能,没有点燃圣火的过程,现补充其逻辑
    DONE

    3.开发GamePlay距离记录和重开系统
    DONE

    4.策划要在点燃圣火时镜头放大,体现标题感
    URP相机不太好做
    ABANDON

    5.人的死亡动画逻辑之前没做
    DONE

    任务:测试并修复Bug
    DONE

    修复Bug
    1.在教程场景,可破坏障碍物的碎片的层级不对,创烂瞬间会有层级突变的问题。在GamePlay层级,也出现了这个问题。
    已修复
    DONE
    2.现在的高障碍实在太高了,基本有80%以上的概率导致马翻,应该调低障碍物、或者调大马的跳跃力度。
    DONE
    3.跑步动画和跑步音效完全对不上
    加快跑步动画
    DONE
    4.原爆破给力范围太大,会导致旧的碎片也被波及
    已修复
    DONE

commit cab00851df5e4da70b981c564888c53d86de1f95
Author: Roman <1325980292@qq.com>
Date:   Sat Aug 27 02:51:21 2022 +0800

    短学期Log

    任务:替换和实装美术素材
    1.在教程场景布置图标和文字提示内容
    DONE
    2.制作GamePlay中背景元素的动画并实装
    DONE
    3.替换拆迁障碍物美术素材
    DONE

    任务:实装音乐和音效
    1.为两个场景添加音乐,并且在GamePlay场景不重开音乐。
    DONE
    2.实装人摔地音效
    DONE
    3.实装人跑动音效
    DONE
    4.实装人马分离音效
    DONE
    5.实装创烂障碍物音效
    DOING
    6.实装圣火点燃音效
    7.实装马磕磕碰碰音效
    8.实装马似音效
    9.实装马着地音效
    10.实装马起跳音效

    任务:实装特效
    ……

    任务:完善没写的零散逻辑
    1.在教程场景,若人马分离障碍处没接到人,过一个转场然后复位人和马
    **:仍然存在问题,当如此,人的动画失效。这个问题现在不好修,等待人的死亡动画实装后再修
    WAIT
    2.在教程场景,点燃圣火仅有进入触发器后加载转场然后转移场景的功能,没有点燃圣火的过程,现补充其逻辑
    DONE
    3.开发GamePlay距离记录和重开系统
    ……

    修复Bug
    1.在教程场景,可破坏障碍物的碎片的层级不对,创烂瞬间会有层级突变的问题。在GamePlay层级,也出现了这个问题。
    2.现在的高障碍实在太高了,基本有80%以上的概率导致马翻,应该调低障碍物、或者调大马的跳跃力度。

commit 3f0c9dd791c66e7222d6dbb72d8b762b73f06aed
Merge: e6888a7 bc74426
Author: Roman <1325980292@qq.com>
Date:   Fri Aug 26 18:13:00 2022 +0800

    Merge branch 'Saipo' of https://e.coding.net/gensokyogroup/saima/SAIMA into Roman

    # Conflicts:
    #	UserSettings/EditorUserSettings.asset
    #	UserSettings/Layouts/CurrentMaximizeLayout.dwlt
    #	UserSettings/Layouts/default-2021.dwlt

commit e6888a748dc7188bd3c2f7ce4bceade7402f8cec
Author: Roman <1325980292@qq.com>
Date:   Fri Aug 26 18:11:05 2022 +0800

    任务:编写教程和开始场景
    分析:分为五个阶段,每个阶段一屏宽。前四个阶段是教程阶段,分别仅放一种障碍(第一个无障碍)和提示,提示参照茶杯头教程关。
    每个阶段间有明显间断、镜头固定,但当马通过该阶段的障碍后,镜头右移一个阶段。马不能倒退,相机左侧有空气墙。
    到达最终阶段后,按流程点燃圣火进入游戏

    设计:
    1.搭建核心场景,放置障碍物。
    DONE
    2.设计教程及开始场景管理器
    (1.控制相机,使马每通过一个阶段,相机运动到记录的位置
    DONE
    (2.给相机左侧添加空气墙
    DONE
    (3.管理器要明确记录此时玩家处于哪个阶段
    DONE
    (4.当玩家进入点燃圣火触发器,切断操控,等待动画结束或者一些信号,触发从右向左的渐变黑幕遮挡全屏,随后转场到新场景。
    DONE

    至此,基本逻辑完成,需要补充大量细节、替换大量美术素材。

    修复问题:
    1.人马分离障碍连续出现可能导致致命问题,修复使其不能连续出现
    DONE
    2.GamePlay中,远处的山需要补充运动逻辑
    DONE

commit 5d922db6142634cdd20aa7801c0b3c1c4994360c
Author: Roman <1325980292@qq.com>
Date:   Wed Aug 24 17:59:05 2022 +0800

    任务:制作人物动画逻辑
    1.当人物处于Normal时,重复播放骑在马上的动画
    2.当人物被射出,播放一次跳上动画,之后停在最后一帧,等待落到平台上。
    3.当人物进入Walk态,开始重复播放跑步动画。
    4.当人物开始降落,触发一次跳下动画,停在最后一帧
    5.当Recover结束,状态恢复为Normal,重回到骑在马上动画
    DONE

commit d1fe07492af5ce6ea736ef738eb3e38170a08066
Merge: 436eaf0 5e10767
Author: Roman <1325980292@qq.com>
Date:   Wed Aug 24 10:42:58 2022 +0800

    Merge branch 'Saipo' of https://e.coding.net/gensokyogroup/saima/SAIMA into Roman

    # Conflicts:
    #	UserSettings/Layouts/default-2021.dwlt

commit 436eaf01046248372e887eb8276f38e021915790
Author: Roman <1325980292@qq.com>
Date:   Wed Aug 24 10:40:51 2022 +0800

    为了拉取的提交,少量修改
2022-08-27 21:30:16 +08:00

907 lines
31 KiB
C#
Raw Permalink 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
{
// _____ _ _ _
// | __ \ | | | (_)
// | |__) | _| |__ | |_ ___
// | ___/ | | | '_ \| | |/ __|
// | | | |_| | |_) | | | (__
// |_| \__,_|_.__/|_|_|\___|
[FoldoutGroup("SE")][Title("deadSE")]
public AudioClip deadSE;
[FoldoutGroup("SE")][Title("landSE")]
public AudioClip landSE;
[FoldoutGroup("SE")][Title("jumpSE")]
public AudioClip jumpSE;
private AudioSource audioSource;
/// <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>
/// 头部IK控制点
/// </summary>
[BoxGroup("必须绑定的物体")][Header("头部IK控制点")]
public Transform HeadControlPoint;
/// <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;
/// <summary>
/// 要统计多长时间内的输入变化以供加速?】
/// </summary>
[BoxGroup("马的基本运动信息")][Header("要统计多长时间内的输入变化以供加速?")]
public float accelerationTime;
/// <summary>
/// 加速倍率
/// </summary>
[BoxGroup("马的基本运动信息")][Header("加速倍率")]
public float accelerationRate = 1f;
/// <summary>
/// 最后一节马足的骨骼,必须按照左前、右前、左后、右后的顺序绑定
/// </summary>
[ BoxGroup("必须绑定的物体")][Header("最后一节马足的骨骼,必须按照左前、右前、左后、右后的顺序绑定")]
public Transform[] lastFoots;
[BoxGroup("马的基本运动信息")][Header("最后一节马前脚的旋转区间")]
public Vector2 lastFrontFootRotationRange;
[BoxGroup("马的基本运动信息")][Header("最后一节马后脚的旋转区间")]
public Vector2 lastBackFootRotationRange;
[BoxGroup("马的基本运动信息")][Header("马头追踪速度")]
public float headSpeed;
[BoxGroup("马的基本运动信息")][Header("马尾追踪速度")]
public float tailSpeed;
[BoxGroup("马的基本运动信息")][Header("马尾旋转范围")]
public Vector2 horseTailRotationRange;
/// <summary>
/// 马尾骨骼
/// </summary>
[BoxGroup("必须绑定的物体")][Header("马尾IK控制点")]
public Transform tailControlPoint;
// _____ _ _
// | __ \ (_) | |
// | |__) | __ ___ ____ _| |_ ___
// | ___/ '__| \ \ / / _` | __/ _ \
// | | | | | |\ 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;
/// <summary>
/// 最后一节马足的骨骼Transform需要模拟其弯曲
/// </summary>
public Transform lastFootTransform;
}
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 pInputFrontVector;
/// <summary>
/// 输入的马后脚信息
/// </summary>
private Vector2 inputBackVector;
/// <summary>
/// 上一帧马后脚信息
/// </summary>
private Vector2 pInputBackVector;
/// <summary>
/// 前一帧的前马脚输入向量
/// </summary>
private Vector2 previousInputFrontVector;
/// <summary>
/// 前一帧的后马脚输入向量
/// </summary>
private Vector2 previousInputBackVector;
// /// <summary>
// /// 一段时间内的变化总量
// /// </summary>
// private float chargeCount = 0;
/// <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;
/// <summary>
/// 保存了最近若干秒内的输入充能
/// </summary>
private Queue <float> footInputChargeQueue;
/// <summary>
/// 应有的输入加速累计队列的长度
/// </summary>
private int footInputChargeQueueLength;
/// <summary>
/// 本帧中,累计的充能值
/// </summary>
public float footInputCharge = 0;
/// <summary>
/// 马头控制点的Pos应同步于此
/// </summary>
private Transform horseHeadDelay;
/// <summary>
/// 马头的默认位置
/// </summary>
private Transform horseHeadDefault;
/// <summary>
/// 马尾的控制点的Pos应同步于此
/// </summary>
private Transform horseTailDelay;
/// <summary>
/// 马尾的默认位置
/// </summary>
private Transform horseTailDefault;
private JumpState previousJumpState = JumpState.Ready;
void Start()
{
//初始化
Init();
//找到必要的物体和组件
FindSth();
//计算必要的量
CaculateSth();
}
void Update()
{
//更新一些数据
UpdateDataEarly();
if(horseState != HorseState.Dead)
{
//计算马脚目标位置
CaculateTargetPosition();
//让马脚朝着目标移动
MoveFoot();
//让马整体运动
MoveHorse();
//计算此时马脚在Y轴上的进程并根据此进程对马蹄旋转插值模拟最后一节马蹄的弯曲
CaculateLastFootRotation();
//跳跃检测和物理实现
if(horseState == HorseState.Jump) CheckJump();
//让马头控制点运动
MoveHorseHead();
//让马尾根据马刚体速度运动
MoveHorseTail();
// //防止马翻
// Debug.Log(transform.eulerAngles.z);
// if(transform.eulerAngles.z > 30 && transform.eulerAngles.z < 330)
// {
// //看是离45度还是315度更近
// if(Mathf.Abs(transform.eulerAngles.z - 30) < Mathf.Abs(transform.eulerAngles.z - 330))
// {
// transform.eulerAngles = new Vector3(0, 0, 30);
// }
// else
// {
// transform.eulerAngles = new Vector3(0, 0, 330);
// }
// }
}
//更新一些数据
UpdateDataLate();
}
void FixedUpdate() {
CaculateFootInputCharge();
}
void MoveHorseTail(){
Vector2 dir = horseTailDefault.position - horseTailDelay.position;
Vector2 horseV = horseRig.velocity;
dir = new Vector2(
dir.x * 0.05f,
dir.y * 0.05f
);
horseTailDelay.position += new Vector3(
dir.x * Time.deltaTime * tailSpeed,
dir.y * Time.deltaTime * tailSpeed,
0
);
tailControlPoint.position = horseTailDelay.position;
}
void MoveHorseHead(){
Vector2 dir = horseHeadDefault.position - horseHeadDelay.position;
Vector2 horseV = horseRig.velocity;
dir = new Vector2(
dir.x += horseV.x * 0.05f,
dir.y -= horseV.x*0.05f
);
horseHeadDelay.position += new Vector3(
dir.x * Time.deltaTime * headSpeed,
dir.y * Time.deltaTime * headSpeed,
0
);
HeadControlPoint.position = horseHeadDelay.position;
}
void CaculateLastFootRotation(){
for(int i = 0; i < 4; i++){
//用脚底深度减去马足深度ABS。得到一个Progress
float footBottle = (i < 2) ? frontFoot.position.y : backFoot.position.y;
float footY = foots[i].footRealTransform.position.y;
float progress = Mathf.Abs(footBottle - footY) / (2*footMoveRadius);
//if(i == 1) Debug.Log("左前脚Progress" + progress);
Vector2 lastFootRotationRange = (i < 2) ? lastFrontFootRotationRange : lastBackFootRotationRange;
//插值,让最后一节马脚弯曲
foots[i].lastFootTransform.rotation = Quaternion.Euler(
0,0,
Mathf.Lerp(
lastFootRotationRange.x,
lastFootRotationRange.y,
progress)
);
}
}
void CaculateFootInputCharge()
{
//计算上一帧和这一帧的输入的角度差
float angleDiff = Vector2.Angle(pInputFrontVector, inputFrontVector) +
Vector2.Angle(pInputBackVector, inputBackVector);
//把它存入队列
footInputChargeQueue.Enqueue(angleDiff);
//如果队列的长度超过了一定的数量,就把最前面的一个去掉
if(footInputChargeQueue.Count > footInputChargeQueueLength)
footInputChargeQueue.Dequeue();
//计算马脚的输入充能
footInputCharge = 0;
foreach(float angle in footInputChargeQueue)
footInputCharge += angle;
//估算了一下充能最快大概是每秒3000.这里/3000 * 时间即可得到一个大约0-1的值
footInputCharge /= (3000 * accelerationTime);
//Debug.Log("footInputCharge: " + footInputCharge);
//更新上一帧信息
pInputFrontVector = inputFrontVector;
pInputBackVector = inputBackVector;
}
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<Collider2D> temp = new List<Collider2D>();
GetComponentsInChildren<Collider2D>(temp);
foreach(Collider2D box in temp)
box.enabled = false;
horseRig.velocity = Vector2.one * deadFlySpeed;
//播放音效
audioSource.clip = deadSE;
audioSource.Play();
}
/// <summary>
/// 一次跳跃的最终触发函数
/// </summary>
private void Jump()
{
if(horseRig.velocity.y > 3f)
return;
//horseRig.velocity = Vector2.zero;
//修改状态至跳跃
jumpState = JumpState.Jump;
Vector2 jumpVelocity = jumpChargeVelocity * jumpForce * -1 * new Vector2(1.5f,1.5f);
Vector2 fix = new Vector2( (jumpChargeVelocity.x > 0 ? -1 : 1) *12f, 33.5f);
jumpVelocity = 1f * jumpVelocity + 0.8f * fix;
//屏蔽出了问题的极大值
if(jumpVelocity.magnitude > 40f) jumpVelocity *= 0.1f;
Debug.Log("Jump Velocity: " + jumpVelocity);
horseRig.velocity = jumpVelocity;
jumpChargeVelocity = Vector2.zero;
//播放音效
audioSource.clip = jumpSE;
audioSource.Play();
}
private bool canJump = true;
/// <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 && canJump){
Jump();
canJump = false;
ActionController.Instance.DelayToCall(
() => {
canJump = true;
},3f
);
}
}
}
}
/// <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 * (1 + footInputCharge * accelerationRate);
//取反,因为是模拟反作用力
v*=-1;
//减弱y轴的影响不要让马在平地上起飞
v *= new Vector2(1,0.2f);
//如果模长超过5那必是触发了什么逆天Bug会导致马起飞需要避免这些错误
if(v.magnitude > 20f){
v = Vector2.zero;
Debug.Log("莫动");
}
//如果马处于跳跃状态,则不让马蹄提供速度
if(horseState == HorseState.Jump)
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;
previousJumpState = jumpState;
}
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;
footInputChargeQueue = new Queue<float>();
//初始化马背接收系统
GameObject.Find("接收人用的马背(不可改名)").AddComponent<HoseBack>();
}
private class HoseBack : MonoBehaviour{
private void OnTriggerEnter2D(Collider2D other) {
//当马背接受到人触发人的Recover();
if(other.TryGetComponent<Person>(out Person person) && person.state == Person.PersonState.FallingOff) person.Recover();
}
}
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("马死后的旋转中心");
//
for(int i = 0; i < 4; i++){
foots[i].lastFootTransform = lastFoots[i];
}
//
horseHeadDelay = GameObject.Find("马头IK延迟不可改名").transform;
horseHeadDefault = transform.Find("马头默认位置(不可改名)");
//
//tailBone = transform.Find("bone_10");
horseTailDelay = GameObject.Find("马尾IK延迟不可改名").transform;
horseTailDefault = transform.Find("马尾默认位置(不可改名)");
//
audioSource = GetComponent<AudioSource>();
}
/// <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;
//计算要记录多少帧的输入用秒计算每秒60次FixedUpdate
footInputChargeQueueLength = (int)accelerationTime * 60;
}
/// <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;
//如果有输入,则把马脚朝着目标移动
Vector3 targetPosition = hasInput ?
foots[i].footTargetPosition :
foots[i].footTargetPosition - new Vector3(0,footMoveRadius + 0.5f,0);
// 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.1f + 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].footControlPoint.position = targetPosition;
// 恢复重力
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;
public bool IsHorseStillAlive() => horseState != HorseState.Dead;
[FoldoutGroup("SE")][Title("马蹄音效")]
public AudioSource[] footSEs;
//撞击地面后,修改跳跃状态
void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Ground")
{
//播放马蹄点地音效
Random.Range(0,footSEs.Length);
footSEs[Random.Range(0,footSEs.Length)].Play();
transform.Find("马蹄灰尘").GetComponent<ParticleSystem>().Play();
jumpState = JumpState.Ready;
//播放着地音效
if(previousJumpState == JumpState.Jump)
{
audioSource.clip = landSE;
audioSource.Play();
}
}
}
}