Compare commits

28 Commits

Author SHA1 Message Date
Morph01
32f8f2fcde Add Hitmarkers 2024-08-30 18:09:55 +02:00
Morph01
67f7335dba Crosshair really in the center (it was in the top left center of the 40x40 centered rect) 2024-08-30 16:18:16 +02:00
Morph01
791cea356d Fix SubViewport bug when opening the scene FirstPersonPlayer / Zombie (bone collision shape) / Bullet in the editor 2024-08-30 15:49:22 +02:00
Morph01
68d3cec08a simple dynamic crosshair 2024-08-25 18:25:22 +02:00
Morph01
5c6f82c5d7 Random spawn of zombies 2024-08-22 19:40:32 +02:00
Morph01
faf6a9116d Add Dying animation 2024-08-22 17:53:25 +02:00
Morph01
1f52eb2c52 Collision of zombie body parts completed and different damage added depending on the body part hit 2024-08-22 16:15:54 +02:00
Morph01
d88cb9fbff Added zombie spawn animation, red screen and hit stagger when the player is hit. 2024-08-20 16:53:43 +02:00
Morph01
0c667944dd fix attack angle 2024-08-20 16:49:18 +02:00
Morph01
1a14c78ede Zombies with PathFinding Run and Attack ! 2024-08-16 19:17:51 +02:00
Morph01
6160561498 Squashed commit of the following:
commit 4fd60e7874
Author: Morph01 <145839520+Morph01@users.noreply.github.com>
Date:   Fri Aug 16 14:19:29 2024 +0200

    bug animation ???
2024-08-16 14:56:57 +02:00
Morph01
1662691b6e Better Implementation of Timer 2024-08-16 10:36:32 +02:00
Morph01
91d492c2d8 Bullets Colliding and Disappearing 2024-08-15 13:28:16 +02:00
Morph01
b92c5169c4 LaserGun without collisions 2024-08-14 18:46:31 +02:00
ef0bcd0a35 format
All checks were successful
Linux arm64 / Build (push) Successful in 1m2s
2024-08-13 20:21:05 +02:00
841e25a3ac fix import warnings 2024-08-13 20:02:31 +02:00
a43269910d real FPS 2024-08-13 20:00:21 +02:00
083bf2f04d xmake things 2024-08-13 19:59:33 +02:00
436e9a27c5 Revert "add a testing camera fps (switching cameras by pressing t)"
This reverts commit 4707d85150.
2024-08-13 17:40:57 +02:00
Morph01
4707d85150 add a testing camera fps (switching cameras by pressing t)
All checks were successful
Linux arm64 / Build (push) Successful in 57s
2024-08-13 10:10:31 +02:00
d59047d0ae remove template project tag
All checks were successful
Linux arm64 / Build (push) Successful in 56s
2024-08-12 13:28:25 +02:00
e93afbeaf1 fix warnings 2024-08-12 11:51:33 +02:00
d9aba1924f third persson prototype 2024-08-12 11:45:42 +02:00
ba66b7b3b4 remove icon import file 2024-08-12 11:45:17 +02:00
26b62dbdd7 ignore import files 2024-08-12 11:45:00 +02:00
fb0a4fc7a5 remove gdexample 2024-08-12 11:44:52 +02:00
cdbe8c3114 add debug define 2024-08-12 11:43:02 +02:00
35708456d4 add clang format 2024-08-12 11:42:47 +02:00
65 changed files with 29120 additions and 134 deletions

37
.clang-format Normal file
View File

@@ -0,0 +1,37 @@
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: DontAlign
BreakConstructorInitializers: AfterColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
PointerAlignment: Left
SortIncludes: true
SpacesBeforeTrailingComments: 2
UseTab: Always
MaxEmptyLinesToKeep: 5
TabWidth: 4
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
IndentWidth: 4
IndentCaseLabels: true
ColumnLimit: 135
AlwaysBreakTemplateDeclarations: Yes
AllowShortFunctionsOnASingleLine: Empty
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ build/
*.obj *.obj
*.lib *.lib
*.TMP *.TMP
*.import
godot/.godot godot/.godot
godot/lib godot/lib

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,123 @@
[gd_scene load_steps=4 format=3 uid="uid://d38w4ae3qj0k4"]
[ext_resource type="PackedScene" uid="uid://ciex3x7rhv1bx" path="res://Scenes/Weapons/steampunk_rifle.tscn" id="1_i6keg"]
[sub_resource type="CapsuleMesh" id="CapsuleMesh_ky6st"]
[sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_qjfxs"]
points = PackedVector3Array(-0.125207, -0.532801, -0.480507, 0.0227831, 0.47607, 0.498884, 0.169713, 0.559144, 0.464172, 0.231051, -0.803591, 0.320455, 0.40741, 0.651043, -0.243523, -0.482789, 0.594843, 0.0822132, -0.362868, -0.682312, 0.289697, 0.469044, -0.654529, -0.0662713, -0.127444, 0.842701, -0.338103, -0.393435, -0.683942, -0.244717, 0.438255, 0.623309, 0.200849, 0.0841477, 0.977454, 0.114795, -0.0682023, -0.976458, -0.12927, 0.20055, -0.563129, -0.451454, -0.185527, 0.595453, -0.453475, -0.273363, 0.592268, 0.407754, -0.00693649, -0.476823, 0.49966, 0.375821, -0.588614, 0.316955, 0.111579, 0.563059, -0.481177, -0.41725, 0.527866, -0.270497, -0.484546, -0.596972, -0.0665097, -0.279747, 0.908561, 0.0533361, -0.250197, -0.880712, 0.205319, 0.263647, -0.902771, -0.127394, 0.293368, 0.871526, -0.157196, 0.373412, -0.526319, -0.328246, 0.499663, 0.476641, -0.00688856, 0.0531056, 0.875001, 0.324703, -0.154543, -0.590854, 0.465879, -0.0972799, -0.782358, -0.398188, -0.387649, -0.498171, 0.31565, -0.30068, -0.587995, -0.388901)
[node name="FirstPersonPlayer" type="FirstPersonPlayer"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("CapsuleMesh_ky6st")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("ConvexPolygonShape3D_qjfxs")
[node name="Head" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.578545, 0)
[node name="Camera" type="Camera3D" parent="Head"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0)
[node name="Steampunk Rifle" parent="Head/Camera" instance=ExtResource("1_i6keg")]
transform = Transform3D(1, 0, 0, 0, 0.998135, -0.0610485, 0, 0.0610485, 0.998135, 0.19, -0.18, -0.55)
[node name="UserInterface" type="Control" parent="."]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
[node name="Crosshair" type="Crosshair" parent="UserInterface"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
[node name="Top" type="Line2D" parent="UserInterface/Crosshair"]
points = PackedVector2Array(20, 15, 20, 5)
width = 2.0
[node name="Right" type="Line2D" parent="UserInterface/Crosshair"]
points = PackedVector2Array(25, 20, 35, 20)
width = 2.0
[node name="Bottom" type="Line2D" parent="UserInterface/Crosshair"]
points = PackedVector2Array(20, 25, 20, 35)
width = 2.0
[node name="Left" type="Line2D" parent="UserInterface/Crosshair"]
points = PackedVector2Array(15, 20, 5, 20)
width = 2.0
[node name="HitMarkers" type="HitMarkers" parent="UserInterface"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
[node name="TopRight" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(22.5, 17.5, 35, 5)
width = 2.0
[node name="BottomRight" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(22.5, 22.5, 35, 35)
width = 2.0
[node name="BottomLeft" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(17.5, 22.5, 5, 35)
width = 2.0
[node name="TopLeft" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(17.5, 17.5, 5, 5)
width = 2.0
[node name="TopRightHS" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(22.5, 17.5, 35, 5)
width = 2.0
default_color = Color(1, 0, 0, 1)
[node name="BottomRightHS" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(22.5, 22.5, 35, 35)
width = 2.0
default_color = Color(1, 0, 0, 1)
[node name="BottomLeftHS" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(17.5, 22.5, 5, 35)
width = 2.0
default_color = Color(1, 0, 0, 1)
[node name="TopLeftHS" type="Line2D" parent="UserInterface/HitMarkers"]
visible = false
points = PackedVector2Array(17.5, 17.5, 5, 5)
width = 2.0
default_color = Color(1, 0, 0, 1)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,155 @@
[gd_scene load_steps=19 format=3 uid="uid://coue2qehpn4fr"]
[ext_resource type="Texture2D" uid="uid://dujfl12rge3p4" path="res://Assets/Textures/Sky.png" id="1_mnexj"]
[ext_resource type="Texture2D" uid="uid://b8n5rff2a8h2u" path="res://Assets/Textures/Black.png" id="2_fkwcn"]
[ext_resource type="Texture2D" uid="uid://lpbttkw7gpxj" path="res://Assets/Textures/Orange.png" id="3_ux02w"]
[ext_resource type="Texture2D" uid="uid://di3uyny341483" path="res://Assets/Textures/Green.png" id="4_wp15n"]
[ext_resource type="PackedScene" uid="uid://d38w4ae3qj0k4" path="res://Scenes/Characters/first_person_player.tscn" id="5_8ctht"]
[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_6c4vd"]
panorama = ExtResource("1_mnexj")
[sub_resource type="Sky" id="Sky_5ngqa"]
sky_material = SubResource("PanoramaSkyMaterial_6c4vd")
[sub_resource type="Environment" id="Environment_ctwiv"]
background_mode = 2
sky = SubResource("Sky_5ngqa")
tonemap_mode = 2
glow_enabled = true
[sub_resource type="NavigationMesh" id="NavigationMesh_2pdqo"]
vertices = PackedVector3Array(-5.25, 0.5, 1, -5, 0.5, -1.75, -5.5, 0.5, -2, -11.25, 0.5, 1, -5.5, 0.5, -3.75, -3.25, 0.5, -4, -3.25, 0.5, -24.5, -11.25, 0.5, 1, -5.5, 0.5, -2, -5.5, 0.5, -3.75, -24.5, 0.5, 3.25, -11.5, 0.5, 3.25, -11.25, 0.5, 1, -24.5, 0.5, 3.25, -11.25, 0.5, 1, -5.5, 0.5, -3.75, -3.25, 0.5, -24.5, -24.5, 0.5, -24.5, -3.25, 0.5, -4, 3.5, 0.5, -4, 3.5, 0.5, -24.5, -3.25, 0.5, -24.5, 5.5, 0.5, -4, 5.75, 0.5, -3, 24.5, 0.5, -3, 24.5, 0.5, -24.5, 3.5, 0.5, -24.5, 3.5, 0.5, -24.5, 3.5, 0.5, -4, 5.5, 0.5, -4, -5, 0.5, -1.75, -5.25, 0.5, 1, -5, 0.75, 2, 5.25, 0.5, -1.75, -5, 0.75, 2, -7.25, 1.5, 2, -7.25, 1.5, 6, -5, 0.75, 6.25, 24.5, 0.5, -3, 5.75, 0.5, -3, 5.25, 0.5, -1.75, -5.25, 0.5, 7.25, -6.75, 0.5, 7.25, -6.5, 0.5, 24.5, 5.25, 0.5, -1.75, -5, 0.75, 2, -5, 0.75, 6.25, 5.25, 0.5, -1.75, -5, 0.75, 6.25, -5.25, 0.5, 7.25, -6.5, 0.5, 24.5, 24.5, 0.5, 24.5, 24.5, 0.5, -3, -7.25, 1.5, 6, -7.25, 1.5, 2, -14, 3.75, 2, -14, 3.75, 6, -11.5, 0.5, 7, -11.5, 0.5, 3.25, -24.5, 0.5, 3.25, -10, 0.5, 24.5, -9.75, 0.5, 7.25, -11.5, 0.5, 7, -11.5, 0.5, 7, -24.5, 0.5, 3.25, -24.5, 0.5, 24.5, -10, 0.5, 24.5, -10, 0.5, 24.5, -6.5, 0.5, 24.5, -6.75, 0.5, 7.25, -9.75, 0.5, 7.25)
polygons = [PackedInt32Array(1, 0, 2), PackedInt32Array(2, 0, 3), PackedInt32Array(6, 5, 4), PackedInt32Array(7, 9, 8), PackedInt32Array(12, 11, 10), PackedInt32Array(14, 13, 15), PackedInt32Array(15, 13, 16), PackedInt32Array(16, 13, 17), PackedInt32Array(21, 20, 18), PackedInt32Array(18, 20, 19), PackedInt32Array(23, 22, 24), PackedInt32Array(24, 22, 25), PackedInt32Array(25, 22, 26), PackedInt32Array(29, 28, 27), PackedInt32Array(31, 30, 32), PackedInt32Array(32, 30, 33), PackedInt32Array(35, 34, 36), PackedInt32Array(36, 34, 37), PackedInt32Array(40, 39, 38), PackedInt32Array(43, 42, 41), PackedInt32Array(46, 45, 44), PackedInt32Array(48, 47, 49), PackedInt32Array(49, 47, 50), PackedInt32Array(50, 47, 52), PackedInt32Array(50, 52, 51), PackedInt32Array(56, 55, 53), PackedInt32Array(53, 55, 54), PackedInt32Array(59, 58, 57), PackedInt32Array(62, 61, 60), PackedInt32Array(66, 65, 63), PackedInt32Array(63, 65, 64), PackedInt32Array(70, 69, 67), PackedInt32Array(67, 69, 68)]
agent_radius = 0.4
agent_max_slope = 60.0
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ajchh"]
albedo_texture = ExtResource("2_fkwcn")
uv1_triplanar = true
[sub_resource type="PlaneMesh" id="PlaneMesh_mmup0"]
material = SubResource("StandardMaterial3D_ajchh")
size = Vector2(50, 50)
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_26ptr"]
data = PackedVector3Array(25, 0, 25, -25, 0, 25, 25, 0, -25, -25, 0, 25, -25, 0, -25, 25, 0, -25)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jkvud"]
albedo_texture = ExtResource("3_ux02w")
uv1_triplanar = true
[sub_resource type="BoxMesh" id="BoxMesh_plpqy"]
material = SubResource("StandardMaterial3D_jkvud")
size = Vector3(10, 3, 1)
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_v7prx"]
data = PackedVector3Array(-5, 1.5, 0.5, 5, 1.5, 0.5, -5, -1.5, 0.5, 5, 1.5, 0.5, 5, -1.5, 0.5, -5, -1.5, 0.5, 5, 1.5, -0.5, -5, 1.5, -0.5, 5, -1.5, -0.5, -5, 1.5, -0.5, -5, -1.5, -0.5, 5, -1.5, -0.5, 5, 1.5, 0.5, 5, 1.5, -0.5, 5, -1.5, 0.5, 5, 1.5, -0.5, 5, -1.5, -0.5, 5, -1.5, 0.5, -5, 1.5, -0.5, -5, 1.5, 0.5, -5, -1.5, -0.5, -5, 1.5, 0.5, -5, -1.5, 0.5, -5, -1.5, -0.5, 5, 1.5, 0.5, -5, 1.5, 0.5, 5, 1.5, -0.5, -5, 1.5, 0.5, -5, 1.5, -0.5, 5, 1.5, -0.5, -5, -1.5, 0.5, 5, -1.5, 0.5, -5, -1.5, -0.5, 5, -1.5, 0.5, 5, -1.5, -0.5, -5, -1.5, -0.5)
[sub_resource type="PrismMesh" id="PrismMesh_0l7qq"]
left_to_right = -2.0
size = Vector3(5, 5, 5)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pfpgv"]
albedo_texture = ExtResource("4_wp15n")
uv1_triplanar = true
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_rit6o"]
data = PackedVector3Array(-12.5, 2.5, 2.5, 2.5, -2.5, 2.5, -2.5, -2.5, 2.5, -12.5, 2.5, -2.5, -2.5, -2.5, -2.5, 2.5, -2.5, -2.5, -12.5, 2.5, 2.5, -12.5, 2.5, -2.5, 2.5, -2.5, 2.5, -12.5, 2.5, -2.5, 2.5, -2.5, -2.5, 2.5, -2.5, 2.5, -12.5, 2.5, -2.5, -12.5, 2.5, 2.5, -2.5, -2.5, -2.5, -12.5, 2.5, 2.5, -2.5, -2.5, 2.5, -2.5, -2.5, -2.5, -2.5, -2.5, 2.5, 2.5, -2.5, 2.5, -2.5, -2.5, -2.5, 2.5, -2.5, 2.5, 2.5, -2.5, -2.5, -2.5, -2.5, -2.5)
[node name="World" type="World"]
[node name="Map" type="Node3D" parent="."]
[node name="WorldEnvironment" type="WorldEnvironment" parent="Map"]
environment = SubResource("Environment_ctwiv")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Map"]
transform = Transform3D(-0.866026, -0.433013, 0.249999, 0.5, -0.750001, 0.433012, -1.3411e-07, 0.499999, 0.866026, 0, 0, 0)
shadow_enabled = true
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="Map"]
navigation_mesh = SubResource("NavigationMesh_2pdqo")
[node name="Floor" type="MeshInstance3D" parent="Map/NavigationRegion3D"]
mesh = SubResource("PlaneMesh_mmup0")
skeleton = NodePath("../../..")
[node name="StaticBody3D" type="StaticBody3D" parent="Map/NavigationRegion3D/Floor"]
collision_layer = 3
collision_mask = 3
[node name="CollisionShape3D" type="CollisionShape3D" parent="Map/NavigationRegion3D/Floor/StaticBody3D"]
shape = SubResource("ConcavePolygonShape3D_26ptr")
[node name="Wall" type="MeshInstance3D" parent="Map/NavigationRegion3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, -3)
mesh = SubResource("BoxMesh_plpqy")
skeleton = NodePath("../../..")
[node name="StaticBody3D" type="StaticBody3D" parent="Map/NavigationRegion3D/Wall"]
collision_layer = 3
collision_mask = 3
[node name="CollisionShape3D" type="CollisionShape3D" parent="Map/NavigationRegion3D/Wall/StaticBody3D"]
shape = SubResource("ConcavePolygonShape3D_v7prx")
[node name="Slope" type="MeshInstance3D" parent="Map/NavigationRegion3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 1, 4)
mesh = SubResource("PrismMesh_0l7qq")
skeleton = NodePath("../../..")
surface_material_override/0 = SubResource("StandardMaterial3D_pfpgv")
[node name="StaticBody3D" type="StaticBody3D" parent="Map/NavigationRegion3D/Slope"]
collision_layer = 3
collision_mask = 3
[node name="CollisionShape3D" type="CollisionShape3D" parent="Map/NavigationRegion3D/Slope/StaticBody3D"]
shape = SubResource("ConcavePolygonShape3D_rit6o")
[node name="Spawns" type="Node3D" parent="Map"]
[node name="Spawn1" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 18.6089)
[node name="Spawn2" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.8451, 0, 18.6089)
[node name="Spawn3" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.8451, 0, 0.0245247)
[node name="Spawn4" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(-0.269021, -0.963134, 0, 0.963134, -0.269021, 0, 0, 0, 1, 19.0777, 0, -20.2546)
[node name="Spawn5" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(-0.269021, -0.963134, 0, 0.963134, -0.269021, 0, 0, 0, 1, 1.64303, 0, -20.2546)
[node name="Spawn6" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(-0.269021, -0.963134, 0, 0.963134, -0.269021, 0, 0, 0, 1, -19.0255, 0, -20.2546)
[node name="Spawn7" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(-0.269021, -0.963134, 0, 0.963134, -0.269021, 0, 0, 0, 1, -19.0255, 0, 0.256641)
[node name="Spawn8" type="Node3D" parent="Map/Spawns"]
transform = Transform3D(-0.269021, -0.963134, 0, 0.963134, -0.269021, 0, 0, 0, 1, -19.0255, 0, 16.4132)
[node name="FirstPersonPlayer" parent="." instance=ExtResource("5_8ctht")]
[node name="UI" type="Control" parent="."]
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="HitRect" type="ColorRect" parent="UI"]
visible = false
layout_mode = 0
offset_left = -177.0
offset_top = -96.0
offset_right = 1365.0
offset_bottom = 780.0
color = Color(1, 0, 0, 0.34902)
[node name="ZombieSpawnTimer" type="Timer" parent="."]
wait_time = 5.0
autostart = true

View File

@@ -0,0 +1,54 @@
[gd_scene load_steps=6 format=3 uid="uid://do601jl7p1u22"]
[sub_resource type="BoxMesh" id="BoxMesh_ibwn0"]
size = Vector3(0.05, 0.05, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2qdhl"]
albedo_color = Color(1, 0.764706, 0.294118, 1)
emission_enabled = true
emission = Color(0.568627, 1, 0.313726, 1)
emission_energy_multiplier = 5.0
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_618v8"]
direction = Vector3(0, 0, 1)
initial_velocity_min = 3.0
initial_velocity_max = 5.0
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_gn2fy"]
albedo_color = Color(1, 1, 0.443137, 1)
emission_enabled = true
emission = Color(0.568627, 1, 0.313726, 1)
emission_energy_multiplier = 8.0
[sub_resource type="BoxMesh" id="BoxMesh_5un5e"]
material = SubResource("StandardMaterial3D_gn2fy")
size = Vector3(0.04, 0.04, 0.04)
[node name="Bullet" type="Bullet"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -56304.6)
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.6)
mesh = SubResource("BoxMesh_ibwn0")
surface_material_override/0 = SubResource("StandardMaterial3D_2qdhl")
[node name="RayCast3D" type="RayCast3D" parent="."]
target_position = Vector3(0, 0, -0.6)
collision_mask = 2
collide_with_areas = true
[node name="GPUParticles3D" type="GPUParticles3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.7)
emitting = false
one_shot = true
explosiveness = 1.0
visibility_aabb = AABB(-1.00001, -6.06334, -1.00001, 2.00002, 7.06267, 41.4102)
process_material = SubResource("ParticleProcessMaterial_618v8")
draw_pass_1 = SubResource("BoxMesh_5un5e")
[node name="Timer" type="Timer" parent="."]
wait_time = 10.0
one_shot = true
autostart = true
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]

View File

@@ -0,0 +1,36 @@
[gd_scene load_steps=4 format=3 uid="uid://ciex3x7rhv1bx"]
[ext_resource type="PackedScene" uid="uid://bkskefixbe2cw" path="res://Assets/Models/Weapons/Steampunk Rifle.glb" id="1_0bnv5"]
[sub_resource type="Animation" id="Animation_20uwx"]
resource_name = "Shoot"
length = 0.1
step = 0.01
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Node:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.01, 0.1),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [Vector3(0, 0, 0), Vector3(0, 0, 0.05), Vector3(0, 0, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_113bo"]
_data = {
"Shoot": SubResource("Animation_20uwx")
}
[node name="Steampunk Rifle" instance=ExtResource("1_0bnv5")]
[node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
libraries = {
"": SubResource("AnimationLibrary_113bo")
}
[node name="RayCast3D" type="RayCast3D" parent="." index="2"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.6)
target_position = Vector3(0, 0, -1)

View File

@@ -0,0 +1,63 @@
extends CharacterBody3D
const LERP_VALUE : float = 0.15
var snap_vector : Vector3 = Vector3.DOWN
var speed : float
@export_group("Movement variables")
@export var walk_speed : float = 2.0
@export var run_speed : float = 5.0
@export var jump_strength : float = 15.0
@export var gravity : float = 50.0
const ANIMATION_BLEND : float = 7.0
@onready var player_mesh : Node3D = $Mesh
@onready var spring_arm_pivot : Node3D = $SpringArmPivot
@onready var animator : AnimationTree = $AnimationTree
func _physics_process(delta):
var move_direction : Vector3 = Vector3.ZERO
move_direction.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
move_direction.z = Input.get_action_strength("move_backwards") - Input.get_action_strength("move_forwards")
move_direction = move_direction.rotated(Vector3.UP, spring_arm_pivot.rotation.y)
velocity.y -= gravity * delta
if Input.is_action_pressed("run"):
speed = run_speed
else:
speed = walk_speed
velocity.x = move_direction.x * speed
velocity.z = move_direction.z * speed
if move_direction:
player_mesh.rotation.y = lerp_angle(player_mesh.rotation.y, atan2(velocity.x, velocity.z), LERP_VALUE)
var just_landed := is_on_floor() and snap_vector == Vector3.ZERO
var is_jumping := is_on_floor() and Input.is_action_just_pressed("jump")
if is_jumping:
velocity.y = jump_strength
snap_vector = Vector3.ZERO
elif just_landed:
snap_vector = Vector3.DOWN
apply_floor_snap()
move_and_slide()
animate(delta)
func animate(delta):
if is_on_floor():
animator.set("parameters/ground_air_transition/transition_request", "grounded")
if velocity.length() > 0:
if speed == run_speed:
animator.set("parameters/iwr_blend/blend_amount", lerp(animator.get("parameters/iwr_blend/blend_amount"), 1.0, delta * ANIMATION_BLEND))
else:
animator.set("parameters/iwr_blend/blend_amount", lerp(animator.get("parameters/iwr_blend/blend_amount"), 0.0, delta * ANIMATION_BLEND))
else:
animator.set("parameters/iwr_blend/blend_amount", lerp(animator.get("parameters/iwr_blend/blend_amount"), -1.0, delta * ANIMATION_BLEND))
else:
animator.set("parameters/ground_air_transition/transition_request", "air")

View File

@@ -0,0 +1,30 @@
extends Node3D
@export_group("FOV")
@export var change_fov_on_run : bool
@export var normal_fov : float = 75.0
@export var run_fov : float = 90.0
const CAMERA_BLEND : float = 0.05
@onready var spring_arm : SpringArm3D = $SpringArm3D
@onready var camera : Camera3D = $SpringArm3D/Camera3D
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _unhandled_input(event):
if event is InputEventMouseMotion:
rotate_y(-event.relative.x * 0.005)
spring_arm.rotate_x(-event.relative.y * 0.005)
spring_arm.rotation.x = clamp(spring_arm.rotation.x, -PI/4, PI/4)
func _physics_process(_delta):
if change_fov_on_run:
if owner.is_on_floor():
if Input.is_action_pressed("run"):
camera.fov = lerp(camera.fov, run_fov, CAMERA_BLEND)
else:
camera.fov = lerp(camera.fov, normal_fov, CAMERA_BLEND)
else:
camera.fov = lerp(camera.fov, normal_fov, CAMERA_BLEND)

19
godot/bullet.tscn Normal file
View File

@@ -0,0 +1,19 @@
[gd_scene load_steps=3 format=3 uid="uid://bt0551kcc5tw4"]
[sub_resource type="BoxMesh" id="BoxMesh_ibwn0"]
size = Vector3(0.05, 0.05, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2qdhl"]
albedo_color = Color(1, 0.764706, 0.294118, 1)
emission_enabled = true
emission = Color(0.568627, 1, 0.313726, 1)
emission_energy_multiplier = 5.0
[node name="Bullet" type="Bullet"]
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_ibwn0")
surface_material_override/0 = SubResource("StandardMaterial3D_2qdhl")
[node name="RayCast3D" type="RayCast3D" parent="."]
target_position = Vector3(0, 0, -0.6)

View File

@@ -1 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z" fill="#478cbf"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg> <svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 949 B

After

Width:  |  Height:  |  Size: 950 B

View File

@@ -1,37 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cuend5rtkhbnp"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -1,10 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://uiwigxsdnci1"]
[ext_resource type="Texture2D" uid="uid://cuend5rtkhbnp" path="res://icon.svg" id="1_q81dj"]
[node name="Node2D" type="Node2D"]
[node name="GDExample" type="GDExample" parent="."]
position = Vector2(5.20978, 6.34048)
texture = ExtResource("1_q81dj")
centered = false

View File

@@ -11,6 +11,49 @@ config_version=5
[application] [application]
config/name="Blitz3" config/name="Blitz3"
run/main_scene="res://main.tscn" run/main_scene="res://Scenes/Levels/world.tscn"
config/features=PackedStringArray("4.2", "Forward Plus") config/features=PackedStringArray("4.2", "Forward Plus")
config/icon="res://icon.svg" config/icon="res://icon.svg"
[input]
move_left={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null)
]
}
move_right={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null)
]
}
move_forwards={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null)
]
}
move_backwards={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null)
]
}
sprint={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
jump={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
]
}
escape={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
shoot={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(351, 9),"global_position":Vector2(355, 50),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}

View File

@@ -0,0 +1,72 @@
#include "BoneCollisionShape.h"
#include <godot_cpp/classes/engine.hpp>
using namespace godot;
namespace blitz {
void BoneCollisionShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_head_damage"), &BoneCollisionShape::get_head_damage);
ClassDB::bind_method(D_METHOD("set_head_damage", "m_HeadDamage"), &BoneCollisionShape::set_head_damage);
ADD_PROPERTY(PropertyInfo(Variant::INT, "m_HeadDamage"), "set_head_damage", "get_head_damage");
ClassDB::bind_method(D_METHOD("get_body_damage"), &BoneCollisionShape::get_body_damage);
ClassDB::bind_method(D_METHOD("set_body_damage", "m_BodyDamage"), &BoneCollisionShape::set_body_damage);
ADD_PROPERTY(PropertyInfo(Variant::INT, "m_BodyDamage"), "set_head_damage", "get_body_damage");
ADD_SIGNAL(MethodInfo("a_ZombieHeadShotHit", PropertyInfo(Variant::INT, "dam")));
ADD_SIGNAL(MethodInfo("a_ZombieBodyShotHit", PropertyInfo(Variant::INT, "dam")));
ClassDB::bind_method(D_METHOD("headshot_hit"), &BoneCollisionShape::headshot_hit);
ClassDB::bind_method(D_METHOD("bodyshot_hit"), &BoneCollisionShape::bodyshot_hit);
}
BoneCollisionShape::BoneCollisionShape() {}
BoneCollisionShape::~BoneCollisionShape() {}
void BoneCollisionShape::_ready() {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
m_Crosshair = Object::cast_to<Crosshair>(
get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->find_child("Crosshair"));
DEV_ASSERT(m_Crosshair);
m_HitMarkers = Object::cast_to<HitMarkers>(
get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->get_parent()->find_child("HitMarkers"));
DEV_ASSERT(m_HitMarkers);
}
void BoneCollisionShape::set_head_damage(int a_D) {
m_HeadDamage = a_D;
}
int BoneCollisionShape::get_head_damage() const {
return m_HeadDamage;
}
void BoneCollisionShape::set_body_damage(int a_D) {
m_BodyDamage = a_D;
}
int BoneCollisionShape::get_body_damage() const {
return m_BodyDamage;
}
void BoneCollisionShape::headshot_hit() {
ERR_PRINT("zombie headshot hit");
m_Crosshair->set_dot_color(Color(1, 0, 0, 1));
m_HitMarkers->hsvisible(true);
emit_signal("a_ZombieHeadShotHit", m_HeadDamage);
}
void BoneCollisionShape::bodyshot_hit() {
ERR_PRINT("zombie body hit");
m_HitMarkers->visible(true);
emit_signal("a_ZombieBodyShotHit", m_BodyDamage);
}
} // namespace blitz

34
src/BoneCollisionShape.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "Crosshair.h"
#include "HitMarkers.h"
#include <godot_cpp/classes/area3d.hpp>
namespace blitz {
class BoneCollisionShape : public godot::Area3D {
GDCLASS(BoneCollisionShape, godot::Area3D)
protected:
static void _bind_methods();
public:
BoneCollisionShape();
~BoneCollisionShape();
void _ready();
void headshot_hit();
void bodyshot_hit();
private:
int m_HeadDamage = 2;
int m_BodyDamage = 1;
void set_head_damage(int d);
int get_head_damage() const;
void set_body_damage(int d);
int get_body_damage() const;
Crosshair* m_Crosshair;
HitMarkers* m_HitMarkers;
};
} // namespace blitz

73
src/Bullet.cpp Normal file
View File

@@ -0,0 +1,73 @@
#include "Bullet.h"
#include "BoneCollisionShape.h"
#include <godot_cpp/classes/engine.hpp>
using namespace godot;
namespace blitz {
static constexpr float BULLET_SPEED = 40.0f;
void Bullet::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_timer_timeout"), &Bullet::_on_timer_timeout);
}
Bullet::Bullet() {}
Bullet::~Bullet() {}
void Bullet::_ready() {
m_Particles = Object::cast_to<GPUParticles3D>(find_child("GPUParticles3D"));
DEV_ASSERT(m_Particles);
m_Ray = Object::cast_to<RayCast3D>(find_child("RayCast3D"));
DEV_ASSERT(m_Ray);
m_Mesh = Object::cast_to<MeshInstance3D>(find_child("MeshInstance3D"));
DEV_ASSERT(m_Mesh);
m_Timer = memnew(Timer);
add_child(m_Timer);
m_Timer->connect("timeout", callable_mp(this, &Bullet::_on_timer_timeout));
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
m_Crosshair = Object::cast_to<Crosshair>(get_parent()->find_child("Crosshair"));
DEV_ASSERT(m_Crosshair);
m_HitMarkers = Object::cast_to<HitMarkers>(get_parent()->find_child("HitMarkers"));
DEV_ASSERT(m_HitMarkers);
}
void Bullet::_physics_process(float a_Delta) {
Vector3 movement = Vector3(0, 0, -BULLET_SPEED) * a_Delta;
set_position(get_transform().xform(movement));
if (m_Ray->is_colliding()) {
m_Mesh->set_visible(false);
m_Particles->set_emitting(true);
m_Ray->set_enabled(false);
Object* collider = m_Ray->get_collider();
if (!collider)
return;
auto* area_collider = Object::cast_to<BoneCollisionShape>(collider);
if (!(area_collider && area_collider->is_in_group("enemy")))
return;
const StringName& area_name = area_collider->get_name();
area_collider->call(area_name.match("HeadArea3D") ? "headshot_hit" : "bodyshot_hit");
m_Timer->set_wait_time(1.0);
m_Timer->start();
}
}
void Bullet::_on_timer_timeout() {
m_Crosshair->set_dot_color(Color(1, 1, 1, 1));
m_HitMarkers->visible();
m_HitMarkers->hsvisible();
queue_free();
}
} // namespace blitz

35
src/Bullet.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include "Crosshair.h"
#include "HitMarkers.h"
#include <godot_cpp/classes/gpu_particles3d.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/ray_cast3d.hpp>
#include <godot_cpp/classes/timer.hpp>
namespace blitz {
class Bullet : public godot::Node3D {
GDCLASS(Bullet, godot::Node3D)
protected:
static void _bind_methods();
public:
Bullet();
~Bullet();
void _ready();
void _physics_process(float delta);
private:
godot::GPUParticles3D* m_Particles;
godot::RayCast3D* m_Ray;
godot::MeshInstance3D* m_Mesh;
godot::Timer* m_Timer;
Crosshair* m_Crosshair;
HitMarkers* m_HitMarkers;
void _on_timer_timeout();
};
} // namespace blitz

70
src/Crosshair.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "Crosshair.h"
using namespace godot;
namespace blitz {
void Crosshair::_bind_methods() {}
Crosshair::Crosshair() :
m_DotRadius(1.0f),
m_DotColor(Color(1, 1, 1, 1)),
m_CrosshairSpeed(0.25f),
m_CrosshairDistance(2.0f),
m_Origin(Vector3(0, 0, 0)),
m_Pos(Vector2(0, 0)) {}
Crosshair::~Crosshair() {}
void Crosshair::_ready() {
// Catch the parent node directly without SubViewport problem in the editor thx to "recursivity" (if we go up the tree one more
// notch its bugging)
Node* root_node = get_parent();
while (root_node != nullptr && !root_node->is_class("FirstPersonPlayer")) {
root_node = root_node->get_parent();
}
if (root_node != nullptr && root_node->is_class("FirstPersonPlayer")) {
m_Player = Object::cast_to<FirstPersonPlayer>(root_node);
DEV_ASSERT(m_Player);
} else {
ERR_PRINT("FirstPersonPlayer not found in parent hierarchy.");
}
m_CrosshairLines = {Object::cast_to<Line2D>(find_child("Top")), Object::cast_to<Line2D>(find_child("Right")),
Object::cast_to<Line2D>(find_child("Bottom")), Object::cast_to<Line2D>(find_child("Left"))};
for (size_t i = 0; i < m_CrosshairLines.size(); i++) {
DEV_ASSERT(m_CrosshairLines[i]);
}
queue_redraw();
}
void Crosshair::_process(float a_Delta) {
adjust_crosshair_lines();
}
void Crosshair::_draw() {
draw_circle(Vector2(20, 20), m_DotRadius, m_DotColor);
}
void Crosshair::adjust_crosshair_lines() {
Vector3 vel = m_Player->get_real_velocity();
float speed = m_Origin.distance_to(vel);
m_CrosshairLines[0]->set_position(
m_CrosshairLines[0]->get_position().lerp(m_Pos + Vector2(0, -speed * m_CrosshairDistance), m_CrosshairSpeed));
m_CrosshairLines[1]->set_position(
m_CrosshairLines[1]->get_position().lerp(m_Pos + Vector2(speed * m_CrosshairDistance, 0), m_CrosshairSpeed));
m_CrosshairLines[2]->set_position(
m_CrosshairLines[2]->get_position().lerp(m_Pos + Vector2(0, speed * m_CrosshairDistance), m_CrosshairSpeed));
m_CrosshairLines[3]->set_position(
m_CrosshairLines[3]->get_position().lerp(m_Pos + Vector2(-speed * m_CrosshairDistance, 0), m_CrosshairSpeed));
}
void Crosshair::set_dot_color(Color a_Color) {
m_DotColor = a_Color;
queue_redraw();
}
} // namespace blitz

35
src/Crosshair.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include "FirstPersonPlayer.h"
#include <godot_cpp/classes/center_container.hpp>
#include <godot_cpp/classes/line2d.hpp>
namespace blitz {
class Crosshair : public godot::CenterContainer {
GDCLASS(Crosshair, godot::CenterContainer)
protected:
static void _bind_methods();
public:
Crosshair();
~Crosshair();
void _ready();
void _process(float delta);
void _draw();
void set_dot_color(godot::Color col);
private:
godot::Color m_DotColor;
float m_DotRadius;
std::array<godot::Line2D*, 4> m_CrosshairLines;
FirstPersonPlayer* m_Player;
float m_CrosshairSpeed;
float m_CrosshairDistance;
godot::Vector3 m_Origin;
godot::Vector2 m_Pos;
void adjust_crosshair_lines();
};
} // namespace blitz

169
src/FirstPersonPlayer.cpp Normal file
View File

@@ -0,0 +1,169 @@
#include "FirstPersonPlayer.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/input_map.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/variant/signal.hpp>
using namespace godot;
namespace blitz {
static constexpr float WALK_SPEED = 5.0f;
static constexpr float SPRINT_SPEED = 7.0f;
static constexpr float JUMP_VELOCITY = 4.5f;
static constexpr float GRAVITY = 9.81f;
static constexpr float SENSITIVITY = 0.003f;
static constexpr float HIT_STAGGER = 8.0f;
static constexpr float BOB_FREQ = 2.0f;
static constexpr float BOB_AMP = 0.08f;
static constexpr float AIR_MOVEMENT = 3.0f;
static constexpr float GROUND_FRICTION = 7.0f;
static constexpr float BASE_FOV = 75.0f;
static constexpr float FOV_CHANGE = 1.5f;
static constexpr float FOV_TRANSITION = 8.0f;
static constexpr float MIN_FOV_VELOCITY = 0.5;
static constexpr float MAX_FOV_VELOCITY = SPRINT_SPEED * 2.0f;
void FirstPersonPlayer::_bind_methods() {
ADD_SIGNAL(MethodInfo("a_PlayerHit"));
}
FirstPersonPlayer::FirstPersonPlayer() : m_BobTime(0) {}
FirstPersonPlayer::~FirstPersonPlayer() {}
void FirstPersonPlayer::_ready() {
InputMap::get_singleton()->load_from_project_settings();
if (!Engine::get_singleton()->is_editor_hint()) {
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
}
m_Head = Object::cast_to<Node3D>(find_child("Head"));
DEV_ASSERT(m_Head);
m_Camera = Object::cast_to<Camera3D>(m_Head->find_child("Camera"));
DEV_ASSERT(m_Camera);
m_WeaponAnimation = Object::cast_to<AnimationPlayer>(find_child("AnimationPlayer"));
DEV_ASSERT(m_WeaponAnimation);
m_GunBarrel = Object::cast_to<RayCast3D>(find_child("RayCast3D"));
DEV_ASSERT(m_GunBarrel);
m_BulletScene = ResourceLoader::get_singleton()->load("res://Scenes/Weapons/bullet.tscn");
if (!m_BulletScene.is_valid()) {
ERR_PRINT("Failed to load bullet scene.");
}
}
void FirstPersonPlayer::_unhandled_input(const godot::Ref<godot::InputEvent>& a_Event) {
auto* event = Object::cast_to<InputEventMouseMotion>(a_Event.ptr());
if (event)
UpdateCamera(*event);
// TODO: remove
if (Input::get_singleton()->is_action_just_pressed("escape")) {
Input::MouseMode current = Input::get_singleton()->get_mouse_mode();
Input::get_singleton()->set_mouse_mode(
(current == Input::MOUSE_MODE_CAPTURED) ? Input::MOUSE_MODE_VISIBLE : Input::MOUSE_MODE_CAPTURED);
}
}
void FirstPersonPlayer::_physics_process(float a_Delta) {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
auto* Input = Input::get_singleton();
if (!is_on_floor())
set_velocity(get_velocity() - Vector3{0, GRAVITY * a_Delta, 0});
if (Input->is_action_pressed("jump") && is_on_floor())
set_velocity({get_velocity().x, JUMP_VELOCITY, get_velocity().z});
m_Speed = Input->is_action_pressed("sprint") ? SPRINT_SPEED : WALK_SPEED;
UpdatePosition(a_Delta);
UpdateFOV(a_Delta);
UpdateBobbing(a_Delta);
if (Input->is_action_pressed("shoot")) {
if (!m_WeaponAnimation->is_playing()) {
m_WeaponAnimation->play("Shoot");
m_BulletInstance = m_BulletScene->instantiate();
m_BulletInstance->set("position", m_GunBarrel->get_global_transform().origin);
m_BulletInstance->set("rotation", m_GunBarrel->get_global_transform().basis.get_euler());
get_parent()->add_child(m_BulletInstance);
}
}
move_and_slide();
}
void FirstPersonPlayer::UpdateBobbing(float a_Delta) {
m_BobTime += a_Delta * get_velocity().length() * is_on_floor();
Vector3 newPos{static_cast<float>(Math::cos(m_BobTime * BOB_FREQ / 2.0) * BOB_AMP),
static_cast<float>(Math::sin(m_BobTime * BOB_FREQ) * BOB_AMP), 0};
m_Camera->set_transform({m_Camera->get_transform().basis, newPos});
}
void FirstPersonPlayer::UpdateCamera(const InputEventMouseMotion& a_Event) {
m_Head->rotate_y(-a_Event.get_relative().x * SENSITIVITY);
m_Camera->rotate_x(-a_Event.get_relative().y * SENSITIVITY);
float rotationX = m_Camera->get_rotation().x;
CLAMP(rotationX, Math::deg_to_rad(-40.0), Math::deg_to_rad(60.0));
m_Camera->set_rotation({rotationX, get_rotation().y, get_rotation().z});
}
void FirstPersonPlayer::UpdatePosition(float delta) {
auto* Input = Input::get_singleton();
Vector2 inputDirection = Input->get_vector("move_left", "move_right", "move_forwards", "move_backwards");
Vector3 direction = (m_Head->get_transform().basis.xform(Vector3(inputDirection.x, 0, inputDirection.y))).normalized();
if (is_on_floor()) {
if (!direction.is_zero_approx()) {
set_velocity({direction.x * m_Speed, get_velocity().y, direction.z * m_Speed});
} else {
set_velocity({Math::lerp(static_cast<float>(get_velocity().x), static_cast<float>(direction.x * m_Speed),
static_cast<float>(delta * GROUND_FRICTION)),
get_velocity().y,
Math::lerp(static_cast<float>(get_velocity().z), static_cast<float>(direction.z * m_Speed),
static_cast<float>(delta * GROUND_FRICTION))});
}
} else {
set_velocity({Math::lerp(static_cast<float>(get_velocity().x), static_cast<float>(direction.x * m_Speed),
static_cast<float>(delta * AIR_MOVEMENT)),
get_velocity().y,
Math::lerp(static_cast<float>(get_velocity().z), static_cast<float>(direction.z * m_Speed),
static_cast<float>(delta * AIR_MOVEMENT))});
}
}
void FirstPersonPlayer::UpdateFOV(float a_Delta) {
float velocityClamped = Math::clamp(get_velocity().length(), MIN_FOV_VELOCITY, MAX_FOV_VELOCITY);
float targetFOV = BASE_FOV + FOV_CHANGE * velocityClamped;
m_Camera->set_fov(Math::lerp(m_Camera->get_fov(), targetFOV, a_Delta * FOV_TRANSITION));
}
void FirstPersonPlayer::hit(Vector3 a_Dir) {
emit_signal("a_PlayerHit");
this->set_velocity(a_Dir * HIT_STAGGER);
}
} // namespace blitz

46
src/FirstPersonPlayer.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include <godot_cpp/classes/animation_player.hpp>
#include <godot_cpp/classes/camera3d.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/ray_cast3d.hpp>
#include <godot_cpp/classes/ref.hpp>
namespace blitz {
class FirstPersonPlayer : public godot::CharacterBody3D {
GDCLASS(FirstPersonPlayer, godot::CharacterBody3D)
protected:
static void _bind_methods();
public:
FirstPersonPlayer();
~FirstPersonPlayer();
// Godot overrides
void _unhandled_input(const godot::Ref<godot::InputEvent>&);
void _physics_process(float delta);
void _ready();
void hit(godot::Vector3 dir);
private:
godot::Camera3D* m_Camera;
godot::Node3D* m_Head;
godot::AnimationPlayer* m_WeaponAnimation;
godot::RayCast3D* m_GunBarrel;
godot::Node* m_BulletInstance;
godot::Ref<godot::PackedScene> m_BulletScene;
float m_BobTime;
float m_Speed;
void UpdateBobbing(float delta);
void UpdateFOV(float delta);
void UpdateCamera(const godot::InputEventMouseMotion&);
void UpdatePosition(float delta);
};
} // namespace blitz

37
src/HitMarkers.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "HitMarkers.h"
using namespace godot;
namespace blitz {
void HitMarkers::_bind_methods() {}
HitMarkers::HitMarkers() {}
HitMarkers::~HitMarkers() {}
void HitMarkers::_ready() {
m_HitMarkerLines = {Object::cast_to<Line2D>(find_child("TopRight")), Object::cast_to<Line2D>(find_child("BottomRight")),
Object::cast_to<Line2D>(find_child("BottomLeft")), Object::cast_to<Line2D>(find_child("TopLeft"))};
for (size_t i = 0; i < m_HitMarkerLines.size(); i++) {
DEV_ASSERT(m_HitMarkerLines[i]);
}
m_HitMarkerHSLines = {Object::cast_to<Line2D>(find_child("TopRightHS")), Object::cast_to<Line2D>(find_child("BottomRightHS")),
Object::cast_to<Line2D>(find_child("BottomLeftHS")), Object::cast_to<Line2D>(find_child("TopLeftHS"))};
for (size_t i = 0; i < m_HitMarkerHSLines.size(); i++) {
DEV_ASSERT(m_HitMarkerHSLines[i]);
}
this->visible();
}
void HitMarkers::visible(bool a_Bool) {
for (size_t i = 0; i < m_HitMarkerLines.size(); i++) {
m_HitMarkerLines[i]->set_visible(a_Bool);
}
}
void HitMarkers::hsvisible(bool a_Bool) {
for (size_t i = 0; i < m_HitMarkerHSLines.size(); i++) {
m_HitMarkerHSLines[i]->set_visible(a_Bool);
}
}
} // namespace blitz

25
src/HitMarkers.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <godot_cpp/classes/center_container.hpp>
#include <godot_cpp/classes/line2d.hpp>
namespace blitz {
class HitMarkers : public godot::CenterContainer {
GDCLASS(HitMarkers, godot::CenterContainer)
protected:
static void _bind_methods();
public:
HitMarkers();
~HitMarkers();
void _ready();
void visible(bool visiblity = false);
void hsvisible(bool visiblity = false);
private:
std::array<godot::Line2D*, 4> m_HitMarkerLines;
std::array<godot::Line2D*, 4> m_HitMarkerHSLines;
};
} // namespace blitz

111
src/Player.cpp Normal file
View File

@@ -0,0 +1,111 @@
#include "Player.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_map.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
static const float WalkSpeed = 2.0;
static const float RunSpeed = 5.0;
static const float JumpStrength = 15.0;
static const float Gravity = 50.0;
static const float LerpValue = 0.15;
static const float AnimationBlend = 7.0;
namespace blitz {
void Player::_bind_methods() {}
Player::Player() {}
Player::~Player() {}
void Player::_ready() {
godot::InputMap::get_singleton()->load_from_project_settings();
m_PlayerMesh = Object::cast_to<godot::Node3D>(get_child(0));
m_SpringArmPivot = Object::cast_to<godot::Node3D>(get_child(2));
m_AnimationTree = Object::cast_to<godot::AnimationTree>(get_child(4));
DEV_ASSERT(m_PlayerMesh);
DEV_ASSERT(m_SpringArmPivot);
DEV_ASSERT(m_AnimationTree);
apply_floor_snap();
animate(0);
}
void Player::_physics_process(float delta) {
if (godot::Engine::get_singleton()->is_editor_hint())
return;
auto* Input = godot::Input::get_singleton();
godot::Vector3 move_direction{0, 0, 0};
move_direction.x = Input->get_action_strength("move_right") - Input->get_action_strength("move_left");
move_direction.z = Input->get_action_strength("move_backwards") - Input->get_action_strength("move_forwards");
move_direction = move_direction.rotated({0, 1, 0}, m_SpringArmPivot->get_rotation().y);
godot::Vector3 newVelocity = get_velocity();
newVelocity.y -= Gravity * delta;
set_velocity(newVelocity);
if (Input->is_action_pressed("run"))
m_Speed = RunSpeed;
else
m_Speed = WalkSpeed;
newVelocity = get_velocity();
newVelocity.x = move_direction.x * m_Speed;
newVelocity.z = move_direction.z * m_Speed;
set_velocity(newVelocity);
if (move_direction != godot::Vector3{0, 0, 0}) {
godot::Vector3 newRotation = m_PlayerMesh->get_rotation();
newRotation.y = godot::UtilityFunctions::lerp_angle(
newRotation.y, godot::UtilityFunctions::atan2(get_velocity().x, get_velocity().z), LerpValue);
m_PlayerMesh->set_rotation(newRotation);
}
bool justLanded = is_on_floor() && m_SnapVector == godot::Vector3{0, 0, 0};
bool isJumping = is_on_floor() && Input->is_action_just_pressed("jump");
if (isJumping) {
newVelocity = get_velocity();
newVelocity.y = JumpStrength;
set_velocity(newVelocity);
m_SnapVector.zero();
} else if (justLanded) {
m_SnapVector = {0, -1, 0};
}
apply_floor_snap();
move_and_slide();
animate(delta);
}
void Player::animate(float delta) {
if (is_on_floor()) {
m_AnimationTree->set("parameters/ground_air_transition/transition_request", "grounded");
if (get_velocity().length() > 0) {
if (m_Speed == RunSpeed) {
m_AnimationTree->set("parameters/iwr_blend/blend_amount",
godot::UtilityFunctions::lerp(
m_AnimationTree->get("parameters/iwr_blend/blend_amount"), 1.0, delta * AnimationBlend));
} else {
m_AnimationTree->set("parameters/iwr_blend/blend_amount",
godot::UtilityFunctions::lerp(
m_AnimationTree->get("parameters/iwr_blend/blend_amount"), 0.0, delta * AnimationBlend));
}
} else {
m_AnimationTree->set("parameters/iwr_blend/blend_amount",
godot::UtilityFunctions::lerp(
m_AnimationTree->get("parameters/iwr_blend/blend_amount"), -1.0, delta * AnimationBlend));
}
} else {
m_AnimationTree->set("parameters/ground_air_transition/transition_request", "air");
}
}
} // namespace blitz

37
src/Player.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <godot_cpp/classes/animation_tree.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
namespace blitz {
class Player : public godot::CharacterBody3D {
GDCLASS(Player, godot::CharacterBody3D);
protected:
static void _bind_methods();
public:
Player();
~Player();
void _ready();
void _physics_process(float delta);
void animate(float delta);
private:
godot::Node3D* m_PlayerMesh;
godot::Node3D* m_SpringArmPivot;
godot::AnimationTree* m_AnimationTree;
godot::Vector3 m_SnapVector;
float m_Speed;
/*
@onready var player_mesh : Node3D = $Mesh
@onready var spring_arm_pivot : Node3D = $SpringArmPivot
@onready var animator : AnimationTree = $AnimationTree
*/
};
} // namespace blitz

65
src/SpringArmPivot.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "SpringArmPivot.h"
#include "Player.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/spring_arm3d.hpp>
#include <godot_cpp/core/error_macros.hpp>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
static const float NormalFov = 75.0;
static const float RunFov = 90.0;
static const float CameraBlend = 0.05;
namespace blitz {
void SpringArmPivot::_bind_methods() {
godot::ClassDB::bind_method(godot::D_METHOD("get_dynfov"), &SpringArmPivot::IsFovDynamic);
godot::ClassDB::bind_method(godot::D_METHOD("set_dynfov", "dynamic_fov"), &SpringArmPivot::SetDynamicFov);
ADD_PROPERTY(godot::PropertyInfo(godot::Variant::BOOL, "dynamic_fov"), "set_dynfov", "get_dynfov");
}
SpringArmPivot::SpringArmPivot() {}
SpringArmPivot::~SpringArmPivot() {}
void SpringArmPivot::_ready() {
m_SpringArm = Object::cast_to<godot::SpringArm3D>(get_child(0));
m_Camera = Object::cast_to<godot::Camera3D>(m_SpringArm->get_child(0));
DEV_ASSERT(m_SpringArm);
DEV_ASSERT(m_Camera);
if (!godot::Engine::get_singleton()->is_editor_hint()) {
godot::Input::get_singleton()->set_mouse_mode(godot::Input::MOUSE_MODE_CAPTURED);
}
}
void SpringArmPivot::_unhandled_input(const godot::Ref<godot::InputEvent>& p_event) {
auto* event = Object::cast_to<godot::InputEventMouseMotion>(p_event.ptr());
if (event) {
rotate_y(-event->get_relative().x * 0.005);
m_SpringArm->rotate_x(-event->get_relative().y * 0.005);
godot::Vector3 rotationClamped = m_SpringArm->get_rotation();
rotationClamped.x = godot::UtilityFunctions::clamp(rotationClamped.x, -Math_PI / 4, Math_PI / 4);
m_SpringArm->set_rotation(rotationClamped);
}
}
void SpringArmPivot::_physics_process(float delta) {
if (m_DynamicFOV) {
auto* parent = Object::cast_to<Player>(get_owner());
if (parent->is_on_floor()) {
if (godot::Input::get_singleton()->is_action_pressed("run")) {
m_Camera->set_fov(godot::UtilityFunctions::lerp(m_Camera->get_fov(), RunFov, CameraBlend));
} else {
m_Camera->set_fov(godot::UtilityFunctions::lerp(m_Camera->get_fov(), NormalFov, CameraBlend));
}
} else {
m_Camera->set_fov(godot::UtilityFunctions::lerp(m_Camera->get_fov(), NormalFov, CameraBlend));
}
}
}
} // namespace blitz

38
src/SpringArmPivot.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <godot_cpp/classes/input_event.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/spring_arm3d.hpp>
#include <godot_cpp/classes/camera3d.hpp>
namespace blitz {
class SpringArmPivot : public godot::Node3D {
GDCLASS(SpringArmPivot, godot::Node3D);
protected:
static void _bind_methods();
public:
SpringArmPivot();
~SpringArmPivot();
void _ready();
void _unhandled_input(const godot::Ref<godot::InputEvent>& p_event);
void _physics_process(float delta);
void SetDynamicFov(bool fov) {
m_DynamicFOV = fov;
}
bool IsFovDynamic() const {
return m_DynamicFOV;
}
private:
godot::SpringArm3D* m_SpringArm;
godot::Camera3D* m_Camera;
bool m_DynamicFOV = false;
};
} // namespace blitz

87
src/World.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include "World.h"
#include "FirstPersonPlayer.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
namespace blitz {
void World::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_first_person_player_hit"), &World::_on_first_person_player_hit);
ClassDB::bind_method(D_METHOD("_on_timer_timeout"), &World::_on_timer_timeout);
ClassDB::bind_method(D_METHOD("_on_zombie_spawn_timer_timeout"), &World::_on_zombie_spawn_timer_timeout);
}
World::World() {}
World::~World() {}
void World::_ready() {
FirstPersonPlayer* player = Object::cast_to<FirstPersonPlayer>(find_child("FirstPersonPlayer"));
DEV_ASSERT(player);
if (player) {
player->connect("a_PlayerHit", callable_mp(this, &World::_on_first_person_player_hit));
}
m_RedRect = Object::cast_to<ColorRect>(find_child("HitRect"));
DEV_ASSERT(m_RedRect);
m_Timer = memnew(Timer);
add_child(m_Timer);
m_Timer->connect("timeout", callable_mp(this, &World::_on_timer_timeout));
m_ZombieSpawnTimer = memnew(Timer);
add_child(m_ZombieSpawnTimer);
m_ZombieSpawnTimer->connect("timeout", callable_mp(this, &World::_on_zombie_spawn_timer_timeout));
m_Spawns = Object::cast_to<Node3D>(find_child("Spawns"));
DEV_ASSERT(m_Spawns);
m_NavigationRegion = Object::cast_to<NavigationRegion3D>(find_child("NavigationRegion3D"));
DEV_ASSERT(m_NavigationRegion);
m_ZombieScene = ResourceLoader::get_singleton()->load("res://Scenes/Characters/zombie.tscn");
if (!m_ZombieScene.is_valid()) {
ERR_PRINT("Failed to load zombie scene.");
}
m_RandomId.instantiate();
m_RandomId->randomize();
m_ZombieSpawnTimer->start();
}
void World::_on_first_person_player_hit() {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
UtilityFunctions::print("Player hit detected in World.");
m_RedRect->set_visible(true);
m_Timer->set_wait_time(0.2);
m_Timer->start();
}
void World::_on_timer_timeout() {
m_RedRect->set_visible(false);
}
Node* World::get_random_child(Node* a_ParentNode) {
m_ZombieSpawnTimer->set_wait_time(4.0);
if (!a_ParentNode || a_ParentNode->get_child_count() == 0) {
return nullptr;
}
int32_t id = m_RandomId->randi() % a_ParentNode->get_child_count();
return a_ParentNode->get_child(id);
}
void World::_on_zombie_spawn_timer_timeout() {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
auto spawns = Object::cast_to<Node3D>(World::get_random_child(m_Spawns));
auto spawn_points = spawns->get_global_position();
m_ZombieInstance = Object::cast_to<Zombie>(m_ZombieScene->instantiate());
DEV_ASSERT(m_ZombieInstance);
m_ZombieInstance->set_position(spawn_points);
m_NavigationRegion->add_child(m_ZombieInstance);
}
} // namespace blitz

40
src/World.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include "Zombie.h"
#include <godot_cpp/classes/color_rect.hpp>
#include <godot_cpp/classes/navigation_region3d.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/random_number_generator.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/timer.hpp>
namespace blitz {
class World : public godot::Node3D {
GDCLASS(World, godot::Node3D);
protected:
static void _bind_methods();
public:
World();
~World();
void _ready();
private:
godot::ColorRect* m_RedRect;
godot::Timer* m_Timer;
godot::Timer* m_ZombieSpawnTimer;
godot::Node3D* m_Spawns;
godot::NavigationRegion3D* m_NavigationRegion;
Zombie* m_ZombieInstance;
godot::Ref<godot::PackedScene> m_ZombieScene;
godot::Ref<godot::RandomNumberGenerator> m_RandomId;
void _on_timer_timeout();
void _on_zombie_spawn_timer_timeout();
void _on_first_person_player_hit();
Node* get_random_child(Node* parent_node);
};
} // namespace blitz

134
src/Zombie.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include "Zombie.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/core/math.hpp>
using namespace godot;
namespace blitz {
static constexpr float SPEED = 2.0f;
static constexpr float ATTACK_RANGE = 2.0f;
void Zombie::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_m_PlayerPath", "m_PlayerPath"), &Zombie::set_m_PlayerPath);
ClassDB::bind_method(D_METHOD("get_m_PlayerPath"), &Zombie::get_m_PlayerPath);
ClassDB::bind_method(D_METHOD("hit_finished"), &Zombie::hit_finished);
ClassDB::bind_method(D_METHOD("_on_area_3d_a_zombie_head_shot_hit", "m_Health"), &Zombie::_on_area_3d_a_zombie_head_shot_hit);
ClassDB::bind_method(D_METHOD("_on_area_3d_a_zombie_body_shot_hit", "m_Health"), &Zombie::_on_area_3d_a_zombie_body_shot_hit);
ClassDB::bind_method(D_METHOD("_on_timer_timeout"), &Zombie::_on_timer_timeout);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "m_PlayerPath"), "set_m_PlayerPath", "get_m_PlayerPath");
}
Zombie::Zombie() {}
Zombie::~Zombie() {}
void Zombie::_ready() {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
godot::Node* scene_root = Object::cast_to<godot::Node>(get_tree()->get_root()->get_child(0));
if (scene_root) {
m_Player = Object::cast_to<FirstPersonPlayer>(scene_root->find_child("FirstPersonPlayer", true));
DEV_ASSERT(m_Player);
} else {
ERR_PRINT("Scene root not found or invalid.");
}
m_NavigationAgent = Object::cast_to<NavigationAgent3D>(find_child("NavigationAgent3D"));
DEV_ASSERT(m_NavigationAgent);
m_AnimationTree = Object::cast_to<AnimationTree>(find_child("AnimationTree"));
DEV_ASSERT(m_AnimationTree);
m_StateMachine = Object::cast_to<AnimationNodeStateMachinePlayback>(m_AnimationTree->get("parameters/playback"));
DEV_ASSERT(m_StateMachine);
this->set_velocity(Vector3(0, 0, 0));
m_HeadCollision = Object::cast_to<BoneCollisionShape>(find_child("HeadArea3D"));
DEV_ASSERT(m_HeadCollision);
m_HeadCollision->connect("a_ZombieHeadShotHit", Callable(this, "_on_area_3d_a_zombie_head_shot_hit"));
connect_collision_shapes(m_BodyPartsCollision);
m_Timer = memnew(Timer);
add_child(m_Timer);
m_Timer->connect("timeout", callable_mp(this, &Zombie::_on_timer_timeout));
}
void Zombie::_process(float a_Delta) {
#if DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
if (m_StateMachine->get_current_node().match("Run")) {
m_NavigationAgent->set_target_position(m_Player->get_global_position());
m_Velocity = (m_NavigationAgent->get_next_path_position() - this->get_global_position()).normalized() * SPEED;
this->set_velocity(m_Velocity);
look_at(Vector3(this->get_global_position().x + this->get_velocity().x, this->get_global_position().y,
this->get_global_position().z + this->get_velocity().z));
} else if (m_StateMachine->get_current_node().match("Attack")) {
this->set_velocity(Vector3(0, 0, 0));
look_at(Vector3(m_Player->get_global_position().x + this->get_velocity().x, this->get_global_position().y,
m_Player->get_global_position().z + this->get_velocity().z));
}
m_AnimationTree->set("parameters/conditions/run", !_target_in_range());
m_AnimationTree->set("parameters/conditions/attack", _target_in_range());
move_and_slide();
}
bool Zombie::_target_in_range() {
return this->get_global_position().distance_to(m_Player->get_global_position()) < ATTACK_RANGE;
}
void Zombie::set_m_PlayerPath(const NodePath& path) {
m_PlayerPath = path;
}
NodePath Zombie::get_m_PlayerPath() const {
return m_PlayerPath;
}
void Zombie::hit_finished() {
if (this->get_global_position().distance_to(m_Player->get_global_position()) < (ATTACK_RANGE + 1.0)) {
Vector3 dir = this->get_global_position().direction_to(m_Player->get_global_position());
m_Player->hit(dir);
}
}
void Zombie::apply_damage(int dam) {
m_Health -= dam;
if (m_Health <= 0) {
m_AnimationTree->set("parameters/conditions/die", true);
this->set_velocity(Vector3(0, 0, 0));
m_Timer->set_wait_time(4.0);
m_Timer->start();
}
}
void Zombie::_on_area_3d_a_zombie_head_shot_hit(int dam) {
apply_damage(dam);
}
void Zombie::_on_area_3d_a_zombie_body_shot_hit(int dam) {
apply_damage(dam);
}
void Zombie::connect_collision_shapes(const std::vector<StringName>& body_parts) {
for (const StringName& part : body_parts) {
StringName area_name = String(part) + "Area3D";
BoneCollisionShape* collision_shape = Object::cast_to<BoneCollisionShape>(find_child(area_name));
DEV_ASSERT(collision_shape);
collision_shape->connect("a_ZombieBodyShotHit", Callable(this, "_on_area_3d_a_zombie_body_shot_hit"));
}
}
void Zombie::_on_timer_timeout() {
queue_free();
}
} // namespace blitz

51
src/Zombie.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include "BoneCollisionShape.h"
#include "FirstPersonPlayer.h"
#include <godot_cpp/classes/animation_node_state_machine_playback.hpp>
#include <godot_cpp/classes/animation_tree.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/navigation_agent3d.hpp>
#include <godot_cpp/variant/node_path.hpp>
#include <godot_cpp/variant/string_name.hpp>
#include <godot_cpp/classes/timer.hpp>
namespace blitz {
class Zombie : public godot::CharacterBody3D {
GDCLASS(Zombie, godot::CharacterBody3D)
protected:
static void _bind_methods();
public:
Zombie();
~Zombie();
void _ready();
void _process(float delta);
bool _target_in_range();
void _on_area_3d_a_zombie_head_shot_hit(int dam);
void _on_area_3d_a_zombie_body_shot_hit(int dam);
private:
godot::NavigationAgent3D* m_NavigationAgent;
FirstPersonPlayer* m_Player;
godot::NodePath m_PlayerPath;
godot::Vector3 m_Velocity;
godot::AnimationTree* m_AnimationTree;
godot::AnimationNodeStateMachinePlayback* m_StateMachine;
BoneCollisionShape* m_HeadCollision;
std::vector<godot::StringName> m_BodyPartsCollision = {"Neck", "Spine", "Hips", "LeftArm", "LeftForearm", "LeftHand", "RightArm",
"RightForearm", "RightHand", "LeftUpLeg", "RightUpLeg", "LeftLeg", "RightLeg", "LeftFoot", "RightFoot"};
int m_Health = 6;
godot::Timer* m_Timer;
void set_m_PlayerPath(const godot::NodePath& path);
godot::NodePath get_m_PlayerPath() const;
void hit_finished();
void apply_damage(int dam);
void connect_collision_shapes(const std::vector<godot::StringName>& body_parts);
void _on_timer_timeout();
};
} // namespace blitz

View File

@@ -1,28 +0,0 @@
#include "gdexample.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void GDExample::_bind_methods() {
// ClassDB::bind_method(D_METHOD("_process", "delta"), &GDExample::_process);
}
GDExample::GDExample()
{
time_passed = 0.0;
}
GDExample::~GDExample() {}
void GDExample::_process(float delta)
{
time_passed += delta;
auto new_position = Vector2(
10.0 + (10.0 * sin(time_passed * 2.0)),
10.0 + (10.0 * cos(time_passed * 1.5)));
set_position(new_position);
}

View File

@@ -1,24 +0,0 @@
#ifndef GDEXAMPLE_H
#define GDEXAMPLE_H
#include <godot_cpp/classes/sprite2d.hpp>
namespace godot
{
class GDExample : public Sprite2D
{
GDCLASS(GDExample, Sprite2D)
private:
double time_passed;
protected:
static void _bind_methods();
public:
GDExample();
~GDExample();
void _process(float delta);
};
}
#endif

View File

@@ -1,6 +1,14 @@
#include "register_types.h" #include "register_types.h"
#include "gdexample.h" #include "BoneCollisionShape.h"
#include "Bullet.h"
#include "Crosshair.h"
#include "FirstPersonPlayer.h"
#include "HitMarkers.h"
#include "Player.h"
#include "SpringArmPivot.h"
#include "World.h"
#include "Zombie.h"
#include <gdextension_interface.h> #include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp> #include <godot_cpp/core/defs.hpp>
@@ -8,37 +16,35 @@
using namespace godot; using namespace godot;
void initialize_example_module(ModuleInitializationLevel p_level) void initialize_example_module(ModuleInitializationLevel p_level) {
{ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) return;
{ }
return;
}
ClassDB::register_class<GDExample>(); ClassDB::register_class<blitz::Player>();
ClassDB::register_class<blitz::SpringArmPivot>();
ClassDB::register_class<blitz::FirstPersonPlayer>();
ClassDB::register_class<blitz::Bullet>();
ClassDB::register_class<blitz::Zombie>();
ClassDB::register_class<blitz::BoneCollisionShape>();
ClassDB::register_class<blitz::World>();
ClassDB::register_class<blitz::Crosshair>();
ClassDB::register_class<blitz::HitMarkers>();
} }
void uninitialize_example_module(ModuleInitializationLevel p_level) void uninitialize_example_module(ModuleInitializationLevel p_level) {
{ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) return;
{ }
return;
}
} }
extern "C" extern "C" GDExtensionBool GDE_EXPORT library_init(GDExtensionInterfaceGetProcAddress p_get_proc,
{ const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization* r_initialization) {
GDExtensionBool GDE_EXPORT library_init( godot::GDExtensionBinding::InitObject init_obj(p_get_proc, p_library, r_initialization);
GDExtensionInterfaceGetProcAddress p_get_proc,
const GDExtensionClassLibraryPtr p_library,
GDExtensionInitialization *r_initialization)
{
godot::GDExtensionBinding::InitObject init_obj(p_get_proc, p_library, r_initialization);
init_obj.register_initializer(initialize_example_module); init_obj.register_initializer(initialize_example_module);
init_obj.register_terminator(uninitialize_example_module); init_obj.register_terminator(uninitialize_example_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init(); return init_obj.init();
} }
}

View File

@@ -1,3 +1,9 @@
if is_mode("debug") then
add_defines("DEBUG_ENABLED")
end
-- more on https://xmake.io/#/manual/project_target -- more on https://xmake.io/#/manual/project_target
target(PROJECT_NAME) target(PROJECT_NAME)
set_kind("shared") set_kind("shared")
@@ -35,8 +41,9 @@ target(PROJECT_NAME)
on_run( on_run(
(function(godot_project_folder) (function(godot_project_folder)
return function(target) return function(target)
os.execv("echo", {"godot --path", godot_project_folder}) local cmd = format("godot --path %s", godot_project_folder)
os.exec("godot --path " .. godot_project_folder) os.execv("echo", {cmd})
os.exec(cmd)
end end
end)(GODOT_PROJECT_FOLDER) end)(GODOT_PROJECT_FOLDER)
) )

View File

@@ -6,7 +6,7 @@
-- target_platform: the target platform, like "Windows Desktop" -- target_platform: the target platform, like "Windows Desktop"
-- name: the export execute name, it will be endswith ".exe" on windows -- name: the export execute name, it will be endswith ".exe" on windows
task("export") task("export")
on_run((function(godot_project_folder, publish_folder, project_name) on_run((function(godot_project_folder, publish_folder, project_name)
return function(target_platform) return function(target_platform)
local name = godot_project_folder local name = godot_project_folder
@@ -37,8 +37,10 @@ task("export")
local export_path = path.absolute(path.join(publish_folder, project_name)) local export_path = path.absolute(path.join(publish_folder, project_name))
os.execv("echo", {"godot", godot_project_file, export_mode, target_platform, export_path}) local cmd = format("godot %s --headless --%s %s %s", godot_project_file, export_mode, "\"" .. target_platform .. "\"", export_path)
os.exec("godot %s --headless --%s %s %s", godot_project_file, export_mode, "\"" .. target_platform .. "\"", export_path)
os.execv("echo", {cmd})
os.exec(cmd)
end end
end)(GODOT_PROJECT_FOLDER, PUBLISH_FOLDER, PROJECT_NAME)) end)(GODOT_PROJECT_FOLDER, PUBLISH_FOLDER, PROJECT_NAME))
@@ -202,3 +204,22 @@ android.release.arm64 = "res://lib/libProjectName.android_release_arm64.so"
description = "Generate gdextension file", description = "Generate gdextension file",
} }
task_end() task_end()
task("import-assets")
on_run((function(godot_project_folder, project_name)
return function ()
local godot_project_file = path.join(godot_project_folder, "project.godot")
local cmd = format("godot --headless --import %s", godot_project_file)
os.execv("echo", {cmd})
os.exec(cmd)
end
end)(GODOT_PROJECT_FOLDER, PROJECT_NAME))
set_menu {
usage = "xmake import-assets",
description = "Import project assets",
}
task_end()