抽出AiRole类
This commit is contained in:
parent
2228c4c850
commit
dd8583f5bf
381
DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs
Normal file
381
DungeonShooting_Godot/src/game/activity/role/ai/AiRole.cs
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
|
||||||
|
using AiState;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ai角色
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class AiRole : Role
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 目标是否在视野内
|
||||||
|
/// </summary>
|
||||||
|
public bool TargetInView { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人身上的状态机控制器
|
||||||
|
/// </summary>
|
||||||
|
public StateController<AiRole, AIStateEnum> StateController { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
|
||||||
|
/// </summary>
|
||||||
|
[Export, ExportFillNode]
|
||||||
|
public RayCast2D ViewRay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导航代理
|
||||||
|
/// </summary>
|
||||||
|
[Export, ExportFillNode]
|
||||||
|
public NavigationAgent2D NavigationAgent2D { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导航代理中点
|
||||||
|
/// </summary>
|
||||||
|
[Export, ExportFillNode]
|
||||||
|
public Marker2D NavigationPoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不通过武发射子弹的开火点
|
||||||
|
/// </summary>
|
||||||
|
[Export, ExportFillNode]
|
||||||
|
public Marker2D FirePoint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前敌人所看向的对象, 也就是枪口指向的对象
|
||||||
|
/// </summary>
|
||||||
|
public ActivityObject LookTarget { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击锁定目标时间
|
||||||
|
/// </summary>
|
||||||
|
public float LockingTime { get; set; } = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 锁定目标已经走过的时间
|
||||||
|
/// </summary>
|
||||||
|
public float LockTargetTime { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
|
||||||
|
/// </summary>
|
||||||
|
public float ViewRange { get; set; } = 250;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发现玩家后跟随玩家的视野半径
|
||||||
|
/// </summary>
|
||||||
|
public float TailAfterViewRange { get; set; } = 400;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背后的视野半径, 单位像素
|
||||||
|
/// </summary>
|
||||||
|
public float BackViewRange { get; set; } = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击间隔时间, 秒
|
||||||
|
/// </summary>
|
||||||
|
public float AttackInterval { get; set; } = 0;
|
||||||
|
|
||||||
|
public override void OnInit()
|
||||||
|
{
|
||||||
|
base.OnInit();
|
||||||
|
IsAi = true;
|
||||||
|
|
||||||
|
StateController = AddComponent<StateController<AiRole, AIStateEnum>>();
|
||||||
|
|
||||||
|
//注册Ai状态机
|
||||||
|
StateController.Register(new AiNormalState());
|
||||||
|
StateController.Register(new AiTailAfterState());
|
||||||
|
StateController.Register(new AiFollowUpState());
|
||||||
|
StateController.Register(new AiLeaveForState());
|
||||||
|
StateController.Register(new AiSurroundState());
|
||||||
|
StateController.Register(new AiFindAmmoState());
|
||||||
|
StateController.Register(new AiAttackState());
|
||||||
|
StateController.Register(new AiAstonishedState());
|
||||||
|
StateController.Register(new AiNotifyState());
|
||||||
|
|
||||||
|
//默认状态
|
||||||
|
StateController.ChangeStateInstant(AIStateEnum.AiNormal);
|
||||||
|
|
||||||
|
//NavigationAgent2D.VelocityComputed += OnVelocityComputed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
|
||||||
|
/// </summary>
|
||||||
|
public bool CheckUsableWeaponInUnclaimed()
|
||||||
|
{
|
||||||
|
foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
|
||||||
|
{
|
||||||
|
//判断是否能拾起武器, 条件: 相同的房间
|
||||||
|
if (unclaimedWeapon.AffiliationArea == AffiliationArea)
|
||||||
|
{
|
||||||
|
if (!unclaimedWeapon.IsTotalAmmoEmpty())
|
||||||
|
{
|
||||||
|
if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//判断是否可以移除该标记
|
||||||
|
var enemy = unclaimedWeapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
|
||||||
|
if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
|
||||||
|
{
|
||||||
|
unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
|
||||||
|
{
|
||||||
|
unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 寻找可用的武器
|
||||||
|
/// </summary>
|
||||||
|
public Weapon FindTargetWeapon()
|
||||||
|
{
|
||||||
|
Weapon target = null;
|
||||||
|
var position = Position;
|
||||||
|
foreach (var weapon in World.Weapon_UnclaimedWeapons)
|
||||||
|
{
|
||||||
|
//判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
|
||||||
|
if (weapon.AffiliationArea == AffiliationArea)
|
||||||
|
{
|
||||||
|
//还有弹药
|
||||||
|
if (!weapon.IsTotalAmmoEmpty())
|
||||||
|
{
|
||||||
|
//查询是否有其他敌人标记要拾起该武器
|
||||||
|
if (weapon.HasSign(SignNames.AiFindWeaponSign))
|
||||||
|
{
|
||||||
|
var enemy = weapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
|
||||||
|
if (enemy == this) //就是自己标记的
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
|
||||||
|
{
|
||||||
|
weapon.RemoveSign(SignNames.AiFindWeaponSign);
|
||||||
|
}
|
||||||
|
else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
|
||||||
|
{
|
||||||
|
weapon.RemoveSign(SignNames.AiFindWeaponSign);
|
||||||
|
}
|
||||||
|
else //放弃这把武器
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == null) //第一把武器
|
||||||
|
{
|
||||||
|
target = weapon;
|
||||||
|
}
|
||||||
|
else if (target.Position.DistanceSquaredTo(position) >
|
||||||
|
weapon.Position.DistanceSquaredTo(position)) //距离更近
|
||||||
|
{
|
||||||
|
target = weapon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取武器攻击范围 (最大距离值与最小距离的中间值)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="weight">从最小到最大距离的过渡量, 0 - 1, 默认 0.5</param>
|
||||||
|
public float GetWeaponRange(float weight = 0.5f)
|
||||||
|
{
|
||||||
|
if (WeaponPack.ActiveItem != null)
|
||||||
|
{
|
||||||
|
var attribute = WeaponPack.ActiveItem.Attribute;
|
||||||
|
return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回目标点是否在视野范围内
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsInViewRange(Vector2 target)
|
||||||
|
{
|
||||||
|
var isForward = IsPositionInForward(target);
|
||||||
|
if (isForward)
|
||||||
|
{
|
||||||
|
if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回目标点是否在跟随状态下的视野半径内
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsInTailAfterViewRange(Vector2 target)
|
||||||
|
{
|
||||||
|
var isForward = IsPositionInForward(target);
|
||||||
|
if (isForward)
|
||||||
|
{
|
||||||
|
if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
|
||||||
|
/// </summary>
|
||||||
|
public bool TestViewRayCast(Vector2 target)
|
||||||
|
{
|
||||||
|
ViewRay.Enabled = true;
|
||||||
|
ViewRay.TargetPosition = ViewRay.ToLocal(target);
|
||||||
|
ViewRay.ForceRaycastUpdate();
|
||||||
|
return ViewRay.IsColliding();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
|
||||||
|
/// </summary>
|
||||||
|
public void TestViewRayCastOver()
|
||||||
|
{
|
||||||
|
ViewRay.Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI 拾起武器操作
|
||||||
|
/// </summary>
|
||||||
|
public void DoPickUpWeapon()
|
||||||
|
{
|
||||||
|
//这几个状态不需要主动拾起武器操作
|
||||||
|
var state = StateController.CurrState;
|
||||||
|
if (state == AIStateEnum.AiNormal || state == AIStateEnum.AiNotify || state == AIStateEnum.AiAstonished || state == AIStateEnum.AiAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//拾起地上的武器
|
||||||
|
if (InteractiveItem is Weapon weapon)
|
||||||
|
{
|
||||||
|
if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
|
||||||
|
{
|
||||||
|
TriggerInteractive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//没弹药了
|
||||||
|
if (weapon.IsTotalAmmoEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
|
||||||
|
if (index != -1) //与武器背包中武器类型相同, 补充子弹
|
||||||
|
{
|
||||||
|
if (!WeaponPack.GetItem(index).IsAmmoFull())
|
||||||
|
{
|
||||||
|
TriggerInteractive();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// var index2 = Holster.FindWeapon((we, i) =>
|
||||||
|
// we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
|
||||||
|
var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
|
||||||
|
if (index2 != -1) //扔掉没子弹的武器
|
||||||
|
{
|
||||||
|
ThrowWeapon(index2);
|
||||||
|
TriggerInteractive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (Holster.HasVacancy()) //有空位, 拾起武器
|
||||||
|
// {
|
||||||
|
// TriggerInteractive();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取锁定目标的剩余时间
|
||||||
|
/// </summary>
|
||||||
|
public float GetLockRemainderTime()
|
||||||
|
{
|
||||||
|
var weapon = WeaponPack.ActiveItem;
|
||||||
|
if (weapon == null)
|
||||||
|
{
|
||||||
|
return LockingTime - LockTargetTime;
|
||||||
|
}
|
||||||
|
return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LookTargetPosition(Vector2 pos)
|
||||||
|
{
|
||||||
|
LookTarget = null;
|
||||||
|
base.LookTargetPosition(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行移动操作
|
||||||
|
/// </summary>
|
||||||
|
public void DoMove()
|
||||||
|
{
|
||||||
|
// //计算移动
|
||||||
|
// NavigationAgent2D.MaxSpeed = EnemyRoleState.MoveSpeed;
|
||||||
|
// var nextPos = NavigationAgent2D.GetNextPathPosition();
|
||||||
|
// NavigationAgent2D.Velocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
|
||||||
|
|
||||||
|
AnimatedSprite.Play(AnimatorNames.Run);
|
||||||
|
//计算移动
|
||||||
|
var nextPos = NavigationAgent2D.GetNextPathPosition();
|
||||||
|
BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行站立操作
|
||||||
|
/// </summary>
|
||||||
|
public void DoIdle()
|
||||||
|
{
|
||||||
|
AnimatedSprite.Play(AnimatorNames.Idle);
|
||||||
|
BasisVelocity = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新房间中标记的目标位置
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateMarkTargetPosition()
|
||||||
|
{
|
||||||
|
if (LookTarget != null)
|
||||||
|
{
|
||||||
|
AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void OnVelocityComputed(Vector2 velocity)
|
||||||
|
// {
|
||||||
|
// if (Mathf.Abs(velocity.X) >= 0.01f && Mathf.Abs(velocity.Y) >= 0.01f)
|
||||||
|
// {
|
||||||
|
// AnimatedSprite.Play(AnimatorNames.Run);
|
||||||
|
// BasisVelocity = velocity;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发现目标时的惊讶状态
|
/// 发现目标时的惊讶状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiAstonishedState : StateBase<Enemy, AIStateEnum>
|
public class AiAstonishedState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 下一个状态
|
/// 下一个状态
|
|
@ -1,12 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ai 攻击状态
|
/// ai 攻击状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiAttackState : StateBase<Enemy, AIStateEnum>
|
public class AiAttackState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上一个状态
|
/// 上一个状态
|
||||||
|
@ -209,7 +209,7 @@ public class AiAttackState : StateBase<Enemy, AIStateEnum>
|
||||||
|
|
||||||
if (AttackState == AiAttackEnum.AttackInterval) //触发攻击完成
|
if (AttackState == AiAttackEnum.AttackInterval) //触发攻击完成
|
||||||
{
|
{
|
||||||
Master.AttackTimer = weapon.Attribute.TriggerInterval + Master.EnemyRoleState.AttackInterval;
|
Master.AttackTimer = weapon.Attribute.TriggerInterval + Master.AttackInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ai 寻找弹药, 进入该状态需要在参数中传入目标武器对象
|
/// Ai 寻找弹药, 进入该状态需要在参数中传入目标武器对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiFindAmmoState : StateBase<Enemy, AIStateEnum>
|
public class AiFindAmmoState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标武器
|
/// 目标武器
|
|
@ -2,12 +2,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
|
/// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiFollowUpState : StateBase<Enemy, AIStateEnum>
|
public class AiFollowUpState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
//导航目标点刷新计时器
|
//导航目标点刷新计时器
|
||||||
private float _navigationUpdateTimer = 0;
|
private float _navigationUpdateTimer = 0;
|
||||||
|
@ -72,7 +72,7 @@ public class AiFollowUpState : StateBase<Enemy, AIStateEnum>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inAttackRange = distanceSquared <= Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2);
|
inAttackRange = distanceSquared <= Mathf.Pow(Master.ViewRange * 0.7f, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Master.NavigationAgent2D.IsNavigationFinished())
|
if (!Master.NavigationAgent2D.IsNavigationFinished())
|
||||||
|
@ -121,7 +121,7 @@ public class AiFollowUpState : StateBase<Enemy, AIStateEnum>
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//距离够近, 可以切换到环绕模式
|
//距离够近, 可以切换到环绕模式
|
||||||
if (distanceSquared <= Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2))
|
if (distanceSquared <= Mathf.Pow(Master.ViewRange * 0.7f, 2))
|
||||||
{
|
{
|
||||||
ChangeState(AIStateEnum.AiSurround);
|
ChangeState(AIStateEnum.AiSurround);
|
||||||
}
|
}
|
|
@ -2,12 +2,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 收到其他敌人通知, 前往发现目标的位置
|
/// 收到其他敌人通知, 前往发现目标的位置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiLeaveForState : StateBase<Enemy, AIStateEnum>
|
public class AiLeaveForState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
//导航目标点刷新计时器
|
//导航目标点刷新计时器
|
||||||
private float _navigationUpdateTimer = 0;
|
private float _navigationUpdateTimer = 0;
|
|
@ -2,12 +2,12 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AI 正常状态
|
/// AI 正常状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiNormalState : StateBase<Enemy, AIStateEnum>
|
public class AiNormalState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
//下一个运动的坐标
|
//下一个运动的坐标
|
||||||
private Vector2 _nextPos;
|
private Vector2 _nextPos;
|
|
@ -1,11 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发现目标, 通知其它敌人
|
/// 发现目标, 通知其它敌人
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiNotifyState : StateBase<Enemy, AIStateEnum>
|
public class AiNotifyState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
private float _timer;
|
private float _timer;
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 距离目标足够近, 在目标附近随机移动, 并开火
|
/// 距离目标足够近, 在目标附近随机移动, 并开火
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiSurroundState : StateBase<Enemy, AIStateEnum>
|
public class AiSurroundState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
//是否移动结束
|
//是否移动结束
|
||||||
private bool _isMoveOver;
|
private bool _isMoveOver;
|
||||||
|
@ -151,7 +151,7 @@ public class AiSurroundState : StateBase<Enemy, AIStateEnum>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.EnemyRoleState.ViewRange * 0.7f, 2)) //玩家离开正常射击范围
|
if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.ViewRange * 0.7f, 2)) //玩家离开正常射击范围
|
||||||
{
|
{
|
||||||
ChangeState(AIStateEnum.AiFollowUp);
|
ChangeState(AIStateEnum.AiFollowUp);
|
||||||
}
|
}
|
|
@ -2,12 +2,12 @@
|
||||||
using System;
|
using System;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace EnemyState;
|
namespace AiState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AI 发现玩家, 跟随玩家, 但是不在视野范围内
|
/// AI 发现玩家, 跟随玩家, 但是不在视野范围内
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AiTailAfterState : StateBase<Enemy, AIStateEnum>
|
public class AiTailAfterState : StateBase<AiRole, AIStateEnum>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标是否在视野半径内
|
/// 目标是否在视野半径内
|
|
@ -2,69 +2,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Config;
|
using Config;
|
||||||
using EnemyState;
|
using AiState;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 高级敌人,可以携带武器
|
/// 敌人,可以携带武器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tool]
|
[Tool]
|
||||||
public partial class Enemy : Role
|
public partial class Enemy : AiRole
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 目标是否在视野内
|
|
||||||
/// </summary>
|
|
||||||
public bool TargetInView { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 敌人身上的状态机控制器
|
|
||||||
/// </summary>
|
|
||||||
public StateController<Enemy, AIStateEnum> StateController { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
|
|
||||||
/// </summary>
|
|
||||||
[Export, ExportFillNode]
|
|
||||||
public RayCast2D ViewRay { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 导航代理
|
|
||||||
/// </summary>
|
|
||||||
[Export, ExportFillNode]
|
|
||||||
public NavigationAgent2D NavigationAgent2D { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 导航代理中点
|
|
||||||
/// </summary>
|
|
||||||
[Export, ExportFillNode]
|
|
||||||
public Marker2D NavigationPoint { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不通过武发射子弹的开火点
|
|
||||||
/// </summary>
|
|
||||||
[Export, ExportFillNode]
|
|
||||||
public Marker2D FirePoint { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前敌人所看向的对象, 也就是枪口指向的对象
|
|
||||||
/// </summary>
|
|
||||||
public ActivityObject LookTarget { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 攻击锁定目标时间
|
|
||||||
/// </summary>
|
|
||||||
public float LockingTime { get; set; } = 1f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 锁定目标已经走过的时间
|
|
||||||
/// </summary>
|
|
||||||
public float LockTargetTime { get; set; } = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 敌人属性
|
|
||||||
/// </summary>
|
|
||||||
public EnemyRoleState EnemyRoleState { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 敌人属性
|
/// 敌人属性
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -117,10 +63,6 @@ public partial class Enemy : Role
|
||||||
public override void OnInit()
|
public override void OnInit()
|
||||||
{
|
{
|
||||||
base.OnInit();
|
base.OnInit();
|
||||||
|
|
||||||
IsAi = true;
|
|
||||||
|
|
||||||
StateController = AddComponent<StateController<Enemy, AIStateEnum>>();
|
|
||||||
|
|
||||||
AttackLayer = PhysicsLayer.Obstacle | PhysicsLayer.Player;
|
AttackLayer = PhysicsLayer.Obstacle | PhysicsLayer.Player;
|
||||||
EnemyLayer = PhysicsLayer.Player;
|
EnemyLayer = PhysicsLayer.Player;
|
||||||
|
@ -130,28 +72,11 @@ public partial class Enemy : Role
|
||||||
|
|
||||||
MaxHp = 20;
|
MaxHp = 20;
|
||||||
Hp = 20;
|
Hp = 20;
|
||||||
|
|
||||||
//注册Ai状态机
|
|
||||||
StateController.Register(new AiNormalState());
|
|
||||||
StateController.Register(new AiTailAfterState());
|
|
||||||
StateController.Register(new AiFollowUpState());
|
|
||||||
StateController.Register(new AiLeaveForState());
|
|
||||||
StateController.Register(new AiSurroundState());
|
|
||||||
StateController.Register(new AiFindAmmoState());
|
|
||||||
StateController.Register(new AiAttackState());
|
|
||||||
StateController.Register(new AiAstonishedState());
|
|
||||||
StateController.Register(new AiNotifyState());
|
|
||||||
|
|
||||||
//默认状态
|
|
||||||
StateController.ChangeStateInstant(AIStateEnum.AiNormal);
|
|
||||||
|
|
||||||
//NavigationAgent2D.VelocityComputed += OnVelocityComputed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override RoleState OnCreateRoleState()
|
protected override RoleState OnCreateRoleState()
|
||||||
{
|
{
|
||||||
var roleState = new EnemyRoleState();
|
var roleState = new RoleState();
|
||||||
EnemyRoleState = roleState;
|
|
||||||
var enemyBase = GetEnemyAttribute(ActivityBase.Id).Clone();
|
var enemyBase = GetEnemyAttribute(ActivityBase.Id).Clone();
|
||||||
_enemyAttribute = enemyBase;
|
_enemyAttribute = enemyBase;
|
||||||
|
|
||||||
|
@ -161,10 +86,10 @@ public partial class Enemy : Role
|
||||||
roleState.MoveSpeed = enemyBase.MoveSpeed;
|
roleState.MoveSpeed = enemyBase.MoveSpeed;
|
||||||
roleState.Acceleration = enemyBase.Acceleration;
|
roleState.Acceleration = enemyBase.Acceleration;
|
||||||
roleState.Friction = enemyBase.Friction;
|
roleState.Friction = enemyBase.Friction;
|
||||||
roleState.ViewRange = enemyBase.ViewRange;
|
ViewRange = enemyBase.ViewRange;
|
||||||
roleState.TailAfterViewRange = enemyBase.TailAfterViewRange;
|
TailAfterViewRange = enemyBase.TailAfterViewRange;
|
||||||
roleState.BackViewRange = enemyBase.BackViewRange;
|
BackViewRange = enemyBase.BackViewRange;
|
||||||
roleState.AttackInterval = enemyBase.AttackInterval;
|
AttackInterval = enemyBase.AttackInterval;
|
||||||
|
|
||||||
roleState.Gold = Mathf.Max(0, Utils.Random.RandomConfigRange(enemyBase.Gold));
|
roleState.Gold = Mathf.Max(0, Utils.Random.RandomConfigRange(enemyBase.Gold));
|
||||||
return roleState;
|
return roleState;
|
||||||
|
@ -240,10 +165,10 @@ public partial class Enemy : Role
|
||||||
MountPoint.SetLookAt(pos);
|
MountPoint.SetLookAt(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EnemyRoleState.CanPickUpWeapon)
|
if (RoleState.CanPickUpWeapon)
|
||||||
{
|
{
|
||||||
//拾起武器操作
|
//拾起武器操作
|
||||||
EnemyPickUpWeapon();
|
DoPickUpWeapon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,276 +236,6 @@ public partial class Enemy : Role
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
|
|
||||||
/// </summary>
|
|
||||||
public bool CheckUsableWeaponInUnclaimed()
|
|
||||||
{
|
|
||||||
foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
|
|
||||||
{
|
|
||||||
//判断是否能拾起武器, 条件: 相同的房间
|
|
||||||
if (unclaimedWeapon.AffiliationArea == AffiliationArea)
|
|
||||||
{
|
|
||||||
if (!unclaimedWeapon.IsTotalAmmoEmpty())
|
|
||||||
{
|
|
||||||
if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//判断是否可以移除该标记
|
|
||||||
var enemy = unclaimedWeapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
|
|
||||||
if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
|
|
||||||
{
|
|
||||||
unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
|
|
||||||
{
|
|
||||||
unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 寻找可用的武器
|
|
||||||
/// </summary>
|
|
||||||
public Weapon FindTargetWeapon()
|
|
||||||
{
|
|
||||||
Weapon target = null;
|
|
||||||
var position = Position;
|
|
||||||
foreach (var weapon in World.Weapon_UnclaimedWeapons)
|
|
||||||
{
|
|
||||||
//判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
|
|
||||||
if (weapon.AffiliationArea == AffiliationArea)
|
|
||||||
{
|
|
||||||
//还有弹药
|
|
||||||
if (!weapon.IsTotalAmmoEmpty())
|
|
||||||
{
|
|
||||||
//查询是否有其他敌人标记要拾起该武器
|
|
||||||
if (weapon.HasSign(SignNames.AiFindWeaponSign))
|
|
||||||
{
|
|
||||||
var enemy = weapon.GetSign<Enemy>(SignNames.AiFindWeaponSign);
|
|
||||||
if (enemy == this) //就是自己标记的
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
|
|
||||||
{
|
|
||||||
weapon.RemoveSign(SignNames.AiFindWeaponSign);
|
|
||||||
}
|
|
||||||
else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
|
|
||||||
{
|
|
||||||
weapon.RemoveSign(SignNames.AiFindWeaponSign);
|
|
||||||
}
|
|
||||||
else //放弃这把武器
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target == null) //第一把武器
|
|
||||||
{
|
|
||||||
target = weapon;
|
|
||||||
}
|
|
||||||
else if (target.Position.DistanceSquaredTo(position) >
|
|
||||||
weapon.Position.DistanceSquaredTo(position)) //距离更近
|
|
||||||
{
|
|
||||||
target = weapon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取武器攻击范围 (最大距离值与最小距离的中间值)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="weight">从最小到最大距离的过渡量, 0 - 1, 默认 0.5</param>
|
|
||||||
public float GetWeaponRange(float weight = 0.5f)
|
|
||||||
{
|
|
||||||
if (WeaponPack.ActiveItem != null)
|
|
||||||
{
|
|
||||||
var attribute = WeaponPack.ActiveItem.Attribute;
|
|
||||||
return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回目标点是否在视野范围内
|
|
||||||
/// </summary>
|
|
||||||
public bool IsInViewRange(Vector2 target)
|
|
||||||
{
|
|
||||||
var isForward = IsPositionInForward(target);
|
|
||||||
if (isForward)
|
|
||||||
{
|
|
||||||
if (GlobalPosition.DistanceSquaredTo(target) <= EnemyRoleState.ViewRange * EnemyRoleState.ViewRange) //没有超出视野半径
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回目标点是否在跟随状态下的视野半径内
|
|
||||||
/// </summary>
|
|
||||||
public bool IsInTailAfterViewRange(Vector2 target)
|
|
||||||
{
|
|
||||||
var isForward = IsPositionInForward(target);
|
|
||||||
if (isForward)
|
|
||||||
{
|
|
||||||
if (GlobalPosition.DistanceSquaredTo(target) <= EnemyRoleState.TailAfterViewRange * EnemyRoleState.TailAfterViewRange) //没有超出视野半径
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
|
|
||||||
/// </summary>
|
|
||||||
public bool TestViewRayCast(Vector2 target)
|
|
||||||
{
|
|
||||||
ViewRay.Enabled = true;
|
|
||||||
ViewRay.TargetPosition = ViewRay.ToLocal(target);
|
|
||||||
ViewRay.ForceRaycastUpdate();
|
|
||||||
return ViewRay.IsColliding();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
|
|
||||||
/// </summary>
|
|
||||||
public void TestViewRayCastOver()
|
|
||||||
{
|
|
||||||
ViewRay.Enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AI 拾起武器操作
|
|
||||||
/// </summary>
|
|
||||||
private void EnemyPickUpWeapon()
|
|
||||||
{
|
|
||||||
//这几个状态不需要主动拾起武器操作
|
|
||||||
var state = StateController.CurrState;
|
|
||||||
if (state == AIStateEnum.AiNormal || state == AIStateEnum.AiNotify || state == AIStateEnum.AiAstonished || state == AIStateEnum.AiAttack)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//拾起地上的武器
|
|
||||||
if (InteractiveItem is Weapon weapon)
|
|
||||||
{
|
|
||||||
if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
|
|
||||||
{
|
|
||||||
TriggerInteractive();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//没弹药了
|
|
||||||
if (weapon.IsTotalAmmoEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
|
|
||||||
if (index != -1) //与武器背包中武器类型相同, 补充子弹
|
|
||||||
{
|
|
||||||
if (!WeaponPack.GetItem(index).IsAmmoFull())
|
|
||||||
{
|
|
||||||
TriggerInteractive();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// var index2 = Holster.FindWeapon((we, i) =>
|
|
||||||
// we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
|
|
||||||
var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
|
|
||||||
if (index2 != -1) //扔掉没子弹的武器
|
|
||||||
{
|
|
||||||
ThrowWeapon(index2);
|
|
||||||
TriggerInteractive();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (Holster.HasVacancy()) //有空位, 拾起武器
|
|
||||||
// {
|
|
||||||
// TriggerInteractive();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取锁定目标的剩余时间
|
|
||||||
/// </summary>
|
|
||||||
public float GetLockRemainderTime()
|
|
||||||
{
|
|
||||||
var weapon = WeaponPack.ActiveItem;
|
|
||||||
if (weapon == null)
|
|
||||||
{
|
|
||||||
return LockingTime - LockTargetTime;
|
|
||||||
}
|
|
||||||
return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LookTargetPosition(Vector2 pos)
|
|
||||||
{
|
|
||||||
LookTarget = null;
|
|
||||||
base.LookTargetPosition(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行移动操作
|
|
||||||
/// </summary>
|
|
||||||
public void DoMove()
|
|
||||||
{
|
|
||||||
// //计算移动
|
|
||||||
// NavigationAgent2D.MaxSpeed = EnemyRoleState.MoveSpeed;
|
|
||||||
// var nextPos = NavigationAgent2D.GetNextPathPosition();
|
|
||||||
// NavigationAgent2D.Velocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
|
|
||||||
|
|
||||||
AnimatedSprite.Play(AnimatorNames.Run);
|
|
||||||
//计算移动
|
|
||||||
var nextPos = NavigationAgent2D.GetNextPathPosition();
|
|
||||||
BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行站立操作
|
|
||||||
/// </summary>
|
|
||||||
public void DoIdle()
|
|
||||||
{
|
|
||||||
AnimatedSprite.Play(AnimatorNames.Idle);
|
|
||||||
BasisVelocity = Vector2.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新房间中标记的目标位置
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateMarkTargetPosition()
|
|
||||||
{
|
|
||||||
if (LookTarget != null)
|
|
||||||
{
|
|
||||||
AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从标记出生时调用, 预加载波不会调用
|
/// 从标记出生时调用, 预加载波不会调用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -590,13 +245,4 @@ public partial class Enemy : Role
|
||||||
StateController.Enable = false;
|
StateController.Enable = false;
|
||||||
this.CallDelay(0.7f, () => StateController.Enable = true);
|
this.CallDelay(0.7f, () => StateController.Enable = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void OnVelocityComputed(Vector2 velocity)
|
|
||||||
// {
|
|
||||||
// if (Mathf.Abs(velocity.X) >= 0.01f && Mathf.Abs(velocity.Y) >= 0.01f)
|
|
||||||
// {
|
|
||||||
// AnimatedSprite.Play(AnimatorNames.Run);
|
|
||||||
// BasisVelocity = velocity;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
|
|
||||||
public class EnemyRoleState : RoleState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
|
|
||||||
/// </summary>
|
|
||||||
public float ViewRange = 250;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发现玩家后跟随玩家的视野半径
|
|
||||||
/// </summary>
|
|
||||||
public float TailAfterViewRange = 400;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 背后的视野半径, 单位像素
|
|
||||||
/// </summary>
|
|
||||||
public float BackViewRange = 50;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 攻击间隔时间, 秒
|
|
||||||
/// </summary>
|
|
||||||
public float AttackInterval = 0;
|
|
||||||
}
|
|
|
@ -82,7 +82,7 @@ public partial class NoWeaponEnemy : Enemy
|
||||||
{
|
{
|
||||||
if (name == AnimatorNames.Attack)
|
if (name == AnimatorNames.Attack)
|
||||||
{
|
{
|
||||||
AttackTimer = EnemyRoleState.AttackInterval;
|
AttackTimer = AttackInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ using Godot;
|
||||||
/// 商店老板
|
/// 商店老板
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Tool]
|
[Tool]
|
||||||
public partial class ShopBoss : Role
|
public partial class ShopBoss : AiRole
|
||||||
{
|
{
|
||||||
public override void OnCreateWithMark(RoomPreinstall roomPreinstall, ActivityMark activityMark)
|
public override void OnCreateWithMark(RoomPreinstall roomPreinstall, ActivityMark activityMark)
|
||||||
{
|
{
|
Loading…
Reference in New Issue
Block a user