My first 3D game with Godot 4.4

This commit is contained in:
2025-08-18 18:48:02 -04:00
commit aef6af1eca
89 changed files with 10495 additions and 0 deletions

22
player/bullet_3d.gd Normal file
View File

@@ -0,0 +1,22 @@
extends Area3D
const SPEED = 55.0
const RANGE = 80.0
var travelled_distance = 0.0
func _physics_process(delta: float) -> void:
var distance = SPEED * delta
position += -transform.basis.z * distance
if travelled_distance >= RANGE:
queue_free()
else:
travelled_distance += distance
func _on_body_entered(body: Node3D) -> void:
queue_free()
if body.has_method("take_damage"):
body.take_damage(randf_range(25.0, 50.0))

1
player/bullet_3d.gd.uid Normal file
View File

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

19
player/bullet_3d.tscn Normal file
View File

@@ -0,0 +1,19 @@
[gd_scene load_steps=4 format=3 uid="uid://ct6nuuuphybba"]
[ext_resource type="Script" uid="uid://cjqjeylpmrmth" path="res://player/bullet_3d.gd" id="1_hgguu"]
[ext_resource type="PackedScene" uid="uid://b2dxu3lnx1jx5" path="res://player/projectile/projectile_3d.tscn" id="1_kixxk"]
[sub_resource type="SphereShape3D" id="SphereShape3D_hgguu"]
radius = 0.238564
[node name="Bullet3D" type="Area3D"]
top_level = true
collision_mask = 3
script = ExtResource("1_hgguu")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_hgguu")
[node name="Projectile3D" parent="." instance=ExtResource("1_kixxk")]
[connection signal="body_entered" from="." to="." method="_on_body_entered"]

BIN
player/cookie/Cookie.glb Normal file

Binary file not shown.

View File

@@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dbnc0jij3obmp"
path="res://.godot/imported/Cookie.glb-a84f141391d2f606ef2284a88ee937af.scn"
[deps]
source_file="res://player/cookie/Cookie.glb"
dest_files=["res://.godot/imported/Cookie.glb-a84f141391d2f606ef2284a88ee937af.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,52 @@
[gd_scene load_steps=5 format=3 uid="uid://ci5254r7er0uc"]
[ext_resource type="PackedScene" uid="uid://dbnc0jij3obmp" path="res://player/cookie/Cookie.glb" id="1_nk7nc"]
[sub_resource type="Animation" id="Animation_nsu0p"]
resource_name = "idle"
length = 3.0
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Cookie:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 3),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector3(0, 0, -0.785398), Vector3(0, 6.28319, -0.785398)]
}
[sub_resource type="Animation" id="Animation_a8ig3"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Cookie:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, 0, -0.785398)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_2flrl"]
_data = {
&"RESET": SubResource("Animation_a8ig3"),
&"idle": SubResource("Animation_nsu0p")
}
[node name="cookie_model" type="Node3D"]
[node name="Cookie" parent="." instance=ExtResource("1_nk7nc")]
transform = Transform3D(0.0212132, 0.0212132, 0, -0.0212132, 0.0212132, 0, 0, 0, 0.03, 0, 0.6, 0)
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
&"": SubResource("AnimationLibrary_2flrl")
}
autoplay = "idle"

17
player/cookie_3d.gd Normal file
View File

@@ -0,0 +1,17 @@
extends Area3D
var food_ready: bool = true
func _on_body_entered(body: Node3D) -> void:
if body.has_method("heal") and food_ready == true:
$cookie_model.visible = false
food_ready = false
body.heal()
%RespawnTimer.start()
else:
print(str(body) + " wants to eat a cookie, but doesn't know how!")
return
func _on_respawn_timer_timeout() -> void:
food_ready = true
$cookie_model.visible = true

1
player/cookie_3d.gd.uid Normal file
View File

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

24
player/cookie_3d.tscn Normal file
View File

@@ -0,0 +1,24 @@
[gd_scene load_steps=4 format=3 uid="uid://ht0ktfrqr1ef"]
[ext_resource type="Script" uid="uid://dowji2oraklfp" path="res://player/cookie_3d.gd" id="1_ta3vh"]
[ext_resource type="PackedScene" uid="uid://ci5254r7er0uc" path="res://player/cookie/cookie_model.tscn" id="1_yyge1"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ta3vh"]
height = 1.2
[node name="cookie_3d" type="Area3D"]
script = ExtResource("1_ta3vh")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_ta3vh")
[node name="cookie_model" parent="." instance=ExtResource("1_yyge1")]
[node name="RespawnTimer" type="Timer" parent="."]
unique_name_in_owner = true
wait_time = 15.0
one_shot = true
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
[connection signal="timeout" from="RespawnTimer" to="." method="_on_respawn_timer_timeout"]

6
player/gun_model.gd Normal file
View File

@@ -0,0 +1,6 @@
extends Node3D
@onready var animation_player: AnimationPlayer = %AnimationPlayer
func recoil() -> void:
animation_player.play("recoil")

1
player/gun_model.gd.uid Normal file
View File

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

78
player/gun_model.tscn Normal file
View File

@@ -0,0 +1,78 @@
[gd_scene load_steps=6 format=3 uid="uid://dkb2mu7n4t308"]
[ext_resource type="PackedScene" uid="uid://buif8h7ixc1cy" path="res://player/simple_gun/gun_model.glb" id="1_81uiy"]
[ext_resource type="Script" uid="uid://dwu1ndn4oojx4" path="res://player/gun_model.gd" id="2_3tpoa"]
[sub_resource type="Animation" id="Animation_3tpoa"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("BlueGun:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, -1.5708, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("BlueGun:position")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, -0.299353, -0.146614)]
}
[sub_resource type="Animation" id="Animation_81uiy"]
resource_name = "recoil"
length = 0.2
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("BlueGun:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.05, 0.1, 0.15, 0.2),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1),
"update": 0,
"values": [Vector3(0, -1.5708, 0), Vector3(1.13133e-08, -1.5708, 0.261799), Vector3(1.85986e-08, -1.5708, 0.436332), Vector3(9.29928e-09, -1.5708, 0.218166), Vector3(0, -1.5708, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("BlueGun:position")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.1, 0.15, 0.2),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 0,
"values": [Vector3(0, -0.299353, -0.146614), Vector3(0, -0.299353, -0.246614), Vector3(0, -0.299353, -0.196614), Vector3(0, -0.299353, -0.146614)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_3tpoa"]
_data = {
&"RESET": SubResource("Animation_3tpoa"),
&"recoil": SubResource("Animation_81uiy")
}
[node name="gun_model" instance=ExtResource("1_81uiy")]
script = ExtResource("2_3tpoa")
[node name="BlueGun" parent="." index="0"]
transform = Transform3D(-3.61999e-06, 0, -1, 0, 1, 0, 1, 0, -3.61999e-06, 0, -0.299353, -0.146614)
[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
unique_name_in_owner = true
libraries = {
&"": SubResource("AnimationLibrary_3tpoa")
}
speed_scale = 1.25

83
player/player.gd Normal file
View File

@@ -0,0 +1,83 @@
extends CharacterBody3D
const SPEED: float = 7
const HEALTH_MAX: float = 100.0
const MOUSE_SENSE: float = 0.35
var health: float = 50.0
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
if (Input.mouse_mode == Input.MOUSE_MODE_CAPTURED):
if (event is InputEventMouseMotion):
rotation_degrees.y -= event.relative.x * MOUSE_SENSE
$Camera3D.rotation_degrees.x -= event.relative.y * MOUSE_SENSE
$Camera3D.rotation_degrees.x = clamp(
$Camera3D.rotation_degrees.x, -85.0, 85.0
)
elif (event is InputEventMouseButton):
match event.button_index:
MOUSE_BUTTON_LEFT:
var timer: Timer = $Camera3D/gun_model/ShootTimer
if (event.is_pressed()):
print("left MB pressed")
$Camera3D/gun_model.recoil()
shoot()
timer.start()
else:
print("left MB released")
timer.stop()
_:
# unhandled mouse events
pass
else:
# other unhandled input
pass
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _physics_process(delta: float) -> void:
%HealthBar.set("value", health)
# Player model camera is -Z aligned
var input_direction_2D = Input.get_vector(
"move_left", "move_right", "move_forward", "move_back"
)
var direction = transform.basis * Vector3(
input_direction_2D.x, 0.0, input_direction_2D.y
)
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
velocity.y -= 9.80665 * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = 3.5
move_and_slide()
func shoot() -> void:
const BULLET_3D = preload("res://player/bullet_3d.tscn")
var new_bullet = BULLET_3D.instantiate()
%Marker3D.add_child(new_bullet)
new_bullet.global_transform = %Marker3D.global_transform
%AudioStreamPlayer.play()
func _on_shoot_timer_timeout() -> void:
$Camera3D/gun_model.recoil()
shoot()
func heal(amount: float = 35.0) -> void:
# TODO: add values and other fancy healing stuff, for now this works
health = clampf((health + amount), 0.0, HEALTH_MAX)
func hurt(amount: float) -> void:
health = clampf((health - amount), 0.0, HEALTH_MAX)

1
player/player.gd.uid Normal file
View File

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

View File

@@ -0,0 +1,14 @@
shader_type spatial;
render_mode ambient_light_disabled, shadows_disabled, unshaded;
uniform vec3 tail_color : source_color;
uniform vec3 head_color : source_color;
void vertex() {
float def = sin(UV.y * 2.0 * PI - TIME * 20.0) + 1.0 / 2.0;
VERTEX.xz += NORMAL.xz * def * 0.05 * UV.y * COLOR.x;
}
void fragment() {
ALBEDO = mix(head_color, tail_color, COLOR.x);
}

View File

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

View File

@@ -0,0 +1,16 @@
[gd_scene load_steps=4 format=3 uid="uid://b2dxu3lnx1jx5"]
[ext_resource type="PackedScene" uid="uid://o1s0yu8jt5h4" path="res://player/projectile/projectile_model.glb" id="1_ndq6i"]
[ext_resource type="Shader" uid="uid://b4wc5wsyakc0a" path="res://player/projectile/projectile.gdshader" id="2_chfaf"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_htc8w"]
render_priority = 0
shader = ExtResource("2_chfaf")
shader_parameter/tail_color = Color(0.863943, 0.713942, 0.177483, 1)
shader_parameter/head_color = Color(0.700086, 0.267569, 6.73831e-07, 1)
[node name="Projectile3D" instance=ExtResource("1_ndq6i")]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 0, 0, 0)
[node name="Projectile" parent="." index="0"]
material_override = SubResource("ShaderMaterial_htc8w")

Binary file not shown.

View File

@@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://o1s0yu8jt5h4"
path="res://.godot/imported/projectile_model.glb-828324a605ffc923b73f3dcca1df4ac2.scn"
[deps]
source_file="res://player/projectile/projectile_model.glb"
dest_files=["res://.godot/imported/projectile_model.glb-828324a605ffc923b73f3dcca1df4ac2.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

BIN
player/reticle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

34
player/reticle.png.import Normal file
View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://daxb87ykphjeh"
path="res://.godot/imported/reticle.png-94d8174638f634e8c9dc538a8a25a59b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://player/reticle.png"
dest_files=["res://.godot/imported/reticle.png-94d8174638f634e8c9dc538a8a25a59b.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

Binary file not shown.

View File

@@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://buif8h7ixc1cy"
path="res://.godot/imported/gun_model.glb-1353dadf5f32e414e4a769d740e7949e.scn"
[deps]
source_file="res://player/simple_gun/gun_model.glb"
dest_files=["res://.godot/imported/gun_model.glb-1353dadf5f32e414e4a769d740e7949e.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

View File

@@ -0,0 +1,36 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d4gvfj2l8l2mj"
path.s3tc="res://.godot/imported/gun_model_gun_palette.png-5e9639c5a5afbe289b00847128bc0590.s3tc.ctex"
path.etc2="res://.godot/imported/gun_model_gun_palette.png-5e9639c5a5afbe289b00847128bc0590.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://player/simple_gun/gun_model_gun_palette.png"
dest_files=["res://.godot/imported/gun_model_gun_palette.png-5e9639c5a5afbe289b00847128bc0590.s3tc.ctex", "res://.godot/imported/gun_model_gun_palette.png-5e9639c5a5afbe289b00847128bc0590.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dxfe1l1p7klxa"
path="res://.godot/imported/simple_gun_gun_palette.png-9f1ece1a39a41c24787881bd26d0b826.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://player/simple_gun/simple_gun_gun_palette.png"
dest_files=["res://.godot/imported/simple_gun_gun_palette.png-9f1ece1a39a41c24787881bd26d0b826.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