using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using DG.Tweening; using Sirenix.OdinInspector; /// /// 玩家类,控制玩家相关的东西 /// 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; // _____ _ _ // | __ \ (_) | | // | |__) | __ ___ ____ _| |_ ___ // | ___/ '__| \ \ / / _` | __/ _ \ // | | | | | |\ 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; // _____ _ _ ____ _ // / ____| | | | _ \ | | // | | __ _| | | |_) | __ _ ___| | __ // | | / _` | | | _ < / _` |/ __| |/ / // | |___| (_| | | | |_) | (_| | (__| < // \_____\__,_|_|_|____/ \__,_|\___|_|\_\ void Start() { Init();//初始化一些参数 } void Update() { CountCD(); ChangeAnimator(); } 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); } /// /// 被爱欲品抓住时用协程触发 /// private void CatchingHarm(){ HPLeft -= FindObjectOfType().ATK; } /// /// 塞钱 /// private void GiveMoney(){ specialMoneyCount--; } /// /// 捡到钱了 /// private void IPickedACoin(){ specialMoneyCount++; } // _____ _ _ _ _ // / ____| | | (_) (_) // | | ___ | | |_ ___ _ ___ _ __ // | | / _ \| | | / __| |/ _ \| '_ \ // | |___| (_) | | | \__ \ | (_) | | | | // \_____\___/|_|_|_|___/_|\___/|_| |_| 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; //震动屏幕和手柄 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 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; } } } }