From ef8fd0db9ffd805f43337ca82d7a1d5a7a4533c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=A8=E8=90=BD=E5=9F=BA=E5=9B=B4=E8=99=BE?=
<3161880837@qq.com>
Date: Tue, 26 Aug 2025 11:39:47 +0800
Subject: [PATCH] feat: Enhance game mechanics and structure
- Added `ColorBar` class for visual representation of values.
- Expanded `EntityBase` with new properties: `isBoss` and `weapons`, and implemented damage handling and entity generation methods.
- Updated `EntityStateBar` to use `ColorBar` for health representation.
- Introduced `BulletBase` scene and script for bullet mechanics, including damage handling and generation.
- Created `WorldTool` for managing the game world and root node.
- Implemented `EntityTool` for retrieving entities from hurtboxes.
- Added `GameRule` for managing game rules like friendly fire.
- Updated scene files to reflect new structures and added necessary resources.
---
components/Abstracts/BulletBase.tscn | 16 ++++++++
components/Bullets/PurpleCrystal.tscn | 26 +++++++++++++
resources/bullets/diamond/frames/0.svg | 25 +++++++++++++
resources/bullets/diamond/frames/0.svg.import | 37 +++++++++++++++++++
resources/bullets/purple-crystal/frames/0.svg | 27 ++++++++++++++
.../purple-crystal/frames/0.svg.import | 37 +++++++++++++++++++
scripts/Statemachine/BulletBase.gd | 33 +++++++++++++++++
scripts/Statemachine/ColorBar.gd | 1 +
scripts/Statemachine/EntityBase.gd | 25 +++++++++++++
scripts/Statemachine/EntityStateBar.gd | 2 +-
scripts/Tools/EntityTool.gd | 10 +++++
scripts/Tools/GameRule.gd | 3 ++
scripts/Tools/WorldTool.gd | 9 +++++
world.tscn | 6 ++-
14 files changed, 254 insertions(+), 3 deletions(-)
create mode 100644 components/Abstracts/BulletBase.tscn
create mode 100644 components/Bullets/PurpleCrystal.tscn
create mode 100644 resources/bullets/diamond/frames/0.svg
create mode 100644 resources/bullets/diamond/frames/0.svg.import
create mode 100644 resources/bullets/purple-crystal/frames/0.svg
create mode 100644 resources/bullets/purple-crystal/frames/0.svg.import
create mode 100644 scripts/Statemachine/BulletBase.gd
create mode 100644 scripts/Tools/EntityTool.gd
create mode 100644 scripts/Tools/GameRule.gd
create mode 100644 scripts/Tools/WorldTool.gd
diff --git a/components/Abstracts/BulletBase.tscn b/components/Abstracts/BulletBase.tscn
new file mode 100644
index 0000000..5b39fc6
--- /dev/null
+++ b/components/Abstracts/BulletBase.tscn
@@ -0,0 +1,16 @@
+[gd_scene load_steps=4 format=3 uid="uid://crtdkysmnkith"]
+
+[ext_resource type="Script" path="res://scripts/Statemachine/BulletBase.gd" id="1_pklpq"]
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_vypy3"]
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_ecl7m"]
+
+[node name="BulletBase" type="Area2D"]
+script = ExtResource("1_pklpq")
+
+[node name="texture" type="AnimatedSprite2D" parent="."]
+sprite_frames = SubResource("SpriteFrames_vypy3")
+
+[node name="hitbox" type="CollisionShape2D" parent="."]
+shape = SubResource("CircleShape2D_ecl7m")
diff --git a/components/Bullets/PurpleCrystal.tscn b/components/Bullets/PurpleCrystal.tscn
new file mode 100644
index 0000000..b992a5a
--- /dev/null
+++ b/components/Bullets/PurpleCrystal.tscn
@@ -0,0 +1,26 @@
+[gd_scene load_steps=5 format=3 uid="uid://uj0aqhm8wgy8"]
+
+[ext_resource type="PackedScene" uid="uid://crtdkysmnkith" path="res://components/Abstracts/BulletBase.tscn" id="1_45mh7"]
+[ext_resource type="Texture2D" uid="uid://c7hyatbuieaj" path="res://resources/bullets/purple-crystal/frames/0.svg" id="2_ca3pq"]
+
+[sub_resource type="SpriteFrames" id="SpriteFrames_r86b3"]
+animations = [{
+"frames": [{
+"duration": 1.0,
+"texture": ExtResource("2_ca3pq")
+}],
+"loop": true,
+"name": &"default",
+"speed": 5.0
+}]
+
+[sub_resource type="CircleShape2D" id="CircleShape2D_ty1as"]
+
+[node name="PurpleCrystal" instance=ExtResource("1_45mh7")]
+
+[node name="texture" parent="." index="0"]
+sprite_frames = SubResource("SpriteFrames_r86b3")
+
+[node name="hitbox" parent="." index="1"]
+position = Vector2(0, -13)
+shape = SubResource("CircleShape2D_ty1as")
diff --git a/resources/bullets/diamond/frames/0.svg b/resources/bullets/diamond/frames/0.svg
new file mode 100644
index 0000000..c31e15c
--- /dev/null
+++ b/resources/bullets/diamond/frames/0.svg
@@ -0,0 +1,25 @@
+
\ No newline at end of file
diff --git a/resources/bullets/diamond/frames/0.svg.import b/resources/bullets/diamond/frames/0.svg.import
new file mode 100644
index 0000000..5b559f1
--- /dev/null
+++ b/resources/bullets/diamond/frames/0.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dfptmc7clhtx7"
+path="res://.godot/imported/0.svg-b76264cbe10324fb670faecc659cdc73.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/bullets/diamond/frames/0.svg"
+dest_files=["res://.godot/imported/0.svg-b76264cbe10324fb670faecc659cdc73.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/resources/bullets/purple-crystal/frames/0.svg b/resources/bullets/purple-crystal/frames/0.svg
new file mode 100644
index 0000000..26ffcb7
--- /dev/null
+++ b/resources/bullets/purple-crystal/frames/0.svg
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/resources/bullets/purple-crystal/frames/0.svg.import b/resources/bullets/purple-crystal/frames/0.svg.import
new file mode 100644
index 0000000..49db2bb
--- /dev/null
+++ b/resources/bullets/purple-crystal/frames/0.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c7hyatbuieaj"
+path="res://.godot/imported/0.svg-ba68a30343badda4169c625106c39869.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://resources/bullets/purple-crystal/frames/0.svg"
+dest_files=["res://.godot/imported/0.svg-ba68a30343badda4169c625106c39869.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/scripts/Statemachine/BulletBase.gd b/scripts/Statemachine/BulletBase.gd
new file mode 100644
index 0000000..ed6f36b
--- /dev/null
+++ b/scripts/Statemachine/BulletBase.gd
@@ -0,0 +1,33 @@
+extends Area2D
+class_name BulletBase
+
+@export var speed: float = 1
+@export var damage: float = 10
+
+var launcher: EntityBase = null
+
+func _ready():
+ area_entered.connect(hit)
+
+func hit(target: Node):
+ var entity: EntityBase = EntityTool.fromHurtbox(target)
+ if !entity || !launcher: return
+ if entity == launcher: return
+ if GameRule.allowFriendlyFire:
+ if entity.isPlayer() == launcher.isPlayer(): return
+ entity.takeDamage(self)
+
+static func generate(
+ bullet: PackedScene,
+ launchBy: EntityBase,
+ spawnPosition: Vector2,
+ spawnRotation: float,
+ addToWorld: bool = true
+ ):
+ var instance: BulletBase = bullet.instantiate()
+ instance.launcher = launchBy
+ instance.position = spawnPosition
+ instance.rotation = spawnRotation
+ if addToWorld:
+ WorldTool.rootNode.add_child(instance)
+ return instance
diff --git a/scripts/Statemachine/ColorBar.gd b/scripts/Statemachine/ColorBar.gd
index 9275e26..b73fb29 100644
--- a/scripts/Statemachine/ColorBar.gd
+++ b/scripts/Statemachine/ColorBar.gd
@@ -1,5 +1,6 @@
@tool
extends Control
+class_name ColorBar
@export var minValue: float = 0
@export var maxValue: float = 100
diff --git a/scripts/Statemachine/EntityBase.gd b/scripts/Statemachine/EntityBase.gd
index 99f5008..4c68434 100644
--- a/scripts/Statemachine/EntityBase.gd
+++ b/scripts/Statemachine/EntityBase.gd
@@ -3,6 +3,8 @@ class_name EntityBase # 这是个抽象类
@export var maxHealth: float = 100
@export var movementSpeed: float = 1
+@export var isBoss: bool = false
+@export var weapons: Array[Node2D] = []
@onready var animatree: AnimationTree = $"%animatree"
@onready var texture: AnimatedSprite2D = $"%texture"
@@ -28,9 +30,32 @@ func move(direction: Vector2):
var currentDirection = sign(direction.x)
if currentDirection != 0:
lastDirection = currentDirection
+func takeDamage(bullet: BulletBase):
+ health -= bullet.damage
+ if health <= 0:
+ die()
+
+# 关于分组
+func isPlayer():
+ return is_in_group("players")
# 抽象方法
func ai():
pass
func attack(_type: int):
pass
+func die():
+ queue_free()
+
+static func generate(
+ entity: PackedScene,
+ spawnPosition: Vector2,
+ spawnRotation: float,
+ addtoWorld: bool = true
+):
+ var instance = entity.instance()
+ instance.position = spawnPosition
+ instance.rotation = spawnRotation
+ if addtoWorld:
+ WorldTool.rootNode.add_child(instance)
+ return instance
diff --git a/scripts/Statemachine/EntityStateBar.gd b/scripts/Statemachine/EntityStateBar.gd
index 1881c6e..a9de469 100644
--- a/scripts/Statemachine/EntityStateBar.gd
+++ b/scripts/Statemachine/EntityStateBar.gd
@@ -2,7 +2,7 @@ extends Node2D
@export var entity: EntityBase
-@onready var healthBar = $"%health"
+@onready var healthBar: ColorBar = $"%health"
func _process(_delta):
if entity:
diff --git a/scripts/Tools/EntityTool.gd b/scripts/Tools/EntityTool.gd
new file mode 100644
index 0000000..0e77464
--- /dev/null
+++ b/scripts/Tools/EntityTool.gd
@@ -0,0 +1,10 @@
+class_name EntityTool
+
+static func fromHurtbox(node: Node) -> EntityBase:
+ if node is Area2D:
+ var texture = node.get_parent()
+ if texture is AnimatedSprite2D:
+ var entity = texture.get_parent()
+ if entity is EntityBase:
+ return entity as EntityBase
+ return null
\ No newline at end of file
diff --git a/scripts/Tools/GameRule.gd b/scripts/Tools/GameRule.gd
new file mode 100644
index 0000000..36757f1
--- /dev/null
+++ b/scripts/Tools/GameRule.gd
@@ -0,0 +1,3 @@
+class_name GameRule
+
+static var allowFriendlyFire: bool = false # 是否允许友军伤害
\ No newline at end of file
diff --git a/scripts/Tools/WorldTool.gd b/scripts/Tools/WorldTool.gd
new file mode 100644
index 0000000..2b507a5
--- /dev/null
+++ b/scripts/Tools/WorldTool.gd
@@ -0,0 +1,9 @@
+extends Node2D
+class_name WorldTool
+
+static var rootNode: Node2D
+static var tree: SceneTree
+
+func _ready():
+ tree = get_tree()
+ rootNode = self
diff --git a/world.tscn b/world.tscn
index 9072d21..2aac90e 100644
--- a/world.tscn
+++ b/world.tscn
@@ -1,8 +1,10 @@
-[gd_scene load_steps=2 format=3 uid="uid://dmxi1ikn6avig"]
+[gd_scene load_steps=3 format=3 uid="uid://dmxi1ikn6avig"]
[ext_resource type="PackedScene" uid="uid://bm7ymrri6pykb" path="res://components/Characters/Rooster.tscn" id="1_7jr0n"]
+[ext_resource type="Script" path="res://scripts/Tools/WorldTool.gd" id="1_ei0ch"]
[node name="world" type="Node2D"]
+script = ExtResource("1_ei0ch")
-[node name="rooster" parent="." instance=ExtResource("1_7jr0n")]
+[node name="rooster" parent="." groups=["players"] instance=ExtResource("1_7jr0n")]
position = Vector2(394, 274)