Splatter effect scales with source node scale

This commit is contained in:
Rob Kelly 2025-04-20 00:23:41 -06:00
parent eba7d3d06b
commit 9d8f1f1767
12 changed files with 62 additions and 32 deletions

View File

@ -13,12 +13,6 @@ extends Node3D
@onready var prop_test_spawn_point: Marker3D = %PropTestSpawnPoint @onready var prop_test_spawn_point: Marker3D = %PropTestSpawnPoint
@onready var item_test_spawn_point: Marker3D = %ItemTestSpawnPoint @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: func reset() -> void:
print("Resetting level!") print("Resetting level!")
@ -26,6 +20,10 @@ func reset() -> void:
Callable(bulkhead, "close").call() Callable(bulkhead, "close").call()
Callable(open_switch, "enable").call() Callable(open_switch, "enable").call()
Callable(close_switch, "disable").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(signal_test_spawn_point, signal_test_scene)
_do_spawn(prop_test_spawn_point, prop_test_scene) _do_spawn(prop_test_spawn_point, prop_test_scene)
_do_spawn(item_test_spawn_point, item_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: func spawn_nodule() -> void:
var nodule_scene: PackedScene = load("res://src/world/gunk_node/grunk_nodule.tscn")
_do_spawn(nodule_spawn_point, nodule_scene) _do_spawn(nodule_spawn_point, nodule_scene)
func spawn_alarm() -> void: func spawn_alarm() -> void:
var alarm_scene: PackedScene = load("res://src/world/mechanics/alarm/gunk_alarm.tscn")
_do_spawn(alarm_spawn_point, alarm_scene) _do_spawn(alarm_spawn_point, alarm_scene)

View File

@ -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

View File

@ -0,0 +1 @@
uid://di5b65ehsfatj

View File

@ -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://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="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://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="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"] [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") curve = SubResource("Curve_y6klh")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_5xu2x"] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_5xu2x"]
resource_local_to_scene = true
lifetime_randomness = 0.57 lifetime_randomness = 0.57
emission_shape = 1 emission_shape = 1
emission_sphere_radius = 1.0 emission_sphere_radius = 1.0
@ -65,6 +67,7 @@ point_count = 2
curve = SubResource("Curve_t00bd") curve = SubResource("Curve_t00bd")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_2iem1"] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_2iem1"]
resource_local_to_scene = true
lifetime_randomness = 0.55 lifetime_randomness = 0.55
emission_shape = 1 emission_shape = 1
emission_sphere_radius = 0.9 emission_sphere_radius = 0.9
@ -112,8 +115,10 @@ one_shot = true
explosiveness = 0.45 explosiveness = 0.45
process_material = SubResource("ParticleProcessMaterial_5xu2x") process_material = SubResource("ParticleProcessMaterial_5xu2x")
draw_pass_1 = SubResource("QuadMesh_y6klh") draw_pass_1 = SubResource("QuadMesh_y6klh")
script = ExtResource("2_grvat")
[node name="SubSplatter" type="GPUParticles3D" parent="."] [node name="SubSplatter" type="GPUParticles3D" parent="."]
unique_name_in_owner = true
sorting_offset = 9.0 sorting_offset = 9.0
emitting = false emitting = false
amount = 4 amount = 4
@ -131,8 +136,9 @@ one_shot = true
autostart = true autostart = true
[node name="SplatterSFX" type="AudioStreamPlayer3D" parent="."] [node name="SplatterSFX" type="AudioStreamPlayer3D" parent="."]
unique_name_in_owner = true
stream = SubResource("AudioStreamRandomizer_6adkd") stream = SubResource("AudioStreamRandomizer_6adkd")
volume_db = -16.0 volume_db = -12.0
unit_size = 6.0 unit_size = 6.0
autoplay = true autoplay = true
bus = &"SFX" bus = &"SFX"

View File

@ -25,9 +25,6 @@ const MIN_CHITTER_INTERVAL := 4.0
@export var chitter_time_mean := 60.0 @export var chitter_time_mean := 60.0
@export var chitter_time_st_dev := 30.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 mesh_instance: MeshInstance3D = %MeshInstance3D
@onready var chitter_sfx: AudioStreamPlayer3D = %ChitterSFX @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)) 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: func start_chitter_timer() -> void:
var interval := maxf(MIN_CHITTER_INTERVAL, randfn(chitter_time_mean, chitter_time_st_dev)) var interval := maxf(MIN_CHITTER_INTERVAL, randfn(chitter_time_mean, chitter_time_st_dev))
chitter_timer.start(interval) chitter_timer.start(interval)

View File

@ -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="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="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://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"] [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_layer = 36
collision_mask = 0 collision_mask = 0
script = ExtResource("1_iyr82") script = ExtResource("1_iyr82")
splatter_scene = ExtResource("2_m8r0a")
durability = 3.0 durability = 3.0
metadata/_custom_type_script = "uid://bypgxi0gy56yk" metadata/_custom_type_script = "uid://bypgxi0gy56yk"

View File

@ -13,6 +13,9 @@ const JITTER_FADE_RATE := 0.8
## Value added to the player's grunk total on destruction. ## Value added to the player's grunk total on destruction.
@export var value := 1000.0 @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 _sustained_damage := 0.0
var _hit_this_frame := false var _hit_this_frame := false
@ -55,6 +58,9 @@ func collect() -> void:
## Derived types should override `_destroy` as a lifecycle method. ## Derived types should override `_destroy` as a lifecycle method.
func destroy() -> void: func destroy() -> void:
Game.manager.collect_grunk(value) Game.manager.collect_grunk(value)
var splatter := GrunkSplatter.build(splatter_scale * scale.x)
add_sibling(splatter)
splatter.global_position = global_position
_destroy() _destroy()
destroyed.emit() destroyed.emit()
queue_free() queue_free()

View File

@ -103,7 +103,7 @@ seamless = true
seamless_blend_skirt = 0.5 seamless_blend_skirt = 0.5
noise = ExtResource("7_aqwgb") noise = ExtResource("7_aqwgb")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_qmfft"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_umjw2"]
resource_local_to_scene = true resource_local_to_scene = true
render_priority = 0 render_priority = 0
shader = ExtResource("6_6agnv") shader = ExtResource("6_6agnv")
@ -237,7 +237,7 @@ skeleton = NodePath("GunkHallBody")
[node name="GunkHallBody" parent="GunkHall" instance=ExtResource("4_7v7un")] [node name="GunkHallBody" parent="GunkHall" instance=ExtResource("4_7v7un")]
unique_name_in_owner = true unique_name_in_owner = true
initial_mask = ExtResource("5_llot1") 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"] [node name="CollisionShape3D" type="CollisionShape3D" parent="GunkHall/GunkHallBody"]
shape = SubResource("ConcavePolygonShape3D_qjnj2") shape = SubResource("ConcavePolygonShape3D_qjnj2")

View File

@ -7,8 +7,6 @@ signal triggered
@export var quick_connect_to: SignalNode: @export var quick_connect_to: SignalNode:
set = _editor_connect set = _editor_connect
@export var splatter_scene: PackedScene
# NOTE # NOTE
# trigger oscillation animation was generated using the formula # 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} # 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() cooldown_timer.start()
func _destroy() -> void:
add_sibling(splatter_scene.instantiate())
func _editor_connect(node: SignalNode) -> void: func _editor_connect(node: SignalNode) -> void:
triggered.connect(node.trigger, CONNECT_PERSIST) triggered.connect(node.trigger, CONNECT_PERSIST)
self.notify_property_list_changed() self.notify_property_list_changed()

View File

@ -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="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"] [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"] [sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_2ibh1"]
@ -58,7 +57,6 @@ _data = {
collision_layer = 36 collision_layer = 36
collision_mask = 0 collision_mask = 0
script = ExtResource("1_htscg") script = ExtResource("1_htscg")
splatter_scene = ExtResource("2_2ibh1")
durability = 3.0 durability = 3.0
value = 4000.0 value = 4000.0
metadata/_custom_type_script = "uid://bypgxi0gy56yk" metadata/_custom_type_script = "uid://bypgxi0gy56yk"

View File

@ -8,9 +8,11 @@ signal triggered
## Emitted when `pulse` is called, after a short delay. ## Emitted when `pulse` is called, after a short delay.
signal pulsed signal pulsed
@export_category("Editor Tools")
@export var quick_connect_to: SignalNode: @export var quick_connect_to: SignalNode:
set = _editor_connect set = _editor_connect
@export_category("Game Scenes")
@export var editor_arrow_scene: PackedScene @export var editor_arrow_scene: PackedScene
var _busy := false var _busy := false

View File

@ -115,6 +115,7 @@ collision_mask = 0
script = ExtResource("1_rdv5j") script = ExtResource("1_rdv5j")
editor_arrow_scene = ExtResource("2_nfkbq") editor_arrow_scene = ExtResource("2_nfkbq")
value = 800.0 value = 800.0
splatter_scale = 0.2
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
gi_mode = 2 gi_mode = 2