268 lines
7.7 KiB
C#
268 lines
7.7 KiB
C#
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<Rigidbody2D>();
|
||
// anim = GetComponent<Animator>();
|
||
pacMan = GameObject.Find("PacManPlayer");//找到主角
|
||
pacManComponent = pacMan.GetComponent<PacMan>();
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|