diff --git a/DungeonShooting_Godot/prefab/role/Role.tscn b/DungeonShooting_Godot/prefab/role/Role.tscn
index d1e173b..28ad803 100644
--- a/DungeonShooting_Godot/prefab/role/Role.tscn
+++ b/DungeonShooting_Godot/prefab/role/Role.tscn
@@ -129,6 +129,7 @@
position = Vector2( 0, -12 )
frames = SubResource( 6 )
animation = "idle"
+frame = 1
playing = true
[node name="Collision" type="CollisionShape2D" parent="."]
diff --git a/DungeonShooting_Godot/prefab/weapon/bullet/Bullet.tscn b/DungeonShooting_Godot/prefab/weapon/bullet/Bullet.tscn
index cc8610a..5b9b099 100644
--- a/DungeonShooting_Godot/prefab/weapon/bullet/Bullet.tscn
+++ b/DungeonShooting_Godot/prefab/weapon/bullet/Bullet.tscn
@@ -13,19 +13,26 @@
} ]
[sub_resource type="RectangleShape2D" id=2]
-extents = Vector2( 5.5, 2 )
+extents = Vector2( 6, 2 )
[node name="Bullet" type="Node"]
script = ExtResource( 1 )
-CollisionLayer = 2
[node name="ShadowSprite" type="Sprite" parent="."]
material = ExtResource( 2 )
[node name="AnimatedSprite" type="AnimatedSprite" parent="."]
-position = Vector2( -1, 0 )
+modulate = Color( 1.5, 1.5, 1.5, 1 )
+position = Vector2( 2.38419e-07, 0 )
frames = SubResource( 1 )
[node name="Collision" type="CollisionShape2D" parent="."]
position = Vector2( 1.5, 0 )
+
+[node name="CollisionArea" type="Area2D" parent="."]
+collision_layer = 0
+collision_mask = 0
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="CollisionArea"]
+position = Vector2( 2, 0 )
shape = SubResource( 2 )
diff --git a/DungeonShooting_Godot/project.godot b/DungeonShooting_Godot/project.godot
index acd52e2..e1ec7c8 100644
--- a/DungeonShooting_Godot/project.godot
+++ b/DungeonShooting_Godot/project.godot
@@ -26,6 +26,7 @@
window/size/width=1920
window/size/height=1080
+window/size/resizable=false
window/dpi/allow_hidpi=true
window/vsync/use_vsync=false
window/stretch/mode="2d"
diff --git a/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png b/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png
new file mode 100644
index 0000000..3cea799
--- /dev/null
+++ b/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png
Binary files differ
diff --git a/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png.import b/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png.import
new file mode 100644
index 0000000..b2003cd
--- /dev/null
+++ b/DungeonShooting_Godot/resource/sprite/bullet/bullet2.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/bullet2.png-6ca9527061677971732c8192cb1aa209.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resource/sprite/bullet/bullet2.png"
+dest_files=[ "res://.import/bullet2.png-6ca9527061677971732c8192cb1aa209.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=false
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/DungeonShooting_Godot/scene/Room.tscn b/DungeonShooting_Godot/scene/Room.tscn
index 9f664bf..a9137ae 100644
--- a/DungeonShooting_Godot/scene/Room.tscn
+++ b/DungeonShooting_Godot/scene/Room.tscn
@@ -1,12 +1,19 @@
-[gd_scene load_steps=4 format=2]
+[gd_scene load_steps=5 format=2]
[ext_resource path="res://resource/map/dungeon_test.tmx" type="PackedScene" id=2]
[ext_resource path="res://src/game/room/RoomManager.cs" type="Script" id=3]
[ext_resource path="res://src/game/camera/GameCamera.cs" type="Script" id=5]
+[sub_resource type="Environment" id=1]
+background_mode = 4
+glow_enabled = true
+
[node name="Room" type="Node2D"]
script = ExtResource( 3 )
+[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
+environment = SubResource( 1 )
+
[node name="MapRoot" type="Node2D" parent="."]
z_index = -10
@@ -15,7 +22,6 @@
[node name="Camera2D" type="Camera2D" parent="."]
position = Vector2( 253, 219 )
current = true
-process_mode = 0
limit_smoothed = true
editor_draw_drag_margin = true
script = ExtResource( 5 )
diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs
index 6416fd1..4de5ab2 100644
--- a/DungeonShooting_Godot/src/framework/ActivityObject.cs
+++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs
@@ -339,10 +339,15 @@
public void RemoveComponent(Component component)
{
- if (ContainsComponent(component))
+ for (int i = 0; i < _components.Count; i++)
{
- component.OnUnMount();
- component._SetActivityObject(null);
+ if (_components[i].Value == component)
+ {
+ _components.RemoveAt(i);
+ component.OnUnMount();
+ component._SetActivityObject(null);
+ return;
+ }
}
}
@@ -381,10 +386,10 @@
{
if (!temp.IsStart)
{
- temp.Start();
+ temp.Ready();
}
- temp.Update(delta);
+ temp.Process(delta);
}
}
}
@@ -509,10 +514,10 @@
{
if (!temp.IsStart)
{
- temp.Start();
+ temp.Ready();
}
- temp.PhysicsUpdate(delta);
+ temp.PhysicsProcess(delta);
}
}
}
@@ -547,6 +552,7 @@
CallDeferred(nameof(Destroy));
}
+ //返回该组件是否被挂载到当前物体上
private bool ContainsComponent(Component component)
{
for (int i = 0; i < _components.Count; i++)
diff --git a/DungeonShooting_Godot/src/framework/Component.cs b/DungeonShooting_Godot/src/framework/Component.cs
index 3e1d828..16c81e0 100644
--- a/DungeonShooting_Godot/src/framework/Component.cs
+++ b/DungeonShooting_Godot/src/framework/Component.cs
@@ -30,6 +30,69 @@
}
///
+ /// 当前组件所挂载物体的缩放
+ ///
+ public Vector2 Scale
+ {
+ get => ActivityObject.Scale;
+ set => ActivityObject.Scale = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的全局缩放
+ ///
+ public Vector2 GlobalScale
+ {
+ get => ActivityObject.GlobalScale;
+ set => ActivityObject.GlobalScale = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的旋转角度
+ ///
+ public float Rotation
+ {
+ get => ActivityObject.Rotation;
+ set => ActivityObject.Rotation = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的全局旋转角度
+ ///
+ public float GlobalRotation
+ {
+ get => ActivityObject.GlobalRotation;
+ set => ActivityObject.GlobalRotation = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的角度制旋转角度
+ ///
+ public float RotationDegrees
+ {
+ get => ActivityObject.RotationDegrees;
+ set => ActivityObject.RotationDegrees = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的全局角度制旋转角度
+ ///
+ public float GlobalRotationDegrees
+ {
+ get => ActivityObject.GlobalRotationDegrees;
+ set => ActivityObject.GlobalRotationDegrees = value;
+ }
+
+ ///
+ /// 当前组件所挂载物体的ZIndex
+ ///
+ public int ZIndex
+ {
+ get => ActivityObject.ZIndex;
+ set => ActivityObject.ZIndex = value;
+ }
+
+ ///
/// 当前组件是否显示
///
public bool Visible
@@ -52,9 +115,27 @@
public CollisionShape2D Collision => ActivityObject.Collision;
///
- /// 是否启用当前组件
+ /// 是否启用当前组件, 如果禁用, 则不会调用 Process 和 PhysicsProcess
///
- public bool Enable { get; set; } = true;
+ public bool Enable
+ {
+ get => _enable;
+ set
+ {
+ if (!_enable && value)
+ {
+ OnEnable();
+ }
+ else if (_enable && !value)
+ {
+ OnDisable();
+ }
+
+ _enable = value;
+ }
+ }
+
+ private bool _enable = true;
///
/// 是否被销毁
@@ -65,25 +146,25 @@
internal bool IsStart = false;
///
- /// 第一次调用 Update 或 PhysicsUpdate 之前调用
+ /// 第一次调用 Process 或 PhysicsProcess 之前调用
///
- public virtual void Start()
+ public virtual void Ready()
{
}
///
- /// 如果启用了组件, 则每帧会调用一次 Update
+ /// 如果启用了组件, 则每帧会调用一次 Process
///
///
- public virtual void Update(float delta)
+ public virtual void Process(float delta)
{
}
///
- /// 如果启用了组件, 则每物理帧会调用一次 PhysicsUpdate
+ /// 如果启用了组件, 则每物理帧会调用一次 PhysicsProcess
///
///
- public virtual void PhysicsUpdate(float delta)
+ public virtual void PhysicsProcess(float delta)
{
}
@@ -109,6 +190,20 @@
}
///
+ /// 当组件启用时调用
+ ///
+ public virtual void OnEnable()
+ {
+ }
+
+ ///
+ /// 当组件禁用时调用
+ ///
+ public virtual void OnDisable()
+ {
+ }
+
+ ///
/// 当组件销毁
///
public void Destroy()
diff --git a/DungeonShooting_Godot/src/framework/IProcess.cs b/DungeonShooting_Godot/src/framework/IProcess.cs
index bc64da8..3e5777f 100644
--- a/DungeonShooting_Godot/src/framework/IProcess.cs
+++ b/DungeonShooting_Godot/src/framework/IProcess.cs
@@ -7,10 +7,10 @@
///
/// 普通帧每帧调用
///
- void Update(float delta);
+ void Process(float delta);
///
/// 物理帧每帧调用
///
- void PhysicsUpdate(float delta);
+ void PhysicsProcess(float delta);
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index 517f5ef..51aa4d2 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -44,6 +44,9 @@
///
public RoomUI Ui { get; private set; }
+ ///
+ /// 全局根节点
+ ///
public Node2D GlobalNodeRoot { get; private set; }
public GameApplication()
diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs
index 4c43084..5e40c91 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs
@@ -8,18 +8,25 @@
public Bullet(string scenePath, float maxDistance, Vector2 position, float rotation, uint targetLayer) :
base(scenePath)
{
+ CollisionArea = GetNode("CollisionArea");
+ CollisionArea.CollisionMask = targetLayer;
+ CollisionArea.Connect("body_entered", this, nameof(_BodyEntered));
+
+ Collision.Disabled = true;
+
MaxDistance = maxDistance;
- CollisionMask = targetLayer;
Position = position;
Rotation = rotation;
ShadowOffset = new Vector2(0, 5);
}
+ public Area2D CollisionArea { get; }
+
// 最大飞行距离
private float MaxDistance;
// 子弹飞行速度
- private float FlySpeed = 600;
+ private float FlySpeed = 450;
//当前子弹已经飞行的距离
private float CurrFlyDistance = 0;
@@ -34,35 +41,29 @@
public override void _PhysicsProcess(float delta)
{
base._PhysicsProcess(delta);
-
//移动
- var kinematicCollision = MoveAndCollide(new Vector2(FlySpeed * delta, 0).Rotated(Rotation));
- //有碰撞
- if (kinematicCollision == null)
+ Position += new Vector2(FlySpeed * delta, 0).Rotated(Rotation);
+ //距离太大, 自动销毁
+ CurrFlyDistance += FlySpeed * delta;
+ if (CurrFlyDistance >= MaxDistance)
{
- //距离太大, 自动销毁
- CurrFlyDistance += FlySpeed * delta;
- if (CurrFlyDistance >= MaxDistance)
- {
- Destroy();
- }
- }
- else
- {
- var collider = kinematicCollision.Collider;
- if (collider is Role role)
- {
- role.Hit(1);
- }
-
- //播放受击动画
- Node2D hit = ResourceManager.Load(ResourcePath.prefab_effect_Hit_tscn).Instance();
- hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
- hit.GlobalPosition = kinematicCollision.Position;
- GameApplication.Instance.Room.GetRoot(true).AddChild(hit);
-
Destroy();
}
+ }
+ private void _BodyEntered(Node2D other)
+ {
+ if (other is Role role)
+ {
+ role.Hit(1);
+ }
+
+ //播放受击动画
+ Node2D hit = ResourceManager.Load(ResourcePath.prefab_effect_Hit_tscn).Instance();
+ hit.RotationDegrees = MathUtils.RandRangeInt(0, 360);
+ hit.GlobalPosition = GlobalPosition;
+ GameApplication.Instance.Room.GetRoot(true).AddChild(hit);
+
+ Destroy();
}
}
\ 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
index b1162a5..c2d6da6 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/gun/Gun.cs
@@ -23,8 +23,8 @@
ScatteringRangeBackSpeed = 40;
//连发
ContinuousShoot = true;
- AmmoCapacity = 120;
- MaxAmmoCapacity = 120 * 70;
+ AmmoCapacity = 30;
+ MaxAmmoCapacity = 30 * 70;
//扳机检测间隔
TriggerInterval = 0f;
//连发数量
@@ -106,7 +106,7 @@
GameCamera.Main.ProcessDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
}
//播放射击音效
- SoundManager.PlaySoundEffect("ordinaryBullet.ogg", this, 6f);
+ SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet_ogg, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), 6f);
}
protected override void OnShoot(float fireRotation)
diff --git a/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs b/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
index 8d04498..f1424b1 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/gun/Shotgun.cs
@@ -72,7 +72,7 @@
GameCamera.Main.ProcessDirectionalShake(Vector2.Right.Rotated(GlobalRotation) * 1.5f);
}
//播放射击音效
- SoundManager.PlaySoundEffect("ordinaryBullet.ogg", this, 6f);
+ SoundManager.PlaySoundEffectPosition(ResourcePath.resource_sound_sfx_ordinaryBullet_ogg, GameApplication.Instance.ViewToGlobalPosition(GlobalPosition), 6f);
}
protected override void OnShoot(float fireRotation)
diff --git a/DungeonShooting_Godot/src/game/item/weapon/shell/ShellCase.cs b/DungeonShooting_Godot/src/game/item/weapon/shell/ShellCase.cs
index cf520ef..60e73d6 100644
--- a/DungeonShooting_Godot/src/game/item/weapon/shell/ShellCase.cs
+++ b/DungeonShooting_Godot/src/game/item/weapon/shell/ShellCase.cs
@@ -20,6 +20,6 @@
{
//30秒后销毁
await ToSignal(GetTree().CreateTimer(30), "timeout");
- QueueFree();
+ Destroy();
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/manager/ResourceManager.cs b/DungeonShooting_Godot/src/game/manager/ResourceManager.cs
index 4c69cd1..686c730 100644
--- a/DungeonShooting_Godot/src/game/manager/ResourceManager.cs
+++ b/DungeonShooting_Godot/src/game/manager/ResourceManager.cs
@@ -14,9 +14,11 @@
{
_shadowMaterial = ResourceLoader.Load(ResourcePath.resource_materlal_Blend_tres);
}
+
return _shadowMaterial;
}
}
+
private static ShaderMaterial _shadowMaterial;
///
@@ -30,9 +32,11 @@
{
_shadowShader = ResourceLoader.Load(ResourcePath.resource_materlal_Blend_gdshader);
}
+
return _shadowShader;
}
}
+
private static Shader _shadowShader;
private static readonly Dictionary CachePack = new Dictionary();
@@ -55,7 +59,12 @@
CachePack.Add(path, pack);
return pack as T;
}
+ else
+ {
+ GD.PrintErr("加载资源失败, 未找到资源: " + path);
+ }
}
- return default(T);
+
+ return default;
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/manager/SoundManager.cs b/DungeonShooting_Godot/src/game/manager/SoundManager.cs
index 2cf4f34..d7dfd26 100644
--- a/DungeonShooting_Godot/src/game/manager/SoundManager.cs
+++ b/DungeonShooting_Godot/src/game/manager/SoundManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Godot;
///
@@ -15,70 +16,158 @@
///
public class SoundManager
{
- public static SoundManager Instance { get => SingleTon.singleTon; }
+ private static Stack _streamPlayer2DStack = new Stack();
+ private static Stack _streamPlayerStack = new Stack();
- private static class SingleTon
+ ///
+ /// 2D音频播放节点
+ ///
+ private class AudioPlayer2D : AudioStreamPlayer2D
{
- internal static SoundManager singleTon = new SoundManager();
+ public override void _Ready()
+ {
+ Connect("finished", this, nameof(OnPlayFinish));
+ }
+
+ public void OnPlayFinish()
+ {
+ RecycleAudioPlayer2D(this);
+ }
}
- private static AudioStreamPlayer audioStreamPlayer = new AudioStreamPlayer();
-
-
///
- /// 背景音乐路径
+ /// 音频播放节点
///
- public static string BGMPath = "res://resource/sound/bgm/";
- ///
- /// 音效路径
- ///
- public static string SFXPath = "res://resource/sound/sfx/";
+ private class AudioPlayer : AudioStreamPlayer
+ {
+ public override void _Ready()
+ {
+ Connect("finished", this, nameof(OnPlayFinish));
+ }
+
+ public void OnPlayFinish()
+ {
+ RecycleAudioPlayer(this);
+ }
+ }
///
/// 播放声音 用于bgm
///
- /// bgm名字 在resource/sound/bgm/目录下
- /// 需要播放声音得节点 将成为音频播放节点的父节点
+ /// bgm路径
/// 音量
- public static void PlayeMusic(string soundName, Node node, float volume)
+ public static AudioStreamPlayer PlayMusic(string soundName, float volume = 0.5f)
{
- AudioStream sound = ResourceManager.Load(BGMPath + soundName);
- if (sound != null)
- {
- AudioStreamPlayer soundNode = new AudioStreamPlayer();
- node.AddChild(soundNode);
- soundNode.Stream = sound;
- soundNode.Bus = Enum.GetName(typeof(BUS), 0);
- soundNode.VolumeDb = volume;
- soundNode.Play();
- }
- else
- {
- GD.Print("没有这个资源!!!");
- }
+ var sound = ResourceManager.Load(soundName);
+ var soundNode = GetAudioPlayerInstance();
+ GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
+ soundNode.Stream = sound;
+ soundNode.Bus = Enum.GetName(typeof(BUS), 0);
+ soundNode.VolumeDb = volume;
+ soundNode.Play();
+ return soundNode;
}
+
///
/// 添加并播放音效 用于音效
///
- /// 音效文件名字 在resource/sound/sfx/目录下
- /// 需要播放声音得节点 将成为音频播放节点的父节点
+ /// 音效文件路径
/// 音量
- public static void PlaySoundEffect(string soundName, Node node, float volume = 0f)
+ public static AudioStreamPlayer PlaySoundEffect(string soundName, float volume = 0.5f)
{
- AudioStream sound = ResourceManager.Load(SFXPath + soundName);
- if (sound != null)
+ var sound = ResourceManager.Load(soundName);
+ var soundNode = GetAudioPlayerInstance();
+ GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
+ soundNode.Stream = sound;
+ soundNode.Bus = Enum.GetName(typeof(BUS), 1);
+ soundNode.VolumeDb = volume;
+ soundNode.Play();
+ return soundNode;
+ }
+
+ ///
+ /// 在指定的节点下播放音效 用于音效
+ ///
+ /// 音效文件路径
+ /// 发声节点所在全局坐标
+ /// 音量
+ /// 挂载节点, 为null则挂载到房间根节点下
+ public static AudioStreamPlayer2D PlaySoundEffectPosition(string soundName, Vector2 pos, float volume = 0.5f, Node2D target = null)
+ {
+ var sound = ResourceManager.Load(soundName);
+ var soundNode = GetAudioPlayer2DInstance();
+ if (target != null)
{
- AudioStreamPlayer soundNode = new AudioStreamPlayer();
- node.AddChild(soundNode);
- soundNode.Stream = sound;
- soundNode.Bus = Enum.GetName(typeof(BUS), 1);
- soundNode.VolumeDb = volume;
- soundNode.Play();
- GD.Print("bus:", soundNode.Bus);
+ target.AddChild(soundNode);
}
else
{
- GD.Print("没有这个资源!!!");
+ GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
}
+
+ soundNode.GlobalPosition = pos;
+ soundNode.Stream = sound;
+ soundNode.Bus = Enum.GetName(typeof(BUS), 1);
+ soundNode.VolumeDb = volume;
+ soundNode.Play();
+ return soundNode;
+ }
+
+ ///
+ /// 获取2D音频播放节点
+ ///
+ private static AudioPlayer2D GetAudioPlayer2DInstance()
+ {
+ if (_streamPlayer2DStack.Count > 0)
+ {
+ return _streamPlayer2DStack.Pop();
+ }
+
+ var inst = new AudioPlayer2D();
+ inst.AreaMask = 0;
+ return inst;
+ }
+
+ ///
+ /// 获取音频播放节点
+ ///
+ private static AudioPlayer GetAudioPlayerInstance()
+ {
+ if (_streamPlayerStack.Count > 0)
+ {
+ return _streamPlayerStack.Pop();
+ }
+
+ return new AudioPlayer();
+ }
+
+ ///
+ /// 回收2D音频播放节点
+ ///
+ private static void RecycleAudioPlayer2D(AudioPlayer2D inst)
+ {
+ var parent = inst.GetParent();
+ if (parent != null)
+ {
+ parent.RemoveChild(inst);
+ }
+
+ inst.Stream = null;
+ _streamPlayer2DStack.Push(inst);
+ }
+
+ ///
+ /// 回收音频播放节点
+ ///
+ private static void RecycleAudioPlayer(AudioPlayer inst)
+ {
+ var parent = inst.GetParent();
+ if (parent != null)
+ {
+ parent.RemoveChild(inst);
+ }
+
+ inst.Stream = null;
+ _streamPlayerStack.Push(inst);
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/Enemy.cs b/DungeonShooting_Godot/src/game/role/Enemy.cs
index 922e7f6..20b3a77 100644
--- a/DungeonShooting_Godot/src/game/role/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/role/Enemy.cs
@@ -5,15 +5,26 @@
{
AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Props | PhysicsLayer.Player;
Camp = CampEnum.Camp2;
+
+ MoveSpeed = 20;
+ LookTarget = GameApplication.Instance.Room.Player;
}
public override void _Process(float delta)
{
base._Process(delta);
-
- if (Holster.ActiveWeapon != null)
+ Attack();
+ }
+
+ public override void _PhysicsProcess(float delta)
+ {
+ base._PhysicsProcess(delta);
+
+ if (LookTarget != null)
{
- Holster.ActiveWeapon.Trigger();
+ AnimatedSprite.Animation = AnimatorNames.ReverseRun;
+ Velocity = (LookTarget.GlobalPosition - GlobalPosition).Normalized() * MoveSpeed;
+ CalcMove(delta);
}
}
}
diff --git a/DungeonShooting_Godot/src/game/role/Player.cs b/DungeonShooting_Godot/src/game/role/Player.cs
index d1dadc4..89b3681 100644
--- a/DungeonShooting_Godot/src/game/role/Player.cs
+++ b/DungeonShooting_Godot/src/game/role/Player.cs
@@ -2,51 +2,16 @@
public class Player : Role
{
+
///
/// 移动加速度
///
- public float Acceleration = 1500f;
-
+ public float Acceleration { get; set; } = 1500f;
+
///
/// 移动摩擦力
///
- public float Friction = 800f;
- ///
- /// 移动速度
- ///
- public Vector2 Velocity = Vector2.Zero;
-
- ///
- /// 当前护盾值
- ///
- public int Shield
- {
- get => _shield;
- protected set
- {
- int temp = _shield;
- _shield = value;
- if (temp != _shield) OnChangeShield(_shield);
- }
- }
- private int _shield = 0;
-
- ///
- /// 最大护盾值
- ///
- public int MaxShield
- {
- get => _maxShield;
- protected set
- {
- int temp = _maxShield;
- _maxShield = value;
- if (temp != _maxShield) OnChangeMaxShield(_maxShield);
- }
- }
- private int _maxShield = 0;
-
- [Export] public PackedScene GunPrefab;
+ public float Friction { get; set; } = 800f;
public Player(): base(ResourcePath.prefab_role_Player_tscn)
{
@@ -139,11 +104,9 @@
public override void _PhysicsProcess(float delta)
{
base._PhysicsProcess(delta);
- Move(delta);
+ HandleMoveInput(delta);
//播放动画
PlayAnim();
- //GlobalPosition = GlobalPosition.Round();
- //AnimatedSprite.Playing = false;
}
protected override void OnChangeHp(int hp)
@@ -173,12 +136,12 @@
}
}
- protected void OnChangeShield(int shield)
+ protected override void OnChangeShield(int shield)
{
GameApplication.Instance.Ui.SetShield(shield);
}
- protected void OnChangeMaxShield(int maxShield)
+ protected override void OnChangeMaxShield(int maxShield)
{
GameApplication.Instance.Ui.SetMaxShield(maxShield);
}
@@ -211,20 +174,33 @@
}
}
- private void Move(float delta)
+ //处理角色移动的输入
+ private void HandleMoveInput(float delta)
{
//角色移动
// 得到输入的 vector2 getvector方法返回值已经归一化过了noemalized
Vector2 dir = Input.GetVector("move_left", "move_right", "move_up", "move_down");
- // 移动. 如果移动的数值接近0(是用 摇杆可能出现 方向 可能会出现浮点),就fricition的值 插值 到 0
+ // 移动. 如果移动的数值接近0(是用 摇杆可能出现 方向 可能会出现浮点),就friction的值 插值 到 0
// 如果 有输入 就以当前速度,用acceleration 插值到 对应方向 * 最大速度
- if (Mathf.IsZeroApprox(dir.x)) Velocity.x = Mathf.MoveToward(Velocity.x, 0, Friction * delta);
- else Velocity.x = Mathf.MoveToward(Velocity.x, dir.x * MoveSpeed, Acceleration * delta);
+ if (Mathf.IsZeroApprox(dir.x))
+ {
+ Velocity = new Vector2(Mathf.MoveToward(Velocity.x, 0, Friction * delta), Velocity.y);
+ }
+ else
+ {
+ Velocity = new Vector2(Mathf.MoveToward(Velocity.x, dir.x * MoveSpeed, Acceleration * delta), Velocity.y);
+ }
- if (Mathf.IsZeroApprox(dir.y)) Velocity.y = Mathf.MoveToward(Velocity.y, 0, Friction * delta);
- else Velocity.y = Mathf.MoveToward(Velocity.y, dir.y * MoveSpeed, Acceleration * delta);
-
- Velocity = MoveAndSlide(Velocity);
+ if (Mathf.IsZeroApprox(dir.y))
+ {
+ Velocity = new Vector2(Velocity.x, Mathf.MoveToward(Velocity.y, 0, Friction * delta));
+ }
+ else
+ {
+ Velocity = new Vector2(Velocity.x, Mathf.MoveToward(Velocity.y, dir.y * MoveSpeed, Acceleration * delta));
+ }
+
+ CalcMove(delta);
}
// 播放动画
diff --git a/DungeonShooting_Godot/src/game/role/Role.cs b/DungeonShooting_Godot/src/game/role/Role.cs
index 51e9377..1387d2b 100644
--- a/DungeonShooting_Godot/src/game/role/Role.cs
+++ b/DungeonShooting_Godot/src/game/role/Role.cs
@@ -69,6 +69,16 @@
private FaceDirection _face;
///
+ /// 是否启用角色移动
+ ///
+ public bool EnableMove { get; set; } = true;
+
+ ///
+ /// 移动速度
+ ///
+ public Vector2 Velocity { get; set; } = Vector2.Zero;
+
+ ///
/// 血量
///
public int Hp
@@ -97,18 +107,53 @@
}
}
private int _maxHp = 0;
+
+ ///
+ /// 当前护盾值
+ ///
+ public int Shield
+ {
+ get => _shield;
+ protected set
+ {
+ int temp = _shield;
+ _shield = value;
+ if (temp != _shield) OnChangeShield(_shield);
+ }
+ }
+ private int _shield = 0;
+
+ ///
+ /// 最大护盾值
+ ///
+ public int MaxShield
+ {
+ get => _maxShield;
+ protected set
+ {
+ int temp = _maxShield;
+ _maxShield = value;
+ if (temp != _maxShield) OnChangeMaxShield(_maxShield);
+ }
+ }
+ private int _maxShield = 0;
///
/// 当前角色所看向的对象, 也就是枪口指向的对象
///
public ActivityObject LookTarget { get; set; }
+ ///
+ ///
+ ///
+ public StateCtr StateCtr { get; }
+
//初始缩放
- private Vector2 StartScele;
+ private Vector2 _startScale;
//所有角色碰撞的道具
- private readonly List InteractiveItemList = new List();
+ private readonly List _interactiveItemList = new List();
- private CheckInteractiveResult TempResultData;
+ private CheckInteractiveResult _tempResultData;
///
/// 可以互动的道具
@@ -128,6 +173,20 @@
protected virtual void OnChangeMaxHp(int maxHp)
{
}
+
+ ///
+ /// 护盾值改变时调用
+ ///
+ protected virtual void OnChangeShield(int shield)
+ {
+ }
+
+ ///
+ /// 最大护盾值改变时调用
+ ///
+ protected virtual void OnChangeMaxShield(int maxShield)
+ {
+ }
///
/// 当受伤时调用
@@ -165,7 +224,7 @@
{
base._Ready();
AnimationPlayer = GetNode("AnimationPlayer");
- StartScele = Scale;
+ _startScale = Scale;
MountPoint = GetNode("MountPoint");
MountPoint.Master = this;
BackMountPoint = GetNode("BackMountPoint");
@@ -209,12 +268,12 @@
//检查可互动的道具
bool findFlag = false;
- for (int i = 0; i < InteractiveItemList.Count; i++)
+ for (int i = 0; i < _interactiveItemList.Count; i++)
{
- var item = InteractiveItemList[i];
+ var item = _interactiveItemList[i];
if (item == null)
{
- InteractiveItemList.RemoveAt(i--);
+ _interactiveItemList.RemoveAt(i--);
}
else
{
@@ -230,12 +289,12 @@
InteractiveItem = item;
ChangeInteractiveItem(result);
}
- else if (result.ShowIcon != TempResultData.ShowIcon) //切换状态
+ else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态
{
ChangeInteractiveItem(result);
}
}
- TempResultData = result;
+ _tempResultData = result;
}
}
}
@@ -248,6 +307,17 @@
}
///
+ /// 计算角色移动
+ ///
+ public virtual void CalcMove(float delta)
+ {
+ if (EnableMove && Velocity != Vector2.Zero)
+ {
+ Velocity = MoveAndSlide(Velocity);
+ }
+ }
+
+ ///
/// 拾起一个武器, 并且切换到这个武器
///
/// 武器对象
@@ -256,7 +326,7 @@
if (Holster.PickupWeapon(weapon) != -1)
{
//从可互动队列中移除
- InteractiveItemList.Remove(weapon);
+ _interactiveItemList.Remove(weapon);
}
}
@@ -356,12 +426,12 @@
if (face == FaceDirection.Right)
{
RotationDegrees = 0;
- Scale = StartScele;
+ Scale = _startScale;
}
else
{
RotationDegrees = 180;
- Scale = new Vector2(StartScele.x, -StartScele.y);
+ Scale = new Vector2(_startScale.x, -_startScale.y);
}
}
}
@@ -393,9 +463,9 @@
ActivityObject propObject = other.AsActivityObject();
if (propObject != null)
{
- if (!InteractiveItemList.Contains(propObject))
+ if (!_interactiveItemList.Contains(propObject))
{
- InteractiveItemList.Add(propObject);
+ _interactiveItemList.Add(propObject);
}
}
}
@@ -409,9 +479,9 @@
ActivityObject propObject = other.AsActivityObject();
if (propObject != null)
{
- if (InteractiveItemList.Contains(propObject))
+ if (_interactiveItemList.Contains(propObject))
{
- InteractiveItemList.Remove(propObject);
+ _interactiveItemList.Remove(propObject);
}
if (InteractiveItem == propObject)
{
diff --git a/DungeonShooting_Godot/src/game/role/state/IState.cs b/DungeonShooting_Godot/src/game/role/state/IState.cs
new file mode 100644
index 0000000..8276dd0
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/state/IState.cs
@@ -0,0 +1,44 @@
+///
+/// 状态接口
+///
+public interface IState
+{
+ ///
+ /// 当前状态对象对应的状态枚举类型
+ ///
+ StateEnum StateType { get; }
+
+ ///
+ /// 当前状态对象挂载的角色对象
+ ///
+ Role Role { get; set; }
+
+ ///
+ /// 当前状态对象所处的状态机对象
+ ///
+ StateCtr StateController { get; set; }
+
+ ///
+ /// 当从其他状态进入到当前状态时调用
+ ///
+ /// 上一个状态类型
+ /// 切换当前状态时附带的参数
+ void Enter(StateEnum prev, params object[] args);
+
+ ///
+ /// 物理帧每帧更新
+ ///
+ void PhysicsProcess(float delta);
+
+ ///
+ /// 是否允许切换至下一个状态
+ ///
+ /// 下一个状态类型
+ bool CanChangeState(StateEnum next);
+
+ ///
+ /// 从当前状态退出时调用
+ ///
+ /// 下一个状态类型
+ void Exit(StateEnum next);
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/state/StateCtr.cs b/DungeonShooting_Godot/src/game/role/state/StateCtr.cs
new file mode 100644
index 0000000..d0305ce
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/state/StateCtr.cs
@@ -0,0 +1,109 @@
+using Godot;
+using System.Collections.Generic;
+
+///
+/// 角色状态机控制器
+///
+public class StateCtr : Component
+{
+ ///
+ /// 当前活跃的状态
+ ///
+ public IState CurrState => _currState;
+ private IState _currState;
+
+ ///
+ /// 负责存放状态实例对象
+ ///
+ private readonly Dictionary _states = new Dictionary();
+
+ ///
+ /// 记录下当前帧是否有改变的状态
+ ///
+ private bool _isChangeState;
+
+ public override void PhysicsProcess(float delta)
+ {
+ _isChangeState = false;
+ if (CurrState != null)
+ {
+ CurrState.PhysicsProcess(delta);
+ //判断当前帧是否有改变的状态, 如果有, 则重新调用 PhysicsProcess() 方法
+ if (_isChangeState)
+ {
+ PhysicsProcess(delta);
+ }
+ }
+ }
+
+ ///
+ /// 往状态机力注册一个新的状态
+ ///
+ public void Register(IState state)
+ {
+ if (GetStateInstance(state.StateType) != null)
+ {
+ GD.PrintErr("当前状态已经在状态机中注册:", state);
+ return;
+ }
+ state.Role = ActivityObject as Role;
+ state.StateController = this;
+ _states.Add(state.StateType, state);
+ }
+
+ ///
+ /// 立即切换到下一个指定状态, 并且这一帧会被调用 PhysicsProcess
+ ///
+ public void ChangeState(StateEnum next, params object[] arg)
+ {
+ _changeState(false, next, arg);
+ }
+
+ ///
+ /// 切换到下一个指定状态, 下一帧才会调用 PhysicsProcess
+ ///
+ public void ChangeStateLate(StateEnum next, params object[] arg)
+ {
+ _changeState(true, next, arg);
+ }
+
+ ///
+ /// 根据状态类型获取相应的状态对象
+ ///
+ private IState GetStateInstance(StateEnum stateType)
+ {
+ _states.TryGetValue(stateType, out var v);
+ return v;
+ }
+
+ ///
+ /// 切换状态
+ ///
+ private void _changeState(bool late, StateEnum next, params object[] arg)
+ {
+ if (_currState != null && _currState.StateType == next)
+ {
+ return;
+ }
+ var newState = GetStateInstance(next);
+ if (newState == null)
+ {
+ GD.PrintErr("当前状态机未找到相应状态:" + next);
+ return;
+ }
+ if (_currState == null)
+ {
+ _currState = newState;
+ newState.Enter(StateEnum.None, arg);
+ }
+ else if (_currState.CanChangeState(next))
+ {
+ _isChangeState = !late;
+ var prev = _currState.StateType;
+ _currState.Exit(next);
+ GD.Print("nextState => " + next);
+ _currState = newState;
+ _currState.Enter(prev, arg);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/state/StateEnum.cs b/DungeonShooting_Godot/src/game/role/state/StateEnum.cs
new file mode 100644
index 0000000..151b523
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/role/state/StateEnum.cs
@@ -0,0 +1,8 @@
+
+public enum StateEnum
+{
+ None = 0,
+ Idle = 1,
+ Run = 2,
+ Move = 3,
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/room/RoomManager.cs b/DungeonShooting_Godot/src/game/room/RoomManager.cs
index e7769d2..d8f1587 100644
--- a/DungeonShooting_Godot/src/game/room/RoomManager.cs
+++ b/DungeonShooting_Godot/src/game/room/RoomManager.cs
@@ -38,8 +38,7 @@
public override void _Ready()
{
//播放bgm
- SoundManager.PlayeMusic("intro.ogg", this, -17f);
- _enemy.LookTarget = Player;
+ SoundManager.PlayMusic(ResourcePath.resource_sound_bgm_Intro_ogg, -17f);
_enemy.PickUpWeapon(WeaponManager.GetGun("1001"));
WeaponManager.GetGun("1001").PutDown(new Vector2(80, 100));