diff --git a/DungeonShooting_Godot/scene/Main.tscn b/DungeonShooting_Godot/scene/Main.tscn
index aff7b6d..35a4274 100644
--- a/DungeonShooting_Godot/scene/Main.tscn
+++ b/DungeonShooting_Godot/scene/Main.tscn
@@ -24,6 +24,7 @@
[node name="Main" type="Node2D"]
script = ExtResource( 3 )
+Debug = true
CursorPack = ExtResource( 4 )
RoomPath = NodePath("ViewCanvas/ViewportContainer/Viewport/Room")
ViewportPath = NodePath("ViewCanvas/ViewportContainer/Viewport")
diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs
index 530d06a..a527242 100644
--- a/DungeonShooting_Godot/src/framework/ActivityObject.cs
+++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs
@@ -13,17 +13,17 @@
/// 是否是调试模式
///
public static bool IsDebug { get; set; }
-
+
///
/// 当前物体类型id, 用于区分是否是同一种物体, 如果不是通过 ActivityObject.Create() 函数创建出来的对象那么 ItemId 为 null
///
public string ItemId { get; private set; }
-
+
///
/// 是否放入 ySort 节点下
///
public bool UseYSort { get; }
-
+
///
/// 当前物体显示的精灵图像, 节点名称必须叫 "AnimatedSprite", 类型为 AnimatedSprite
///
@@ -44,7 +44,7 @@
///
///
public AnimationPlayer AnimationPlayer { get; }
-
+
///
/// 是否调用过 Destroy() 函数
///
@@ -69,6 +69,9 @@
//存储投抛该物体时所产生的数据
private ObjectThrowData _throwData;
+ //标记字典
+ private Dictionary _signMap;
+
public ActivityObject(string scenePath)
{
//加载预制体
@@ -130,9 +133,10 @@
//切换阴影动画
ShadowSprite.Texture = AnimatedSprite.Frames.GetFrame(anim, frame);
}
+
_prevAnimation = anim;
_prevAnimationFrame = frame;
-
+
CalcShadow();
ShadowSprite.Visible = true;
}
@@ -507,20 +511,21 @@
//切换阴影动画
ShadowSprite.Texture = AnimatedSprite.Frames.GetFrame(anim, AnimatedSprite.Frame);
}
+
_prevAnimation = anim;
_prevAnimationFrame = frame;
-
+
//计算阴影
CalcShadow();
}
-
+
//调试绘制
if (IsDebug)
{
Update();
}
}
-
+
public override void _PhysicsProcess(float delta)
{
//更新组件
@@ -584,7 +589,7 @@
}
}
-
+
///
/// 销毁物体
///
@@ -596,7 +601,7 @@
}
IsDestroyed = true;
-
+
OnDestroy();
QueueFree();
var arr = _components.ToArray();
@@ -729,6 +734,72 @@
}
///
+ /// 设置标记, 用于在物体上记录自定义数据
+ ///
+ /// 标记名称
+ /// 存入值
+ public void SetSign(string name, object v)
+ {
+ if (_signMap == null)
+ {
+ _signMap = new Dictionary();
+ }
+
+ _signMap[name] = v;
+ }
+
+ ///
+ /// 返回是否存在指定名称的标记数据
+ ///
+ public bool HasSign(string name)
+ {
+ return _signMap == null ? false : _signMap.ContainsKey(name);
+ }
+
+ ///
+ /// 根据名称获取标记值
+ ///
+ public object GetSign(string name)
+ {
+ if (_signMap == null)
+ {
+ return null;
+ }
+
+ _signMap.TryGetValue(name, out var value);
+ return value;
+ }
+
+ ///
+ /// 根据名称获取标记值
+ ///
+ public T GetSign(string name)
+ {
+ if (_signMap == null)
+ {
+ return default;
+ }
+
+ _signMap.TryGetValue(name, out var value);
+ if (value is T v)
+ {
+ return v;
+ }
+ return default;
+ }
+
+ ///
+ /// 根据名称删除标记
+ ///
+ public void RemoveSign(string name)
+ {
+ if (_signMap != null)
+ {
+ _signMap.Remove(name);
+ }
+ }
+
+ ///
/// 通过 ItemId 实例化 ActivityObject 对象
///
public static T Create(string itemId) where T : ActivityObject
diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
index 297cfd3..a870c8b 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
@@ -1,5 +1,6 @@
using Godot;
using System;
+using System.Collections.Generic;
///
/// 武器的基类
@@ -7,6 +8,11 @@
public abstract class Weapon : ActivityObject
{
///
+ /// 所有被扔在地上的武器
+ ///
+ public static readonly HashSet UnclaimedWeapons = new HashSet();
+
+ ///
/// 武器的类型 id
///
public string TypeId { get; }
@@ -279,6 +285,24 @@
return 1;
}
+ public override void _EnterTree()
+ {
+ base._EnterTree();
+
+ //收集落在地上的武器
+ if (Master == null && GetParent() == GameApplication.Instance.Room.GetRoot(false))
+ {
+ UnclaimedWeapons.Add(this);
+ }
+ }
+
+ public override void _ExitTree()
+ {
+ base._ExitTree();
+
+ UnclaimedWeapons.Remove(this);
+ }
+
public override void _Process(float delta)
{
base._Process(delta);
@@ -927,12 +951,14 @@
ZIndex = 0;
//禁用碰撞
CollisionShape2D.Disabled = true;
+ //清除 Ai 拾起标记
+ RemoveSign(AiFindAmmoState.AiFindWeaponSign);
OnPickUp(master);
}
///
/// 触发移除, 这个函数由 Holster 对象调用
- /// a
+ ///
public void Remove()
{
Master = null;
@@ -961,4 +987,14 @@
HideShadowSprite();
OnConceal();
}
+
+ //-------------------------------- Ai相关 -----------------------------
+
+ ///
+ /// 获取 Ai 对于该武器的评分, 评分越高, 代表 Ai 会越优先选择该武器, 如果为 -1, 则表示 Ai 不会使用该武器
+ ///
+ public float GetAiScore()
+ {
+ return 1;
+ }
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs b/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
index 4a6c8aa..39551b1 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
@@ -165,4 +165,9 @@
/// 开火后武器口角度恢复速度倍数
///
public float UpliftAngleRestore = 1f;
+
+ //------------------------------ Ai相关 -----------------------------
+
+ //public bool Ai
+
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/StateController.cs b/DungeonShooting_Godot/src/game/role/StateController.cs
index ce64474..969dcdf 100644
--- a/DungeonShooting_Godot/src/game/role/StateController.cs
+++ b/DungeonShooting_Godot/src/game/role/StateController.cs
@@ -138,7 +138,6 @@
_isChangeState = !late;
var prev = CurrStateBase.State;
CurrStateBase.Exit(next);
- GD.Print("nextState => " + next);
CurrStateBase = newState;
CurrStateBase.Enter(prev, arg);
}
diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
index 773a1f9..74bd59d 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs
@@ -166,7 +166,14 @@
}
else //所有子弹打光, 去寻找武器
{
- //StateController.ChangeStateLate(AiStateEnum.AiFindAmmo);
+ //如果存在有子弹的武器
+ foreach (var unclaimedWeapon in Weapon.UnclaimedWeapons)
+ {
+ if (!unclaimedWeapon.IsTotalAmmoEmpty())
+ {
+ StateController.ChangeStateLate(AiStateEnum.AiFindAmmo);
+ }
+ }
}
}
else if (weapon.Reloading) //换弹中
@@ -268,6 +275,13 @@
///
private void EnemyPickUpWeapon()
{
+ //这几个状态不需要主动拾起武器操作
+ var state = StateController.CurrState;
+ if (state == AiStateEnum.AiNormal)
+ {
+ return;
+ }
+
//拾起地上的武器
if (InteractiveItem is Weapon weapon)
{
@@ -296,7 +310,7 @@
var index2 = Holster.FindWeapon(we =>
we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
- if (index2 != -1) //则扔掉没子弹的武器
+ if (index2 != -1) //扔掉没子弹的武器
{
ThrowWeapon(index2);
TriggerInteractive();
diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs
index 5ef4304..182dd06 100644
--- a/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs
+++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs
@@ -1,4 +1,5 @@
+using System.Linq;
using Godot;
///
@@ -6,22 +7,161 @@
///
public class AiFindAmmoState : StateBase
{
+ ///
+ /// Ai 对武器的标记名称, 一旦有该标记, Ai AiFindAmmoState 状态下寻找可用武器将忽略该武器
+ ///
+ public const string AiFindWeaponSign = "AiFindWeaponSign";
+
+
+ private Weapon _target;
+
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 1f;
+
public AiFindAmmoState() : base(AiStateEnum.AiFindAmmo)
{
}
public override void Enter(AiStateEnum prev, params object[] args)
{
- GD.Print("寻找武器");
+ _navigationUpdateTimer = 0;
+ FindTargetWeapon();
+ if (_target == null)
+ {
+ ChangeStateLate(prev);
+ return;
+ }
+
+ //标记武器
+ _target.SetSign(AiFindWeaponSign, Master);
}
public override void PhysicsProcess(float delta)
{
+ var activeWeapon = Master.Holster.ActiveWeapon;
+ if (activeWeapon != null && !activeWeapon.IsTotalAmmoEmpty()) //已经有弹药了
+ {
+ ChangeStateLate(AiStateEnum.AiNormal);
+ return;
+ }
+ if (_target != null)
+ {
+ //更新目标位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ var position = _target.GlobalPosition;
+ if (Master.NavigationAgent2D.GetTargetLocation() != position)
+ {
+ Master.NavigationAgent2D.SetTargetLocation(position);
+ }
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ //枪口指向玩家
+ Master.LookTargetPosition(Player.Current.GlobalPosition);
+
+ if (_target.IsQueuedForDeletion() || _target.IsTotalAmmoEmpty()) //已经被销毁, 或者弹药已经被其他角色捡走
+ {
+ //再去寻找其他武器
+ FindTargetWeapon();
+
+ if (_target == null) //也没有其他可用的武器了
+ {
+ ChangeStateLate(AiStateEnum.AiNormal);
+ }
+ }
+ else if (_target.Master == Master) //已经被自己拾起
+ {
+ ChangeStateLate(AiStateEnum.AiNormal);
+ }
+ else if (_target.Master != null) //武器已经被其他角色拾起!
+ {
+ //再去寻找其他武器
+ FindTargetWeapon();
+
+ if (_target == null) //也没有其他可用的武器了
+ {
+ ChangeStateLate(AiStateEnum.AiNormal);
+ }
+ }
+ else
+ {
+ //向武器移动
+ if (!Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ //计算移动
+ var nextPos = Master.NavigationAgent2D.GetNextLocation();
+ Master.AnimatedSprite.Animation = AnimatorNames.Run;
+ Master.Velocity =
+ (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() *
+ Master.MoveSpeed;
+ Master.CalcMove(delta);
+ }
+ }
+ }
+ else
+ {
+
+ }
}
public override void DebugDraw()
{
-
+ if (_target != null)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(_target.GlobalPosition), Colors.Purple);
+ }
+ }
+
+ private void FindTargetWeapon()
+ {
+ _target = null;
+ var position = Master.Position;
+ foreach (var weapon in Weapon.UnclaimedWeapons)
+ {
+ if (!weapon.IsTotalAmmoEmpty())
+ {
+ //查询是否有其他敌人标记要拾起该武器
+ if (weapon.HasSign(AiFindWeaponSign))
+ {
+ var enemy = weapon.GetSign(AiFindWeaponSign);
+ if (enemy == Master) //就是自己标记的
+ {
+
+ }
+ else if (enemy == null || enemy.IsQueuedForDeletion()) //标记当前武器的敌人已经被销毁
+ {
+ weapon.RemoveSign(AiFindWeaponSign);
+ }
+ else //放弃这把武器
+ {
+ continue;
+ }
+ }
+
+ if (_target == null) //第一把武器
+ {
+ _target = weapon;
+ }
+ else if (_target.Position.DistanceSquaredTo(position) >
+ weapon.Position.DistanceSquaredTo(position)) //距离更近
+ {
+ _target = weapon;
+ }
+ }
+ }
+
+ //设置目标点
+ if (_target != null)
+ {
+ Master.NavigationAgent2D.SetTargetLocation(_target.GlobalPosition);
+ }
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs
index c08cbcd..9cb6d31 100644
--- a/DungeonShooting_Godot/src/game/room/RoomManager.cs
+++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs
@@ -83,7 +83,7 @@
var enemy1 = new Enemy();
enemy1.Name = "Enemy";
enemy1.PutDown(new Vector2(150, 300));
- enemy1.PickUpWeapon(WeaponManager.GetGun("1003"));
+ //enemy1.PickUpWeapon(WeaponManager.GetGun("1003"));
enemy1.PickUpWeapon(WeaponManager.GetGun("1001"));
// for (int i = 0; i < 10; i++)
@@ -109,15 +109,15 @@
// enemy3.PickUpWeapon(WeaponManager.GetGun("1002"));
// WeaponManager.GetGun("1001").PutDown(new Vector2(80, 100));
- // WeaponManager.GetGun("1001").PutDown(new Vector2(80, 80));
+ WeaponManager.GetGun("1001").PutDown(new Vector2(80, 80));
// WeaponManager.GetGun("1002").PutDown(new Vector2(80, 120));
// WeaponManager.GetGun("1003").PutDown(new Vector2(120, 80));
WeaponManager.GetGun("1003").PutDown(new Vector2(180, 80));
- WeaponManager.GetGun("1003").PutDown(new Vector2(180, 180));
+ //WeaponManager.GetGun("1003").PutDown(new Vector2(180, 180));
WeaponManager.GetGun("1002").PutDown(new Vector2(180, 120));
- WeaponManager.GetGun("1004").PutDown(new Vector2(220, 120));
+ //WeaponManager.GetGun("1004").PutDown(new Vector2(220, 120));
}
public override void _Process(float delta)