Grunkled beast w/ procedural animation

This commit is contained in:
Rob Kelly 2025-03-14 11:46:49 -06:00
parent eb683e7358
commit 6746b42326
28 changed files with 1246 additions and 49 deletions

BIN
asset_dev/beast/beast.blend Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,731 @@
{
"asset":{
"generator":"Khronos glTF Blender I/O v4.3.47",
"version":"2.0"
},
"scene":0,
"scenes":[
{
"name":"Scene",
"nodes":[
24
]
}
],
"nodes":[
{
"name":"IK_Knee.F.L",
"rotation":[
0.1497122347354889,
-0.8684139847755432,
0.3859991431236267,
0.2728516459465027
],
"scale":[
0.9999998807907104,
1.000000238418579,
1
],
"translation":[
-0.6344399452209473,
-0.8762551546096802,
-0.982624888420105
]
},
{
"children":[
0
],
"name":"IK_Leg.F.L",
"rotation":[
2.56550283239676e-08,
2.841698005795479e-07,
-3.3527612686157227e-08,
1
],
"scale":[
1.0000001192092896,
1,
0.9999999403953552
],
"translation":[
-8.568167686462402e-08,
0.5844804644584656,
-1.7462298274040222e-08
]
},
{
"children":[
1
],
"name":"Leg3.F.L",
"rotation":[
-0.2818467915058136,
0.06332080811262131,
0.21096482872962952,
0.933834433555603
],
"scale":[
1.0000001192092896,
1,
1
],
"translation":[
-6.301623756144181e-08,
1.4211536645889282,
-6.055779522284865e-08
]
},
{
"children":[
2
],
"name":"Leg2.F.L",
"rotation":[
-1.1081444739602375e-07,
-5.585075157910069e-08,
0.0015022342558950186,
0.999998927116394
],
"scale":[
0.9999999403953552,
0.9999999403953552,
1
],
"translation":[
2.1605956135317683e-08,
1.5049428939819336,
-3.592887765080377e-08
]
},
{
"children":[
3
],
"name":"Leg1.F.L",
"rotation":[
-0.5007143020629883,
0.4992845952510834,
-0.5007867813110352,
0.4992120862007141
],
"scale":[
1,
0.9999998211860657,
0.9999999403953552
],
"translation":[
-0.4196379482746124,
1.06613290309906,
-0.6239852905273438
]
},
{
"name":"IK_Knee.B.L",
"rotation":[
0.14971226453781128,
0.868414044380188,
-0.3859991133213043,
0.2728515863418579
],
"scale":[
1,
1.0000001192092896,
0.9999999403953552
],
"translation":[
0.6344399452209473,
-0.876255214214325,
-0.9826248288154602
]
},
{
"children":[
5
],
"name":"IK_Leg.B.L",
"rotation":[
3.978493978706865e-08,
-4.42785704990456e-07,
1.3038516932795119e-08,
1
],
"scale":[
1,
0.9999999403953552,
0.9999999403953552
],
"translation":[
-2.561137080192566e-09,
0.5844804644584656,
-1.1641532182693481e-10
]
},
{
"children":[
6
],
"name":"Leg3.B.L",
"rotation":[
-0.2818467617034912,
-0.06332068145275116,
-0.2109648585319519,
0.933834433555603
],
"scale":[
1,
1,
1.0000001192092896
],
"translation":[
3.4115803515533116e-09,
1.4211534261703491,
-6.063782365117731e-08
]
},
{
"children":[
7
],
"name":"Leg2.B.L",
"rotation":[
-1.1077454331598346e-07,
3.10951406845561e-08,
-0.0015022143488749862,
0.999998927116394
],
"scale":[
0.9999999403953552,
0.9999999403953552,
1
],
"translation":[
1.4497345546260476e-08,
1.5049431324005127,
-6.810057584516471e-08
]
},
{
"children":[
8
],
"name":"Leg1.B.L",
"rotation":[
-0.4992845356464386,
0.5007143020629883,
-0.4992120862007141,
0.5007867813110352
],
"scale":[
1,
0.9999998807907104,
0.9999999403953552
],
"translation":[
-0.4196379482746124,
-0.06613290309906006,
-0.6239852905273438
]
},
{
"name":"IK_Knee.F.R",
"rotation":[
0.14971230924129486,
0.8684141039848328,
-0.38599905371665955,
0.2728515863418579
],
"scale":[
1,
0.9999997615814209,
1
],
"translation":[
0.6344401836395264,
-0.8762552738189697,
-0.9826245307922363
]
},
{
"children":[
10
],
"name":"IK_Leg.F.R",
"rotation":[
-1.8553691916167736e-10,
-2.9994407668709755e-07,
-7.450580596923828e-09,
1
],
"translation":[
2.584420144557953e-08,
0.5844805240631104,
-1.6298145055770874e-09
]
},
{
"children":[
11
],
"name":"Leg3.F.R",
"rotation":[
-0.2818467915058136,
-0.06332076340913773,
-0.21096479892730713,
0.933834433555603
],
"scale":[
1,
1,
1.0000001192092896
],
"translation":[
6.662163798409892e-08,
1.4211536645889282,
-6.519984907527032e-08
]
},
{
"children":[
12
],
"name":"Leg2.F.R",
"rotation":[
-1.0645147341392658e-07,
5.170326744519116e-08,
-0.001502234023064375,
0.999998927116394
],
"scale":[
0.9999999403953552,
0.9999999403953552,
1
],
"translation":[
-1.3940734788775444e-08,
1.5049428939819336,
-2.2469436089522787e-08
]
},
{
"children":[
13
],
"name":"Leg1.F.R",
"rotation":[
-0.5007143020629883,
-0.4992845356464386,
0.5007867813110352,
0.4992121160030365
],
"scale":[
1,
0.9999998211860657,
0.9999999403953552
],
"translation":[
0.4196379482746124,
1.06613290309906,
-0.6239852905273438
]
},
{
"name":"IK_Knee.B.R",
"rotation":[
0.14971211552619934,
-0.868414044380188,
0.3859991133213043,
0.2728516757488251
],
"translation":[
-0.6344397664070129,
-0.8762551546096802,
-0.9826247692108154
]
},
{
"children":[
15
],
"name":"IK_Leg.B.R",
"rotation":[
2.153683809069662e-08,
5.460678949020803e-07,
-5.587935802964239e-08,
1
],
"scale":[
1,
0.9999999403953552,
0.9999999403953552
],
"translation":[
1.5832483768463135e-08,
0.5844804644584656,
-1.1699739843606949e-08
]
},
{
"children":[
16
],
"name":"Leg3.B.R",
"rotation":[
-0.2818467617034912,
0.06332061439752579,
0.21096491813659668,
0.933834433555603
],
"scale":[
1,
1,
1.0000001192092896
],
"translation":[
2.3854802577716328e-08,
1.4211534261703491,
-5.756010779123244e-08
]
},
{
"children":[
17
],
"name":"Leg2.B.R",
"rotation":[
-1.070358877086619e-07,
-7.306381721861044e-09,
0.0015022143488749862,
0.999998927116394
],
"scale":[
0.9999999403953552,
0.9999999403953552,
1
],
"translation":[
-6.83940015733242e-09,
1.5049428939819336,
-5.24299252901983e-08
]
},
{
"children":[
18
],
"name":"Leg1.B.R",
"rotation":[
-0.4992845952510834,
-0.5007142424583435,
0.4992120563983917,
0.5007868409156799
],
"scale":[
1,
0.9999998807907104,
0.9999999403953552
],
"translation":[
0.4196378290653229,
-0.06613290309906006,
-0.6239852905273438
]
},
{
"children":[
4,
9,
14,
19
],
"name":"Body",
"rotation":[
-0.5,
-0.5,
-0.5,
0.5
],
"scale":[
1,
0.9999999403953552,
1
],
"translation":[
-0.5,
4,
0
]
},
{
"children":[
20
],
"name":"Root"
},
{
"mesh":0,
"name":"Body",
"skin":0
},
{
"mesh":1,
"name":"Leg",
"skin":0
},
{
"children":[
22,
23,
21
],
"name":"Armature"
}
],
"meshes":[
{
"name":"Icosphere",
"primitives":[
{
"attributes":{
"POSITION":0,
"NORMAL":1,
"TEXCOORD_0":2,
"JOINTS_0":3,
"WEIGHTS_0":4
},
"indices":5
}
]
},
{
"name":"Cone",
"primitives":[
{
"attributes":{
"POSITION":7,
"NORMAL":8,
"TEXCOORD_0":9,
"JOINTS_0":10,
"WEIGHTS_0":11
},
"indices":12
}
]
}
],
"skins":[
{
"inverseBindMatrices":6,
"joints":[
21,
20,
4,
3,
2,
1,
0,
9,
8,
7,
6,
5,
14,
13,
12,
11,
10,
19,
18,
17,
16,
15
],
"name":"Armature"
}
],
"accessors":[
{
"bufferView":0,
"componentType":5126,
"count":1024,
"max":[
1.2054719924926758,
4.738262176513672,
0.7221531867980957
],
"min":[
-1.1375094652175903,
3.0816688537597656,
-0.7221531867980957
],
"type":"VEC3"
},
{
"bufferView":1,
"componentType":5126,
"count":1024,
"type":"VEC3"
},
{
"bufferView":2,
"componentType":5126,
"count":1024,
"type":"VEC2"
},
{
"bufferView":3,
"componentType":5121,
"count":1024,
"type":"VEC4"
},
{
"bufferView":4,
"componentType":5126,
"count":1024,
"type":"VEC4"
},
{
"bufferView":5,
"componentType":5123,
"count":1416,
"type":"SCALAR"
},
{
"bufferView":6,
"componentType":5126,
"count":22,
"type":"MAT4"
},
{
"bufferView":7,
"componentType":5126,
"count":480,
"max":[
0.8181655406951904,
3.48964786529541,
0.7158815860748291
],
"min":[
-0.8181655406951904,
0,
-0.7158815860748291
],
"type":"VEC3"
},
{
"bufferView":8,
"componentType":5126,
"count":480,
"type":"VEC3"
},
{
"bufferView":9,
"componentType":5126,
"count":480,
"type":"VEC2"
},
{
"bufferView":10,
"componentType":5121,
"count":480,
"type":"VEC4"
},
{
"bufferView":11,
"componentType":5126,
"count":480,
"type":"VEC4"
},
{
"bufferView":12,
"componentType":5123,
"count":672,
"type":"SCALAR"
}
],
"bufferViews":[
{
"buffer":0,
"byteLength":12288,
"byteOffset":0,
"target":34962
},
{
"buffer":0,
"byteLength":12288,
"byteOffset":12288,
"target":34962
},
{
"buffer":0,
"byteLength":8192,
"byteOffset":24576,
"target":34962
},
{
"buffer":0,
"byteLength":4096,
"byteOffset":32768,
"target":34962
},
{
"buffer":0,
"byteLength":16384,
"byteOffset":36864,
"target":34962
},
{
"buffer":0,
"byteLength":2832,
"byteOffset":53248,
"target":34963
},
{
"buffer":0,
"byteLength":1408,
"byteOffset":56080
},
{
"buffer":0,
"byteLength":5760,
"byteOffset":57488,
"target":34962
},
{
"buffer":0,
"byteLength":5760,
"byteOffset":63248,
"target":34962
},
{
"buffer":0,
"byteLength":3840,
"byteOffset":69008,
"target":34962
},
{
"buffer":0,
"byteLength":1920,
"byteOffset":72848,
"target":34962
},
{
"buffer":0,
"byteLength":7680,
"byteOffset":74768,
"target":34962
},
{
"buffer":0,
"byteLength":1344,
"byteOffset":82448,
"target":34963
}
],
"buffers":[
{
"byteLength":83792,
"uri":"grunk_beast.bin"
}
]
}

View File

@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bi5mxt5s4aq1a"
path="res://.godot/imported/grunk_beast.gltf-120de489875ed9abbf222f99c2d4d2ab.scn"
[deps]
source_file="res://assets/npc/grunk_beast/grunk_beast.gltf"
dest_files=["res://.godot/imported/grunk_beast.gltf-120de489875ed9abbf222f99c2d4d2ab.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

View File

@ -0,0 +1,11 @@
extends Camera3D
## Tracks a target
const SMOOTHING := 10.0
@export var target: Node3D
func _process(delta: float) -> void:
var target_basis := Basis.looking_at(target.global_position - global_position, Vector3.UP)
global_basis = global_basis.slerp(target_basis, SMOOTHING * delta).orthonormalized()

View File

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

View File

@ -0,0 +1,20 @@
class_name FrameSkipper extends Node
## Disables child processing every N frames for some N
@export var frame_skip := 1
@onready var frame_counter := frame_skip
func _set_child_process_mode(mode: ProcessMode) -> void:
for c: Node in get_children():
c.process_mode = mode
func _process(_delta: float) -> void:
if frame_counter == 0:
_set_child_process_mode(Node.PROCESS_MODE_INHERIT)
frame_counter = frame_skip
else:
_set_child_process_mode(Node.PROCESS_MODE_DISABLED)
frame_counter -= 1

View File

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

View File

@ -0,0 +1,67 @@
[gd_scene load_steps=11 format=3 uid="uid://cbxlfnlmgdvsq"]
[ext_resource type="PackedScene" uid="uid://d2664rpg4losx" path="res://src/world/grunk_beast/grunk_beast.tscn" id="1_6yv42"]
[ext_resource type="Script" uid="uid://bukihqt1lybnx" path="res://levels/grunkbeast_test/frame_skipper.gd" id="1_eco5q"]
[ext_resource type="Script" uid="uid://cpt8dy0csa3eu" path="res://levels/grunkbeast_test/fixed_camera.gd" id="2_77sam"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_goufh"]
[sub_resource type="Sky" id="Sky_pka60"]
sky_material = SubResource("ProceduralSkyMaterial_goufh")
[sub_resource type="Environment" id="Environment_bg05n"]
background_mode = 2
sky = SubResource("Sky_pka60")
[sub_resource type="PlaneMesh" id="PlaneMesh_mn2wa"]
size = Vector2(50, 50)
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_mn2wa"]
[sub_resource type="CylinderMesh" id="CylinderMesh_4n07c"]
top_radius = 2.0
bottom_radius = 2.0
height = 1.0
[sub_resource type="CylinderShape3D" id="CylinderShape3D_6yv42"]
height = 1.0
radius = 2.0
[node name="GrunkbeastTest" type="Node3D"]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866025, -0.156955, -0.474726, 0.5, 0.271854, 0.82225, 0, -0.949453, 0.31391, 0, 0, 0)
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_bg05n")
[node name="WorldFloor" type="StaticBody3D" parent="."]
[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldFloor"]
mesh = SubResource("PlaneMesh_mn2wa")
skeleton = NodePath("../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldFloor"]
shape = SubResource("WorldBoundaryShape3D_mn2wa")
[node name="Cylinder" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5, 1, -4.5)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Cylinder"]
mesh = SubResource("CylinderMesh_4n07c")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Cylinder"]
shape = SubResource("CylinderShape3D_6yv42")
[node name="FrameSkipper" type="Node" parent="."]
script = ExtResource("1_eco5q")
frame_skip = 3
[node name="GrunkBeast" parent="FrameSkipper" instance=ExtResource("1_6yv42")]
move_speed = 8.0
step_time = 0.06
[node name="Camera3D" type="Camera3D" parent="." node_paths=PackedStringArray("target")]
transform = Transform3D(0.252101, -0.522855, 0.81429, 0, 0.841469, 0.540306, -0.967701, -0.136212, 0.212135, 6.63551, 7.43415, 1.1904)
script = ExtResource("2_77sam")
target = NodePath("../FrameSkipper/GrunkBeast")

View File

@ -4,6 +4,33 @@
[ext_resource type="Script" uid="uid://lrsv0185bfu" path="res://src/ui/hud/player_hud.gd" id="2_j6lpx"] [ext_resource type="Script" uid="uid://lrsv0185bfu" path="res://src/ui/hud/player_hud.gd" id="2_j6lpx"]
[ext_resource type="PackedScene" uid="uid://cq8qcp5xg41e0" path="res://src/ui/hud/grunk_counter/grunk_counter.tscn" id="3_5be8f"] [ext_resource type="PackedScene" uid="uid://cq8qcp5xg41e0" path="res://src/ui/hud/grunk_counter/grunk_counter.tscn" id="3_5be8f"]
[sub_resource type="Animation" id="Animation_n6jee"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("GrunkAlert:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("GrunkAlert2:modulate")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0)]
}
[sub_resource type="Animation" id="Animation_5be8f"] [sub_resource type="Animation" id="Animation_5be8f"]
resource_name = "grunk_alert" resource_name = "grunk_alert"
length = 4.0 length = 4.0
@ -33,33 +60,6 @@ tracks/1/keys = {
"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] "values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
} }
[sub_resource type="Animation" id="Animation_n6jee"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("GrunkAlert:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("GrunkAlert2:modulate")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ud8na"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_ud8na"]
_data = { _data = {
&"RESET": SubResource("Animation_n6jee"), &"RESET": SubResource("Animation_n6jee"),

View File

@ -0,0 +1,45 @@
class_name GrunkBeast extends Node3D
## The nefarious grunkbeest!
##
## Procedural animation adapted from https://github.com/CBerry22/Godot-4.0-Procedural-Animation
@export var move_speed := 6.0
@export var turn_speed := 6.0
@export var ground_offset := -1.5
@export_category("Step Parameters")
@export var step_height := 1.2
@export var step_time := 0.1
@export var step_distance := 1.0
@export var step_target_offset := 10.0
@onready var target_fl: BeastIKTarget = $Armature/TargetFL
@onready var target_fr: BeastIKTarget = $Armature/TargetFR
@onready var target_bl: BeastIKTarget = $Armature/TargetBL
@onready var target_br: BeastIKTarget = $Armature/TargetBR
func _process(delta: float) -> void:
# Reorient based on relative leg position
var p1 := Plane(target_bl.global_position, target_fl.global_position, target_fr.global_position)
var p2 := Plane(target_fr.global_position, target_br.global_position, target_bl.global_position)
var normal := (p1.normal + p2.normal).normalized()
var reoriented_basis := (
Basis(normal.cross(basis.z), normal, basis.x.cross(normal)).orthonormalized()
)
basis = basis.slerp(reoriented_basis, move_speed * delta).orthonormalized()
# Reposition body to hang between legs
var centroid := (
(target_fl.position + target_fr.position + target_bl.position + target_br.position) / 4.0
)
var target_pos := centroid + basis.y * ground_offset
var distance := basis.tdoty(target_pos - position)
position = position.lerp(position + basis.y * distance, move_speed * delta)
# Movement
var direction := Input.get_axis("ui_down", "ui_up")
translate(Vector3(0, 0, direction) * move_speed * delta)
var a_direction := Input.get_axis("ui_right", "ui_left")
rotate_object_local(Vector3.UP, a_direction * turn_speed * delta)

View File

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

View File

@ -0,0 +1,198 @@
[gd_scene load_steps=16 format=3 uid="uid://d2664rpg4losx"]
[ext_resource type="PackedScene" uid="uid://bi5mxt5s4aq1a" path="res://assets/npc/grunk_beast/grunk_beast.gltf" id="1_hoss2"]
[ext_resource type="Script" uid="uid://dkll8s6kwb41r" path="res://src/world/grunk_beast/start_ik.gd" id="2_qic24"]
[ext_resource type="Script" uid="uid://dgy2ubsj1fotp" path="res://src/world/grunk_beast/grunk_beast.gd" id="2_qqnhb"]
[ext_resource type="Shader" uid="uid://ckxc0ngd37rtk" path="res://src/shaders/gunk.gdshader" id="4_0gxpq"]
[ext_resource type="Script" uid="uid://7is3sa00qejh" path="res://src/world/grunk_beast/ik_target.gd" id="4_3gbao"]
[ext_resource type="Script" uid="uid://bpyovjodpxjpb" path="res://src/world/grunk_beast/step_ray.gd" id="4_faau1"]
[ext_resource type="Script" uid="uid://c1gitpy7s78ev" path="res://src/world/grunk_beast/target_container.gd" id="5_wffas"]
[ext_resource type="Texture2D" uid="uid://cm1jrvx7ftx4c" path="res://assets/black.png" id="5_xuag8"]
[ext_resource type="FastNoiseLite" uid="uid://cnlvdtx68giv6" path="res://assets/materials/gunk_noise.tres" id="6_mbqcc"]
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_2roq2"]
width = 256
height = 256
depth = 32
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("6_mbqcc")
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_fk1xc"]
width = 256
height = 256
depth = 32
seamless = true
seamless_blend_skirt = 0.5
noise = ExtResource("6_mbqcc")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_7fplw"]
frequency = 0.0703
[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_omayi"]
width = 32
height = 32
depth = 128
noise = SubResource("FastNoiseLite_7fplw")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_vvw1q"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("4_0gxpq")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
shader_parameter/pixellation = 128.0
shader_parameter/time_pixellation = 30.0
shader_parameter/roughness = 0.15
shader_parameter/specular_contribution = 0.8
shader_parameter/emission_strength = 0.02
shader_parameter/uv_scale = Vector2(2, 2)
shader_parameter/time_scale = 0.2
shader_parameter/edge_bleed = 0.25
shader_parameter/gunk_mask = ExtResource("5_xuag8")
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_2roq2")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_fk1xc")
shader_parameter/jitter_magnitude = 0.3
shader_parameter/jitter_time_scale = 0.6
shader_parameter/jitter_noise = SubResource("NoiseTexture3D_omayi")
shader_parameter/vertex_inflation = 0.0
shader_parameter/inflation_pixellation = 10.0
[sub_resource type="ShaderMaterial" id="ShaderMaterial_ml8dd"]
resource_local_to_scene = true
render_priority = 0
shader = ExtResource("4_0gxpq")
shader_parameter/color_1 = Color(0, 0.03, 0.1, 1)
shader_parameter/color_2 = Color(0, 0.1, 0.3, 1)
shader_parameter/emission_color = Color(0.25, 0.88, 1, 1)
shader_parameter/pixellation = 128.0
shader_parameter/time_pixellation = 30.0
shader_parameter/roughness = 0.15
shader_parameter/specular_contribution = 0.8
shader_parameter/emission_strength = 0.02
shader_parameter/uv_scale = Vector2(2, 2)
shader_parameter/time_scale = 0.2
shader_parameter/edge_bleed = 0.25
shader_parameter/gunk_mask = ExtResource("5_xuag8")
shader_parameter/gunk_noise = SubResource("NoiseTexture3D_2roq2")
shader_parameter/gunk_normal_map = SubResource("NoiseTexture3D_fk1xc")
shader_parameter/jitter_magnitude = 0.1
shader_parameter/jitter_time_scale = 0.6
shader_parameter/jitter_noise = SubResource("NoiseTexture3D_omayi")
shader_parameter/vertex_inflation = 0.0
shader_parameter/inflation_pixellation = 10.0
[node name="GrunkBeast" instance=ExtResource("1_hoss2")]
script = ExtResource("2_qqnhb")
[node name="Armature" parent="." index="0"]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, 0, 0, 0)
[node name="Skeleton3D" parent="Armature" index="0"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
[node name="Body" parent="Armature/Skeleton3D" index="0"]
surface_material_override/0 = SubResource("ShaderMaterial_vvw1q")
[node name="Leg" parent="Armature/Skeleton3D" index="1"]
surface_material_override/0 = SubResource("ShaderMaterial_ml8dd")
[node name="FrontLeftLeg" type="SkeletonIK3D" parent="Armature/Skeleton3D" index="2"]
root_bone = &"Leg1.F.L"
tip_bone = &"IK_Leg.F.L"
use_magnet = true
magnet = Vector3(0, 10, 0)
target_node = NodePath("../../TargetFL")
script = ExtResource("2_qic24")
[node name="FrontRightLeg" type="SkeletonIK3D" parent="Armature/Skeleton3D" index="3"]
root_bone = &"Leg1.F.R"
tip_bone = &"IK_Leg.F.R"
use_magnet = true
magnet = Vector3(0, 10, 0)
target_node = NodePath("../../TargetFR")
script = ExtResource("2_qic24")
[node name="BackLeftLeg" type="SkeletonIK3D" parent="Armature/Skeleton3D" index="4"]
root_bone = &"Leg1.B.L"
tip_bone = &"IK_Leg.B.L"
use_magnet = true
magnet = Vector3(0, 10, 0)
target_node = NodePath("../../TargetBL")
script = ExtResource("2_qic24")
[node name="BackRightLeg" type="SkeletonIK3D" parent="Armature/Skeleton3D" index="5"]
root_bone = &"Leg1.B.R"
tip_bone = &"IK_Leg.B.R"
use_magnet = true
magnet = Vector3(0, 10, 0)
target_node = NodePath("../../TargetBR")
script = ExtResource("2_qic24")
[node name="TargetFL" type="Marker3D" parent="Armature" index="1" node_paths=PackedStringArray("step_target", "neighbor", "diagonal")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5, 0, 1.5)
top_level = true
script = ExtResource("4_3gbao")
step_target = NodePath("../../StepTargets/RayFL/StepFL")
neighbor = NodePath("../TargetFR")
diagonal = NodePath("../TargetBR")
[node name="TargetFR" type="Marker3D" parent="Armature" index="2" node_paths=PackedStringArray("step_target", "neighbor", "diagonal")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.5, 0, 1.5)
top_level = true
script = ExtResource("4_3gbao")
step_target = NodePath("../../StepTargets/RayFR/StepFR")
neighbor = NodePath("../TargetFL")
diagonal = NodePath("../TargetBL")
[node name="TargetBL" type="Marker3D" parent="Armature" index="3" node_paths=PackedStringArray("step_target", "neighbor", "diagonal")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, -2)
top_level = true
script = ExtResource("4_3gbao")
step_target = NodePath("../../StepTargets/RayBL/StepBL")
neighbor = NodePath("../TargetBR")
diagonal = NodePath("../TargetFR")
[node name="TargetBR" type="Marker3D" parent="Armature" index="4" node_paths=PackedStringArray("step_target", "neighbor", "diagonal")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, -2)
top_level = true
script = ExtResource("4_3gbao")
step_target = NodePath("../../StepTargets/RayBR/StepBR")
neighbor = NodePath("../TargetBL")
diagonal = NodePath("../TargetFL")
[node name="StepTargets" type="Node3D" parent="." index="1"]
script = ExtResource("5_wffas")
[node name="RayFL" type="RayCast3D" parent="StepTargets" index="0"]
transform = Transform3D(0.996195, -0.0871557, 0, 0.0868241, 0.992404, 0.0871557, -0.00759612, -0.0868241, 0.996195, 1.5, 3.5, 1.5)
target_position = Vector3(0, -10, 0)
script = ExtResource("4_faau1")
[node name="StepFL" type="Marker3D" parent="StepTargets/RayFL" index="0"]
transform = Transform3D(0.996195, 0.0868241, -0.00759612, -0.0871557, 0.992404, -0.0868241, 9.31323e-10, 0.0871557, 0.996195, -0.303884, -3.47341, -0.305045)
[node name="RayFR" type="RayCast3D" parent="StepTargets" index="1"]
transform = Transform3D(0.996195, 0.0871557, 0, -0.0868241, 0.992404, 0.0871557, 0.00759612, -0.0868241, 0.996195, -1.5, 3.5, 1.5)
target_position = Vector3(0, -10, 0)
script = ExtResource("4_faau1")
[node name="StepFR" type="Marker3D" parent="StepTargets/RayFR" index="0"]
transform = Transform3D(0.996195, -0.0868241, 0.00759612, 0.0871557, 0.992404, -0.0868241, -9.31323e-10, 0.0871557, 0.996195, 0.303884, -3.47341, -0.305045)
[node name="RayBL" type="RayCast3D" parent="StepTargets" index="2"]
transform = Transform3D(0.996195, -0.0871557, 0, 0.0868241, 0.992404, -0.0871557, 0.00759612, 0.0868241, 0.996195, 2, 3.5, -2)
target_position = Vector3(0, -10, 0)
script = ExtResource("4_faau1")
[node name="StepBL" type="Marker3D" parent="StepTargets/RayBL" index="0"]
transform = Transform3D(0.996195, 0.0868241, 0.00759612, -0.0871557, 0.992404, 0.0868241, -9.31323e-10, -0.0871557, 0.996195, -0.303884, -3.47341, 0.305045)
[node name="RayBR" type="RayCast3D" parent="StepTargets" index="3"]
transform = Transform3D(0.996195, 0.0871557, 0, -0.0868241, 0.992404, -0.0871557, -0.00759612, 0.0868241, 0.996195, -2, 3.5, -2)
target_position = Vector3(0, -10, 0)
script = ExtResource("4_faau1")
[node name="StepBR" type="Marker3D" parent="StepTargets/RayBR" index="0"]
transform = Transform3D(0.996195, -0.0868241, -0.00759612, 0.0871557, 0.992404, 0.0868241, 1.39698e-09, -0.0871557, 0.996195, 0.303884, -3.47341, 0.305045)

View File

@ -0,0 +1,49 @@
class_name BeastIKTarget extends Marker3D
const CRITICAL_ANGLE := 0.1
@export var step_target: Node3D
@export var neighbor: BeastIKTarget
@export var diagonal: BeastIKTarget
var stepping := false
var _resting := false
@onready var parent := owner as GrunkBeast
func step() -> void:
# Tween a rough arc
var up := parent.basis.y
var midpoint := 0.5 * (global_position + step_target.global_position) + up * parent.step_height
var time := 0.5 * parent.step_time
stepping = true
# TODO: tween this?
global_basis = step_target.global_basis
var tween := create_tween()
tween.tween_property(self, "global_position", midpoint, time)
tween.tween_property(self, "global_position", step_target.global_position, time)
tween.tween_callback(_end_step)
func _end_step() -> void:
stepping = false
_resting = true
func _process(_delta: float) -> void:
if _resting:
_resting = false
return
if (
!stepping
and !neighbor.stepping
and global_position.distance_to(step_target.global_position) >= parent.step_distance
):
step()
diagonal.step()

View File

@ -0,0 +1 @@
uid://7is3sa00qejh

View File

@ -0,0 +1,7 @@
@tool
extends SkeletonIK3D
## Simply starts the IK simulation on ready.
func _ready() -> void:
start()

View File

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

View File

@ -0,0 +1,15 @@
extends RayCast3D
## Snaps step targets to surfaces
@export var step_target: Node3D
func _ready() -> void:
if not step_target:
step_target = get_child(0) as Node3D
func _physics_process(_delta: float) -> void:
var point := get_collision_point()
if point:
step_target.global_position = point

View File

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

View File

@ -0,0 +1,10 @@
extends Node3D
@onready var parent := owner as GrunkBeast
@onready var prev_position := parent.global_position
func _process(_delta: float) -> void:
var diff := parent.global_position - prev_position
global_position = parent.global_position + diff * parent.step_target_offset
prev_position = parent.global_position

View File

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

View File

@ -17,21 +17,6 @@ height = 0.2
[sub_resource type="SphereShape3D" id="SphereShape3D_gk1l0"] [sub_resource type="SphereShape3D" id="SphereShape3D_gk1l0"]
radius = 0.1 radius = 0.1
[sub_resource type="Animation" id="Animation_rdv5j"]
resource_name = "trigger"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("MeshInstance3D:mesh:material:emission_energy_multiplier")
tracks/0/interp = 2
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.166667, 1),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 2.0, 0.0]
}
[sub_resource type="Animation" id="Animation_ipm58"] [sub_resource type="Animation" id="Animation_ipm58"]
length = 0.001 length = 0.001
tracks/0/type = "value" tracks/0/type = "value"
@ -47,6 +32,21 @@ tracks/0/keys = {
"values": [0.0] "values": [0.0]
} }
[sub_resource type="Animation" id="Animation_rdv5j"]
resource_name = "trigger"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("MeshInstance3D:mesh:material:emission_energy_multiplier")
tracks/0/interp = 2
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.166667, 1),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [0.0, 2.0, 0.0]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ipm58"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_ipm58"]
_data = { _data = {
&"RESET": SubResource("Animation_ipm58"), &"RESET": SubResource("Animation_ipm58"),

View File

@ -5,18 +5,18 @@
- [x] [[nodules]] - [x] [[nodules]]
- [ ] #equipment - [ ] #equipment
- [ ] [[spray beam]] - [ ] [[spray beam]]
- [ ] Spray nozzle asset - [x] Spray nozzle asset
- [x] Point spray - [x] Point spray
- [x] Wide-angle spray - [x] Wide-angle spray
- [x] Tall-angle spray? - [x] Tall-angle spray?
- [ ] Spray-can? - [ ] Spray-can?
- [ ] [[chisel]] - [x] [[toothbrush]]
- [ ] [[radar]] - [ ] [[radar]]
- [ ] [[mp3 player]] - [ ] [[mp3 player]]
- [ ] [[manual]] - [ ] [[manual]]
- [ ] #mechanics - [ ] #mechanics
- [ ] [[grunk alert]] - [x] [[grunk alert]]
- [ ] [[alarm]] - [x] [[alarm]]
- [ ] [[hotwire]] - [ ] [[hotwire]]
- [ ] [[tripwire]] - [ ] [[tripwire]]
- [ ] [[looker]] - [ ] [[looker]]

View File

@ -1,6 +1,6 @@
Does nothing on its own, but increases the [[grunk alert]] when activated by a connected component like a [[tripwire]] or [[listener]]. Does nothing on its own, but increases the [[grunk alert]] when activated by a connected component like a [[tripwire]] or [[listener]].
Will activate if the player tries to remove with the spray beam. Can be safely deactivated & harvested with the [[chisel]]. Will activate if the player tries to remove with the spray beam. Can be safely deactivated & harvested with the [[toothbrush]].
For flavor, maybe screeches or something when activated? For flavor, maybe screeches or something when activated?

View File

@ -6,4 +6,4 @@ The player is employed (sentenced) to harvest grunk, which is a somewhat valuabl
## #mechanics ## #mechanics
Grunk can be cleared using the [[spray beam]] or [[chisel]]. #mechanics components must have a continuous [[grunk]] connection to an [[alarm]] to activate it. Grunk can be cleared using the [[spray beam]] or [[toothbrush]]. #mechanics components must have a continuous [[grunk]] connection to an [[alarm]] to activate it.

View File

@ -4,7 +4,7 @@ Triggers a connected [[alarm]] when the player makes noise nearby. Noises includ
- #maybe moving without sneaking? - #maybe moving without sneaking?
- #maybe using the MP3 player, just for fun - #maybe using the MP3 player, just for fun
Grunk can be cleared silently with the [[chisel]]. Grunk can be cleared silently with the [[toothbrush]].
tags: #mechanics tags: #mechanics