using System; using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.SocialPlatforms.Impl; using UnityEngine.UIElements; public class RedGhost : MonoBehaviour { // ========== 状态枚举 ========== public enum GhostState { Idle, //开局的待机 Start, //离开重生点出发 Chase, // 追击模式 Frightened, // 恐惧模式,逃跑 Die // 死亡重生 } public GhostState curstate;//当前枚举 [Header("基础配置")] public Rigidbody2D rb; public Animator anim; public GameObject pacMan;//吃豆人主角 private PacMan pacManComponent; //吃豆人脚本 public LayerMask obstadeLayer;//墙的层级 public Transform GhostRevivePoint;//复活点,每个鬼不一样,死亡传送回来 public Transform GhostStartPoint;//从复活点移动到开始追击点的位置 public float stateTimer; //计时器 public bool isFrightened = false;//已经进入恐惧状态,保证update中只执行一次 public float idleDuration;//一开始发呆的时间 public float DieDuration;//死亡时间 public float Score = 1000;//鬼死亡给的分数 [Header("追击逃跑逻辑相关")] private Vector2 currentDirection; //当前方向 public float speed;//基础速度 public float frightenedSpeed;//逃跑速度 // 新增射线参数 [Header("射线设置")] public float castDistance = 1f; // 射线检测长度 public float boxWidth = 0.3f; // 射线宽度 public Color validColor = Color.green; // 有效射线颜色 public Color blockedColor = Color.red; // 受阻射线颜色 private void Awake() { rb = GetComponent(); // anim = GetComponent(); pacMan = GameObject.Find("PacManPlayer");//找到主角 pacManComponent = pacMan.GetComponent(); } private void Update() { switch (curstate) { case GhostState.Idle: Idle(); break; case GhostState.Die: Die(); break; } if(pacManComponent.invincible && curstate == GhostState.Chase && !isFrightened)//未在恐惧状态才进入 { Debug.Log("进入恐惧状态"); isFrightened = true; curstate = GhostState.Frightened; } } private void FixedUpdate() { switch (curstate) { case GhostState.Start: GhostStart(); break; case GhostState.Chase: Chase(); break; case GhostState.Frightened: Frightened(); break; } } private void OnTriggerEnter2D(Collider2D collision) { if (collision.gameObject == pacMan) { if (curstate == GhostState.Frightened) { transform.position = GhostRevivePoint.position;//传送到复活点 isFrightened = false;//恐惧变回false rb.velocity = Vector2.zero;//位置不变 stateTimer = 0;//重新计数 GameManager.instance.AddScore(Score);//加分 curstate = GhostState.Die; } else if (curstate == GhostState.Chase) { // 触发吃豆人死亡逻辑 pacManComponent.die = true; // Time.timeScale = 0;//游戏暂停 } } } private void Idle() { // 待机计时逻辑 stateTimer += Time.deltaTime; if (stateTimer >= idleDuration) { stateTimer = 0; curstate = GhostState.Start; } // anim.SetBool("Moving", false); } private void GhostStart() { // 向起始点移动 Vector2 direction = ((Vector2)GhostStartPoint.position - rb.position).normalized;//方向归一 rb.velocity = direction * speed *Time.deltaTime; if (Vector2.Distance(transform.position, GhostStartPoint.position) < 0.1f)//坐标与目标坐标小于一定值 { transform.position = GhostStartPoint.position; curstate = GhostState.Chase; } } private void Chase() { UpdateMovement(pacMan.transform.position, true); } private void Frightened() { // 直接读取主角的剩余无敌时间 float remainingTime = pacManComponent.remainingInvincibleTime; if (remainingTime <=0)//时间累加大于主角身上的无敌时间,切换回追击 { isFrightened = false;//把恐惧中切换为未恐惧 curstate = GhostState.Chase; // anim.SetBool("Frightened", false); // return; } UpdateMovement(pacMan.transform.position, false); //anim.SetBool("Frightened", true); } private void Die() { stateTimer += Time.deltaTime; if(stateTimer >= DieDuration) { stateTimer = 0; curstate = GhostState.Idle; } //播放死亡动画 } // 核心移动逻辑(追击/逃跑共用) private void UpdateMovement(Vector2 targetPos, bool isChasing) { Vector2[] directions = { Vector2.up, Vector2.down, Vector2.left, Vector2.right }; Vector2 bestDir = Vector2.zero; float bestValue = isChasing ? Mathf.Infinity : -Mathf.Infinity;//正无穷大,负无穷大 foreach (Vector2 dir in directions) { if (dir == -currentDirection) continue; // 循环到的方向等于当前的负方向,跳到下一次循环,禁止直接回头 // 使用BoxCast进行区域检测 bool isBlocked = Physics2D.BoxCast(transform.position,GetBoxSize(dir),0f,dir,castDistance,obstadeLayer); if (isBlocked) continue; // 计算方向价值 float distance = Vector2.Distance(rb.position + dir, targetPos);//当前坐标加方向向量和目标主角坐标的差值 float currentValue = distance;//是追击还是逃跑 if ((isChasing && currentValue < bestValue) || (!isChasing && currentValue > bestValue)) { bestValue = currentValue; bestDir = dir; } } if (bestDir != Vector2.zero) { currentDirection = bestDir; rb.velocity = currentDirection.normalized * (curstate == GhostState.Frightened ? frightenedSpeed : speed) * Time.deltaTime; } } // 根据方向获取不同的射线盒子尺寸 private Vector2 GetBoxSize(Vector2 direction) { return new Vector2( direction.x = boxWidth,//射线长宽 direction.y = boxWidth ); } // 可视化射线 private void OnDrawGizmos() { if (!Application.isPlaying) return; foreach (Vector2 dir in new[] { Vector2.up, Vector2.down, Vector2.left, Vector2.right }) { Gizmos.color = Physics2D.BoxCast(transform.position, GetBoxSize(dir), 0f, dir, castDistance, obstadeLayer) ? blockedColor : validColor; DrawBoxCastGizmo( transform.position, dir, GetBoxSize(dir), castDistance ); } } // 绘制盒子投射区域 private void DrawBoxCastGizmo(Vector2 origin, Vector2 direction, Vector2 size, float distance) { Vector2 endPos = origin + direction * distance; // 绘制起点和终点的盒子 Gizmos.DrawWireCube(origin, size); Gizmos.DrawWireCube(endPos, size); // 绘制连接线 Vector2 perpendicular = new Vector2(-direction.y, direction.x) * size.x / 2; Vector2 edge1 = origin + perpendicular; Vector2 edge2 = origin - perpendicular; Gizmos.DrawLine(edge1, edge1 + direction * distance); Gizmos.DrawLine(edge2, edge2 + direction * distance); } }