Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / role / enemy / Enemy.cs
@小李xl 小李xl on 18 Nov 2022 4 KB Ai状态机调整
#region 基础敌人设计思路
/*
敌人有三种状态: 
状态1: 未发现玩家, 视野不可穿墙, 该状态下敌人移动比较规律, 移动速度较慢, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3, 该房间的敌人不能再回到状态1
状态2: 发现有玩家, 但不知道在哪, 视野不可穿墙, 该情况下敌人移动速度明显加快, 移动不规律, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3
状态3: 明确知道玩家的位置, 视野允许穿墙, 移动速度与状态2一致, 进入该状态时, 敌人之间会相互告知玩家所在位置, 并朝着玩家位置开火,
       如果有墙格挡, 则有一定概率继续开火, 一旦玩家立刻敌人视野超哥一段时间, 敌人自动切换为状态2

敌人状态1只存在于少数房间内, 比如特殊房间, 大部分情况下敌人应该是状态2, 或者玩家进入房间时就被敌人发现
*/
#endregion


using Godot;

/// <summary>
/// 基础敌人
/// </summary>
public class Enemy : Role
{

    /// <summary>
    /// 敌人身上的状态机控制器
    /// </summary>
    public StateController<Enemy, AIStateEnum> StateController { get; }
    
    /// <summary>
    /// 视野半径, 单位像素
    /// </summary>
    public float ViewRange { get; set; } = 200;

    /// <summary>
    /// 背后的视野半径, 单位像素
    /// </summary>
    public float BackViewRange { get; set; } = 50;
    
    /// <summary>
    /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
    /// </summary>
    public RayCast2D ViewRay { get; }
    
    //------------------- 寻路相关 ---------------------------

    /// <summary>
    /// 移动目标标记
    /// </summary>
    public PathSign TargetSign { get; }

    /// <summary>
    /// 寻路标记线段总长度
    /// </summary>
    public float PathSignLength { get; set; } = 500;

    //-------------------------------------------------------
    
    public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn)
    {
        StateController = new StateController<Enemy, AIStateEnum>();
        AddComponent(StateController);
        
        AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Props | PhysicsLayer.Player;
        Camp = CampEnum.Camp2;

        MoveSpeed = 20;
        
        //视野射线
        ViewRay = GetNode<RayCast2D>("ViewRay");
        
        //注册Ai状态机
        StateController.Register(new AINormalState());
        StateController.Register(new AITailAfterState());
        //默认状态
        StateController.ChangeState(AIStateEnum.AINormal);
        
        TargetSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player);
    }

    public override void _Process(float delta)
    {
        base._Process(delta);
        if (GameApplication.Instance.Debug)
        {
            Update();
        }
    }

    public override void _PhysicsProcess(float delta)
    {
        base._PhysicsProcess(delta);
        
        var player = GameApplication.Instance.Room.Player;
        //玩家中心点坐标
        var playerPos = player.MountPoint.GlobalPosition;
        
        //玩家是否在前方
        var isForward = IsPositionInForward(playerPos);
        
        if (isForward) //脸朝向玩家
        {
            // if (GlobalPosition.DistanceSquaredTo(playerPos) <= ViewRange * ViewRange) //没有超出视野半径
            // {
            //     //射线检测墙体
            //     ViewRay.Enabled = true;
            //     var localPos = ViewRay.ToLocal(playerPos);
            //     ViewRay.CastTo = localPos;
            //     ViewRay.ForceRaycastUpdate();
            //
            //     if (ViewRay.IsColliding()) //在视野范围内, 但是碰到墙壁
            //     {
            //         LookTarget = null;
            //         StateController.ChangeState(StateEnum.Idle);
            //     }
            //     else //视野无阻
            //     {
            //         LookTarget = player;
            //         StateController.ChangeState(StateEnum.Run);
            //     }
            //     
            //     ViewRay.Enabled = false;
            // }
            // else //超出视野半径
            // {
            //     LookTarget = null;
            //     StateController.ChangeState(StateEnum.Idle);
            // }
            // //_prevPlayerPos = playerPos;
        }
    }

    public override void _Draw()
    {
        if (GameApplication.Instance.Debug)
        {
            if (TargetSign != null)
            {
                DrawLine(Vector2.Zero,ToLocal(TargetSign.GlobalPosition), Colors.Red);
            }
        }
    }
}