diff --git a/DungeonShooting_Godot/prefab/role/Player0001.tscn b/DungeonShooting_Godot/prefab/role/Player0001.tscn new file mode 100644 index 0000000..dc986d2 --- /dev/null +++ b/DungeonShooting_Godot/prefab/role/Player0001.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=3 uid="uid://dhvpoolrlrbq7"] + +[ext_resource type="Script" path="res://src/game/role/Player.cs" id="1_dl8pn"] +[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="2_ispp0"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_4wbfd"] +resource_local_to_scene = true +shader = ExtResource("2_ispp0") +shader_parameter/blend = Color(0, 0, 0, 0.470588) +shader_parameter/schedule = 1 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_mr55t"] +resource_local_to_scene = true +shader = ExtResource("2_ispp0") +shader_parameter/blend = Color(1, 1, 1, 1) +shader_parameter/schedule = 0 + +[node name="Player0001" type="CharacterBody2D" node_paths=PackedStringArray("ShadowSprite", "AnimatedSprite", "Collision")] +script = ExtResource("1_dl8pn") +ShadowSprite = NodePath("CanvasGroup/ShadowSprite") +AnimatedSprite = NodePath("CanvasGroup/AnimatedSprite") +Collision = NodePath("Collision") + +[node name="CanvasGroup" type="CanvasGroup" parent="."] + +[node name="ShadowSprite" type="Sprite2D" parent="CanvasGroup"] +z_index = -1 +material = SubResource("ShaderMaterial_4wbfd") + +[node name="AnimatedSprite" type="AnimatedSprite2D" parent="CanvasGroup"] +material = SubResource("ShaderMaterial_mr55t") + +[node name="Collision" type="CollisionShape2D" parent="."] diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs index acb7a15..50cf1dc 100644 --- a/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs +++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject.cs @@ -26,21 +26,24 @@ /// 是否是静态物体, 如果为true, 则会禁用移动处理 /// public bool IsStatic { get; set; } - - /// - /// 当前物体显示的精灵图像, 节点名称必须叫 "AnimatedSprite2D", 类型为 AnimatedSprite2D - /// - public AnimatedSprite2D AnimatedSprite { get; private set; } /// /// 当前物体显示的阴影图像, 节点名称必须叫 "ShadowSprite", 类型为 Sprite2D /// - public Sprite2D ShadowSprite { get; private set; } + [Export, ExportFillNode] + public Sprite2D ShadowSprite { get; set; } + + /// + /// 当前物体显示的精灵图像, 节点名称必须叫 "AnimatedSprite2D", 类型为 AnimatedSprite2D + /// + [Export, ExportFillNode] + public AnimatedSprite2D AnimatedSprite { get; set; } /// /// 当前物体碰撞器节点, 节点名称必须叫 "Collision", 类型为 CollisionShape2D /// - public CollisionShape2D Collision { get; private set; } + [Export, ExportFillNode] + public CollisionShape2D Collision { get; set; } /// /// 是否调用过 Destroy() 函数 @@ -62,8 +65,22 @@ /// public Vector2 BasisVelocity { - get => MoveController.BasisVelocity; - set => MoveController.BasisVelocity = value; + get + { + if (MoveController != null) + { + return MoveController.BasisVelocity; + } + + return Vector2.Zero; + } + set + { + if (MoveController != null) + { + MoveController.BasisVelocity = value; + } + } } /// @@ -244,16 +261,25 @@ //初始化节点 private void _InitNode(string itemId, World world) { +#if TOOLS + if (!Engine.IsEditorHint()) + { + if (GetType().GetCustomAttributes(typeof(ToolAttribute), false).Length == 0) + { + throw new Exception($"ActivityObject子类'{GetType().FullName}'没有加[Tool]标记!"); + } + } +#endif World = world; ItemId = itemId; Name = GetType().Name + (_instanceIndex++); - AnimatedSprite = GetNode("AnimatedSprite"); + //AnimatedSprite = GetNode("AnimatedSprite"); _blendShaderMaterial = AnimatedSprite.Material as ShaderMaterial; - ShadowSprite = GetNode("ShadowSprite"); + //ShadowSprite = GetNode("ShadowSprite"); ShadowSprite.Visible = false; - Collision = GetNode("Collision"); + //Collision = GetNode("Collision"); MotionMode = MotionModeEnum.Floating; MoveController = AddComponent(); @@ -266,6 +292,14 @@ /// public sealed override void _Ready() { + + } + + /// + /// 子类需要重写 _EnterTree() 函数, 请重写 EnterTree() + /// + public sealed override void _EnterTree() + { #if TOOLS // 在工具模式下创建的 template 节点自动创建对应的必要子节点 if (Engine.IsEditorHint()) @@ -274,6 +308,14 @@ } #endif } + + /// + /// 子类需要重写 _ExitTree() 函数, 请重写 ExitTree() + /// + public sealed override void _ExitTree() + { + + } /// /// 显示阴影 @@ -361,6 +403,22 @@ public virtual void OnInit() { } + + /// + /// 进入场景树时调用 + /// + public virtual void EnterTree() + { + + } + + /// + /// 离开场景树时调用 + /// + public virtual void ExitTree() + { + + } /// /// 返回是否能与其他ActivityObject互动 diff --git a/DungeonShooting_Godot/src/framework/activity/ActivityObject_EditorTool.cs b/DungeonShooting_Godot/src/framework/activity/ActivityObject_EditorTool.cs index dbed5a4..7564fb1 100644 --- a/DungeonShooting_Godot/src/framework/activity/ActivityObject_EditorTool.cs +++ b/DungeonShooting_Godot/src/framework/activity/ActivityObject_EditorTool.cs @@ -1,9 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Reflection; using Godot; +using UI.EditorTools; public partial class ActivityObject { + /// + /// 该函数只会在编辑器中调用, 用于自定义处理被 [ExportFill] 标记后自动创建的节点 + /// + /// 属性名称 + /// 节点实例 + protected virtual void OnExportFillNode(string propertyName, Node node) + { + switch (propertyName) + { + case "ShadowSprite": + { + var sprite = (Sprite2D)node; + sprite.ZIndex = -1; + var material = + ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); + material.SetShaderParameter("blend", new Color(0, 0, 0, 0.47058824F)); + material.SetShaderParameter("schedule", 1); + sprite.Material = material; + } + break; + case "AnimatedSprite": + { + var animatedSprite = (AnimatedSprite2D)node; + var material = + ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); + material.SetShaderParameter("blend", new Color(1, 1, 1, 1)); + material.SetShaderParameter("schedule", 0); + animatedSprite.Material = material; + } + break; + case "Collision": + { + + } + break; + } + } + private void _InitNodeInEditor() { var parent = GetParent(); @@ -24,66 +66,48 @@ owner = parent; } - var sprite = GetNodeOrNull("ShadowSprite"); - //创建Shadow - if (sprite == null) + var type = GetType(); + var propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var propertyInfoList = new List(); + foreach (var propertyInfo in propertyInfos) { - sprite = new Sprite2D(); - sprite.Name = "ShadowSprite"; - sprite.ZIndex = -1; - var material = - ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); - material.SetShaderParameter("blend", new Color(0, 0, 0, 0.47058824F)); - material.SetShaderParameter("schedule", 1); - sprite.Material = material; - AddChild(sprite); - sprite.Owner = owner; - } - else if (sprite.Material == null) - { - var material = - ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); - material.SetShaderParameter("blend", new Color(0, 0, 0, 0.47058824F)); - material.SetShaderParameter("schedule", 1); - sprite.Material = material; - } + if (propertyInfo.GetCustomAttributes(typeof(ExportFillNodeAttribute), false).Length > 0) + { + if (propertyInfo.GetCustomAttributes(typeof(ExportAttribute), false).Length == 0) + { + EditorToolsPanel.ShowConfirmInEditor("警告", $"'{type.FullName}'中字段'{propertyInfo.Name}'使用了[ExportAutoFill],\n但是并没有加上[Export], 请补上!"); + return; + } - var animatedSprite = GetNodeOrNull("AnimatedSprite"); - //创建 Sprite2D - if (animatedSprite == null) - { - animatedSprite = new AnimatedSprite2D(); - animatedSprite.Name = "AnimatedSprite"; - var material = - ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); - material.SetShaderParameter("blend", new Color(1, 1, 1, 1)); - material.SetShaderParameter("schedule", 0); - animatedSprite.Material = material; - AddChild(animatedSprite); - animatedSprite.Owner = owner; + if (propertyInfo.PropertyType.IsAssignableTo(typeof(Node))) + { + if (propertyInfo.SetMethod == null) + { + EditorToolsPanel.ShowConfirmInEditor("警告", $"请为'{type.FullName}'中的'{propertyInfo.Name}'属性设置set访问器, 或者将set服务器设置成public!"); + return; + } + propertyInfoList.Add(propertyInfo); + } + } } - else if (animatedSprite.Material == null) + foreach (var propertyInfo in propertyInfoList) { - var material = - ResourceManager.Load(ResourcePath.resource_material_Blend_tres, false); - material.SetShaderParameter("blend", new Color(1, 1, 1, 1)); - material.SetShaderParameter("schedule", 0); - animatedSprite.Material = material; - } - - //创建Collision - if (GetNodeOrNull("Collision") == null) - { - var co = new CollisionShape2D(); - co.Name = "Collision"; - AddChild(co); - co.Owner = owner; + var value = propertyInfo.GetValue(this); + if (value == null || ((Node)value).GetParent() == null) + { + var node = GetNodeOrNull(propertyInfo.Name); + if (node == null) + { + node = (Node)Activator.CreateInstance(propertyInfo.PropertyType); + AddChild(node); + node.Name = propertyInfo.Name; + node.Owner = owner; + //自定义处理导出的节点 + OnExportFillNode(propertyInfo.Name, node); + } + propertyInfo.SetValue(this, node); + } } } } - - private Node GetOwnerInEditor() - { - return null; - } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/activity/ExportFillNodeAttribute.cs b/DungeonShooting_Godot/src/framework/activity/ExportFillNodeAttribute.cs new file mode 100644 index 0000000..0616e34 --- /dev/null +++ b/DungeonShooting_Godot/src/framework/activity/ExportFillNodeAttribute.cs @@ -0,0 +1,10 @@ + +using System; + +/// +/// 标记类型为Node的属性, 表示当前属性如果没有赋值, 则在编辑器中自动创建子节点, 必须搭配 [Export] 使用! +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public class ExportFillNodeAttribute : Attribute +{ +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/map/mark/ActivityExpression.cs b/DungeonShooting_Godot/src/framework/map/mark/ActivityExpression.cs deleted file mode 100644 index 040dd1d..0000000 --- a/DungeonShooting_Godot/src/framework/map/mark/ActivityExpression.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -/// -/// 用于 ActivityMark 字段上, 表示当前字段是一个表达式字段 -/// -[AttributeUsage(AttributeTargets.Field)] -public class ActivityExpression : Attribute -{ - -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/map/mark/ActivityExpressionAttribute.cs b/DungeonShooting_Godot/src/framework/map/mark/ActivityExpressionAttribute.cs new file mode 100644 index 0000000..dc0ee12 --- /dev/null +++ b/DungeonShooting_Godot/src/framework/map/mark/ActivityExpressionAttribute.cs @@ -0,0 +1,10 @@ +using System; + +/// +/// 用于 ActivityMark 字段上, 表示当前字段是一个表达式字段 +/// +[AttributeUsage(AttributeTargets.Field)] +public class ActivityExpressionAttribute : Attribute +{ + +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/map/mark/ActivityMark.cs b/DungeonShooting_Godot/src/framework/map/mark/ActivityMark.cs index ec4c0a5..9c91cdd 100644 --- a/DungeonShooting_Godot/src/framework/map/mark/ActivityMark.cs +++ b/DungeonShooting_Godot/src/framework/map/mark/ActivityMark.cs @@ -104,7 +104,7 @@ var tempList = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (var s in tempList) { - if (s.GetCustomAttribute() != null) + if (s.GetCustomAttribute() != null) { fieldInfos.Add(s.Name); } diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs index ad92d9f..1aceed4 100644 --- a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs @@ -280,10 +280,9 @@ return 1; } - public override void _EnterTree() + public override void EnterTree() { - base._EnterTree(); - + base.EnterTree(); //收集落在地上的武器 if (IsInGround()) { @@ -291,10 +290,9 @@ } } - public override void _ExitTree() + public override void ExitTree() { - base._ExitTree(); - + base.ExitTree(); World.Weapon_UnclaimedWeapons.Remove(this); } diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index 85b45af..b52fa59 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -16,6 +16,7 @@ /// /// 基础敌人 /// +[Tool, GlobalClass] public partial class Enemy : Role { /// @@ -92,17 +93,18 @@ StateController.ChangeStateInstant(AiStateEnum.AiNormal); } - public override void _EnterTree() + public override void EnterTree() { + base.EnterTree(); if (!World.Enemy_InstanceList.Contains(this)) { World.Enemy_InstanceList.Add(this); } } - public override void _ExitTree() + public override void ExitTree() { - base._ExitTree(); + base.ExitTree(); World.Enemy_InstanceList.Remove(this); } diff --git a/DungeonShooting_Godot/src/game/ui/editorTools/EditorToolsPanel.cs b/DungeonShooting_Godot/src/game/ui/editorTools/EditorToolsPanel.cs index 9571f29..0589080 100644 --- a/DungeonShooting_Godot/src/game/ui/editorTools/EditorToolsPanel.cs +++ b/DungeonShooting_Godot/src/game/ui/editorTools/EditorToolsPanel.cs @@ -393,4 +393,28 @@ ExcelGenerator.ExportExcel(); ShowTips("提示", "已启动导表程序, 注意查看控制台信息!"); } + + /// + /// 在编辑器中打开一个提示窗口 + /// + public static void ShowTipsInEditor(string title, string message, Action onClose) + { + var editorToolsInstance = UiManager.Get_EditorTools_Instance(); + if (editorToolsInstance.Length > 0) + { + editorToolsInstance[0].ShowTips(title, message, onClose); + } + } + + /// + /// 在编辑器中打开一个询问窗口 + /// + public static void ShowConfirmInEditor(string title, string message, Action onClose = null) + { + var editorToolsInstance = UiManager.Get_EditorTools_Instance(); + if (editorToolsInstance.Length > 0) + { + editorToolsInstance[0].ShowConfirm(title, message, onClose); + } + } }