PacMan/Assets/Scripts/RedGhost.cs

268 lines
7.7 KiB
C#
Raw 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;
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);
}
}