using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using DG.Tweening;
using Sirenix.OdinInspector;
using UnityEngine.SceneManagement;
///
/// 玩家类,控制玩家相关的东西
///
public class MyPlayer : MonoBehaviour, Boomer.I_CanBeBoomedObj
{
// _____ _ _ _
// | __ \ | | | (_)
// | |__) | _| |__ | |_ ___
// | ___/ | | | '_ \| | |/ __|
// | | | |_| | |_) | | | (__
// |_| \__,_|_.__/|_|_|\___|
[Header("玩家平时地面移动的速度")][FoldoutGroup("属性")]
public float speed = 10f;
[Header("玩家跳跃力度的大小")][FoldoutGroup("属性")]
public float jumpForce = 10f;
[FoldoutGroup("CD")][Header("挥动锤子的CD时长")]
public float hammerCD = 1f;
[FoldoutGroup("CD")][Header("发射镰刀的CD时长")]
public float sickleCD = 10f;
[Header("镰刀游戏物体")][FoldoutGroup("预制体")]
public GameObject sicklePrefab;
public enum AtkMethod{镰刀,锤子,反弹炸弹};
///
/// 生命值上限
///
[FoldoutGroup("属性")][Header("生命值上限")]
public float HP;
///
/// 被击飞后的飞行方向的调整值
///
[Header("被攻击后击飞的力度调整值")][FoldoutGroup("其他",false,0)]
public Vector2 hitToflyParameter;
///
/// 攻击方式的倍率列表
///
[DictionaryDrawerSettings()][Header("攻击方式的倍率列表")][ShowInInspector][FoldoutGroup("其他",false,0)]
public static Dictionary atkMethodMagnification;
///
/// 记录此时自己是否被爱欲品缠抱
///
[Header("被缠抱了吗?")][SerializeField][ReadOnly][FoldoutGroup("状态")]
public bool isCatching = false;
///
/// 正抓着玩家的爱欲品
///
[HideInInspector]
public AiYuPin catingAiYuPin;
///
/// 玩家此时捕捉着的可交互物体
///
[Header("玩家此时捕捉着的可交互物体")][FoldoutGroup("状态")][ReadOnly]
public Interactive catching;
///
/// 当前身上有多少特殊硬币,用来塞钱的那种
///
[FoldoutGroup("状态")][Header("当前身上有多少特殊硬币,用来塞钱的那种")]
public int specialMoneyCount = 0;
///
/// 此时是否受着撞钟攻击的影响?
///
[Header("此时是否受着撞钟攻击的影响?")][FoldoutGroup("状态")][ReadOnly]
public bool isAnnoying = false;
public enum Progress{
刚开, 通Fo, 通佛, 过佛,通Yi,通伊, 过伊, 通Ji, 通基, 过基
}
///
/// 玩家进度
///
public static Progress progress = 0;
// _____ _ _
// | __ \ (_) | |
// | |__) | __ ___ ____ _| |_ ___
// | ___/ '__| \ \ / / _` | __/ _ \
// | | | | | |\ V / (_| | || __/
// |_| |_| |_| \_/ \__,_|\__\___|
private Rigidbody2D m_rigidbody;//自身刚体组件
[SerializeField][ReadOnly][FoldoutGroup("状态")]
public int inputDir;//当前输入方向,-1左,1右,0静止
[SerializeField][ReadOnly][FoldoutGroup("状态")]
private bool isLanding;//记录自己当前是否着地
private DOTweenAnimation wavingAnimation;//锤子挥动动画组件
private SpriteRenderer hammerSprite;//锤子的图片组件
private BoxCollider2D hammerCollider;//锤子的碰撞体
[FoldoutGroup("CD")][Header("挥动锤子的CD还剩多长时间")][SerializeField][ReadOnly]
private float hammerCDLeft = 0f;
[FoldoutGroup("CD")][Header("发射镰刀的CD还剩多长时间")][SerializeField][ReadOnly]
private float sickleCDLeft = 0f;
///
/// 面部朝向,-1为左,1为右
///
private int faceDir = 1;//面部朝向
private Transform sickleFirePoint;//镰刀发射点的transform
private Transform sickleClearerL;//左边的镰刀清除触发器
private Transform sickleClearerR;//右边的镰刀清除触发器
[FoldoutGroup("状态",false,1)][Header("玩家现在是否处于控制状态下(物理)")][SerializeField][ReadOnly]
private bool inControl = true;
[SerializeField][ReadOnly][ProgressBar(0,10,0.15f,0.47f,0.74f)][FoldoutGroup("状态")]
private float HPLeft;
///
/// 自身动画控制器组件
///
private Animator m_Animator;
///
/// 是否在跳跃状态
///
private bool isJumping;
///
/// 是否在下落
///
private bool isFalling;
///
/// 是否在攻击
///
private bool isAttacking;
///
/// 是否在丢镰刀
///
private bool isThrowing;
private VibrationManager vibrationManager;
///
///反击侦察器组件
///
private CounterScout counterScout;
///
/// 此时自己是否正在与物体交互
///
[Header("此时自己是否正在与物体交互")][ReadOnly][SerializeField][FoldoutGroup("状态")]
private bool isInteractive = false;
public bool isDead;
// _____ _ _ ____ _
// / ____| | | | _ \ | |
// | | __ _| | | |_) | __ _ ___| | __
// | | / _` | | | _ < / _` |/ __| |/ /
// | |___| (_| | | | |_) | (_| | (__| <
// \_____\__,_|_|_|____/ \__,_|\___|_|\_\
void Start()
{
Init();//初始化一些参数
}
void Update()
{
CountCD();
ChangeAnimator();
// //
// Debug.Log(MyPlayer.progress);
// //
}
void FixedUpdate()
{
Move();//处理水平移动
}
// _ _ _
// | \ | | | |
// | \| | ___ _ __ _ __ ___ __ _| |
// | . ` |/ _ \| '__| '_ ` _ \ / _` | |
// | |\ | (_) | | | | | | | | (_| | |
// |_| \_|\___/|_| |_| |_| |_|\__,_|_|
//初始化函数
private void Init()
{
//找到自己身上的组件和需要的游戏物体
m_rigidbody = GetComponent();
wavingAnimation = transform.Find("锤子的旋转中心").GetComponent();
hammerSprite = transform.Find("锤子的旋转中心").Find("锤子").GetComponent();
hammerCollider = transform.Find("锤子的旋转中心").Find("锤子").GetComponent();
sickleFirePoint = transform.Find("镰刀发射点");
sickleClearerL = transform.Find("镰刀飞出消除触发器左");
sickleClearerL.gameObject.AddComponent();
sickleClearerR = transform.Find("镰刀飞出消除触发器右");
sickleClearerR.gameObject.AddComponent();
m_Animator = GetComponent();
vibrationManager = FindObjectOfType();
counterScout = gameObject.AddComponent();
//初始化生命值
HPLeft = HP;
//初始化攻击倍率字典
atkMethodMagnification = new Dictionary();
atkMethodMagnification.Add(AtkMethod.镰刀,1);
atkMethodMagnification.Add(AtkMethod.锤子,1);
atkMethodMagnification.Add(AtkMethod.反弹炸弹,2);
}
//移动函数,每帧运行一次,处理水平方向移动
private void Move()
{
//只有不受击\不攻击的情况下可以移动
if(inControl && !isAttacking){
//直接修改刚体速度
m_rigidbody.velocity = new Vector2(inputDir * speed,//水平方向以输入方向乘以预设速度大小
m_rigidbody.velocity.y);//垂直方向不变
}
if(isInteractive){
m_rigidbody.velocity = Vector2.zero;
}
}
//计算CD的函数,每帧调用
private void CountCD()
{
if(hammerCDLeft > 0) hammerCDLeft -= Time.deltaTime;
if(sickleCDLeft > 0) sickleCDLeft -= Time.deltaTime;
}
///
/// 转身的时候触发
///
private void TurnAround()
{
faceDir = inputDir;
transform.localScale = new Vector3( //x乘-1以显示转身
transform.localScale.x * -1,
//y、z不变
transform.localScale.y, transform.localScale.z);
}
///
/// 被击飞的时候Call这个
///
/// 被击方向
public void BeHitToFly(int dir)
{
m_rigidbody.velocity = Vector2.zero;
m_rigidbody.velocity += new Vector2(
-1 * dir * hitToflyParameter.x,
hitToflyParameter.y
);
}
///
/// 每Update调用,处理状态机全部放这里
///
protected void ChangeAnimator()
{
//移动动画
if (inputDir == 0) m_Animator.SetBool("isWalking", false);
else if(!isInteractive)m_Animator.SetBool("isWalking", true);
//跳跃动画
m_Animator.SetBool("isJumping",isJumping);
//下落动画
if(m_rigidbody.velocity.y < 0 && !isLanding){
m_Animator.SetBool("isFalling",true);
isJumping = false;
}
if(isLanding) {isFalling = false;m_Animator.SetBool("isFalling",isFalling);}
//锤子攻击动画
m_Animator.SetBool("isAttacking",isAttacking);
//记录着地
m_Animator.SetBool("isLanding",isLanding);
//被击飞动画触发
m_Animator.SetBool("isBeHitting",!inControl);
//丢出镰刀动画
m_Animator.SetBool("isThrowing",isThrowing);
//交互动画
m_Animator.SetBool("isInteractive",isInteractive);
//死亡动画
if(isDead) m_Animator.SetBool("isDead",true);
}
///
/// 被爱欲品抓住时用协程触发
///
private void CatchingHarm(){
HPLeft -= FindObjectOfType().ATK;
//检查死亡
if(DeadCheck()){
//若通过死亡检查,触发死亡事件
if(!isDead)StartCoroutine(OnDead());
}
}
///
/// 塞钱
///
private void GiveMoney(){
specialMoneyCount--;
}
///
/// 捡到钱了
///
private void IPickedACoin(){
specialMoneyCount++;
}
private bool DeadCheck(){
return ((HPLeft > 0) ? false : true);
}
// _____ _ _ _ _
// / ____| | | (_) (_)
// | | ___ | | |_ ___ _ ___ _ __
// | | / _ \| | | / __| |/ _ \| '_ \
// | |___| (_) | | | \__ \ | (_) | | | |
// \_____\___/|_|_|_|___/_|\___/|_| |_|
private void OnCollisionEnter2D(Collision2D collision)//当有物体碰上
{
//创到地面时触发一次
if(collision.transform.tag == "地面"){
//向脚底发射一条短射线
Ray2D ray = new Ray2D(
(Vector2)transform.position + new Vector2(0,-0.3f),
Vector2.down
);
Debug.DrawRay(ray.origin,ray.direction,Color.red,10f);
//获取射线的碰撞结果
RaycastHit2D hit2D;
hit2D = Physics2D.Raycast(ray.origin,ray.direction,0.001f);
//如果射线有结果并且射线创到的是地面,才表示着地了
if(hit2D && hit2D.collider.tag == "地面"){
//通过射线检测,标记自身着地
isLanding = true;
//如果没有被附身,则着地时再表示自己inControl
if(!isCatching)inControl = true;
}
}
}
private void OnCollisionExit2D(Collision2D collision)//当有碰撞体离开
{
if(collision.transform.tag == "地面" && (isJumping || !inControl))
{isLanding = false;}//若碰撞物体标签为地面,标记自身未着地
}
// 以下为操作监听事件
// _____ _ _____ _
// |_ _| | | / ____| | |
// | | _ __ _ __ _ _| |_| (___ _ _ ___| |_ ___ _ __ ___
// | | | '_ \| '_ \| | | | __|\___ \| | | / __| __/ _ \ '_ ` _ \
// _| |_| | | | |_) | |_| | |_ ____) | |_| \__ \ || __/ | | | | |
// |_____|_| |_| .__/ \__,_|\__|_____/ \__, |___/\__\___|_| |_| |_|
// | | __/ |
// |_| |___/
public void OnMove(InputAction.CallbackContext context)//OnMove事件
{
//决定输入方向inputDir
if(!context.ReadValue().Equals(0))
inputDir = (context.ReadValue() > 0) ? 1 : -1;
else inputDir = 0;
if(faceDir * inputDir < 0){
if(inControl && !isAttacking)TurnAround();
if(isCatching && context.started){
catingAiYuPin.OnBreakFree();
Debug.Log("挣脱");
}
}
}
public void OnJump(InputAction.CallbackContext context)//OnJump事件
{
//当按下跳跃键
if(context.performed)
{
if(isLanding && inControl){//如果当前着地
//给予自身刚体
m_rigidbody.velocity = new Vector2(m_rigidbody.velocity.x,//水平方向速度不变
jumpForce);//垂直方向给予预设跳跃速度
//标记自身正在跳跃
isJumping = true;
//解除着地状态
isLanding = false;
vibrationManager.ShakeScream(Vector2.up,0.5f);
StartCoroutine(vibrationManager.ShakePad(0.1f,0.2f,0.2f,VibrationManager.PadShakeitem.跳跃));
}
}
}
public void OnWave(InputAction.CallbackContext context)
{
//当执行Wave动作
if (context.performed && hammerCDLeft <= 0)
{
hammerSprite.DOFade(1, 0f);//把锤子显示,Debug用,后期请删除
hammerCollider.enabled = true;//打开锤子碰撞体
wavingAnimation.DOPlay();//播放挥动锤子动画
hammerCDLeft = hammerCD;//挥动成功,重置CD剩余时间
if(!isAttacking)isAttacking = true;
//空挥轻微震动手柄
StartCoroutine(
vibrationManager.ShakePad(0.1f,0.1f,0.2f,VibrationManager.PadShakeitem.挥动锤子)
);
//检查是否有可反击的炸弹
if(counterScout.catchingBoomer != null && faceDir == -1)
{
//有则执行炸弹的反击功能
counterScout.catchingBoomer.Vengeance();
//剧烈震动手柄
StartCoroutine(vibrationManager.ShakePad(0.8f,0.2f,0.2f,VibrationManager.PadShakeitem.反弹炸弹));
}
}
}
//检测到 镰刀 按键的时候触发
public void OnSickle(InputAction.CallbackContext context)
{
//按下就触发 CD好了 玩家处于控制状态
if(context.started && sickleCDLeft <= 0 && inControl){
//实例化一个镰刀游戏物体
Sickle sickle = Instantiate(
sicklePrefab,
sickleFirePoint.position,
Quaternion.identity
).GetComponent();
//初始化镰刀
sickle.dir = faceDir;
//开始计算镰刀CD
sickleCDLeft = sickleCD;
//轻微震动手柄
StartCoroutine(
vibrationManager.ShakePad(
0.1f,0.1f,0.2f,VibrationManager.PadShakeitem.发射镰刀
)
);
//轻微震动屏幕
vibrationManager.ShakeScream(Vector2.right,0.2f);
//改变标记表示开始播放丢镰刀动画
isThrowing = true;
}
}
public void OnInteractive(InputAction.CallbackContext context){
//当按下交互键
if(context.performed && catching != null){
//根据所捕获物体的不同,区分一下操作
switch(catching.itemName){
case Interactive.ItemName.塞钱箱 :
if(
specialMoneyCount > 0 &&
!FindObjectOfType().hasMoney
){
catching.OnCall();
GiveMoney();
}
break;
case Interactive.ItemName.硬币 :
IPickedACoin();
catching.OnCall();
break;
case Interactive.ItemName.男童 :
if(catching.RetuenState() == (int)Boy.State.wait){
catching.OnCall();
isInteractive = true;
}
break;
default :
catching.OnCall();
isInteractive = true;
break;
}
}
//当抬起交互键触发OnCallCancle
if(context.canceled && catching != null){
catching.OnCallCancel();
isInteractive = false;
}
}
// ______ _
// | ____| | |
// | |____ _____ _ __ | |_
// | __\ \ / / _ \ '_ \| __|
// | |___\ V / __/ | | | |_
// |______\_/ \___|_| |_|\__|
//当挥动锤子动画结束后触发
public void OnWaveEnd()
{
//为挥动动画倒带
wavingAnimation.DORewind();
//把锤子隐藏,Debug用,后期请删除
hammerSprite.DOFade(0,0f);
//关闭锤子的碰撞体
hammerCollider.enabled = false;
}
///
/// 当玩家被打则Call这个函数
///
/// 遭受的攻击力
/// 攻击来源的方向,-1左,1右
public void OnBeHit(float atk, int dir)
{
//触发一个击飞
BeHitToFly(dir);
//标记自身不受控制
inControl = false;
//交互中断
isInteractive = false;
if(catching != null)
catching.OnCallCancel();
//掉血
HPLeft -= atk;
//检查死亡
if(DeadCheck()){
//若通过死亡检查,触发死亡事件
if(!isDead)StartCoroutine(OnDead());
}
//震动屏幕和手柄
StartCoroutine(vibrationManager.ShakePad(0.8f,0.5f,0.2f,VibrationManager.PadShakeitem.被击中));
vibrationManager.ShakeScream(new Vector2(dir,1),1f);
}
///
/// 锤子攻击动画结尾Event调用
///
public void StopAttacking(){isAttacking = false;
//解决面部朝向的问题
if(faceDir * inputDir < 0){
if(inControl && !isAttacking)TurnAround();
if(isCatching){catingAiYuPin.OnBreakFree();}
}
}
///
/// 当玩家被爱欲品抓住后触发这个事件
///
/// 这个爱欲品的爱欲品组件
public void BeCatchedByAiYuPin(AiYuPin aiYuPin){
//标记自身正被抓着
isCatching = true;
//标记自身失去控制
inControl = false;
//每秒掉血,伤害量为爱欲品的攻击力
InvokeRepeating("CatchingHarm",0f,1f);
// //去除刚体速度,防止滑行带来的位置偏移问题
// m_rigidbody.velocity = Vector2.zero;
//锁定刚体,防止狡猾
m_rigidbody.constraints = RigidbodyConstraints2D.FreezePositionX;
//记录正在抓着自己的爱欲品
catingAiYuPin = aiYuPin;
}
///
/// 完全挣脱的时候从爱欲品Call过来
///
public void BreakFreeCompletely(){
//取消被抓住的标记
isCatching = false;
//恢复移动操控
inControl = true;
//取消掉血协程
CancelInvoke("CatchingHarm");
//去掉记录着的抓住玩家的爱欲品
catingAiYuPin = null;
//消除正在攻击状态以解决Bug
isAttacking = false;
//解冻刚体
m_rigidbody.constraints = RigidbodyConstraints2D.FreezeRotation;
}
//脚着地的时候触发,通过状态机调用
public void OnFootTap(){
StartCoroutine(vibrationManager.ShakePad(0f,0.02f,0.05f,VibrationManager.PadShakeitem.移动));
}
//镰刀攻击结尾调用,清除镰刀攻击状态防止多次触发动画
public void OnThrowingEnd(){isThrowing = false;}
///
/// 被撞钟攻击的开始瞬间触发
///
public void OnInAnnoying(){
Debug.Log("我被干扰了");
//标记自身正在被影响
isAnnoying = true;
//减速减益
speed *= 0.5f;
}
///
/// 当离开撞钟攻击范围的时候触发一次
///
public void OnOutAnnoying(){
Debug.Log("我脱离了干扰");
//标记自身离开影响
isAnnoying = false;
//恢复减速减益
speed *= 2f;
}
///
/// 当交互中断,从可交互物体触发
///
public void OnInteractiveException(){
Debug.Log("我超!什么玩意");
isInteractive = false;
}
///
/// 当有男童被救从男童触发
///
///
public void OnSave(Boy boy){
isInteractive = false;
}
///
/// 玩家死亡的时候触发
///
public IEnumerator OnDead(){
yield return new WaitForEndOfFrame();
//关闭操作地图
GetComponent().SwitchCurrentActionMap("NullMap");
//播放死亡动画
isDead = true;
yield return new WaitForSeconds(3f);
//使用转场块
FindObjectOfType().transType = Blcak.Type.竖直;
FindObjectOfType().OnCall();
//重新加载本场景(在转场块执行)
//SceneManager.LoadScene(SceneManager.GetActiveScene().name);
//停止木马
vibrationManager.StopHorseShakeScream();
}
// _____ _ __
// |_ _| | | / _|
// | | _ __ | |_ ___ _ __| |_ __ _ ___ ___
// | | | '_ \| __/ _ \ '__| _/ _` |/ __/ _ \
// _| |_| | | | || __/ | | || (_| | (_| __/
// |_____|_| |_|\__\___|_| |_| \__,_|\___\___|
public void BeBoomed(float atk, int dir, Boomer boomer){
OnBeHit(atk,dir);
}
public Transform ObjTransform(){return transform;}
// _______ _ _____ _
// |__ __| | |/ ____| |
// | | ___ ___ | | | | | __ _ ___ ___
// | |/ _ \ / _ \| | | | |/ _` / __/ __|
// | | (_) | (_) | | |____| | (_| \__ \__ \
// |_|\___/ \___/|_|\_____|_|\__,_|___/___/
private class SickleClearer : MonoBehaviour
{
//一个工具小插件,单独写脚本实在太浪费,所以在这里写一下。
//当触发器检测到有东西进入,会试图获取它身上的镰刀脚本,如果有就说明镰刀到屏幕边缘了,把这个镰刀删除
//没有的话说明是其他物体,不作处理
void OnTriggerEnter2D(Collider2D other)
{
if(other.TryGetComponent(out Sickle temp))
Destroy(temp.gameObject);
}
}
///
/// 反击侦察器,玩家的工具类
///
private class CounterScout : MonoBehaviour
{
///
/// 正捕获着的炸弹
///
public Boomer catchingBoomer;
void OnTriggerEnter2D(Collider2D other){
//如果可捕获的炸弹进入捕获范围
if(other.TryGetComponent(out Boomer temp) &&
temp.isThisCanBeReturned){
//则记录下这个炸弹
catchingBoomer = temp;
}
}
void OnTriggerExit2D(Collider2D other){
//如果捕获着的炸弹离开,清空记录
if(other.TryGetComponent(out Boomer temp) &&
temp == catchingBoomer){
catchingBoomer = null;
}
}
}
}