diff --git a/DungeonShooting_Godot/prefab/role/Player.tscn b/DungeonShooting_Godot/prefab/role/Player.tscn
index 927bc6b..fb6b78b 100644
--- a/DungeonShooting_Godot/prefab/role/Player.tscn
+++ b/DungeonShooting_Godot/prefab/role/Player.tscn
@@ -4,11 +4,10 @@
[ext_resource path="res://src/game/role/Player.cs" type="Script" id=2]
[ext_resource path="res://prefab/weapon/Weapon.tscn" type="PackedScene" id=4]
-
[node name="Player" instance=ExtResource( 1 )]
collision_layer = 8
script = ExtResource( 2 )
GunPrefab = ExtResource( 4 )
[node name="AnimatedSprite" parent="." index="1"]
-frame = 1
+frame = 3
diff --git a/DungeonShooting_Godot/prefab/weapon/Weapon.tscn b/DungeonShooting_Godot/prefab/weapon/Weapon.tscn
index d730340..c095c86 100644
--- a/DungeonShooting_Godot/prefab/weapon/Weapon.tscn
+++ b/DungeonShooting_Godot/prefab/weapon/Weapon.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=7 format=2]
+[gd_scene load_steps=8 format=2]
[ext_resource path="res://resource/materlal/Shadow.gdshader" type="Shader" id=2]
@@ -71,7 +71,9 @@
[sub_resource type="RectangleShape2D" id=1]
extents = Vector2( 7.8, 3.5 )
-[node name="Weapon" type="Node2D"]
+[sub_resource type="RectangleShape2D" id=10]
+
+[node name="Weapon" type="KinematicBody2D"]
[node name="WeaponBody" type="Area2D" parent="."]
collision_layer = 4
@@ -101,3 +103,7 @@
[node name="Collision" type="CollisionShape2D" parent="WeaponBody"]
position = Vector2( 0.59999, 0.199997 )
shape = SubResource( 1 )
+
+[node name="Collision" type="CollisionShape2D" parent="."]
+shape = SubResource( 10 )
+disabled = true
diff --git a/DungeonShooting_Godot/prefab/weapon/bullet/HighSpeedBullet.tscn b/DungeonShooting_Godot/prefab/weapon/bullet/HighSpeedBullet.tscn
index e309e2f..51eaeb8 100644
--- a/DungeonShooting_Godot/prefab/weapon/bullet/HighSpeedBullet.tscn
+++ b/DungeonShooting_Godot/prefab/weapon/bullet/HighSpeedBullet.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=5 format=2]
-[ext_resource path="res://src/game/props/weapon/bullet/HighSpeedBullet.cs" type="Script" id=1]
+[ext_resource path="res://src/game/item/weapon/bullet/HighSpeedBullet.cs" type="Script" id=1]
[ext_resource path="res://prefab/effect/Hit.tscn" type="PackedScene" id=2]
diff --git a/DungeonShooting_Godot/prefab/weapon/bullet/OrdinaryBullets.tscn b/DungeonShooting_Godot/prefab/weapon/bullet/OrdinaryBullets.tscn
index 6297774..11c59c4 100644
--- a/DungeonShooting_Godot/prefab/weapon/bullet/OrdinaryBullets.tscn
+++ b/DungeonShooting_Godot/prefab/weapon/bullet/OrdinaryBullets.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=4 format=2]
-[ext_resource path="res://src/game/props/weapon/bullet/OrdinaryBullets.cs" type="Script" id=1]
+[ext_resource path="res://src/game/item/weapon/bullet/OrdinaryBullets.cs" type="Script" id=1]
[ext_resource path="res://resource/sprite/bullet/bullet.png" type="Texture" id=2]
[ext_resource path="res://prefab/effect/Hit.tscn" type="PackedScene" id=3]
diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs
index b65f714..dbf9cc7 100644
--- a/DungeonShooting_Godot/src/framework/ActivityObject.cs
+++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs
@@ -1,22 +1,31 @@
+using System;
using Godot;
///
/// 房间内活动物体基类
///
-public abstract class ActivityObject : ActivityObject where T : KinematicBody2D
+public abstract class ActivityObject : ActivityObject where T : ActivityObject
{
public ActivityObject()
{
- ComponentControl = CreateComponentControl();
+ ComponentControl = (ComponentControl)Activator.CreateInstance(typeof(ComponentControl), this);
+ Sprite = GetNodeOrNull("Sprite");
+ if (Sprite == null)
+ {
+ GD.PrintErr("ActivityObject节点下必须要有一个'Sprite'节点!");
+ }
+ Collision = GetNodeOrNull("Collision");
+ if (Collision == null)
+ {
+ GD.PrintErr("ActivityObject节点下必须要有一个'Collision'节点!");
+ }
}
-
+
///
/// 组件管理器
///
- public ComponentControl ComponentControl { get; }
-
- public abstract ComponentControl CreateComponentControl();
+ public new ComponentControl ComponentControl { get; }
}
///
@@ -24,15 +33,31 @@
///
public abstract class ActivityObject : KinematicBody2D
{
+
+ ///
+ /// 组件管理器
+ ///
+ public ComponentControl ComponentControl { get; }
+
+ ///
+ /// 当前物体显示的精灵图像, 节点名称必须叫 "Sprite"
+ ///
+ public Sprite Sprite { get; protected set; }
+
+ ///
+ /// 当前物体碰撞器节点, 节点名称必须叫 "Collision"
+ ///
+ public CollisionShape2D Collision { get; protected set; }
+
///
/// 返回是否能与其他ActivityObject互动
///
/// 触发者
- public abstract CheckInteractiveResult CheckInteractive(ActivityObject master) where TU : KinematicBody2D;
+ public abstract CheckInteractiveResult CheckInteractive(ActivityObject master) where TU : ActivityObject;
///
/// 与其它ActivityObject互动时调用
///
/// 触发者
- public abstract void Interactive(ActivityObject master) where TU : KinematicBody2D;
+ public abstract void Interactive(ActivityObject master) where TU : ActivityObject;
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/Component.cs b/DungeonShooting_Godot/src/framework/Component.cs
index acace94..882ccad 100644
--- a/DungeonShooting_Godot/src/framework/Component.cs
+++ b/DungeonShooting_Godot/src/framework/Component.cs
@@ -4,9 +4,9 @@
///
/// 组件基类
///
-public abstract class Component : Component where TG : KinematicBody2D
+public abstract class Component : Component where TG : ActivityObject
{
- public ComponentControl ComponentControl { get; private set; }
+ public new ComponentControl ComponentControl { get; private set; }
public Vector2 Position
{
@@ -25,6 +25,10 @@
get => ComponentControl.Visible;
set => ComponentControl.Visible = value;
}
+
+ public Sprite Sprite => ComponentControl.Sprite;
+
+ public CollisionShape2D Collision => ComponentControl.Collision;
public bool Enable { get; set; } = true;
@@ -33,7 +37,7 @@
///
/// 当组件销毁
///
- public override void Destroy()
+ public new void Destroy()
{
if (IsDestroyed)
{
@@ -60,18 +64,20 @@
///
public abstract class Component : IProcess, IDestroy
{
+ public ComponentControl ComponentControl { get; }
+
///
- /// 该组件所绑定的GameObject的坐标
+ /// 该组件所绑定的ComponentControl的坐标
///
Vector2 Position { get; set; }
///
- /// 该组件所绑定的GameObject的全局坐标
+ /// 该组件所绑定的ComponentControl的全局坐标
///
Vector2 GlobalPosition { get; set; }
///
- /// 该组件所绑定的GameObject的显示状态
+ /// 该组件所绑定的ComponentControl的显示状态
///
bool Visible { get; set; }
@@ -80,9 +86,23 @@
///
bool Enable { get; set; }
+ ///
+ /// 该组件所绑定的ComponentControl显示的精灵
+ ///
+ Sprite Sprite { get; }
+
+ ///
+ /// 该组件所绑定的ComponentControl的碰撞器
+ ///
+ CollisionShape2D Collision { get; }
+
public bool IsDestroyed { get; }
private bool _isReady = false;
+
+ public Component()
+ {
+ }
///
/// 第一次调用 Process 或 PhysicsProcess 之前调用
@@ -116,8 +136,10 @@
public virtual void OnUnMount()
{
}
-
- public abstract void Destroy();
+
+ public void Destroy()
+ {
+ }
internal void _TriggerProcess(float delta)
{
diff --git a/DungeonShooting_Godot/src/framework/ComponentControl.cs b/DungeonShooting_Godot/src/framework/ComponentControl.cs
index e3c548c..b1beb8e 100644
--- a/DungeonShooting_Godot/src/framework/ComponentControl.cs
+++ b/DungeonShooting_Godot/src/framework/ComponentControl.cs
@@ -1,8 +1,10 @@
+using System;
using System.Collections.Generic;
using Godot;
-public class ComponentControl : Node, IDestroy where T : KinematicBody2D
+public class ComponentControl : Node, IDestroy where T : ActivityObject
{
+
public float Altitude { get; set; }
public Vector2 Position
@@ -23,11 +25,15 @@
set => Node.Visible = value;
}
+ public Sprite Sprite => Node.Sprite;
+
+ public CollisionShape2D Collision => Node.Collision;
+
public bool IsDestroyed { get; private set; }
public T Node { get; private set; }
- private List> _components = new List>();
+ private List>> _components = new List>>();
public ComponentControl(T node)
{
@@ -36,41 +42,11 @@
node.AddChild(this);
}
- public void AddComponent(NodeComponent nodeComponent) where TN : Node
- {
- if (!_components.Contains(nodeComponent))
- {
- _components.Add(nodeComponent);
- nodeComponent.SetGameObject(this);
- var parent = nodeComponent.Node.GetParent();
- if (parent == null)
- {
- Node.AddChild(nodeComponent.Node);
- }
- else if (parent != Node)
- {
- parent.RemoveChild(nodeComponent.Node);
- Node.AddChild(nodeComponent.Node);
- }
- nodeComponent.OnMount();
- }
- }
-
- public void RemoveComponent(NodeComponent nodeComponent) where TN : Node
- {
- if (_components.Remove(nodeComponent))
- {
- nodeComponent.SetGameObject(null);
- Node.RemoveChild(nodeComponent.Node);
- nodeComponent.OnUnMount();
- }
- }
-
public void AddComponent(Component component)
{
- if (!_components.Contains(component))
+ if (!ContainsComponent(component))
{
- _components.Add(component);
+ _components.Add(new KeyValuePair>(component.GetType(), component));
component.SetGameObject(this);
component.OnMount();
}
@@ -78,20 +54,41 @@
public void RemoveComponent(Component component)
{
- if (_components.Remove(component))
+ if (ContainsComponent(component))
{
component.SetGameObject(null);
component.OnUnMount();
}
}
+ public Component GetComponent(Type type)
+ {
+ for (int i = 0; i < _components.Count; i++)
+ {
+ var temp = _components[i];
+ if (temp.Key == type)
+ {
+ return temp.Value;
+ }
+ }
+
+ return null;
+ }
+
+ public TC GetComponent() where TC : Component
+ {
+ var component = GetComponent(typeof(TC));
+ if (component == null) return null;
+ return (TC)component;
+ }
+
public override void _Process(float delta)
{
var arr = _components.ToArray();
for (int i = 0; i < arr.Length; i++)
{
if (IsDestroyed) return;
- var temp = arr[i];
+ var temp = arr[i].Value;
if (temp != null && temp.ComponentControl == this && temp.Enable)
{
temp._TriggerProcess(delta);
@@ -105,7 +102,7 @@
for (int i = 0; i < arr.Length; i++)
{
if (IsDestroyed) return;
- var temp = arr[i];
+ var temp = arr[i].Value;
if (temp != null && temp.ComponentControl == this && temp.Enable)
{
temp._TriggerPhysicsProcess(delta);
@@ -125,7 +122,19 @@
var arr = _components.ToArray();
for (int i = 0; i < arr.Length; i++)
{
- arr[i].Destroy();
+ arr[i].Value?.Destroy();
}
}
+
+ private bool ContainsComponent(Component component)
+ {
+ for (int i = 0; i < _components.Count; i++)
+ {
+ if (_components[i].Value == component)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/NodeComponent.cs b/DungeonShooting_Godot/src/framework/NodeComponent.cs
deleted file mode 100644
index 4ae2121..0000000
--- a/DungeonShooting_Godot/src/framework/NodeComponent.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Godot;
-
-public abstract class NodeComponent : Component where TN : Node where TG : KinematicBody2D
-{
- public TN Node { get; }
-
- public NodeComponent(TN inst)
- {
- Node = inst;
- }
-
- public override void OnDestroy()
- {
- Node.QueueFree();
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/common/NodeExtend.cs b/DungeonShooting_Godot/src/game/common/NodeExtend.cs
index 9f1242b..8cb5c41 100644
--- a/DungeonShooting_Godot/src/game/common/NodeExtend.cs
+++ b/DungeonShooting_Godot/src/game/common/NodeExtend.cs
@@ -104,4 +104,20 @@
}
return null;
}
+
+ public static T StartThrow(this ActivityObject node, Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed, float ySpeed, float rotate) where T : ThrowComponent
+ {
+ T throwNode = node.ComponentControl.GetComponent();
+ if (throwNode == null)
+ {
+ throwNode = Activator.CreateInstance();
+ node.ComponentControl.AddComponent(throwNode);
+ }
+ else
+ {
+ throwNode.StopThrow();
+ }
+ throwNode.StartThrow(size, start, startHeight, direction, xSpeed, ySpeed, rotate);
+ return throwNode;
+ }
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/effect/ThrowNode.cs b/DungeonShooting_Godot/src/game/effect/ThrowNode.cs
index 5073764..a5ebe35 100644
--- a/DungeonShooting_Godot/src/game/effect/ThrowNode.cs
+++ b/DungeonShooting_Godot/src/game/effect/ThrowNode.cs
@@ -148,7 +148,8 @@
/// 旋转速度
/// 需要挂载的节点
/// 抛射的节点显示的纹理, 用于渲染阴影用
- public virtual void StartThrow(Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed, float ySpeed, float rotate, Node2D mount, Sprite shadowTarget)
+ public virtual void StartThrow(Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed,
+ float ySpeed, float rotate, Node2D mount, Sprite shadowTarget)
{
StartThrow(size, start, startHeight, direction, xSpeed, ySpeed, rotate, mount);
ShadowTarget = shadowTarget;
@@ -163,6 +164,7 @@
ShadowSprite.Material = ResourceManager.ShadowMaterial;
AddChild(ShadowSprite);
}
+
inversionX = mount.Scale.y < 0 ? true : false;
if (inversionX)
{
@@ -172,6 +174,7 @@
{
ShadowSprite.Scale = shadowTarget.Scale;
}
+
ShadowSprite.Texture = shadowTarget.Texture;
}
else if (ShadowSprite != null)
diff --git a/DungeonShooting_Godot/src/game/item/CheckInteractiveResult.cs b/DungeonShooting_Godot/src/game/item/CheckInteractiveResult.cs
new file mode 100644
index 0000000..eb8676b
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/CheckInteractiveResult.cs
@@ -0,0 +1,28 @@
+
+///
+/// 检测互动返回的数据集
+///
+public class CheckInteractiveResult
+{
+ ///
+ /// 互动物体
+ ///
+ public ActivityObject Target;
+ ///
+ /// 是否可以互动
+ ///
+ public bool CanInteractive;
+ ///
+ /// 互动提示信息
+ ///
+ public string Message;
+ ///
+ /// 互动提示显示的图标
+ ///
+ public string ShowIcon;
+
+ public CheckInteractiveResult(ActivityObject target)
+ {
+ Target = target;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/ThrowComponent.cs b/DungeonShooting_Godot/src/game/item/ThrowComponent.cs
new file mode 100644
index 0000000..6b0d129
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/ThrowComponent.cs
@@ -0,0 +1,21 @@
+
+using Godot;
+
+public class ThrowComponent : Component
+{
+ public override void Ready()
+ {
+
+ }
+
+ public virtual void StartThrow(Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed,
+ float ySpeed, float rotate)
+ {
+
+ }
+
+ public virtual void StopThrow()
+ {
+
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/item/package/Holster.cs b/DungeonShooting_Godot/src/game/item/package/Holster.cs
new file mode 100644
index 0000000..bbe7c9b
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/package/Holster.cs
@@ -0,0 +1,272 @@
+using Godot;
+
+///
+/// 角色身上的武器袋, 存储角色携带的武器
+///
+public class Holster
+{
+ ///
+ /// 插槽类
+ ///
+ public class WeaponSlot
+ {
+ ///
+ /// 是否启用
+ ///
+ public bool Enable = false;
+ ///
+ /// 当前插槽存放的武器类型
+ ///
+ public WeaponWeightType Type = WeaponWeightType.MainWeapon;
+ ///
+ /// 插槽存放的武器
+ ///
+ public Weapon Weapon;
+ }
+
+ ///
+ /// 归属者
+ ///
+ public Role Master { get; }
+
+ ///
+ /// 当前使用的武器对象
+ ///
+ public Weapon ActiveWeapon { get; private set; }
+
+ ///
+ /// 当前使用的武器的索引
+ ///
+ public int ActiveIndex { get; private set; } = 0;
+
+ public WeaponSlot[] SlotList { get; } = new WeaponSlot[4];
+
+ public Holster(Role master)
+ {
+ Master = master;
+
+ //创建武器的插槽, 默认前两个都是启用的
+ WeaponSlot slot1 = new WeaponSlot();
+ slot1.Enable = true;
+ SlotList[0] = slot1;
+
+ WeaponSlot slot2 = new WeaponSlot();
+ slot2.Enable = true;
+ slot2.Type = WeaponWeightType.DeputyWeapon;
+ SlotList[1] = slot2;
+
+ WeaponSlot slot3 = new WeaponSlot();
+ SlotList[2] = slot3;
+
+ WeaponSlot slot4 = new WeaponSlot();
+ slot4.Type = WeaponWeightType.DeputyWeapon;
+ SlotList[3] = slot4;
+ }
+
+ ///
+ /// 根据索引获取武器
+ ///
+ public Weapon GetWeapon(int index) {
+ if (index >= SlotList.Length)
+ {
+ return null;
+ }
+ return SlotList[index].Weapon;
+ }
+
+ ///
+ /// 根据武器id查找武器袋中该武器所在的位置, 如果没有, 则返回 -1
+ ///
+ /// 武器id
+ public int FindWeapon(string id)
+ {
+ for (int i = 0; i < SlotList.Length; i++)
+ {
+ var item = SlotList[i];
+ if (item.Weapon != null && item.Weapon.Id == id)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// 返回是否能放入武器
+ ///
+ /// 武器对象
+ public bool CanPickupWeapon(Weapon weapon)
+ {
+ for (int i = 0; i < SlotList.Length; i++)
+ {
+ var item = SlotList[i];
+ if (item.Enable && weapon.Attribute.WeightType == item.Type && item.Weapon == null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 拾起武器, 存入武器袋中, 返回存放在武器袋的位置, 如果容不下这把武器, 则会返回 -1
+ ///
+ /// 武器对象
+ public int PickupWeapon(Weapon weapon)
+ {
+ for (int i = 0; i < SlotList.Length; i++)
+ {
+ var item = SlotList[i];
+ if (item.Enable && weapon.Attribute.WeightType == item.Type && item.Weapon == null)
+ {
+ weapon.Pickup();
+ item.Weapon = weapon;
+ ExchangeByIndex(i);
+ weapon._PickUpWeapon(Master);
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// 移除指定位置的武器, 并返回这个武器对象, 如果移除正在使用的这把武器, 则会自动切换到上一把武器
+ ///
+ /// 所在武器袋的位置索引
+ public Weapon RmoveWeapon(int index)
+ {
+ if (index < 0 || index >= SlotList.Length)
+ {
+ return null;
+ }
+ var slot = SlotList[index];
+ if (slot.Weapon == null)
+ {
+ return null;
+ }
+ var weapon = slot.Weapon;
+ weapon.GetParent().RemoveChild(weapon);
+ slot.Weapon = null;
+
+ //如果是当前手持的武器, 就需要调用切换武器操作
+ if (index == ActiveIndex)
+ {
+ //没有其他武器了
+ if (ExchangePrev() == index)
+ {
+ ActiveIndex = 0;
+ ActiveWeapon = null;
+ }
+ }
+ weapon._ThrowOutWeapon();
+ return weapon;
+ }
+
+ ///
+ /// 切换到上一个武器
+ ///
+ public int ExchangePrev()
+ {
+ var index = ActiveIndex - 1;
+ do
+ {
+ if (index < 0)
+ {
+ index = SlotList.Length - 1;
+ }
+ if (ExchangeByIndex(index))
+ {
+ return index;
+ }
+ } while (index-- != ActiveIndex);
+ return -1;
+ }
+
+ ///
+ /// 切换到下一个武器,
+ ///
+ public int ExchangeNext()
+ {
+ var index = ActiveIndex + 1;
+ do
+ {
+ if (index >= SlotList.Length)
+ {
+ index = 0;
+ }
+ if (ExchangeByIndex(index))
+ {
+ return index;
+ }
+ }
+ while (index++ != ActiveIndex);
+ return -1;
+ }
+
+ ///
+ /// 切换到指定索引的武器
+ ///
+ public bool ExchangeByIndex(int index)
+ {
+ if (index == ActiveIndex && ActiveWeapon != null) return true;
+ if (index < 0 || index > SlotList.Length) return false;
+ var slot = SlotList[index];
+ if (slot == null || slot.Weapon == null) return false;
+
+ //将上一把武器放到背后
+ if (ActiveWeapon != null)
+ {
+ var tempParent = ActiveWeapon.GetParentOrNull();
+ if (tempParent != null)
+ {
+ tempParent.RemoveChild(ActiveWeapon);
+ Master.BackMountPoint.AddChild(ActiveWeapon);
+ if (ActiveIndex == 0)
+ {
+ ActiveWeapon.Position = new Vector2(0, 5);
+ ActiveWeapon.RotationDegrees = 50;
+ ActiveWeapon.Scale = new Vector2(-1, 1);
+ }
+ else if (ActiveIndex == 1)
+ {
+ ActiveWeapon.Position = new Vector2(0, 0);
+ ActiveWeapon.RotationDegrees = 120;
+ ActiveWeapon.Scale = new Vector2(1, -1);
+ }
+ else if (ActiveIndex == 2)
+ {
+ ActiveWeapon.Position = new Vector2(0, 5);
+ ActiveWeapon.RotationDegrees = 310;
+ ActiveWeapon.Scale = new Vector2(1, 1);
+ }
+ else if (ActiveIndex == 3)
+ {
+ ActiveWeapon.Position = new Vector2(0, 0);
+ ActiveWeapon.RotationDegrees = 60;
+ ActiveWeapon.Scale = new Vector2(1, 1);
+ }
+ ActiveWeapon._Conceal();
+ }
+ }
+
+ //更改父节点
+ var parent = slot.Weapon.GetParentOrNull();
+ if (parent == null)
+ {
+ Master.MountPoint.AddChild(slot.Weapon);
+ }
+ else if (parent != Master.MountPoint)
+ {
+ parent.RemoveChild(slot.Weapon);
+ Master.MountPoint.AddChild(slot.Weapon);
+ }
+
+ slot.Weapon.Position = Vector2.Zero;
+ slot.Weapon.Scale = Vector2.One;
+ slot.Weapon.RotationDegrees = 0;
+ ActiveWeapon = slot.Weapon;
+ ActiveIndex = index;
+ ActiveWeapon._Active();
+ return true;
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/item/weapon/RegisterWeapon.cs b/DungeonShooting_Godot/src/game/item/weapon/RegisterWeapon.cs
new file mode 100644
index 0000000..3cbcd57
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/RegisterWeapon.cs
@@ -0,0 +1,23 @@
+using System;
+
+///
+/// 用作 Weapon 子类上, 用于注册武器, 允许同时存在多个 RegisterWeapon 特性
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class RegisterWeapon : Attribute
+{
+
+ public string Id { get; private set; }
+ public Type AttributeType { get; private set; }
+
+ public RegisterWeapon(string id)
+ {
+ Id = id;
+ }
+
+ public RegisterWeapon(string id, Type attributeType)
+ {
+ Id = id;
+ AttributeType = attributeType;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/RegisterWeaponFunction.cs b/DungeonShooting_Godot/src/game/item/weapon/RegisterWeaponFunction.cs
new file mode 100644
index 0000000..1796a95
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/RegisterWeaponFunction.cs
@@ -0,0 +1,16 @@
+using System;
+
+///
+/// 用作静态函数上, 用于注册武器, 函数必须返回一个 Weapon 对象, 且参数为 string id,
+/// 那么它看起来应该像这样: static Weapon Method(string id);
+///
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+public class RegisterWeaponFunction : Attribute
+{
+ public string Id { get; private set; }
+
+ public RegisterWeaponFunction(string id)
+ {
+ Id = id;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/ThrowGun.cs b/DungeonShooting_Godot/src/game/item/weapon/ThrowGun.cs
new file mode 100644
index 0000000..d3c652a
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/ThrowGun.cs
@@ -0,0 +1,44 @@
+using Godot;
+
+public class ThrowWeapon : ThrowNode
+{
+
+ private bool fristOver = true;
+
+ public override void _Ready()
+ {
+ base._Ready();
+ ZIndex = 2;
+ }
+
+ public override void StartThrow(Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed, float ySpeed, float rotate, Node2D mount)
+ {
+ base.StartThrow(size, start, startHeight, direction, xSpeed, ySpeed, rotate, mount);
+ fristOver = true;
+ }
+
+ protected override void OnOver()
+ {
+ if (fristOver)
+ {
+ fristOver = false;
+ if (Mount is Weapon gun)
+ {
+ gun._FallToGround();
+ }
+ }
+ //如果落地高度不够低, 再抛一次
+ if (StartYSpeed > 1)
+ {
+ StartThrow(Size, GlobalPosition, 0, Direction, XSpeed * 0.8f, StartYSpeed * 0.5f, RotateSpeed * 0.5f, null);
+ }
+ else //结束
+ {
+ base.OnOver();
+ }
+ }
+ protected override void OnMaxHeight(float height)
+ {
+ ZIndex = 0;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
new file mode 100644
index 0000000..752862c
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs
@@ -0,0 +1,784 @@
+using Godot;
+using System;
+
+///
+/// 武器的基类
+///
+public abstract class Weapon : ActivityObject
+{
+
+ ///
+ /// 武器的唯一id
+ ///
+ public string Id { get; }
+
+ ///
+ /// 开火回调事件
+ ///
+ public event Action FireEvent;
+
+ ///
+ /// 属性数据
+ ///
+ public WeaponAttribute Attribute { get; private set; }
+
+ ///
+ /// 武器的图片
+ ///
+ public Sprite WeaponSprite { get; private set; }
+
+ ///
+ /// 动画播放器
+ ///
+ ///
+ public AnimationPlayer AnimationPlayer { get; private set; }
+
+ ///
+ /// 武器攻击的目标阵营
+ ///
+ public CampEnum TargetCamp { get; set; }
+
+ ///
+ /// 该武器的拥有者
+ ///
+ public Role Master { get; private set; }
+
+ ///
+ /// 当前弹夹弹药剩余量
+ ///
+ public int CurrAmmo { get; private set; }
+ ///
+ /// 剩余弹药量
+ ///
+ public int ResidueAmmo { get; private set; }
+
+ ///
+ /// 武器管的开火点
+ ///
+ public Position2D FirePoint { get; private set; }
+ ///
+ /// 武器管的原点
+ ///
+ public Position2D OriginPoint { get; private set; }
+ ///
+ /// 弹壳抛出的点
+ ///
+ public Position2D ShellPoint { get; private set; }
+ ///
+ /// 碰撞器节点
+ ///
+ ///
+ public CollisionShape2D CollisionShape2D { get; private set; }
+ ///
+ /// 武器的当前散射半径
+ ///
+ public float CurrScatteringRange { get; private set; } = 0;
+ ///
+ /// 是否在换弹中
+ ///
+ ///
+ public bool Reloading { get; private set; } = false;
+ ///
+ /// 换弹计时器
+ ///
+ public float ReloadTimer { get; private set; } = 0;
+ ///
+ /// 换弹进度 (0 - 1)
+ ///
+ public float ReloadProgress
+ {
+ get
+ {
+ if (Attribute.AloneReload)
+ {
+ var num = 1f / Attribute.AmmoCapacity;
+ return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
+ }
+ return ReloadTimer / Attribute.ReloadTime;
+ }
+ }
+
+ //是否按下
+ private bool triggerFlag = false;
+ //扳机计时器
+ private float triggerTimer = 0;
+ //开火前延时时间
+ private float delayedTime = 0;
+ //开火间隙时间
+ private float fireInterval = 0;
+ //开火武器口角度
+ private float fireAngle = 0;
+ //攻击冷却计时
+ private float attackTimer = 0;
+ //攻击状态
+ private bool attackFlag = false;
+ //按下的时间
+ private float downTimer = 0;
+ //松开的时间
+ private float upTimer = 0;
+ //连发次数
+ private float continuousCount = 0;
+ //连发状态记录
+ private bool continuousShootFlag = false;
+
+ ///
+ /// 根据属性创建一把武器
+ ///
+ /// 武器唯一id
+ /// 属性
+ public Weapon(string id, WeaponAttribute attribute)
+ {
+ Id = id;
+ Attribute = attribute;
+ //加载预制体
+ var tempPrefab = ResourceManager.Load(Attribute.WeaponPrefab);
+ if (tempPrefab == null)
+ {
+ throw new Exception("WeaponAttribute中未设置'WeaponPrefab'属性!");
+ }
+ var tempNode = tempPrefab.Instance();
+ var body = tempNode.GetChild(0);
+ tempNode.RemoveChild(body);
+ AddChild(body);
+
+ WeaponSprite = GetNode("WeaponBody/WeaponSprite");
+ FirePoint = GetNode("WeaponBody/FirePoint");
+ OriginPoint = GetNode("WeaponBody/OriginPoint");
+ ShellPoint = GetNode("WeaponBody/ShellPoint");
+ AnimationPlayer = GetNode("WeaponBody/AnimationPlayer");
+ CollisionShape2D = GetNode("WeaponBody/Collision");
+
+ //更新图片
+ WeaponSprite.Texture = ResourceLoader.Load(Attribute.Sprite);
+ WeaponSprite.Position = Attribute.CenterPosition;
+ //开火位置
+ FirePoint.Position = new Vector2(Attribute.FirePosition.x, -Attribute.FirePosition.y);
+ OriginPoint.Position = new Vector2(0, -Attribute.FirePosition.y);
+
+ //弹药量
+ CurrAmmo = Attribute.AmmoCapacity;
+ //剩余弹药量
+ ResidueAmmo = Attribute.MaxAmmoCapacity - Attribute.AmmoCapacity;
+ }
+
+ ///
+ /// 当按下扳机时调用
+ ///
+ protected abstract void OnDownTrigger();
+
+ ///
+ /// 当松开扳机时调用
+ ///
+ protected abstract void OnUpTrigger();
+
+ ///
+ /// 单次开火时调用的函数
+ ///
+ protected abstract void OnFire();
+
+ ///
+ /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
+ /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
+ ///
+ protected abstract void OnShoot();
+
+ ///
+ /// 当开始换弹时调用
+ ///
+ protected abstract void OnReload();
+
+ ///
+ /// 当换弹完成时调用
+ ///
+ protected abstract void OnReloadFinish();
+
+ ///
+ /// 当武器被拾起时调用
+ ///
+ /// 拾起该武器的角色
+ protected abstract void OnPickUp(Role master);
+
+ ///
+ /// 当武器从武器袋中扔掉时调用
+ ///
+ protected abstract void OnThrowOut();
+
+ ///
+ /// 当武器被激活时调用, 也就是使用当武器是调用
+ ///
+ protected abstract void OnActive();
+
+ ///
+ /// 当武器被收起时调用
+ ///
+ protected abstract void OnConceal();
+
+ public override void _Process(float delta)
+ {
+ if (Master == null) //这把武器被扔在地上
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
+ triggerFlag = false;
+ attackFlag = false;
+ attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
+ CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
+ continuousCount = 0;
+ delayedTime = 0;
+ }
+ else if (Master.Holster.ActiveWeapon != this) //当前武器没有被使用
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
+ triggerFlag = false;
+ attackFlag = false;
+ attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
+ CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
+ continuousCount = 0;
+ delayedTime = 0;
+ }
+ else //正在使用中
+ {
+
+ //换弹
+ if (Reloading)
+ {
+ ReloadTimer -= delta;
+ if (ReloadTimer <= 0)
+ {
+ ReloadSuccess();
+ }
+ }
+
+ if (triggerFlag)
+ {
+ if (upTimer > 0) //第一帧按下扳机
+ {
+ upTimer = 0;
+ DownTrigger();
+ }
+ downTimer += delta;
+ }
+ else
+ {
+ if (downTimer > 0) //第一帧松开扳机
+ {
+ downTimer = 0;
+ UpTrigger();
+ }
+ upTimer += delta;
+ }
+
+ // 攻击的计时器
+ if (attackTimer > 0)
+ {
+ attackTimer -= delta;
+ if (attackTimer < 0)
+ {
+ delayedTime += attackTimer;
+ attackTimer = 0;
+ }
+ }
+ else if (delayedTime > 0) //攻击延时
+ {
+ delayedTime -= delta;
+ if (attackTimer < 0)
+ {
+ delayedTime = 0;
+ }
+ }
+
+ //连发判断
+ if (continuousCount > 0 && delayedTime <= 0 && attackTimer <= 0)
+ {
+ //开火
+ TriggerFire();
+ }
+
+ if (!attackFlag && attackTimer <= 0)
+ {
+ CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
+ }
+ triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
+ triggerFlag = false;
+ attackFlag = false;
+
+ //武器身回归
+ Position = Position.MoveToward(Vector2.Zero, 35 * delta);
+ if (fireInterval == 0)
+ {
+ RotationDegrees = 0;
+ }
+ else
+ {
+ RotationDegrees = Mathf.Lerp(0, fireAngle, attackTimer / fireInterval);
+ }
+ }
+ }
+
+ ///
+ /// 扳机函数, 调用即视为扣动扳机
+ ///
+ public void Trigger()
+ {
+ //是否第一帧按下
+ var justDown = downTimer == 0;
+ //是否能发射
+ var flag = false;
+ if (continuousCount <= 0) //不能处于连发状态下
+ {
+ if (Attribute.ContinuousShoot) //自动射击
+ {
+ if (triggerTimer > 0)
+ {
+ if (continuousShootFlag)
+ {
+ flag = true;
+ }
+ }
+ else
+ {
+ flag = true;
+ if (delayedTime <= 0 && attackTimer <= 0)
+ {
+ continuousShootFlag = true;
+ }
+ }
+ }
+ else //半自动
+ {
+ if (justDown && triggerTimer <= 0)
+ {
+ flag = true;
+ }
+ }
+ }
+
+ if (flag)
+ {
+ var fireFlag = true; //是否能开火
+ if (Reloading) //换弹中
+ {
+ if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ }
+ else
+ {
+ fireFlag = false;
+ }
+ }
+ else if (CurrAmmo <= 0)
+ {
+ fireFlag = false;
+ //子弹不够
+ _Reload();
+ }
+
+ if (fireFlag)
+ {
+ if (justDown)
+ {
+ //开火前延时
+ delayedTime = Attribute.DelayedTime;
+ //扳机按下间隔
+ triggerTimer = Attribute.TriggerInterval;
+ //连发数量
+ if (!Attribute.ContinuousShoot)
+ {
+ continuousCount = MathUtils.RandRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
+ }
+ }
+ if (delayedTime <= 0 && attackTimer <= 0)
+ {
+ TriggerFire();
+ }
+ attackFlag = true;
+ }
+
+ }
+ triggerFlag = true;
+ }
+
+ ///
+ /// 刚按下扳机
+ ///
+ private void DownTrigger()
+ {
+ OnDownTrigger();
+ }
+
+ ///
+ /// 刚松开扳机
+ ///
+ private void UpTrigger()
+ {
+ continuousShootFlag = false;
+ if (delayedTime > 0)
+ {
+ continuousCount = 0;
+ }
+ OnUpTrigger();
+ }
+
+ ///
+ /// 触发开火
+ ///
+ private void TriggerFire()
+ {
+ continuousCount = continuousCount > 0 ? continuousCount - 1 : 0;
+
+ //减子弹数量
+ CurrAmmo--;
+ //开火间隙
+ fireInterval = 60 / Attribute.StartFiringSpeed;
+ //攻击冷却
+ attackTimer += fireInterval;
+
+ //触发开火函数
+ OnFire();
+
+ //开火发射的子弹数量
+ var bulletCount = MathUtils.RandRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
+ //武器口角度
+ var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
+
+ //创建子弹
+ for (int i = 0; i < bulletCount; i++)
+ {
+ //先算武器口方向
+ Rotation = (float)GD.RandRange(-angle, angle);
+ //发射子弹
+ OnShoot();
+ }
+
+ //当前的散射半径
+ CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue, Attribute.FinalScatteringRange);
+ //武器的旋转角度
+ RotationDegrees -= Attribute.UpliftAngle;
+ fireAngle = RotationDegrees;
+ //武器身位置
+ Position = new Vector2(Mathf.Max(-Attribute.MaxBacklash, Position.x - MathUtils.RandRange(Attribute.MinBacklash, Attribute.MaxBacklash)), Position.y);
+
+ if (FireEvent != null)
+ {
+ FireEvent(this);
+ }
+ }
+
+ ///
+ /// 返回弹药是否到达上限
+ ///
+ public bool IsFullAmmo()
+ {
+ return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
+ }
+
+ ///
+ /// 返回是否弹药耗尽
+ ///
+ public bool IsEmptyAmmo()
+ {
+ return CurrAmmo + ResidueAmmo == 0;
+ }
+
+ ///
+ /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
+ ///
+ /// 弹药数量
+ public int PickUpAmmo(int count)
+ {
+ var num = ResidueAmmo;
+ ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
+ return count - ResidueAmmo + num;
+ }
+
+ ///
+ /// 触发换弹
+ ///
+ public void _Reload()
+ {
+ if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
+ {
+ Reloading = true;
+ ReloadTimer = Attribute.ReloadTime;
+ OnReload();
+ }
+ }
+
+ ///
+ /// 换弹计时器时间到, 执行换弹操作
+ ///
+ private void ReloadSuccess()
+ {
+ if (Attribute.AloneReload) //单独装填
+ {
+ if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
+ {
+ if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
+ {
+ ResidueAmmo -= Attribute.AloneReloadCount;
+ CurrAmmo += Attribute.AloneReloadCount;
+ }
+ else //子弹满了
+ {
+ var num = Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ ResidueAmmo -= num;
+ }
+ }
+ else if (ResidueAmmo != 0) //剩余子弹不足
+ {
+ if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
+ {
+ CurrAmmo += ResidueAmmo;
+ ResidueAmmo = 0;
+ }
+ else //子弹满了
+ {
+ var num = Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ ResidueAmmo -= num;
+ }
+ }
+ if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
+ {
+ Reloading = false;
+ ReloadTimer = 0;
+ OnReloadFinish();
+ }
+ else
+ {
+ ReloadTimer = Attribute.ReloadTime;
+ OnReload();
+ }
+ }
+ else //换弹结束
+ {
+ if (ResidueAmmo >= Attribute.AmmoCapacity)
+ {
+ ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
+ CurrAmmo = Attribute.AmmoCapacity;
+ }
+ else
+ {
+ CurrAmmo = ResidueAmmo;
+ ResidueAmmo = 0;
+ }
+ Reloading = false;
+ ReloadTimer = 0;
+ OnReloadFinish();
+ }
+ }
+
+ public override CheckInteractiveResult CheckInteractive(ActivityObject master)
+ {
+ var result = new CheckInteractiveResult(this);
+
+ if (master is Role roleMaster) //碰到角色
+ {
+ if (Master == null)
+ {
+ var masterWeapon = roleMaster.Holster.ActiveWeapon;
+ //查找是否有同类型武器
+ var index = roleMaster.Holster.FindWeapon(Id);
+ if (index != -1) //如果有这个武器
+ {
+ if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
+ {
+ var targetWeapon = roleMaster.Holster.GetWeapon(index);
+ if (!targetWeapon.IsFullAmmo()) //背包里面的武器子弹未满
+ {
+ //可以互动拾起弹药
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = "res://resource/sprite/ui/icon/icon_bullet.png";
+ return result;
+ }
+ }
+ }
+ else //没有武器
+ {
+ if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
+ {
+ //可以互动, 拾起武器
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = "res://resource/sprite/ui/icon/icon_pickup.png";
+ return result;
+ }
+ else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
+ {
+ //可以互动, 切换武器
+ result.CanInteractive = true;
+ result.Message = Attribute.Name;
+ result.ShowIcon = "res://resource/sprite/ui/icon/icon_replace.png";
+ return result;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public override void Interactive(ActivityObject master)
+ {
+ if (master is Role roleMaster) //与role碰撞
+ {
+ //查找是否有同类型武器
+ var index = roleMaster.Holster.FindWeapon(Id);
+ if (index != -1) //如果有这个武器
+ {
+ if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
+ {
+ return;
+ }
+
+ var weapon = roleMaster.Holster.GetWeapon(index);
+ //子弹上限
+ var maxCount = Attribute.MaxAmmoCapacity;
+ //是否捡到子弹
+ var flag = false;
+ if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
+ {
+ var count = weapon.PickUpAmmo(ResidueAmmo);
+ if (count != ResidueAmmo)
+ {
+ ResidueAmmo = count;
+ flag = true;
+ }
+ }
+
+ if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
+ {
+ var count = weapon.PickUpAmmo(CurrAmmo);
+ if (count != CurrAmmo)
+ {
+ CurrAmmo = count;
+ flag = true;
+ }
+ }
+
+ //播放互动效果
+ if (flag)
+ {
+ this.StartThrow(new Vector2(20, 20), GlobalPosition, 0, 0,
+ MathUtils.RandRangeInt(-20, 20), MathUtils.RandRangeInt(20, 50),
+ MathUtils.RandRangeInt(-180, 180), WeaponSprite);
+ }
+ }
+ else //没有武器
+ {
+ if (roleMaster.Holster.PickupWeapon(this) == -1)
+ {
+ var slot = roleMaster.Holster.SlotList[roleMaster.Holster.ActiveIndex];
+ if (slot.Type == Attribute.WeightType)
+ {
+ var weapon = roleMaster.Holster.RmoveWeapon(roleMaster.Holster.ActiveIndex);
+ weapon.StartThrowWeapon(roleMaster);
+ roleMaster.PickUpWeapon(this);
+ }
+ }
+ }
+ }
+ }
+
+ public Vector2 GetItemPosition()
+ {
+ return GlobalPosition;
+ }
+
+ ///
+ /// 触发落到地面
+ ///
+ public void _FallToGround()
+ {
+ //启用碰撞
+ CollisionShape2D.Disabled = false;
+ }
+
+ ///
+ /// 触发拾起
+ ///
+ public void _PickUpWeapon(Role master)
+ {
+ Master = master;
+ //握把位置
+ WeaponSprite.Position = Attribute.HoldPosition;
+ //清除泛白效果
+ ShaderMaterial sm = WeaponSprite.Material as ShaderMaterial;
+ sm.SetShaderParam("schedule", 0);
+ //停止动画
+ AnimationPlayer.Stop();
+ ZIndex = 0;
+ //禁用碰撞
+ CollisionShape2D.Disabled = true;
+ OnPickUp(master);
+ }
+
+ ///
+ /// 触发抛出
+ /// a
+ public void _ThrowOutWeapon()
+ {
+ Master = null;
+ WeaponSprite.Position = Attribute.CenterPosition;
+ AnimationPlayer.Play("Floodlight");
+ OnThrowOut();
+ }
+
+ ///
+ /// 触发启用武器
+ ///
+ public void _Active()
+ {
+ OnActive();
+ }
+
+ ///
+ /// 触发收起武器
+ ///
+ public void _Conceal()
+ {
+ OnConceal();
+ }
+
+ ///
+ /// 实例化并返回子弹对象
+ ///
+ /// 子弹的预制体
+ protected T CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null) where T : Node2D, IBullet
+ {
+ return (T)CreateBullet(bulletPack, globalPostion, globalRotation, parent);
+ }
+
+ ///
+ /// 实例化并返回子弹对象
+ ///
+ /// 子弹的预制体
+ protected IBullet CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null)
+ {
+ // 实例化子弹
+ Node2D bullet = bulletPack.Instance();
+ // 设置坐标
+ bullet.GlobalPosition = globalPostion;
+ // 旋转角度
+ bullet.GlobalRotation = globalRotation;
+ if (parent == null)
+ {
+ RoomManager.Current.SortRoot.AddChild(bullet);
+ }
+ else
+ {
+ parent.AddChild(bullet);
+ }
+ // 调用初始化
+ IBullet result = (IBullet)bullet;
+ result.Init(TargetCamp, this, null);
+ return result;
+ }
+}
\ 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
new file mode 100644
index 0000000..c6cad38
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/WeaponAttribute.cs
@@ -0,0 +1,152 @@
+using Godot;
+
+///
+/// 武器上的属性
+///
+public class WeaponAttribute
+{
+ ///
+ /// 武器显示的名称
+ ///
+ public string Name = "Gun1";
+ ///
+ /// 武器 Prefab, 必须继承场景 "res://prefab/weapon/Weapon.tscn"
+ ///
+ public string WeaponPrefab = "res://prefab/weapon/Weapon.tscn";
+ ///
+ /// 武器类型
+ ///
+ public WeaponWeightType WeightType = WeaponWeightType.MainWeapon;
+ ///
+ /// 武器的图片
+ ///
+ public string Sprite = "res://resource/sprite/gun/gun1.png";
+ ///
+ /// 是否连续发射, 如果为false, 则每次发射都需要扣动扳机
+ ///
+ public bool ContinuousShoot = true;
+ ///
+ /// 弹夹容量
+ ///
+ public int AmmoCapacity = 30;
+ ///
+ /// 弹药容量上限
+ ///
+ public int MaxAmmoCapacity = 90;
+ ///
+ /// 装弹时间, 单位: 秒
+ ///
+ public float ReloadTime = 1.5f;
+ ///
+ /// 每粒子弹是否是单独装填, 如果是, 那么每上一发子弹的时间就是 ReloadTime, 可以做霰弹武器装填效果
+ ///
+ public bool AloneReload = false;
+ ///
+ /// 单独装填时每次装填子弹数量, 必须要将 'AloneReload' 属性设置为 true
+ ///
+ public int AloneReloadCount = 1;
+ ///
+ /// 单独装填的子弹时可以立即射击, 必须要将 'AloneReload' 属性设置为 true
+ ///
+ public bool AloneReloadCanShoot = false;
+ ///
+ /// 是否为松发开火, 也就是松开扳机才开火, 若要启用该属性, 必须将 'ContinuousShoot' 设置为 false
+ ///
+ public bool LooseShoot = false;
+ ///
+ /// 连续发射最小次数, 仅当 ContinuousShoot 为 false 时生效
+ ///
+ public int MinContinuousCount = 3;
+ ///
+ /// 连续发射最大次数, 仅当 ContinuousShoot 为 false 时生效
+ ///
+ public int MaxContinuousCount = 3;
+ ///
+ /// 按下一次扳机后需要多长时间才能再次按下
+ ///
+ public float TriggerInterval = 0;
+ ///
+ /// 初始射速, 初始每秒分钟能发射多少发子弹
+ ///
+ public float StartFiringSpeed = 300;
+ ///
+ /// 最终射速, 最终每秒分钟能发射多少发子弹
+ ///
+ public float FinalFiringSpeed = 300;
+ ///
+ /// 按下扳机并开火后射速增加速率
+ ///
+ public float FiringSpeedAddSpeed = 2;
+ ///
+ /// 松开扳机后射速消散速率
+ ///
+ public float FiringSpeedBackSpeed = 10;
+ ///
+ /// 单次开火发射子弹最小数量
+ ///
+ public int MinFireBulletCount = 1;
+ ///
+ /// 单次开火发射子弹最大数量
+ ///
+ public int MaxFireBulletCount = 1;
+ ///
+ /// 开火前延时
+ ///
+ public float DelayedTime = 0f;
+ ///
+ /// 初始散射半径
+ ///
+ public float StartScatteringRange = 0;
+ ///
+ /// 最终散射半径
+ ///
+ public float FinalScatteringRange = 20;
+ ///
+ /// 每次发射后散射增加值
+ ///
+ public float ScatteringRangeAddValue = 2;
+ ///
+ /// 松开扳机后散射销退速率
+ ///
+ public float ScatteringRangeBackSpeed = 10;
+ ///
+ /// 子弹飞行最大距离
+ ///
+ public float MaxDistance = 600;
+ ///
+ /// 子弹飞行最小距离
+ ///
+ public float MinDistance = 800;
+ ///
+ /// 武器精灵的旋转中心坐标
+ ///
+ public Vector2 CenterPosition = new Vector2(0, 0);
+ ///
+ /// 开火位置
+ ///
+ public Vector2 FirePosition = new Vector2(11, 0);
+ ///
+ /// 握把位置
+ ///
+ public Vector2 HoldPosition = new Vector2(4, -3);
+ ///
+ /// 重量
+ ///
+ public float Weight = 11;
+ ///
+ /// 最大后坐力 (仅用于开火后武器身抖动)
+ ///
+ public float MaxBacklash = 4;
+ ///
+ /// 最小后坐力 (仅用于开火后武器身抖动)
+ ///
+ public float MinBacklash = 2;
+ ///
+ /// 开火后武器口上抬角度
+ ///
+ public float UpliftAngle = 30;
+ ///
+ /// 开火后武器口角度恢复速度倍数
+ ///
+ public float UpliftAngleRestore = 1;
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/WeaponManager.cs b/DungeonShooting_Godot/src/game/item/weapon/WeaponManager.cs
new file mode 100644
index 0000000..d5e8bf1
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/WeaponManager.cs
@@ -0,0 +1,118 @@
+using System.Reflection;
+using System;
+using System.Collections.Generic;
+using Godot;
+
+///
+/// 武器管理类, 负责武器注册和创建
+///
+public static class WeaponManager
+{
+ private static Dictionary> registerData = new Dictionary>();
+
+ ///
+ /// 从一个指定的程序集中扫描并注册武器对象
+ ///
+ /// 数据集
+ public static void RegisterWeaponFromAssembly(Assembly assembly)
+ {
+ var types = assembly.GetTypes();
+ foreach (var type in types)
+ {
+ //注册类
+ Attribute[] attribute = Attribute.GetCustomAttributes(type, typeof(RegisterWeapon), false);
+ if (attribute != null && attribute.Length > 0)
+ {
+ if (!typeof(Weapon).IsAssignableFrom(type))
+ {
+ throw new Exception($"注册武器类'{type.FullName}'没有继承类'Weapon'!");
+ }
+ var atts = (RegisterWeapon[])attribute;
+ foreach (var att in atts)
+ {
+ //注册类
+ if (att.AttributeType == null) //没有指定属性类型
+ {
+ RegisterWeapon(att.Id, () =>
+ {
+ return Activator.CreateInstance(type, att.Id, new WeaponAttribute()) as Weapon;
+ });
+ }
+ else
+ {
+ if (!typeof(WeaponAttribute).IsAssignableFrom(att.AttributeType))
+ {
+ throw new Exception($"注册武器类'{type.FullName}'标注的特性中参数'AttributeType'类型没有继承'WeaponAttribute'!");
+ }
+ RegisterWeapon(att.Id, () =>
+ {
+ return Activator.CreateInstance(type, att.Id, Activator.CreateInstance(att.AttributeType)) as Weapon;
+ });
+ }
+ }
+ }
+
+ //注册函数
+ MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+ foreach (var method in methods)
+ {
+ Attribute mAttribute;
+ //
+ if ((mAttribute = Attribute.GetCustomAttribute(method, typeof(RegisterWeaponFunction), false)) != null)
+ {
+ if (!typeof(Weapon).IsAssignableFrom(method.ReturnType)) //返回值类型不是 Weapon
+ {
+ throw new Exception($"注册武器函数'{method.DeclaringType.FullName}.{method.Name}'返回值类型不为'Weapon'!");
+ }
+ var args = method.GetParameters();
+ if (args == null || args.Length != 1 || args[0].ParameterType != typeof(string)) //参数类型不正确
+ {
+ throw new Exception($"注册武器函数'{method.DeclaringType.FullName}.{method.Name}'参数不满足(string id)类型");
+ }
+ var att = (RegisterWeaponFunction)mAttribute;
+ //注册函数
+ RegisterWeapon(att.Id, () =>
+ {
+ return method.Invoke(null, new object[] { att.Id }) as Weapon;
+ });
+ }
+ }
+ }
+ }
+
+ ///
+ /// 注册当个武器对象
+ ///
+ /// 武器唯一id, 该id不能重复
+ /// 获取武器时的回调函数, 函数返回武器对象
+ public static void RegisterWeapon(string id, Func callBack)
+ {
+ if (registerData.ContainsKey(id))
+ {
+ throw new Exception($"武器id: '{id} 已经被注册!'");
+ }
+ registerData.Add(id, callBack);
+ }
+
+ ///
+ /// 根据武器唯一id获取
+ ///
+ /// 武器id
+ public static Weapon GetGun(string id)
+ {
+ if (registerData.TryGetValue(id, out var callback))
+ {
+ return callback();
+ }
+ throw new Exception($"当前武器'{id}未被注册'");
+ }
+
+ ///
+ /// 根据武器唯一id获取
+ ///
+ /// 武器id
+ public static T GetGun(string id) where T : Weapon
+ {
+ return (T)GetGun(id);
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs b/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs
new file mode 100644
index 0000000..5528683
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/WeaponWeightType.cs
@@ -0,0 +1,19 @@
+
+///
+/// 根据重量划分的武器类型
+///
+public enum WeaponWeightType
+{
+ ///
+ /// 副武器
+ ///
+ DeputyWeapon = 1,
+ ///
+ /// 主武器
+ ///
+ MainWeapon = 2,
+ ///
+ /// 重型武器
+ ///
+ HeavyWeapon = 3
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs
new file mode 100644
index 0000000..4c19b4b
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs
@@ -0,0 +1,27 @@
+using Godot;
+
+///
+/// 子弹接口
+///
+public interface IBullet
+{
+ ///
+ /// 攻击目标阵营
+ ///
+ CampEnum TargetCamp { get; }
+ ///
+ /// 发射该子弹的武器
+ ///
+ Weapon Gun { get; }
+ ///
+ /// 发射该子弹的物体对象
+ ///
+ Node2D Master { get; }
+ ///
+ /// 初始化基础数据
+ ///
+ /// 攻击的目标阵营
+ /// 发射该子弹的枪对象
+ /// 发射该子弹的角色
+ void Init(CampEnum target, Weapon gun, Node2D master);
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/HighSpeedBullet.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/HighSpeedBullet.cs
new file mode 100644
index 0000000..53ef711
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/HighSpeedBullet.cs
@@ -0,0 +1,72 @@
+using Godot;
+
+///
+/// 高速子弹
+///
+public class HighSpeedBullet : Node2D, IBullet
+{
+ public CampEnum TargetCamp { get; private set; }
+
+ public Weapon Gun { get; private set; }
+
+ public Node2D Master { get; private set; }
+
+ ///
+ /// 碰撞物体后产生的火花
+ ///
+ [Export] public PackedScene Hit;
+
+ //射线检测节点
+ private RayCast2D RayCast2D;
+ private Line2D Line;
+ private float ca = 1;
+
+ public void Init(CampEnum target, Weapon gun, Node2D master)
+ {
+ TargetCamp = target;
+ Gun = gun;
+ Master = master;
+
+ //飞行距离
+ var distance = MathUtils.RandRange(gun.Attribute.MinDistance, gun.Attribute.MaxDistance);
+
+ //初始化子弹数据
+ RayCast2D = GetNode("RayCast2D");
+ Line = GetNode("Line");
+ Modulate = Colors.White;
+
+ // 目标点
+ Vector2 targetPos = new Vector2(distance, 0);
+ RayCast2D.CastTo = targetPos;
+ RayCast2D.ForceRaycastUpdate();
+ if (RayCast2D.IsColliding())
+ {
+ //碰到物体
+ Vector2 collPosition = RayCast2D.GetCollisionPoint();
+ Node2D hit = Hit.Instance();
+ hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
+ hit.GlobalPosition = collPosition;
+ GetTree().CurrentScene.AddChild(hit);
+ //划线的点坐标
+ Line.SetPointPosition(1, new Vector2(Line.GlobalPosition.DistanceTo(collPosition), 0));
+ }
+ else
+ {
+ //划线的点坐标
+ Line.SetPointPosition(1, targetPos);
+ }
+ RayCast2D.Enabled = false;
+ }
+
+ public override void _Process(float delta)
+ {
+ ca -= 12 * delta;
+ if (ca <= 0) {
+ QueueFree();
+ return;
+ }
+ Color c = Modulate;
+ c.a = ca;
+ Modulate = c;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/OrdinaryBullets.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/OrdinaryBullets.cs
new file mode 100644
index 0000000..4458e5f
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/OrdinaryBullets.cs
@@ -0,0 +1,97 @@
+using System;
+using Godot;
+
+///
+/// 普通的子弹
+///
+public class OrdinaryBullets : Node2D, IBullet
+{
+ public CampEnum TargetCamp { get; private set; }
+
+ public Weapon Gun { get; private set; }
+
+ public Node2D Master { get; private set; }
+
+ ///
+ /// 碰撞物体后产生的火花
+ ///
+ [Export] public PackedScene Hit;
+
+ // 起始点坐标
+ private Vector2 StartPosition;
+ // 最大飞行距离
+ private float MaxDistance;
+ // 子弹飞行速度
+ private float FlySpeed = 1500;
+ //当前子弹已经飞行的距离
+ private float CurrFlyDistance = 0;
+ //射线碰撞节点
+ private RayCast2D RayCast;
+ //子弹的精灵
+ private Sprite BulletSprite;
+ //绘制阴影的精灵
+ private Sprite ShadowSprite;
+
+ private int frame = 0;
+
+ public void Init(CampEnum target, Weapon gun, Node2D master)
+ {
+ TargetCamp = target;
+ Gun = gun;
+ Master = master;
+
+ MaxDistance = MathUtils.RandRange(gun.Attribute.MinDistance, gun.Attribute.MaxDistance);
+ StartPosition = GlobalPosition;
+ BulletSprite = GetNode("Bullet");
+ BulletSprite.Visible = false;
+ RayCast = GetNode("RayCast2D");
+
+ //创建阴影
+ ShadowSprite = new Sprite();
+ ShadowSprite.Visible = false;
+ ShadowSprite.ZIndex = -1;
+ ShadowSprite.Texture = BulletSprite.Texture;
+ ShadowSprite.Offset = BulletSprite.Offset;
+ ShadowSprite.Material = ResourceManager.ShadowMaterial;
+ AddChild(ShadowSprite);
+ }
+
+ public override void _Ready()
+ {
+ //生成时播放音效
+ SoundManager.PlaySoundEffect("ordinaryBullet.ogg", this, 6f);
+ }
+
+ public override void _PhysicsProcess(float delta)
+ {
+ if (frame++ == 0)
+ {
+ BulletSprite.Visible = true;
+ ShadowSprite.Visible = true;
+ }
+ //碰到墙壁
+ if (RayCast.IsColliding())
+ {
+ //var target = RayCast.GetCollider();
+ var pos = RayCast.GetCollisionPoint();
+ //播放受击动画
+ Node2D hit = Hit.Instance();
+ hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
+ hit.GlobalPosition = pos;
+ GetTree().CurrentScene.AddChild(hit);
+ QueueFree();
+ }
+ else //没有碰到, 继续移动
+ {
+ ShadowSprite.GlobalPosition = GlobalPosition + new Vector2(0, 5);
+ Position += new Vector2(FlySpeed * delta, 0).Rotated(Rotation);
+
+ CurrFlyDistance += FlySpeed * delta;
+ if (CurrFlyDistance >= MaxDistance)
+ {
+ QueueFree();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
new file mode 100644
index 0000000..31fbc29
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
@@ -0,0 +1,161 @@
+using Godot;
+
+///
+/// 普通的枪
+///
+[RegisterWeapon("1001", typeof(Gun.RifleAttribute))]
+[RegisterWeapon("1003", typeof(Gun.PistolAttribute))]
+public class Gun : Weapon
+{
+ //步枪属性数据
+ private class RifleAttribute : WeaponAttribute
+ {
+ public RifleAttribute()
+ {
+ Name = "步枪";
+ Sprite = "res://resource/sprite/gun/gun4.png";
+ Weight = 40;
+ CenterPosition = new Vector2(0.4f, -2.6f);
+ StartFiringSpeed = 480;
+ StartScatteringRange = 30;
+ FinalScatteringRange = 90;
+ ScatteringRangeAddValue = 2f;
+ ScatteringRangeBackSpeed = 40;
+ //连发
+ ContinuousShoot = true;
+ //扳机检测间隔
+ TriggerInterval = 0f;
+ //连发数量
+ MinContinuousCount = 3;
+ MaxContinuousCount = 3;
+ //开火前延时
+ DelayedTime = 0f;
+ //攻击距离
+ MinDistance = 500;
+ MaxDistance = 600;
+ //发射子弹数量
+ MinFireBulletCount = 1;
+ MaxFireBulletCount = 1;
+ //抬起角度
+ UpliftAngle = 10;
+ //枪身长度
+ FirePosition = new Vector2(16, 1.5f);
+ }
+ }
+
+ //手枪属性数据
+ private class PistolAttribute : WeaponAttribute
+ {
+ public PistolAttribute()
+ {
+ Name = "手枪";
+ Sprite = "res://resource/sprite/gun/gun3.png";
+ Weight = 20;
+ CenterPosition = new Vector2(0.4f, -2.6f);
+ WeightType = WeaponWeightType.DeputyWeapon;
+ StartFiringSpeed = 300;
+ StartScatteringRange = 5;
+ FinalScatteringRange = 60;
+ ScatteringRangeAddValue = 8f;
+ ScatteringRangeBackSpeed = 40;
+ //连发
+ ContinuousShoot = false;
+ AmmoCapacity = 12;
+ MaxAmmoCapacity = 72;
+ //扳机检测间隔
+ TriggerInterval = 0.1f;
+ //连发数量
+ MinContinuousCount = 1;
+ MaxContinuousCount = 1;
+ //开火前延时
+ DelayedTime = 0f;
+ //攻击距离
+ MinDistance = 500;
+ MaxDistance = 600;
+ //发射子弹数量
+ MinFireBulletCount = 1;
+ MaxFireBulletCount = 1;
+ //抬起角度
+ UpliftAngle = 30;
+ //枪身长度
+ FirePosition = new Vector2(10, 1.5f);
+ }
+ }
+
+ ///
+ /// 子弹预制体
+ ///
+ public PackedScene BulletPack;
+ ///
+ /// 弹壳预制体
+ ///
+ public PackedScene ShellPack;
+
+ public Gun(string id, WeaponAttribute attribute): base(id, attribute)
+ {
+ BulletPack = ResourceManager.Load("res://prefab/weapon/bullet/OrdinaryBullets.tscn");
+ ShellPack = ResourceManager.Load("res://prefab/weapon/shell/ShellCase.tscn");
+ }
+
+ protected override void OnFire()
+ {
+ //创建一个弹壳
+ var startPos = GlobalPosition + new Vector2(0, 5);
+ var startHeight = 6;
+ var direction = GlobalRotationDegrees + MathUtils.RandRangeInt(-30, 30) + 180;
+ var xf = MathUtils.RandRangeInt(20, 60);
+ var yf = MathUtils.RandRangeInt(60, 120);
+ var rotate = MathUtils.RandRangeInt(-720, 720);
+ var sprite = ShellPack.Instance();
+ sprite.StartThrow(new Vector2(5, 10), startPos, startHeight, direction, xf, yf, rotate, sprite);
+ //创建抖动
+ MainCamera.Main.ProssesDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
+ }
+
+ protected override void OnShoot()
+ {
+ //创建子弹
+ CreateBullet(BulletPack, FirePoint.GlobalPosition, (FirePoint.GlobalPosition - OriginPoint.GlobalPosition).Angle());
+ }
+
+ protected override void OnReload()
+ {
+
+ }
+
+ protected override void OnReloadFinish()
+ {
+
+ }
+
+ protected override void OnDownTrigger()
+ {
+
+ }
+
+ protected override void OnUpTrigger()
+ {
+
+ }
+
+ protected override void OnPickUp(Role master)
+ {
+
+ }
+
+ protected override void OnThrowOut()
+ {
+
+ }
+
+ protected override void OnActive()
+ {
+
+ }
+
+ protected override void OnConceal()
+ {
+
+ }
+
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
new file mode 100644
index 0000000..66cff82
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
@@ -0,0 +1,125 @@
+using Godot;
+
+[RegisterWeapon("1002", typeof(Shotgun.ShotgunAttribute))]
+public class Shotgun : Weapon
+{
+
+ private class ShotgunAttribute : WeaponAttribute
+ {
+ public ShotgunAttribute()
+ {
+ Name = "霰弹枪";
+ Sprite = "res://resource/sprite/gun/gun2.png";
+ Weight = 40;
+ CenterPosition = new Vector2(0.4f, -2.6f);
+ StartFiringSpeed = 120;
+ StartScatteringRange = 30;
+ FinalScatteringRange = 90;
+ ScatteringRangeAddValue = 50f;
+ ScatteringRangeBackSpeed = 50;
+ //连发
+ ContinuousShoot = false;
+ AmmoCapacity = 7;
+ MaxAmmoCapacity = 42;
+ AloneReload = true;
+ AloneReloadCanShoot = true;
+ ReloadTime = 0.3f;
+ //连发数量
+ MinContinuousCount = 1;
+ MaxContinuousCount = 1;
+ //开火前延时
+ DelayedTime = 0f;
+ //攻击距离
+ MinDistance = 500;
+ MaxDistance = 600;
+ //发射子弹数量
+ MinFireBulletCount = 1;
+ MaxFireBulletCount = 1;
+ //抬起角度
+ UpliftAngle = 15;
+ MaxBacklash = 6;
+ MinBacklash = 5;
+ //枪身长度
+ FirePosition = new Vector2(16, 1.5f);
+ }
+ }
+
+ ///
+ /// 子弹预制体
+ ///
+ public PackedScene BulletPack;
+ ///
+ /// 弹壳预制体
+ ///
+ public PackedScene ShellPack;
+
+ public Shotgun(string id, WeaponAttribute attribute) : base(id, attribute)
+ {
+ BulletPack = ResourceManager.Load("res://prefab/weapon/bullet/OrdinaryBullets.tscn");
+ ShellPack = ResourceManager.Load("res://prefab/weapon/shell/ShellCase.tscn");
+ }
+
+ protected override void OnFire()
+ {
+ //创建一个弹壳
+ var startPos = GlobalPosition + new Vector2(0, 5);
+ var startHeight = 6;
+ var direction = GlobalRotationDegrees + MathUtils.RandRangeInt(-30, 30) + 180;
+ var xf = MathUtils.RandRangeInt(20, 60);
+ var yf = MathUtils.RandRangeInt(60, 120);
+ var rotate = MathUtils.RandRangeInt(-720, 720);
+ var sprite = ShellPack.Instance();
+ sprite.StartThrow(new Vector2(5, 10), startPos, startHeight, direction, xf, yf, rotate, sprite);
+ //创建抖动
+ MainCamera.Main.ProssesDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
+ }
+
+ protected override void OnShoot()
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ //创建子弹
+ CreateBullet(BulletPack, FirePoint.GlobalPosition, (FirePoint.GlobalPosition - OriginPoint.GlobalPosition).Angle() + MathUtils.RandRange(-20 / 180f * Mathf.Pi, 20 / 180f * Mathf.Pi));
+ }
+ }
+
+ protected override void OnReload()
+ {
+
+ }
+
+ protected override void OnReloadFinish()
+ {
+
+ }
+
+ protected override void OnDownTrigger()
+ {
+
+ }
+
+ protected override void OnUpTrigger()
+ {
+
+ }
+
+ protected override void OnPickUp(Role master)
+ {
+
+ }
+
+ protected override void OnThrowOut()
+ {
+
+ }
+
+ protected override void OnActive()
+ {
+
+ }
+
+ protected override void OnConceal()
+ {
+
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/item/weapon/shell/ThrowShell.cs b/DungeonShooting_Godot/src/game/item/weapon/shell/ThrowShell.cs
new file mode 100644
index 0000000..914ee5d
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/item/weapon/shell/ThrowShell.cs
@@ -0,0 +1,42 @@
+using Godot;
+
+///
+/// 弹壳
+///
+public class ThrowShell : ThrowNode
+{
+
+ public override void _Ready()
+ {
+ base._Ready();
+ ZIndex = 2;
+ }
+
+ protected override void OnOver()
+ {
+ //如果落地高度不够低, 再抛一次
+ if (StartYSpeed > 1)
+ {
+ StartThrow(Size, GlobalPosition, 0, Direction, XSpeed * 0.8f, StartYSpeed * 0.5f, RotateSpeed * 0.5f, null);
+ }
+ else
+ {
+ base.OnOver();
+ //等待被销毁
+ AwaitDestroy();
+ }
+ }
+
+ private async void AwaitDestroy()
+ {
+ CollisionShape.Disabled = true;
+ //60秒后销毁
+ await ToSignal(GetTree().CreateTimer(60), "timeout");
+ QueueFree();
+ }
+
+ protected override void OnMaxHeight(float height)
+ {
+ ZIndex = 0;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/CheckInteractiveResult.cs b/DungeonShooting_Godot/src/game/props/CheckInteractiveResult.cs
deleted file mode 100644
index eb8676b..0000000
--- a/DungeonShooting_Godot/src/game/props/CheckInteractiveResult.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-
-///
-/// 检测互动返回的数据集
-///
-public class CheckInteractiveResult
-{
- ///
- /// 互动物体
- ///
- public ActivityObject Target;
- ///
- /// 是否可以互动
- ///
- public bool CanInteractive;
- ///
- /// 互动提示信息
- ///
- public string Message;
- ///
- /// 互动提示显示的图标
- ///
- public string ShowIcon;
-
- public CheckInteractiveResult(ActivityObject target)
- {
- Target = target;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/package/Holster.cs b/DungeonShooting_Godot/src/game/props/package/Holster.cs
deleted file mode 100644
index bbe7c9b..0000000
--- a/DungeonShooting_Godot/src/game/props/package/Holster.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-using Godot;
-
-///
-/// 角色身上的武器袋, 存储角色携带的武器
-///
-public class Holster
-{
- ///
- /// 插槽类
- ///
- public class WeaponSlot
- {
- ///
- /// 是否启用
- ///
- public bool Enable = false;
- ///
- /// 当前插槽存放的武器类型
- ///
- public WeaponWeightType Type = WeaponWeightType.MainWeapon;
- ///
- /// 插槽存放的武器
- ///
- public Weapon Weapon;
- }
-
- ///
- /// 归属者
- ///
- public Role Master { get; }
-
- ///
- /// 当前使用的武器对象
- ///
- public Weapon ActiveWeapon { get; private set; }
-
- ///
- /// 当前使用的武器的索引
- ///
- public int ActiveIndex { get; private set; } = 0;
-
- public WeaponSlot[] SlotList { get; } = new WeaponSlot[4];
-
- public Holster(Role master)
- {
- Master = master;
-
- //创建武器的插槽, 默认前两个都是启用的
- WeaponSlot slot1 = new WeaponSlot();
- slot1.Enable = true;
- SlotList[0] = slot1;
-
- WeaponSlot slot2 = new WeaponSlot();
- slot2.Enable = true;
- slot2.Type = WeaponWeightType.DeputyWeapon;
- SlotList[1] = slot2;
-
- WeaponSlot slot3 = new WeaponSlot();
- SlotList[2] = slot3;
-
- WeaponSlot slot4 = new WeaponSlot();
- slot4.Type = WeaponWeightType.DeputyWeapon;
- SlotList[3] = slot4;
- }
-
- ///
- /// 根据索引获取武器
- ///
- public Weapon GetWeapon(int index) {
- if (index >= SlotList.Length)
- {
- return null;
- }
- return SlotList[index].Weapon;
- }
-
- ///
- /// 根据武器id查找武器袋中该武器所在的位置, 如果没有, 则返回 -1
- ///
- /// 武器id
- public int FindWeapon(string id)
- {
- for (int i = 0; i < SlotList.Length; i++)
- {
- var item = SlotList[i];
- if (item.Weapon != null && item.Weapon.Id == id)
- {
- return i;
- }
- }
- return -1;
- }
-
- ///
- /// 返回是否能放入武器
- ///
- /// 武器对象
- public bool CanPickupWeapon(Weapon weapon)
- {
- for (int i = 0; i < SlotList.Length; i++)
- {
- var item = SlotList[i];
- if (item.Enable && weapon.Attribute.WeightType == item.Type && item.Weapon == null)
- {
- return true;
- }
- }
- return false;
- }
-
- ///
- /// 拾起武器, 存入武器袋中, 返回存放在武器袋的位置, 如果容不下这把武器, 则会返回 -1
- ///
- /// 武器对象
- public int PickupWeapon(Weapon weapon)
- {
- for (int i = 0; i < SlotList.Length; i++)
- {
- var item = SlotList[i];
- if (item.Enable && weapon.Attribute.WeightType == item.Type && item.Weapon == null)
- {
- weapon.Pickup();
- item.Weapon = weapon;
- ExchangeByIndex(i);
- weapon._PickUpWeapon(Master);
- return i;
- }
- }
- return -1;
- }
-
- ///
- /// 移除指定位置的武器, 并返回这个武器对象, 如果移除正在使用的这把武器, 则会自动切换到上一把武器
- ///
- /// 所在武器袋的位置索引
- public Weapon RmoveWeapon(int index)
- {
- if (index < 0 || index >= SlotList.Length)
- {
- return null;
- }
- var slot = SlotList[index];
- if (slot.Weapon == null)
- {
- return null;
- }
- var weapon = slot.Weapon;
- weapon.GetParent().RemoveChild(weapon);
- slot.Weapon = null;
-
- //如果是当前手持的武器, 就需要调用切换武器操作
- if (index == ActiveIndex)
- {
- //没有其他武器了
- if (ExchangePrev() == index)
- {
- ActiveIndex = 0;
- ActiveWeapon = null;
- }
- }
- weapon._ThrowOutWeapon();
- return weapon;
- }
-
- ///
- /// 切换到上一个武器
- ///
- public int ExchangePrev()
- {
- var index = ActiveIndex - 1;
- do
- {
- if (index < 0)
- {
- index = SlotList.Length - 1;
- }
- if (ExchangeByIndex(index))
- {
- return index;
- }
- } while (index-- != ActiveIndex);
- return -1;
- }
-
- ///
- /// 切换到下一个武器,
- ///
- public int ExchangeNext()
- {
- var index = ActiveIndex + 1;
- do
- {
- if (index >= SlotList.Length)
- {
- index = 0;
- }
- if (ExchangeByIndex(index))
- {
- return index;
- }
- }
- while (index++ != ActiveIndex);
- return -1;
- }
-
- ///
- /// 切换到指定索引的武器
- ///
- public bool ExchangeByIndex(int index)
- {
- if (index == ActiveIndex && ActiveWeapon != null) return true;
- if (index < 0 || index > SlotList.Length) return false;
- var slot = SlotList[index];
- if (slot == null || slot.Weapon == null) return false;
-
- //将上一把武器放到背后
- if (ActiveWeapon != null)
- {
- var tempParent = ActiveWeapon.GetParentOrNull();
- if (tempParent != null)
- {
- tempParent.RemoveChild(ActiveWeapon);
- Master.BackMountPoint.AddChild(ActiveWeapon);
- if (ActiveIndex == 0)
- {
- ActiveWeapon.Position = new Vector2(0, 5);
- ActiveWeapon.RotationDegrees = 50;
- ActiveWeapon.Scale = new Vector2(-1, 1);
- }
- else if (ActiveIndex == 1)
- {
- ActiveWeapon.Position = new Vector2(0, 0);
- ActiveWeapon.RotationDegrees = 120;
- ActiveWeapon.Scale = new Vector2(1, -1);
- }
- else if (ActiveIndex == 2)
- {
- ActiveWeapon.Position = new Vector2(0, 5);
- ActiveWeapon.RotationDegrees = 310;
- ActiveWeapon.Scale = new Vector2(1, 1);
- }
- else if (ActiveIndex == 3)
- {
- ActiveWeapon.Position = new Vector2(0, 0);
- ActiveWeapon.RotationDegrees = 60;
- ActiveWeapon.Scale = new Vector2(1, 1);
- }
- ActiveWeapon._Conceal();
- }
- }
-
- //更改父节点
- var parent = slot.Weapon.GetParentOrNull();
- if (parent == null)
- {
- Master.MountPoint.AddChild(slot.Weapon);
- }
- else if (parent != Master.MountPoint)
- {
- parent.RemoveChild(slot.Weapon);
- Master.MountPoint.AddChild(slot.Weapon);
- }
-
- slot.Weapon.Position = Vector2.Zero;
- slot.Weapon.Scale = Vector2.One;
- slot.Weapon.RotationDegrees = 0;
- ActiveWeapon = slot.Weapon;
- ActiveIndex = index;
- ActiveWeapon._Active();
- return true;
- }
-}
diff --git a/DungeonShooting_Godot/src/game/props/weapon/RegisterWeapon.cs b/DungeonShooting_Godot/src/game/props/weapon/RegisterWeapon.cs
deleted file mode 100644
index 3cbcd57..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/RegisterWeapon.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-
-///
-/// 用作 Weapon 子类上, 用于注册武器, 允许同时存在多个 RegisterWeapon 特性
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public class RegisterWeapon : Attribute
-{
-
- public string Id { get; private set; }
- public Type AttributeType { get; private set; }
-
- public RegisterWeapon(string id)
- {
- Id = id;
- }
-
- public RegisterWeapon(string id, Type attributeType)
- {
- Id = id;
- AttributeType = attributeType;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/RegisterWeaponFunction.cs b/DungeonShooting_Godot/src/game/props/weapon/RegisterWeaponFunction.cs
deleted file mode 100644
index 1796a95..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/RegisterWeaponFunction.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-///
-/// 用作静态函数上, 用于注册武器, 函数必须返回一个 Weapon 对象, 且参数为 string id,
-/// 那么它看起来应该像这样: static Weapon Method(string id);
-///
-[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
-public class RegisterWeaponFunction : Attribute
-{
- public string Id { get; private set; }
-
- public RegisterWeaponFunction(string id)
- {
- Id = id;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/ThrowGun.cs b/DungeonShooting_Godot/src/game/props/weapon/ThrowGun.cs
deleted file mode 100644
index d3c652a..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/ThrowGun.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Godot;
-
-public class ThrowWeapon : ThrowNode
-{
-
- private bool fristOver = true;
-
- public override void _Ready()
- {
- base._Ready();
- ZIndex = 2;
- }
-
- public override void StartThrow(Vector2 size, Vector2 start, float startHeight, float direction, float xSpeed, float ySpeed, float rotate, Node2D mount)
- {
- base.StartThrow(size, start, startHeight, direction, xSpeed, ySpeed, rotate, mount);
- fristOver = true;
- }
-
- protected override void OnOver()
- {
- if (fristOver)
- {
- fristOver = false;
- if (Mount is Weapon gun)
- {
- gun._FallToGround();
- }
- }
- //如果落地高度不够低, 再抛一次
- if (StartYSpeed > 1)
- {
- StartThrow(Size, GlobalPosition, 0, Direction, XSpeed * 0.8f, StartYSpeed * 0.5f, RotateSpeed * 0.5f, null);
- }
- else //结束
- {
- base.OnOver();
- }
- }
- protected override void OnMaxHeight(float height)
- {
- ZIndex = 0;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/props/weapon/Weapon.cs
deleted file mode 100644
index 0812079..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/Weapon.cs
+++ /dev/null
@@ -1,789 +0,0 @@
-using Godot;
-using System;
-
-///
-/// 武器的基类
-///
-public abstract class Weapon : ActivityObject
-{
-
- ///
- /// 武器的唯一id
- ///
- public string Id { get; }
-
- ///
- /// 开火回调事件
- ///
- public event Action FireEvent;
-
- ///
- /// 属性数据
- ///
- public WeaponAttribute Attribute { get; private set; }
-
- ///
- /// 武器的图片
- ///
- public Sprite WeaponSprite { get; private set; }
-
- ///
- /// 动画播放器
- ///
- ///
- public AnimationPlayer AnimationPlayer { get; private set; }
-
- ///
- /// 武器攻击的目标阵营
- ///
- public CampEnum TargetCamp { get; set; }
-
- ///
- /// 该武器的拥有者
- ///
- public Role Master { get; private set; }
-
- ///
- /// 当前弹夹弹药剩余量
- ///
- public int CurrAmmo { get; private set; }
- ///
- /// 剩余弹药量
- ///
- public int ResidueAmmo { get; private set; }
-
- ///
- /// 武器管的开火点
- ///
- public Position2D FirePoint { get; private set; }
- ///
- /// 武器管的原点
- ///
- public Position2D OriginPoint { get; private set; }
- ///
- /// 弹壳抛出的点
- ///
- public Position2D ShellPoint { get; private set; }
- ///
- /// 碰撞器节点
- ///
- ///
- public CollisionShape2D CollisionShape2D { get; private set; }
- ///
- /// 武器的当前散射半径
- ///
- public float CurrScatteringRange { get; private set; } = 0;
- ///
- /// 是否在换弹中
- ///
- ///
- public bool Reloading { get; private set; } = false;
- ///
- /// 换弹计时器
- ///
- public float ReloadTimer { get; private set; } = 0;
- ///
- /// 换弹进度 (0 - 1)
- ///
- public float ReloadProgress
- {
- get
- {
- if (Attribute.AloneReload)
- {
- var num = 1f / Attribute.AmmoCapacity;
- return num * (Attribute.AmmoCapacity - CurrAmmo - 1) + num * (ReloadTimer / Attribute.ReloadTime);
- }
- return ReloadTimer / Attribute.ReloadTime;
- }
- }
-
- //是否按下
- private bool triggerFlag = false;
- //扳机计时器
- private float triggerTimer = 0;
- //开火前延时时间
- private float delayedTime = 0;
- //开火间隙时间
- private float fireInterval = 0;
- //开火武器口角度
- private float fireAngle = 0;
- //攻击冷却计时
- private float attackTimer = 0;
- //攻击状态
- private bool attackFlag = false;
- //按下的时间
- private float downTimer = 0;
- //松开的时间
- private float upTimer = 0;
- //连发次数
- private float continuousCount = 0;
- //连发状态记录
- private bool continuousShootFlag = false;
-
- ///
- /// 根据属性创建一把武器
- ///
- /// 武器唯一id
- /// 属性
- public Weapon(string id, WeaponAttribute attribute)
- {
- Id = id;
- Attribute = attribute;
- //加载预制体
- var tempPrefab = ResourceManager.Load(Attribute.WeaponPrefab);
- if (tempPrefab == null)
- {
- throw new Exception("WeaponAttribute中未设置'WeaponPrefab'属性!");
- }
- var tempNode = tempPrefab.Instance();
- var body = tempNode.GetChild(0);
- tempNode.RemoveChild(body);
- AddChild(body);
-
- WeaponSprite = GetNode("WeaponBody/WeaponSprite");
- FirePoint = GetNode("WeaponBody/FirePoint");
- OriginPoint = GetNode("WeaponBody/OriginPoint");
- ShellPoint = GetNode("WeaponBody/ShellPoint");
- AnimationPlayer = GetNode("WeaponBody/AnimationPlayer");
- CollisionShape2D = GetNode("WeaponBody/Collision");
-
- //更新图片
- WeaponSprite.Texture = ResourceLoader.Load(Attribute.Sprite);
- WeaponSprite.Position = Attribute.CenterPosition;
- //开火位置
- FirePoint.Position = new Vector2(Attribute.FirePosition.x, -Attribute.FirePosition.y);
- OriginPoint.Position = new Vector2(0, -Attribute.FirePosition.y);
-
- //弹药量
- CurrAmmo = Attribute.AmmoCapacity;
- //剩余弹药量
- ResidueAmmo = Attribute.MaxAmmoCapacity - Attribute.AmmoCapacity;
- }
-
- ///
- /// 当按下扳机时调用
- ///
- protected abstract void OnDownTrigger();
-
- ///
- /// 当松开扳机时调用
- ///
- protected abstract void OnUpTrigger();
-
- ///
- /// 单次开火时调用的函数
- ///
- protected abstract void OnFire();
-
- ///
- /// 发射子弹时调用的函数, 每发射一枚子弹调用一次,
- /// 如果做霰弹武器效果, 一次开火发射5枚子弹, 则该函数调用5次
- ///
- protected abstract void OnShoot();
-
- ///
- /// 当开始换弹时调用
- ///
- protected abstract void OnReload();
-
- ///
- /// 当换弹完成时调用
- ///
- protected abstract void OnReloadFinish();
-
- ///
- /// 当武器被拾起时调用
- ///
- /// 拾起该武器的角色
- protected abstract void OnPickUp(Role master);
-
- ///
- /// 当武器从武器袋中扔掉时调用
- ///
- protected abstract void OnThrowOut();
-
- ///
- /// 当武器被激活时调用, 也就是使用当武器是调用
- ///
- protected abstract void OnActive();
-
- ///
- /// 当武器被收起时调用
- ///
- protected abstract void OnConceal();
-
- public override ComponentControl CreateComponentControl()
- {
- return new ComponentControl(this);
- }
-
- public override void _Process(float delta)
- {
- if (Master == null) //这把武器被扔在地上
- {
- Reloading = false;
- ReloadTimer = 0;
- triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
- triggerFlag = false;
- attackFlag = false;
- attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
- CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
- continuousCount = 0;
- delayedTime = 0;
- }
- else if (Master.Holster.ActiveWeapon != this) //当前武器没有被使用
- {
- Reloading = false;
- ReloadTimer = 0;
- triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
- triggerFlag = false;
- attackFlag = false;
- attackTimer = attackTimer > 0 ? attackTimer - delta : 0;
- CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
- continuousCount = 0;
- delayedTime = 0;
- }
- else //正在使用中
- {
-
- //换弹
- if (Reloading)
- {
- ReloadTimer -= delta;
- if (ReloadTimer <= 0)
- {
- ReloadSuccess();
- }
- }
-
- if (triggerFlag)
- {
- if (upTimer > 0) //第一帧按下扳机
- {
- upTimer = 0;
- DownTrigger();
- }
- downTimer += delta;
- }
- else
- {
- if (downTimer > 0) //第一帧松开扳机
- {
- downTimer = 0;
- UpTrigger();
- }
- upTimer += delta;
- }
-
- // 攻击的计时器
- if (attackTimer > 0)
- {
- attackTimer -= delta;
- if (attackTimer < 0)
- {
- delayedTime += attackTimer;
- attackTimer = 0;
- }
- }
- else if (delayedTime > 0) //攻击延时
- {
- delayedTime -= delta;
- if (attackTimer < 0)
- {
- delayedTime = 0;
- }
- }
-
- //连发判断
- if (continuousCount > 0 && delayedTime <= 0 && attackTimer <= 0)
- {
- //开火
- TriggerFire();
- }
-
- if (!attackFlag && attackTimer <= 0)
- {
- CurrScatteringRange = Mathf.Max(CurrScatteringRange - Attribute.ScatteringRangeBackSpeed * delta, Attribute.StartScatteringRange);
- }
- triggerTimer = triggerTimer > 0 ? triggerTimer - delta : 0;
- triggerFlag = false;
- attackFlag = false;
-
- //武器身回归
- Position = Position.MoveToward(Vector2.Zero, 35 * delta);
- if (fireInterval == 0)
- {
- RotationDegrees = 0;
- }
- else
- {
- RotationDegrees = Mathf.Lerp(0, fireAngle, attackTimer / fireInterval);
- }
- }
- }
-
- ///
- /// 扳机函数, 调用即视为扣动扳机
- ///
- public void Trigger()
- {
- //是否第一帧按下
- var justDown = downTimer == 0;
- //是否能发射
- var flag = false;
- if (continuousCount <= 0) //不能处于连发状态下
- {
- if (Attribute.ContinuousShoot) //自动射击
- {
- if (triggerTimer > 0)
- {
- if (continuousShootFlag)
- {
- flag = true;
- }
- }
- else
- {
- flag = true;
- if (delayedTime <= 0 && attackTimer <= 0)
- {
- continuousShootFlag = true;
- }
- }
- }
- else //半自动
- {
- if (justDown && triggerTimer <= 0)
- {
- flag = true;
- }
- }
- }
-
- if (flag)
- {
- var fireFlag = true; //是否能开火
- if (Reloading) //换弹中
- {
- if (CurrAmmo > 0 && Attribute.AloneReload && Attribute.AloneReloadCanShoot) //立即停止换弹
- {
- Reloading = false;
- ReloadTimer = 0;
- }
- else
- {
- fireFlag = false;
- }
- }
- else if (CurrAmmo <= 0)
- {
- fireFlag = false;
- //子弹不够
- _Reload();
- }
-
- if (fireFlag)
- {
- if (justDown)
- {
- //开火前延时
- delayedTime = Attribute.DelayedTime;
- //扳机按下间隔
- triggerTimer = Attribute.TriggerInterval;
- //连发数量
- if (!Attribute.ContinuousShoot)
- {
- continuousCount = MathUtils.RandRangeInt(Attribute.MinContinuousCount, Attribute.MaxContinuousCount);
- }
- }
- if (delayedTime <= 0 && attackTimer <= 0)
- {
- TriggerFire();
- }
- attackFlag = true;
- }
-
- }
- triggerFlag = true;
- }
-
- ///
- /// 刚按下扳机
- ///
- private void DownTrigger()
- {
- OnDownTrigger();
- }
-
- ///
- /// 刚松开扳机
- ///
- private void UpTrigger()
- {
- continuousShootFlag = false;
- if (delayedTime > 0)
- {
- continuousCount = 0;
- }
- OnUpTrigger();
- }
-
- ///
- /// 触发开火
- ///
- private void TriggerFire()
- {
- continuousCount = continuousCount > 0 ? continuousCount - 1 : 0;
-
- //减子弹数量
- CurrAmmo--;
- //开火间隙
- fireInterval = 60 / Attribute.StartFiringSpeed;
- //攻击冷却
- attackTimer += fireInterval;
-
- //触发开火函数
- OnFire();
-
- //开火发射的子弹数量
- var bulletCount = MathUtils.RandRangeInt(Attribute.MaxFireBulletCount, Attribute.MinFireBulletCount);
- //武器口角度
- var angle = new Vector2(GameConfig.ScatteringDistance, CurrScatteringRange).Angle();
-
- //创建子弹
- for (int i = 0; i < bulletCount; i++)
- {
- //先算武器口方向
- Rotation = (float)GD.RandRange(-angle, angle);
- //发射子弹
- OnShoot();
- }
-
- //当前的散射半径
- CurrScatteringRange = Mathf.Min(CurrScatteringRange + Attribute.ScatteringRangeAddValue, Attribute.FinalScatteringRange);
- //武器的旋转角度
- RotationDegrees -= Attribute.UpliftAngle;
- fireAngle = RotationDegrees;
- //武器身位置
- Position = new Vector2(Mathf.Max(-Attribute.MaxBacklash, Position.x - MathUtils.RandRange(Attribute.MinBacklash, Attribute.MaxBacklash)), Position.y);
-
- if (FireEvent != null)
- {
- FireEvent(this);
- }
- }
-
- ///
- /// 返回弹药是否到达上限
- ///
- public bool IsFullAmmo()
- {
- return CurrAmmo + ResidueAmmo >= Attribute.MaxAmmoCapacity;
- }
-
- ///
- /// 返回是否弹药耗尽
- ///
- public bool IsEmptyAmmo()
- {
- return CurrAmmo + ResidueAmmo == 0;
- }
-
- ///
- /// 拾起的弹药数量, 如果到达容量上限, 则返回拾取完毕后剩余的弹药数量
- ///
- /// 弹药数量
- public int PickUpAmmo(int count)
- {
- var num = ResidueAmmo;
- ResidueAmmo = Mathf.Min(ResidueAmmo + count, Attribute.MaxAmmoCapacity - CurrAmmo);
- return count - ResidueAmmo + num;
- }
-
- ///
- /// 触发换弹
- ///
- public void _Reload()
- {
- if (CurrAmmo < Attribute.AmmoCapacity && ResidueAmmo > 0 && !Reloading)
- {
- Reloading = true;
- ReloadTimer = Attribute.ReloadTime;
- OnReload();
- }
- }
-
- ///
- /// 换弹计时器时间到, 执行换弹操作
- ///
- private void ReloadSuccess()
- {
- if (Attribute.AloneReload) //单独装填
- {
- if (ResidueAmmo >= Attribute.AloneReloadCount) //剩余子弹充足
- {
- if (CurrAmmo + Attribute.AloneReloadCount <= Attribute.AmmoCapacity)
- {
- ResidueAmmo -= Attribute.AloneReloadCount;
- CurrAmmo += Attribute.AloneReloadCount;
- }
- else //子弹满了
- {
- var num = Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- ResidueAmmo -= num;
- }
- }
- else if (ResidueAmmo != 0) //剩余子弹不足
- {
- if (ResidueAmmo + CurrAmmo <= Attribute.AmmoCapacity)
- {
- CurrAmmo += ResidueAmmo;
- ResidueAmmo = 0;
- }
- else //子弹满了
- {
- var num = Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- ResidueAmmo -= num;
- }
- }
- if (ResidueAmmo == 0 || CurrAmmo == Attribute.AmmoCapacity) //换弹结束
- {
- Reloading = false;
- ReloadTimer = 0;
- OnReloadFinish();
- }
- else
- {
- ReloadTimer = Attribute.ReloadTime;
- OnReload();
- }
- }
- else //换弹结束
- {
- if (ResidueAmmo >= Attribute.AmmoCapacity)
- {
- ResidueAmmo -= Attribute.AmmoCapacity - CurrAmmo;
- CurrAmmo = Attribute.AmmoCapacity;
- }
- else
- {
- CurrAmmo = ResidueAmmo;
- ResidueAmmo = 0;
- }
- Reloading = false;
- ReloadTimer = 0;
- OnReloadFinish();
- }
- }
-
- public override CheckInteractiveResult CheckInteractive(ActivityObject master)
- {
- var result = new CheckInteractiveResult(this);
-
- if (master is Role roleMaster) //碰到角色
- {
- if (Master == null)
- {
- var masterWeapon = roleMaster.Holster.ActiveWeapon;
- //查找是否有同类型武器
- var index = roleMaster.Holster.FindWeapon(Id);
- if (index != -1) //如果有这个武器
- {
- if (CurrAmmo + ResidueAmmo != 0) //子弹不为空
- {
- var targetWeapon = roleMaster.Holster.GetWeapon(index);
- if (!targetWeapon.IsFullAmmo()) //背包里面的武器子弹未满
- {
- //可以互动拾起弹药
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = "res://resource/sprite/ui/icon/icon_bullet.png";
- return result;
- }
- }
- }
- else //没有武器
- {
- if (roleMaster.Holster.CanPickupWeapon(this)) //能拾起武器
- {
- //可以互动, 拾起武器
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = "res://resource/sprite/ui/icon/icon_pickup.png";
- return result;
- }
- else if (masterWeapon != null && masterWeapon.Attribute.WeightType == Attribute.WeightType) //替换武器
- {
- //可以互动, 切换武器
- result.CanInteractive = true;
- result.Message = Attribute.Name;
- result.ShowIcon = "res://resource/sprite/ui/icon/icon_replace.png";
- return result;
- }
- }
- }
- }
-
- return result;
- }
-
- public override void Interactive(ActivityObject master)
- {
- if (master is Role roleMaster) //与role碰撞
- {
- //查找是否有同类型武器
- var index = roleMaster.Holster.FindWeapon(Id);
- if (index != -1) //如果有这个武器
- {
- if (CurrAmmo + ResidueAmmo == 0) //没有子弹了
- {
- return;
- }
-
- var weapon = roleMaster.Holster.GetWeapon(index);
- //子弹上限
- var maxCount = Attribute.MaxAmmoCapacity;
- //是否捡到子弹
- var flag = false;
- if (ResidueAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
- {
- var count = weapon.PickUpAmmo(ResidueAmmo);
- if (count != ResidueAmmo)
- {
- ResidueAmmo = count;
- flag = true;
- }
- }
-
- if (CurrAmmo > 0 && weapon.CurrAmmo + weapon.ResidueAmmo < maxCount)
- {
- var count = weapon.PickUpAmmo(CurrAmmo);
- if (count != CurrAmmo)
- {
- CurrAmmo = count;
- flag = true;
- }
- }
-
- //播放互动效果
- if (flag)
- {
- this.StartThrow(new Vector2(20, 20), GlobalPosition, 0, 0,
- MathUtils.RandRangeInt(-20, 20), MathUtils.RandRangeInt(20, 50),
- MathUtils.RandRangeInt(-180, 180), WeaponSprite);
- }
- }
- else //没有武器
- {
- if (roleMaster.Holster.PickupWeapon(this) == -1)
- {
- var slot = roleMaster.Holster.SlotList[roleMaster.Holster.ActiveIndex];
- if (slot.Type == Attribute.WeightType)
- {
- var weapon = roleMaster.Holster.RmoveWeapon(roleMaster.Holster.ActiveIndex);
- weapon.StartThrowWeapon(roleMaster);
- roleMaster.PickUpWeapon(this);
- }
- }
- }
- }
- }
-
- public Vector2 GetItemPosition()
- {
- return GlobalPosition;
- }
-
- ///
- /// 触发落到地面
- ///
- public void _FallToGround()
- {
- //启用碰撞
- CollisionShape2D.Disabled = false;
- }
-
- ///
- /// 触发拾起
- ///
- public void _PickUpWeapon(Role master)
- {
- Master = master;
- //握把位置
- WeaponSprite.Position = Attribute.HoldPosition;
- //清除泛白效果
- ShaderMaterial sm = WeaponSprite.Material as ShaderMaterial;
- sm.SetShaderParam("schedule", 0);
- //停止动画
- AnimationPlayer.Stop();
- ZIndex = 0;
- //禁用碰撞
- CollisionShape2D.Disabled = true;
- OnPickUp(master);
- }
-
- ///
- /// 触发抛出
- /// a
- public void _ThrowOutWeapon()
- {
- Master = null;
- WeaponSprite.Position = Attribute.CenterPosition;
- AnimationPlayer.Play("Floodlight");
- OnThrowOut();
- }
-
- ///
- /// 触发启用武器
- ///
- public void _Active()
- {
- OnActive();
- }
-
- ///
- /// 触发收起武器
- ///
- public void _Conceal()
- {
- OnConceal();
- }
-
- ///
- /// 实例化并返回子弹对象
- ///
- /// 子弹的预制体
- protected T CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null) where T : Node2D, IBullet
- {
- return (T)CreateBullet(bulletPack, globalPostion, globalRotation, parent);
- }
-
- ///
- /// 实例化并返回子弹对象
- ///
- /// 子弹的预制体
- protected IBullet CreateBullet(PackedScene bulletPack, Vector2 globalPostion, float globalRotation, Node parent = null)
- {
- // 实例化子弹
- Node2D bullet = bulletPack.Instance();
- // 设置坐标
- bullet.GlobalPosition = globalPostion;
- // 旋转角度
- bullet.GlobalRotation = globalRotation;
- if (parent == null)
- {
- RoomManager.Current.SortRoot.AddChild(bullet);
- }
- else
- {
- parent.AddChild(bullet);
- }
- // 调用初始化
- IBullet result = (IBullet)bullet;
- result.Init(TargetCamp, this, null);
- return result;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/WeaponAttribute.cs b/DungeonShooting_Godot/src/game/props/weapon/WeaponAttribute.cs
deleted file mode 100644
index c6cad38..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/WeaponAttribute.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using Godot;
-
-///
-/// 武器上的属性
-///
-public class WeaponAttribute
-{
- ///
- /// 武器显示的名称
- ///
- public string Name = "Gun1";
- ///
- /// 武器 Prefab, 必须继承场景 "res://prefab/weapon/Weapon.tscn"
- ///
- public string WeaponPrefab = "res://prefab/weapon/Weapon.tscn";
- ///
- /// 武器类型
- ///
- public WeaponWeightType WeightType = WeaponWeightType.MainWeapon;
- ///
- /// 武器的图片
- ///
- public string Sprite = "res://resource/sprite/gun/gun1.png";
- ///
- /// 是否连续发射, 如果为false, 则每次发射都需要扣动扳机
- ///
- public bool ContinuousShoot = true;
- ///
- /// 弹夹容量
- ///
- public int AmmoCapacity = 30;
- ///
- /// 弹药容量上限
- ///
- public int MaxAmmoCapacity = 90;
- ///
- /// 装弹时间, 单位: 秒
- ///
- public float ReloadTime = 1.5f;
- ///
- /// 每粒子弹是否是单独装填, 如果是, 那么每上一发子弹的时间就是 ReloadTime, 可以做霰弹武器装填效果
- ///
- public bool AloneReload = false;
- ///
- /// 单独装填时每次装填子弹数量, 必须要将 'AloneReload' 属性设置为 true
- ///
- public int AloneReloadCount = 1;
- ///
- /// 单独装填的子弹时可以立即射击, 必须要将 'AloneReload' 属性设置为 true
- ///
- public bool AloneReloadCanShoot = false;
- ///
- /// 是否为松发开火, 也就是松开扳机才开火, 若要启用该属性, 必须将 'ContinuousShoot' 设置为 false
- ///
- public bool LooseShoot = false;
- ///
- /// 连续发射最小次数, 仅当 ContinuousShoot 为 false 时生效
- ///
- public int MinContinuousCount = 3;
- ///
- /// 连续发射最大次数, 仅当 ContinuousShoot 为 false 时生效
- ///
- public int MaxContinuousCount = 3;
- ///
- /// 按下一次扳机后需要多长时间才能再次按下
- ///
- public float TriggerInterval = 0;
- ///
- /// 初始射速, 初始每秒分钟能发射多少发子弹
- ///
- public float StartFiringSpeed = 300;
- ///
- /// 最终射速, 最终每秒分钟能发射多少发子弹
- ///
- public float FinalFiringSpeed = 300;
- ///
- /// 按下扳机并开火后射速增加速率
- ///
- public float FiringSpeedAddSpeed = 2;
- ///
- /// 松开扳机后射速消散速率
- ///
- public float FiringSpeedBackSpeed = 10;
- ///
- /// 单次开火发射子弹最小数量
- ///
- public int MinFireBulletCount = 1;
- ///
- /// 单次开火发射子弹最大数量
- ///
- public int MaxFireBulletCount = 1;
- ///
- /// 开火前延时
- ///
- public float DelayedTime = 0f;
- ///
- /// 初始散射半径
- ///
- public float StartScatteringRange = 0;
- ///
- /// 最终散射半径
- ///
- public float FinalScatteringRange = 20;
- ///
- /// 每次发射后散射增加值
- ///
- public float ScatteringRangeAddValue = 2;
- ///
- /// 松开扳机后散射销退速率
- ///
- public float ScatteringRangeBackSpeed = 10;
- ///
- /// 子弹飞行最大距离
- ///
- public float MaxDistance = 600;
- ///
- /// 子弹飞行最小距离
- ///
- public float MinDistance = 800;
- ///
- /// 武器精灵的旋转中心坐标
- ///
- public Vector2 CenterPosition = new Vector2(0, 0);
- ///
- /// 开火位置
- ///
- public Vector2 FirePosition = new Vector2(11, 0);
- ///
- /// 握把位置
- ///
- public Vector2 HoldPosition = new Vector2(4, -3);
- ///
- /// 重量
- ///
- public float Weight = 11;
- ///
- /// 最大后坐力 (仅用于开火后武器身抖动)
- ///
- public float MaxBacklash = 4;
- ///
- /// 最小后坐力 (仅用于开火后武器身抖动)
- ///
- public float MinBacklash = 2;
- ///
- /// 开火后武器口上抬角度
- ///
- public float UpliftAngle = 30;
- ///
- /// 开火后武器口角度恢复速度倍数
- ///
- public float UpliftAngleRestore = 1;
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/WeaponManager.cs b/DungeonShooting_Godot/src/game/props/weapon/WeaponManager.cs
deleted file mode 100644
index d5e8bf1..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/WeaponManager.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using System.Reflection;
-using System;
-using System.Collections.Generic;
-using Godot;
-
-///
-/// 武器管理类, 负责武器注册和创建
-///
-public static class WeaponManager
-{
- private static Dictionary> registerData = new Dictionary>();
-
- ///
- /// 从一个指定的程序集中扫描并注册武器对象
- ///
- /// 数据集
- public static void RegisterWeaponFromAssembly(Assembly assembly)
- {
- var types = assembly.GetTypes();
- foreach (var type in types)
- {
- //注册类
- Attribute[] attribute = Attribute.GetCustomAttributes(type, typeof(RegisterWeapon), false);
- if (attribute != null && attribute.Length > 0)
- {
- if (!typeof(Weapon).IsAssignableFrom(type))
- {
- throw new Exception($"注册武器类'{type.FullName}'没有继承类'Weapon'!");
- }
- var atts = (RegisterWeapon[])attribute;
- foreach (var att in atts)
- {
- //注册类
- if (att.AttributeType == null) //没有指定属性类型
- {
- RegisterWeapon(att.Id, () =>
- {
- return Activator.CreateInstance(type, att.Id, new WeaponAttribute()) as Weapon;
- });
- }
- else
- {
- if (!typeof(WeaponAttribute).IsAssignableFrom(att.AttributeType))
- {
- throw new Exception($"注册武器类'{type.FullName}'标注的特性中参数'AttributeType'类型没有继承'WeaponAttribute'!");
- }
- RegisterWeapon(att.Id, () =>
- {
- return Activator.CreateInstance(type, att.Id, Activator.CreateInstance(att.AttributeType)) as Weapon;
- });
- }
- }
- }
-
- //注册函数
- MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
- foreach (var method in methods)
- {
- Attribute mAttribute;
- //
- if ((mAttribute = Attribute.GetCustomAttribute(method, typeof(RegisterWeaponFunction), false)) != null)
- {
- if (!typeof(Weapon).IsAssignableFrom(method.ReturnType)) //返回值类型不是 Weapon
- {
- throw new Exception($"注册武器函数'{method.DeclaringType.FullName}.{method.Name}'返回值类型不为'Weapon'!");
- }
- var args = method.GetParameters();
- if (args == null || args.Length != 1 || args[0].ParameterType != typeof(string)) //参数类型不正确
- {
- throw new Exception($"注册武器函数'{method.DeclaringType.FullName}.{method.Name}'参数不满足(string id)类型");
- }
- var att = (RegisterWeaponFunction)mAttribute;
- //注册函数
- RegisterWeapon(att.Id, () =>
- {
- return method.Invoke(null, new object[] { att.Id }) as Weapon;
- });
- }
- }
- }
- }
-
- ///
- /// 注册当个武器对象
- ///
- /// 武器唯一id, 该id不能重复
- /// 获取武器时的回调函数, 函数返回武器对象
- public static void RegisterWeapon(string id, Func callBack)
- {
- if (registerData.ContainsKey(id))
- {
- throw new Exception($"武器id: '{id} 已经被注册!'");
- }
- registerData.Add(id, callBack);
- }
-
- ///
- /// 根据武器唯一id获取
- ///
- /// 武器id
- public static Weapon GetGun(string id)
- {
- if (registerData.TryGetValue(id, out var callback))
- {
- return callback();
- }
- throw new Exception($"当前武器'{id}未被注册'");
- }
-
- ///
- /// 根据武器唯一id获取
- ///
- /// 武器id
- public static T GetGun(string id) where T : Weapon
- {
- return (T)GetGun(id);
- }
-}
diff --git a/DungeonShooting_Godot/src/game/props/weapon/WeaponWeightType.cs b/DungeonShooting_Godot/src/game/props/weapon/WeaponWeightType.cs
deleted file mode 100644
index 5528683..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/WeaponWeightType.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-
-///
-/// 根据重量划分的武器类型
-///
-public enum WeaponWeightType
-{
- ///
- /// 副武器
- ///
- DeputyWeapon = 1,
- ///
- /// 主武器
- ///
- MainWeapon = 2,
- ///
- /// 重型武器
- ///
- HeavyWeapon = 3
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/props/weapon/bullet/Bullet.cs
deleted file mode 100644
index 4c19b4b..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/bullet/Bullet.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Godot;
-
-///
-/// 子弹接口
-///
-public interface IBullet
-{
- ///
- /// 攻击目标阵营
- ///
- CampEnum TargetCamp { get; }
- ///
- /// 发射该子弹的武器
- ///
- Weapon Gun { get; }
- ///
- /// 发射该子弹的物体对象
- ///
- Node2D Master { get; }
- ///
- /// 初始化基础数据
- ///
- /// 攻击的目标阵营
- /// 发射该子弹的枪对象
- /// 发射该子弹的角色
- void Init(CampEnum target, Weapon gun, Node2D master);
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/bullet/HighSpeedBullet.cs b/DungeonShooting_Godot/src/game/props/weapon/bullet/HighSpeedBullet.cs
deleted file mode 100644
index 53ef711..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/bullet/HighSpeedBullet.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Godot;
-
-///
-/// 高速子弹
-///
-public class HighSpeedBullet : Node2D, IBullet
-{
- public CampEnum TargetCamp { get; private set; }
-
- public Weapon Gun { get; private set; }
-
- public Node2D Master { get; private set; }
-
- ///
- /// 碰撞物体后产生的火花
- ///
- [Export] public PackedScene Hit;
-
- //射线检测节点
- private RayCast2D RayCast2D;
- private Line2D Line;
- private float ca = 1;
-
- public void Init(CampEnum target, Weapon gun, Node2D master)
- {
- TargetCamp = target;
- Gun = gun;
- Master = master;
-
- //飞行距离
- var distance = MathUtils.RandRange(gun.Attribute.MinDistance, gun.Attribute.MaxDistance);
-
- //初始化子弹数据
- RayCast2D = GetNode("RayCast2D");
- Line = GetNode("Line");
- Modulate = Colors.White;
-
- // 目标点
- Vector2 targetPos = new Vector2(distance, 0);
- RayCast2D.CastTo = targetPos;
- RayCast2D.ForceRaycastUpdate();
- if (RayCast2D.IsColliding())
- {
- //碰到物体
- Vector2 collPosition = RayCast2D.GetCollisionPoint();
- Node2D hit = Hit.Instance();
- hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
- hit.GlobalPosition = collPosition;
- GetTree().CurrentScene.AddChild(hit);
- //划线的点坐标
- Line.SetPointPosition(1, new Vector2(Line.GlobalPosition.DistanceTo(collPosition), 0));
- }
- else
- {
- //划线的点坐标
- Line.SetPointPosition(1, targetPos);
- }
- RayCast2D.Enabled = false;
- }
-
- public override void _Process(float delta)
- {
- ca -= 12 * delta;
- if (ca <= 0) {
- QueueFree();
- return;
- }
- Color c = Modulate;
- c.a = ca;
- Modulate = c;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/bullet/OrdinaryBullets.cs b/DungeonShooting_Godot/src/game/props/weapon/bullet/OrdinaryBullets.cs
deleted file mode 100644
index 4458e5f..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/bullet/OrdinaryBullets.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using Godot;
-
-///
-/// 普通的子弹
-///
-public class OrdinaryBullets : Node2D, IBullet
-{
- public CampEnum TargetCamp { get; private set; }
-
- public Weapon Gun { get; private set; }
-
- public Node2D Master { get; private set; }
-
- ///
- /// 碰撞物体后产生的火花
- ///
- [Export] public PackedScene Hit;
-
- // 起始点坐标
- private Vector2 StartPosition;
- // 最大飞行距离
- private float MaxDistance;
- // 子弹飞行速度
- private float FlySpeed = 1500;
- //当前子弹已经飞行的距离
- private float CurrFlyDistance = 0;
- //射线碰撞节点
- private RayCast2D RayCast;
- //子弹的精灵
- private Sprite BulletSprite;
- //绘制阴影的精灵
- private Sprite ShadowSprite;
-
- private int frame = 0;
-
- public void Init(CampEnum target, Weapon gun, Node2D master)
- {
- TargetCamp = target;
- Gun = gun;
- Master = master;
-
- MaxDistance = MathUtils.RandRange(gun.Attribute.MinDistance, gun.Attribute.MaxDistance);
- StartPosition = GlobalPosition;
- BulletSprite = GetNode("Bullet");
- BulletSprite.Visible = false;
- RayCast = GetNode("RayCast2D");
-
- //创建阴影
- ShadowSprite = new Sprite();
- ShadowSprite.Visible = false;
- ShadowSprite.ZIndex = -1;
- ShadowSprite.Texture = BulletSprite.Texture;
- ShadowSprite.Offset = BulletSprite.Offset;
- ShadowSprite.Material = ResourceManager.ShadowMaterial;
- AddChild(ShadowSprite);
- }
-
- public override void _Ready()
- {
- //生成时播放音效
- SoundManager.PlaySoundEffect("ordinaryBullet.ogg", this, 6f);
- }
-
- public override void _PhysicsProcess(float delta)
- {
- if (frame++ == 0)
- {
- BulletSprite.Visible = true;
- ShadowSprite.Visible = true;
- }
- //碰到墙壁
- if (RayCast.IsColliding())
- {
- //var target = RayCast.GetCollider();
- var pos = RayCast.GetCollisionPoint();
- //播放受击动画
- Node2D hit = Hit.Instance();
- hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
- hit.GlobalPosition = pos;
- GetTree().CurrentScene.AddChild(hit);
- QueueFree();
- }
- else //没有碰到, 继续移动
- {
- ShadowSprite.GlobalPosition = GlobalPosition + new Vector2(0, 5);
- Position += new Vector2(FlySpeed * delta, 0).Rotated(Rotation);
-
- CurrFlyDistance += FlySpeed * delta;
- if (CurrFlyDistance >= MaxDistance)
- {
- QueueFree();
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/gun/Gun.cs b/DungeonShooting_Godot/src/game/props/weapon/gun/Gun.cs
deleted file mode 100644
index ca9ce7e..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/gun/Gun.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-using Godot;
-
-[RegisterWeapon("1001", typeof(Gun.RifleAttribute))]
-[RegisterWeapon("1003", typeof(Gun.PistolAttribute))]
-///
-/// 普通的枪
-///
-public class Gun : Weapon
-{
- //步枪属性数据
- private class RifleAttribute : WeaponAttribute
- {
- public RifleAttribute()
- {
- Name = "步枪";
- Sprite = "res://resource/sprite/gun/gun4.png";
- Weight = 40;
- CenterPosition = new Vector2(0.4f, -2.6f);
- StartFiringSpeed = 480;
- StartScatteringRange = 30;
- FinalScatteringRange = 90;
- ScatteringRangeAddValue = 2f;
- ScatteringRangeBackSpeed = 40;
- //连发
- ContinuousShoot = true;
- //扳机检测间隔
- TriggerInterval = 0f;
- //连发数量
- MinContinuousCount = 3;
- MaxContinuousCount = 3;
- //开火前延时
- DelayedTime = 0f;
- //攻击距离
- MinDistance = 500;
- MaxDistance = 600;
- //发射子弹数量
- MinFireBulletCount = 1;
- MaxFireBulletCount = 1;
- //抬起角度
- UpliftAngle = 10;
- //枪身长度
- FirePosition = new Vector2(16, 1.5f);
- }
- }
-
- //手枪属性数据
- private class PistolAttribute : WeaponAttribute
- {
- public PistolAttribute()
- {
- Name = "手枪";
- Sprite = "res://resource/sprite/gun/gun3.png";
- Weight = 20;
- CenterPosition = new Vector2(0.4f, -2.6f);
- WeightType = WeaponWeightType.DeputyWeapon;
- StartFiringSpeed = 300;
- StartScatteringRange = 5;
- FinalScatteringRange = 60;
- ScatteringRangeAddValue = 8f;
- ScatteringRangeBackSpeed = 40;
- //连发
- ContinuousShoot = false;
- AmmoCapacity = 12;
- MaxAmmoCapacity = 72;
- //扳机检测间隔
- TriggerInterval = 0.1f;
- //连发数量
- MinContinuousCount = 1;
- MaxContinuousCount = 1;
- //开火前延时
- DelayedTime = 0f;
- //攻击距离
- MinDistance = 500;
- MaxDistance = 600;
- //发射子弹数量
- MinFireBulletCount = 1;
- MaxFireBulletCount = 1;
- //抬起角度
- UpliftAngle = 30;
- //枪身长度
- FirePosition = new Vector2(10, 1.5f);
- }
- }
-
- ///
- /// 子弹预制体
- ///
- public PackedScene BulletPack;
- ///
- /// 弹壳预制体
- ///
- public PackedScene ShellPack;
-
- public Gun(string id, WeaponAttribute attribute): base(id, attribute)
- {
- BulletPack = ResourceManager.Load("res://prefab/weapon/bullet/OrdinaryBullets.tscn");
- ShellPack = ResourceManager.Load("res://prefab/weapon/shell/ShellCase.tscn");
- }
-
- protected override void OnFire()
- {
- //创建一个弹壳
- var startPos = GlobalPosition + new Vector2(0, 5);
- var startHeight = 6;
- var direction = GlobalRotationDegrees + MathUtils.RandRangeInt(-30, 30) + 180;
- var xf = MathUtils.RandRangeInt(20, 60);
- var yf = MathUtils.RandRangeInt(60, 120);
- var rotate = MathUtils.RandRangeInt(-720, 720);
- var sprite = ShellPack.Instance();
- sprite.StartThrow(new Vector2(5, 10), startPos, startHeight, direction, xf, yf, rotate, sprite);
- //创建抖动
- MainCamera.Main.ProssesDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
- }
-
- protected override void OnShoot()
- {
- //创建子弹
- CreateBullet(BulletPack, FirePoint.GlobalPosition, (FirePoint.GlobalPosition - OriginPoint.GlobalPosition).Angle());
- }
-
- protected override void OnReload()
- {
-
- }
-
- protected override void OnReloadFinish()
- {
-
- }
-
- protected override void OnDownTrigger()
- {
-
- }
-
- protected override void OnUpTrigger()
- {
-
- }
-
- protected override void OnPickUp(Role master)
- {
-
- }
-
- protected override void OnThrowOut()
- {
-
- }
-
- protected override void OnActive()
- {
-
- }
-
- protected override void OnConceal()
- {
-
- }
-
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/props/weapon/gun/Shotgun.cs b/DungeonShooting_Godot/src/game/props/weapon/gun/Shotgun.cs
deleted file mode 100644
index 66cff82..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/gun/Shotgun.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using Godot;
-
-[RegisterWeapon("1002", typeof(Shotgun.ShotgunAttribute))]
-public class Shotgun : Weapon
-{
-
- private class ShotgunAttribute : WeaponAttribute
- {
- public ShotgunAttribute()
- {
- Name = "霰弹枪";
- Sprite = "res://resource/sprite/gun/gun2.png";
- Weight = 40;
- CenterPosition = new Vector2(0.4f, -2.6f);
- StartFiringSpeed = 120;
- StartScatteringRange = 30;
- FinalScatteringRange = 90;
- ScatteringRangeAddValue = 50f;
- ScatteringRangeBackSpeed = 50;
- //连发
- ContinuousShoot = false;
- AmmoCapacity = 7;
- MaxAmmoCapacity = 42;
- AloneReload = true;
- AloneReloadCanShoot = true;
- ReloadTime = 0.3f;
- //连发数量
- MinContinuousCount = 1;
- MaxContinuousCount = 1;
- //开火前延时
- DelayedTime = 0f;
- //攻击距离
- MinDistance = 500;
- MaxDistance = 600;
- //发射子弹数量
- MinFireBulletCount = 1;
- MaxFireBulletCount = 1;
- //抬起角度
- UpliftAngle = 15;
- MaxBacklash = 6;
- MinBacklash = 5;
- //枪身长度
- FirePosition = new Vector2(16, 1.5f);
- }
- }
-
- ///
- /// 子弹预制体
- ///
- public PackedScene BulletPack;
- ///
- /// 弹壳预制体
- ///
- public PackedScene ShellPack;
-
- public Shotgun(string id, WeaponAttribute attribute) : base(id, attribute)
- {
- BulletPack = ResourceManager.Load("res://prefab/weapon/bullet/OrdinaryBullets.tscn");
- ShellPack = ResourceManager.Load("res://prefab/weapon/shell/ShellCase.tscn");
- }
-
- protected override void OnFire()
- {
- //创建一个弹壳
- var startPos = GlobalPosition + new Vector2(0, 5);
- var startHeight = 6;
- var direction = GlobalRotationDegrees + MathUtils.RandRangeInt(-30, 30) + 180;
- var xf = MathUtils.RandRangeInt(20, 60);
- var yf = MathUtils.RandRangeInt(60, 120);
- var rotate = MathUtils.RandRangeInt(-720, 720);
- var sprite = ShellPack.Instance();
- sprite.StartThrow(new Vector2(5, 10), startPos, startHeight, direction, xf, yf, rotate, sprite);
- //创建抖动
- MainCamera.Main.ProssesDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
- }
-
- protected override void OnShoot()
- {
- for (int i = 0; i < 5; i++)
- {
- //创建子弹
- CreateBullet(BulletPack, FirePoint.GlobalPosition, (FirePoint.GlobalPosition - OriginPoint.GlobalPosition).Angle() + MathUtils.RandRange(-20 / 180f * Mathf.Pi, 20 / 180f * Mathf.Pi));
- }
- }
-
- protected override void OnReload()
- {
-
- }
-
- protected override void OnReloadFinish()
- {
-
- }
-
- protected override void OnDownTrigger()
- {
-
- }
-
- protected override void OnUpTrigger()
- {
-
- }
-
- protected override void OnPickUp(Role master)
- {
-
- }
-
- protected override void OnThrowOut()
- {
-
- }
-
- protected override void OnActive()
- {
-
- }
-
- protected override void OnConceal()
- {
-
- }
-}
diff --git a/DungeonShooting_Godot/src/game/props/weapon/shell/ThrowShell.cs b/DungeonShooting_Godot/src/game/props/weapon/shell/ThrowShell.cs
deleted file mode 100644
index 914ee5d..0000000
--- a/DungeonShooting_Godot/src/game/props/weapon/shell/ThrowShell.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Godot;
-
-///
-/// 弹壳
-///
-public class ThrowShell : ThrowNode
-{
-
- public override void _Ready()
- {
- base._Ready();
- ZIndex = 2;
- }
-
- protected override void OnOver()
- {
- //如果落地高度不够低, 再抛一次
- if (StartYSpeed > 1)
- {
- StartThrow(Size, GlobalPosition, 0, Direction, XSpeed * 0.8f, StartYSpeed * 0.5f, RotateSpeed * 0.5f, null);
- }
- else
- {
- base.OnOver();
- //等待被销毁
- AwaitDestroy();
- }
- }
-
- private async void AwaitDestroy()
- {
- CollisionShape.Disabled = true;
- //60秒后销毁
- await ToSignal(GetTree().CreateTimer(60), "timeout");
- QueueFree();
- }
-
- protected override void OnMaxHeight(float height)
- {
- ZIndex = 0;
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/Role.cs b/DungeonShooting_Godot/src/game/role/Role.cs
index d5e6583..dffd83d 100644
--- a/DungeonShooting_Godot/src/game/role/Role.cs
+++ b/DungeonShooting_Godot/src/game/role/Role.cs
@@ -107,11 +107,6 @@
/// 检测是否可互动时的返回值
protected abstract void ChangeInteractiveItem(CheckInteractiveResult result);
- public override ComponentControl CreateComponentControl()
- {
- return new ComponentControl(this);
- }
-
public override CheckInteractiveResult CheckInteractive(ActivityObject master)
{
return null;