From 6d23f9dcd4989cf3b67b67231207b78ccaee8315 Mon Sep 17 00:00:00 2001 From: KirbyKidJ <70983335+KirbyKid256@users.noreply.github.com> Date: Sun, 28 Sep 2025 03:30:45 -0700 Subject: [PATCH] Added Coin Sparkle Effects (#350) * Just the Coin Sparkle stuff FYI, if a property in the json is formatted like `exa.exb` then it reads `exa` as another object and `exb` as a property in `exa` assuming the root `property_node` contains this object. Also primarily for Resource Packs to change how the particles work and should explain how I got there with my personal SMAS skin. * Added Coin Sparkle Texture to Scene Of course I broke it in some form. --- Assets/Sprites/Items/Coin.json | 3 ++ Assets/Sprites/Items/SpinningCoin.json | 3 ++ Assets/Sprites/Particles/CoinSparkle.json | 10 +++++ Scenes/Parts/Particles/CoinSparkle.tscn | 42 +++++++++++++++++++ Scenes/Prefabs/Entities/Items/Coin.tscn | 3 +- .../Prefabs/Entities/Items/SpinningCoin.tscn | 3 +- Scenes/Prefabs/Particles/CoinSparkle.tscn | 18 ++++++-- .../Classes/Components/ResourceSetterNew.gd | 11 ++++- Scripts/Classes/Entities/Items/Coin.gd | 13 ++++-- .../Classes/Entities/Items/SpinningCoin.gd | 17 +++++--- 10 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 Assets/Sprites/Particles/CoinSparkle.json create mode 100644 Scenes/Parts/Particles/CoinSparkle.tscn diff --git a/Assets/Sprites/Items/Coin.json b/Assets/Sprites/Items/Coin.json index ed2b968..ac470ca 100644 --- a/Assets/Sprites/Items/Coin.json +++ b/Assets/Sprites/Items/Coin.json @@ -85,6 +85,9 @@ "loop": true } }, + "properties": { + "can_spawn_particles": true + }, "variations": { "default": {"source": "StaticCoin.png", "rect": [0, 0, 64, 16]}, "Underground": {"source": "StaticCoin.png", "rect": [0, 16, 64, 16]}, diff --git a/Assets/Sprites/Items/SpinningCoin.json b/Assets/Sprites/Items/SpinningCoin.json index 2feead8..0f80196 100644 --- a/Assets/Sprites/Items/SpinningCoin.json +++ b/Assets/Sprites/Items/SpinningCoin.json @@ -31,6 +31,9 @@ "loop": true } }, + "properties": { + "can_spawn_particles": true + }, "variations": { "default": { "source": "SpinningCoin.png", diff --git a/Assets/Sprites/Particles/CoinSparkle.json b/Assets/Sprites/Particles/CoinSparkle.json new file mode 100644 index 0000000..8ac43a2 --- /dev/null +++ b/Assets/Sprites/Particles/CoinSparkle.json @@ -0,0 +1,10 @@ +{ + "properties": { + "amount": 3, + "process_material.emission_shape": 1, + "material.particles_anim_h_frames": 8 + }, + "variations": { + "default": {"source": "CoinSparkle.png", "rect": [0, 0, 64, 8]} + } +} diff --git a/Scenes/Parts/Particles/CoinSparkle.tscn b/Scenes/Parts/Particles/CoinSparkle.tscn new file mode 100644 index 0000000..820a23f --- /dev/null +++ b/Scenes/Parts/Particles/CoinSparkle.tscn @@ -0,0 +1,42 @@ +[gd_scene load_steps=6 format=3 uid="uid://b1ytbn4cu7msu"] + +[ext_resource type="Texture2D" uid="uid://dkebo0uw0dkkw" path="res://Assets/Sprites/Particles/CoinSparkle.png" id="1_0guw6"] +[ext_resource type="Script" uid="uid://cbal8ms2oe1ik" path="res://Scripts/Classes/Components/ResourceSetterNew.gd" id="2_wdqt2"] +[ext_resource type="JSON" path="res://Assets/Sprites/Particles/CoinSparkle.json" id="3_wdqt2"] + +[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_0guw6"] +particles_animation = true +particles_anim_h_frames = 8 +particles_anim_v_frames = 1 +particles_anim_loop = false + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_wdqt2"] +particle_flag_disable_z = true +emission_shape = 1 +emission_sphere_radius = 16.0 +gravity = Vector3(0, 0, 0) +anim_speed_min = 2.0 +anim_speed_max = 2.0 + +[node name="CoinSparkle" type="GPUParticles2D"] +material = SubResource("CanvasItemMaterial_0guw6") +emitting = false +amount = 3 +texture = ExtResource("1_0guw6") +lifetime = 0.5 +one_shot = true +explosiveness = 0.7 +interpolate = false +fract_delta = false +process_material = SubResource("ParticleProcessMaterial_wdqt2") + +[node name="ResourceSetterNew" type="Node" parent="." node_paths=PackedStringArray("node_to_affect", "property_node")] +script = ExtResource("2_wdqt2") +node_to_affect = NodePath("..") +property_node = NodePath("..") +property_name = "texture" +mode = 1 +resource_json = ExtResource("3_wdqt2") +metadata/_custom_type_script = "uid://cbal8ms2oe1ik" + +[connection signal="ready" from="." to="." method="set_emitting" binds= [true]] diff --git a/Scenes/Prefabs/Entities/Items/Coin.tscn b/Scenes/Prefabs/Entities/Items/Coin.tscn index 05fc805..2801bb9 100644 --- a/Scenes/Prefabs/Entities/Items/Coin.tscn +++ b/Scenes/Prefabs/Entities/Items/Coin.tscn @@ -62,9 +62,10 @@ sprite_frames = SubResource("SpriteFrames_nu35s") autoplay = "default" frame_progress = 0.487032 -[node name="ResourceSetterNew" type="Node" parent="Sprite" node_paths=PackedStringArray("node_to_affect")] +[node name="ResourceSetterNew" type="Node" parent="Sprite" node_paths=PackedStringArray("node_to_affect", "property_node")] script = ExtResource("3_uahob") node_to_affect = NodePath("..") +property_node = NodePath("../..") property_name = "sprite_frames" resource_json = ExtResource("4_thymr") metadata/_custom_type_script = "uid://cbal8ms2oe1ik" diff --git a/Scenes/Prefabs/Entities/Items/SpinningCoin.tscn b/Scenes/Prefabs/Entities/Items/SpinningCoin.tscn index eaa7975..751216f 100644 --- a/Scenes/Prefabs/Entities/Items/SpinningCoin.tscn +++ b/Scenes/Prefabs/Entities/Items/SpinningCoin.tscn @@ -51,9 +51,10 @@ sprite_frames = SubResource("SpriteFrames_sax2u") autoplay = "default" frame_progress = 0.710764 -[node name="ResourceSetterNew" type="Node" parent="Sprite" node_paths=PackedStringArray("node_to_affect")] +[node name="ResourceSetterNew" type="Node" parent="Sprite" node_paths=PackedStringArray("node_to_affect", "property_node")] script = ExtResource("3_7mdmn") node_to_affect = NodePath("..") +property_node = NodePath("../..") property_name = "sprite_frames" resource_json = ExtResource("4_b5lmc") metadata/_custom_type_script = "uid://cbal8ms2oe1ik" diff --git a/Scenes/Prefabs/Particles/CoinSparkle.tscn b/Scenes/Prefabs/Particles/CoinSparkle.tscn index eac9681..673db37 100755 --- a/Scenes/Prefabs/Particles/CoinSparkle.tscn +++ b/Scenes/Prefabs/Particles/CoinSparkle.tscn @@ -1,4 +1,8 @@ -[gd_scene load_steps=3 format=3 uid="uid://b1ytbn4cu7msu"] +[gd_scene load_steps=6 format=3 uid="uid://b1ytbn4cu7msu"] + +[ext_resource type="Texture2D" uid="uid://dkebo0uw0dkkw" path="res://Assets/Sprites/Particles/CoinSparkle.png" id="1_4p5kk"] +[ext_resource type="Script" uid="uid://cbal8ms2oe1ik" path="res://Scripts/Classes/Components/ResourceSetterNew.gd" id="2_wdqt2"] +[ext_resource type="JSON" path="res://Assets/Sprites/Particles/CoinSparkle.json" id="3_igsqc"] [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_0guw6"] particles_animation = true @@ -18,6 +22,7 @@ anim_speed_max = 2.0 material = SubResource("CanvasItemMaterial_0guw6") emitting = false amount = 3 +texture = ExtResource("1_4p5kk") lifetime = 0.5 one_shot = true explosiveness = 0.7 @@ -25,8 +30,13 @@ interpolate = false fract_delta = false process_material = SubResource("ParticleProcessMaterial_wdqt2") -[node name="Timer" type="Timer" parent="."] -autostart = true +[node name="ResourceSetterNew" type="Node" parent="." node_paths=PackedStringArray("node_to_affect", "property_node")] +script = ExtResource("2_wdqt2") +node_to_affect = NodePath("..") +property_node = NodePath("..") +property_name = "texture" +mode = 1 +resource_json = ExtResource("3_igsqc") +metadata/_custom_type_script = "uid://cbal8ms2oe1ik" [connection signal="ready" from="." to="." method="set_emitting" binds= [true]] -[connection signal="timeout" from="Timer" to="." method="queue_free"] diff --git a/Scripts/Classes/Components/ResourceSetterNew.gd b/Scripts/Classes/Components/ResourceSetterNew.gd index 90175c8..59b86aa 100644 --- a/Scripts/Classes/Components/ResourceSetterNew.gd +++ b/Scripts/Classes/Components/ResourceSetterNew.gd @@ -168,7 +168,16 @@ func apply_properties(properties := {}) -> void: if value is Array: property_node.set(i, Vector2(value[0], value[1])) else: - property_node.set(i, properties[i]) + var obj = property_node + for p in i.split("."): + if not is_instance_valid(obj): continue + if obj.get(p) is Object: + if obj.has_method("duplicate"): + obj.set(p, obj[p].duplicate(true)) + obj = obj[p] + else: + obj.set(p, properties[i]) + continue func get_variation_json(json := {}) -> Dictionary: var level_theme = Global.level_theme diff --git a/Scripts/Classes/Entities/Items/Coin.gd b/Scripts/Classes/Entities/Items/Coin.gd index b63f8d5..5fa42c0 100644 --- a/Scripts/Classes/Entities/Items/Coin.gd +++ b/Scripts/Classes/Entities/Items/Coin.gd @@ -3,6 +3,8 @@ const COIN_SPARKLE = preload("res://Scenes/Prefabs/Particles/CoinSparkle.tscn") @export var spinning_coin_scene: PackedScene = null +var can_spawn_particles := true + signal collected func area_entered(area: Area2D) -> void: @@ -11,11 +13,16 @@ func area_entered(area: Area2D) -> void: func collect() -> void: collected.emit() + $Hitbox.area_entered.disconnect(area_entered) Global.coins += 1 DiscoLevel.combo_meter += 10 Global.score += 200 AudioManager.play_sfx("coin", global_position) - queue_free() + if can_spawn_particles: + summon_particle() + $Sprite.queue_free() + else: + queue_free() func summon_block_coin() -> void: var node = spinning_coin_scene.instantiate() @@ -25,5 +32,5 @@ func summon_block_coin() -> void: func summon_particle() -> void: var node = COIN_SPARKLE.instantiate() - node.global_position = global_position - add_sibling(node) + node.finished.connect(queue_free) + add_child(node) diff --git a/Scripts/Classes/Entities/Items/SpinningCoin.gd b/Scripts/Classes/Entities/Items/SpinningCoin.gd index 1f6a9fd..a16da8b 100755 --- a/Scripts/Classes/Entities/Items/SpinningCoin.gd +++ b/Scripts/Classes/Entities/Items/SpinningCoin.gd @@ -2,19 +2,26 @@ extends Node2D const COIN_SPARKLE = preload("res://Scenes/Prefabs/Particles/CoinSparkle.tscn") var velocity := Vector2(0, -300) +var can_spawn_particles := true + func _ready() -> void: Global.coins += 1 Global.score += 200 AudioManager.play_sfx("coin", global_position) func _physics_process(delta: float) -> void: - global_position += velocity * delta - velocity.y += (15 / delta) * delta + if get_node_or_null("Sprite") != null: + global_position += velocity * delta + velocity.y += (15 / delta) * delta func vanish() -> void: - queue_free() + if can_spawn_particles: + summon_particle() + $Sprite.queue_free() + else: + queue_free() func summon_particle() -> void: var node = COIN_SPARKLE.instantiate() - node.global_position = global_position - add_sibling(node) + node.finished.connect(queue_free) + add_child(node)