From 9d8f1f1767125bf65f78338a706611f6c86ba8f2 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Sun, 20 Apr 2025 00:23:41 -0600 Subject: [PATCH] Splatter effect scales with source node scale --- levels/mechanic_test/mechanic_test.gd | 12 +++---- src/effects/grunk_splatter/grunk_splatter.gd | 34 +++++++++++++++++++ .../grunk_splatter/grunk_splatter.gd.uid | 1 + .../{ => grunk_splatter}/grunk_splatter.tscn | 10 ++++-- src/world/gunk_node/grunk_nodule.gd | 10 ------ src/world/gunk_node/grunk_nodule.tscn | 4 +-- src/world/gunk_node/gunk_node.gd | 6 ++++ src/world/gunk_node/mechanic_test.tscn | 4 +-- src/world/mechanics/listener/listener.gd | 6 ---- src/world/mechanics/listener/listener.tscn | 4 +-- src/world/mechanics/relay/gunk_relay.gd | 2 ++ src/world/mechanics/relay/gunk_relay.tscn | 1 + 12 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 src/effects/grunk_splatter/grunk_splatter.gd create mode 100644 src/effects/grunk_splatter/grunk_splatter.gd.uid rename src/effects/{ => grunk_splatter}/grunk_splatter.tscn (93%) diff --git a/levels/mechanic_test/mechanic_test.gd b/levels/mechanic_test/mechanic_test.gd index 5a6f7d1..30c20c5 100644 --- a/levels/mechanic_test/mechanic_test.gd +++ b/levels/mechanic_test/mechanic_test.gd @@ -13,12 +13,6 @@ extends Node3D @onready var prop_test_spawn_point: Marker3D = %PropTestSpawnPoint @onready var item_test_spawn_point: Marker3D = %ItemTestSpawnPoint -static var nodule_scene: PackedScene = load("res://src/world/gunk_node/grunk_nodule.tscn") -static var alarm_scene: PackedScene = load("res://src/world/mechanics/alarm/gunk_alarm.tscn") -static var signal_test_scene: PackedScene = load("res://levels/mechanic_test/signal_test.tscn") -static var prop_test_scene: PackedScene = load("res://levels/mechanic_test/prop_test.tscn") -static var item_test_scene: PackedScene = load("res://levels/mechanic_test/item_test.tscn") - func reset() -> void: print("Resetting level!") @@ -26,6 +20,10 @@ func reset() -> void: Callable(bulkhead, "close").call() Callable(open_switch, "enable").call() Callable(close_switch, "disable").call() + + var signal_test_scene: PackedScene = load("res://levels/mechanic_test/signal_test.tscn") + var prop_test_scene: PackedScene = load("res://levels/mechanic_test/prop_test.tscn") + var item_test_scene: PackedScene = load("res://levels/mechanic_test/item_test.tscn") _do_spawn(signal_test_spawn_point, signal_test_scene) _do_spawn(prop_test_spawn_point, prop_test_scene) _do_spawn(item_test_spawn_point, item_test_scene) @@ -39,10 +37,12 @@ func _do_spawn(spawn_point: Node3D, scene: PackedScene) -> void: func spawn_nodule() -> void: + var nodule_scene: PackedScene = load("res://src/world/gunk_node/grunk_nodule.tscn") _do_spawn(nodule_spawn_point, nodule_scene) func spawn_alarm() -> void: + var alarm_scene: PackedScene = load("res://src/world/mechanics/alarm/gunk_alarm.tscn") _do_spawn(alarm_spawn_point, alarm_scene) diff --git a/src/effects/grunk_splatter/grunk_splatter.gd b/src/effects/grunk_splatter/grunk_splatter.gd new file mode 100644 index 0000000..2e17a0c --- /dev/null +++ b/src/effects/grunk_splatter/grunk_splatter.gd @@ -0,0 +1,34 @@ +class_name GrunkSplatter extends GPUParticles3D +## Splatter effect with adjustable volume + +const BASE_VOLUME := -12.0 +const SCALE_FACTOR := 4.0 + +const SCENE := preload("res://src/effects/grunk_splatter/grunk_splatter.tscn") + +@export var effect_scale := 1.0 + +@onready var splatter_sfx: AudioStreamPlayer3D = %SplatterSFX +@onready var sub_splatter: GPUParticles3D = %SubSplatter + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + splatter_sfx.volume_db = BASE_VOLUME + SCALE_FACTOR * log(effect_scale) / log(2) + scale = Vector3.ONE * effect_scale + + # Scale particles themselves + _scale_particles(self) + _scale_particles(sub_splatter) + + +func _scale_particles(emitter: GPUParticles3D) -> void: + var mat := emitter.process_material as ParticleProcessMaterial + mat.scale_max = effect_scale + mat.scale_min = effect_scale + + +static func build(_effect_scale: float = 1.0) -> GrunkSplatter: + var instance: GrunkSplatter = SCENE.instantiate() + instance.effect_scale = _effect_scale + return instance diff --git a/src/effects/grunk_splatter/grunk_splatter.gd.uid b/src/effects/grunk_splatter/grunk_splatter.gd.uid new file mode 100644 index 0000000..cdcd62d --- /dev/null +++ b/src/effects/grunk_splatter/grunk_splatter.gd.uid @@ -0,0 +1 @@ +uid://di5b65ehsfatj diff --git a/src/effects/grunk_splatter.tscn b/src/effects/grunk_splatter/grunk_splatter.tscn similarity index 93% rename from src/effects/grunk_splatter.tscn rename to src/effects/grunk_splatter/grunk_splatter.tscn index f1d7ec3..f36c608 100644 --- a/src/effects/grunk_splatter.tscn +++ b/src/effects/grunk_splatter/grunk_splatter.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=22 format=3 uid="uid://xlt78xc1tmkl"] +[gd_scene load_steps=23 format=3 uid="uid://xlt78xc1tmkl"] [ext_resource type="Texture2D" uid="uid://cgwgmxwjgwbwr" path="res://assets/particles/splatter_2.png" id="1_5xu2x"] [ext_resource type="Texture2D" uid="uid://bhoai6xv53tqm" path="res://assets/particles/splatter_1.png" id="2_bt63p"] +[ext_resource type="Script" uid="uid://di5b65ehsfatj" path="res://src/effects/grunk_splatter/grunk_splatter.gd" id="2_grvat"] [ext_resource type="AudioStream" uid="uid://di0j2xhgfc78s" path="res://assets/sfx/grunk/splat1.wav" id="3_t00bd"] [ext_resource type="AudioStream" uid="uid://d1w5gfmjj7tjk" path="res://assets/sfx/grunk/splat2.wav" id="4_2iem1"] [ext_resource type="Script" uid="uid://c5o1d2shq2qig" path="res://src/world/game_sound/game_sound_emitter.gd" id="5_2iem1"] @@ -22,6 +23,7 @@ point_count = 2 curve = SubResource("Curve_y6klh") [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_5xu2x"] +resource_local_to_scene = true lifetime_randomness = 0.57 emission_shape = 1 emission_sphere_radius = 1.0 @@ -65,6 +67,7 @@ point_count = 2 curve = SubResource("Curve_t00bd") [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_2iem1"] +resource_local_to_scene = true lifetime_randomness = 0.55 emission_shape = 1 emission_sphere_radius = 0.9 @@ -112,8 +115,10 @@ one_shot = true explosiveness = 0.45 process_material = SubResource("ParticleProcessMaterial_5xu2x") draw_pass_1 = SubResource("QuadMesh_y6klh") +script = ExtResource("2_grvat") [node name="SubSplatter" type="GPUParticles3D" parent="."] +unique_name_in_owner = true sorting_offset = 9.0 emitting = false amount = 4 @@ -131,8 +136,9 @@ one_shot = true autostart = true [node name="SplatterSFX" type="AudioStreamPlayer3D" parent="."] +unique_name_in_owner = true stream = SubResource("AudioStreamRandomizer_6adkd") -volume_db = -16.0 +volume_db = -12.0 unit_size = 6.0 autoplay = true bus = &"SFX" diff --git a/src/world/gunk_node/grunk_nodule.gd b/src/world/gunk_node/grunk_nodule.gd index 517699a..2dae8d1 100644 --- a/src/world/gunk_node/grunk_nodule.gd +++ b/src/world/gunk_node/grunk_nodule.gd @@ -25,9 +25,6 @@ const MIN_CHITTER_INTERVAL := 4.0 @export var chitter_time_mean := 60.0 @export var chitter_time_st_dev := 30.0 -@export_category("Game Scenes") -@export var splatter_scene: PackedScene - @onready var mesh_instance: MeshInstance3D = %MeshInstance3D @onready var chitter_sfx: AudioStreamPlayer3D = %ChitterSFX @@ -53,13 +50,6 @@ func _process(delta: float) -> void: shader.set_shader_parameter("vertex_inflation", pow(damage * jitter_inflation_factor, 3)) -func _destroy() -> void: - var splatter: GPUParticles3D = splatter_scene.instantiate() - add_sibling(splatter) - splatter.global_position = global_position - splatter.emitting = true - - func start_chitter_timer() -> void: var interval := maxf(MIN_CHITTER_INTERVAL, randfn(chitter_time_mean, chitter_time_st_dev)) chitter_timer.start(interval) diff --git a/src/world/gunk_node/grunk_nodule.tscn b/src/world/gunk_node/grunk_nodule.tscn index 4b6ca37..6d3419f 100644 --- a/src/world/gunk_node/grunk_nodule.tscn +++ b/src/world/gunk_node/grunk_nodule.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=12 format=4 uid="uid://2yqi5u5eo025"] +[gd_scene load_steps=11 format=4 uid="uid://2yqi5u5eo025"] [ext_resource type="Script" uid="uid://07t7yhijru8f" path="res://src/world/gunk_node/grunk_nodule.gd" id="1_iyr82"] -[ext_resource type="PackedScene" uid="uid://xlt78xc1tmkl" path="res://src/effects/grunk_splatter.tscn" id="2_m8r0a"] [ext_resource type="Material" uid="uid://bmab6i16v748m" path="res://assets/materials/grunk_jittery.material" id="3_eu6j6"] [ext_resource type="AudioStream" uid="uid://bb560r2wvjfht" path="res://assets/sfx/grunk/greeble1.wav" id="4_7fplw"] [ext_resource type="AudioStream" uid="uid://dunakapj3mb0h" path="res://assets/sfx/grunk/greeble2.wav" id="5_omayi"] @@ -55,7 +54,6 @@ stream_3/stream = ExtResource("7_4kci5") collision_layer = 36 collision_mask = 0 script = ExtResource("1_iyr82") -splatter_scene = ExtResource("2_m8r0a") durability = 3.0 metadata/_custom_type_script = "uid://bypgxi0gy56yk" diff --git a/src/world/gunk_node/gunk_node.gd b/src/world/gunk_node/gunk_node.gd index 5f6c074..3d269a9 100644 --- a/src/world/gunk_node/gunk_node.gd +++ b/src/world/gunk_node/gunk_node.gd @@ -13,6 +13,9 @@ const JITTER_FADE_RATE := 0.8 ## Value added to the player's grunk total on destruction. @export var value := 1000.0 +## Scale factor applied to the size and volume of the splatter effect on destruction. +@export var splatter_scale := 1.0 + var _sustained_damage := 0.0 var _hit_this_frame := false @@ -55,6 +58,9 @@ func collect() -> void: ## Derived types should override `_destroy` as a lifecycle method. func destroy() -> void: Game.manager.collect_grunk(value) + var splatter := GrunkSplatter.build(splatter_scale * scale.x) + add_sibling(splatter) + splatter.global_position = global_position _destroy() destroyed.emit() queue_free() diff --git a/src/world/gunk_node/mechanic_test.tscn b/src/world/gunk_node/mechanic_test.tscn index 67036e6..ba03317 100644 --- a/src/world/gunk_node/mechanic_test.tscn +++ b/src/world/gunk_node/mechanic_test.tscn @@ -103,7 +103,7 @@ seamless = true seamless_blend_skirt = 0.5 noise = ExtResource("7_aqwgb") -[sub_resource type="ShaderMaterial" id="ShaderMaterial_qmfft"] +[sub_resource type="ShaderMaterial" id="ShaderMaterial_umjw2"] resource_local_to_scene = true render_priority = 0 shader = ExtResource("6_6agnv") @@ -237,7 +237,7 @@ skeleton = NodePath("GunkHallBody") [node name="GunkHallBody" parent="GunkHall" instance=ExtResource("4_7v7un")] unique_name_in_owner = true initial_mask = ExtResource("5_llot1") -source_gunk_material = SubResource("ShaderMaterial_qmfft") +source_gunk_material = SubResource("ShaderMaterial_umjw2") [node name="CollisionShape3D" type="CollisionShape3D" parent="GunkHall/GunkHallBody"] shape = SubResource("ConcavePolygonShape3D_qjnj2") diff --git a/src/world/mechanics/listener/listener.gd b/src/world/mechanics/listener/listener.gd index 6838f06..cf15604 100644 --- a/src/world/mechanics/listener/listener.gd +++ b/src/world/mechanics/listener/listener.gd @@ -7,8 +7,6 @@ signal triggered @export var quick_connect_to: SignalNode: set = _editor_connect -@export var splatter_scene: PackedScene - # NOTE # trigger oscillation animation was generated using the formula # f(x) = e^(-0.25x) * cos(x * pi / 2 - pi/2) + 1 for x in {0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 20} @@ -27,10 +25,6 @@ func trigger() -> void: cooldown_timer.start() -func _destroy() -> void: - add_sibling(splatter_scene.instantiate()) - - func _editor_connect(node: SignalNode) -> void: triggered.connect(node.trigger, CONNECT_PERSIST) self.notify_property_list_changed() diff --git a/src/world/mechanics/listener/listener.tscn b/src/world/mechanics/listener/listener.tscn index f1cd7cb..e549b23 100644 --- a/src/world/mechanics/listener/listener.tscn +++ b/src/world/mechanics/listener/listener.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=10 format=3 uid="uid://kctp5erogwcb"] +[gd_scene load_steps=9 format=3 uid="uid://kctp5erogwcb"] [ext_resource type="Script" uid="uid://bde7cglaqobkd" path="res://src/world/mechanics/listener/listener.gd" id="1_htscg"] -[ext_resource type="PackedScene" uid="uid://xlt78xc1tmkl" path="res://src/effects/grunk_splatter.tscn" id="2_2ibh1"] [ext_resource type="Script" uid="uid://cfsiyhhrcua6o" path="res://src/world/game_sound/game_sound_listener.gd" id="2_htscg"] [sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_2ibh1"] @@ -58,7 +57,6 @@ _data = { collision_layer = 36 collision_mask = 0 script = ExtResource("1_htscg") -splatter_scene = ExtResource("2_2ibh1") durability = 3.0 value = 4000.0 metadata/_custom_type_script = "uid://bypgxi0gy56yk" diff --git a/src/world/mechanics/relay/gunk_relay.gd b/src/world/mechanics/relay/gunk_relay.gd index 32bd03d..b8d0302 100644 --- a/src/world/mechanics/relay/gunk_relay.gd +++ b/src/world/mechanics/relay/gunk_relay.gd @@ -8,9 +8,11 @@ signal triggered ## Emitted when `pulse` is called, after a short delay. signal pulsed +@export_category("Editor Tools") @export var quick_connect_to: SignalNode: set = _editor_connect +@export_category("Game Scenes") @export var editor_arrow_scene: PackedScene var _busy := false diff --git a/src/world/mechanics/relay/gunk_relay.tscn b/src/world/mechanics/relay/gunk_relay.tscn index ab5aa45..aca8bb4 100644 --- a/src/world/mechanics/relay/gunk_relay.tscn +++ b/src/world/mechanics/relay/gunk_relay.tscn @@ -115,6 +115,7 @@ collision_mask = 0 script = ExtResource("1_rdv5j") editor_arrow_scene = ExtResource("2_nfkbq") value = 800.0 +splatter_scale = 0.2 [node name="MeshInstance3D" type="MeshInstance3D" parent="."] gi_mode = 2