Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a first-person shooter demo #849

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions 3d/first_person_shooter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# First-Person Shooter

This is a demo implementing a retro-styled first-person shooter with a 2.5D
aesthetic (level is 3D, but enemies are 2D sprites).

Controls:

- Mouse, arrow keys or <kbd>Gamepad Right Stick</kbd>: Look around
- <kbd>W</kbd>, <kbd>Gamepad Left Stick</kbd>: Move forward
- <kbd>S</kbd>, <kbd>Gamepad Left Stick</kbd>: Move backward
- <kbd>A</kbd>, <kbd>Gamepad Left Stick</kbd>: Move left
- <kbd>D</kbd>, <kbd>Gamepad Left Stick</kbd>: Move right
- <kbd>Space</kbd>, <kbd>Right mouse button</kbd>, <kbd>Gamepad A/Cross</kbd>: Jump
- <kbd>Left mouse button</kbd>, <kbd>Gamepad Right Trigger</kbd>: Attack (or respawn if dead)
- <kbd>F</kbd>, <kbd>Thumb mouse buttons</kbd>, <kbd>Gamepad B/Circle</kbd>: Toggle flashlight
- <kbd>Escape</kbd>, <kbd>Gamepad D-Pad Up</kbd>: Quit

Language: GDScript

Renderer: Forward Plus

## How does it work?

The base vehicle uses a
[`VehicleBody`](https://docs.godotengine.org/en/latest/classes/class_vehiclebody.html)
node. The trailer truck is tied together using a
[`ConeJointTwist`](https://docs.godotengine.org/en/latest/classes/class_conetwistjoint.html)
node, and the tow truck is tried together using a chain made of
[`RigidBody`](https://docs.godotengine.org/en/latest/classes/class_rigidbody.html)
nodes which are pinned together using
[`PinJoint`](https://docs.godotengine.org/en/latest/classes/class_pinjoint.html) nodes.

## Screenshot

![Screenshot](screenshots/first_person_shooter.webp)

## License

- `player/shotgun/spritesheet.png`, `player/shotgun/*.wav` and
`enemy/spritesheet.png` are Copyright © 2001-2022
[Contributors to the Freedoom project](https://freedoom.github.io/)
and are licensed under
[3-clause BSD](https://github.com/freedoom/freedoom/blob/master/COPYING.adoc).
- `player/water_splash_in.ogg` and `player/water_splash_out.ogg` are Copyright ©
2009-2019 [Red Eclipse Team](https://www.redeclipse.net/) and are licensed under
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
- `player/rain.ogg` is
[Copyright © Paul Hertz ("ignotus")](https://freesound.org/people/ignotus/sounds/14779/)
and is licensed under
[CC BY 3.0](https://creativecommons.org/licenses/by/3.0/).
- `fonts/vga-rom-font.png` is edited from
<https://doomwiki.org/wiki/File:Vga-rom-font.png>, which is considered to be
ineligible for copyright and therefore in the public domain.
19 changes: 19 additions & 0 deletions 3d/first_person_shooter/blob_shadow.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[gd_scene load_steps=3 format=3 uid="uid://b4g77r67peq11"]

[sub_resource type="Gradient" id="Gradient_voc73"]
colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 0)

[sub_resource type="GradientTexture2D" id="GradientTexture2D_rpfu5"]
gradient = SubResource("Gradient_voc73")
fill = 1
fill_from = Vector2(0.5, 0.5)
fill_to = Vector2(0.5, 0.01)

[node name="Decal" type="Decal"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
extents = Vector3(0.6, 1.2, 0.6)
texture_albedo = SubResource("GradientTexture2D_rpfu5")
albedo_mix = 0.5
upper_fade = 1.0
lower_fade = 1.0
cull_mask = 1048573
3 changes: 3 additions & 0 deletions 3d/first_person_shooter/box.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This is a dummy script to give the box a `class_name`.
class_name Box
extends RigidBody3D
56 changes: 56 additions & 0 deletions 3d/first_person_shooter/box.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[gd_scene load_steps=10 format=3 uid="uid://bly64h687ino4"]

[ext_resource type="Script" path="res://box.gd" id="1_iqw5t"]

[sub_resource type="BoxMesh" id="BoxMesh_ey7wh"]
size = Vector3(1.25, 1.25, 1.25)

[sub_resource type="FastNoiseLite" id="FastNoiseLite_vemq1"]
fractal_type = 2
fractal_lacunarity = 16.782
fractal_gain = 1.4

[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_nepn7"]
seamless = true
noise = SubResource("FastNoiseLite_vemq1")

[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_w2v74"]
seamless = true
as_normal_map = true
bump_strength = 0.1
noise = SubResource("FastNoiseLite_vemq1")

[sub_resource type="Gradient" id="Gradient_soa80"]
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 1)

[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_rmhqn"]
seamless = true
color_ramp = SubResource("Gradient_soa80")
noise = SubResource("FastNoiseLite_vemq1")

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ka0y5"]
albedo_color = Color(1.2, 0.85, 0.5, 1)
albedo_texture = SubResource("NoiseTexture2D_nepn7")
roughness = 0.5
roughness_texture = SubResource("NoiseTexture2D_rmhqn")
normal_enabled = true
normal_texture = SubResource("NoiseTexture2D_w2v74")
uv1_scale = Vector3(0.2, 0.133, 1)
uv1_triplanar_sharpness = 15.455
texture_filter = 4

[sub_resource type="BoxShape3D" id="BoxShape3D_abq30"]
size = Vector3(1.25, 1.25, 1.25)

[node name="Box" type="RigidBody3D"]
script = ExtResource("1_iqw5t")

[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_ey7wh")
surface_material_override/0 = SubResource("StandardMaterial3D_ka0y5")

[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_abq30")

[node name="GPUParticlesCollisionBox3D" type="GPUParticlesCollisionBox3D" parent="."]
size = Vector3(1.26, 1.26, 1.26)
52 changes: 52 additions & 0 deletions 3d/first_person_shooter/bullet.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
extends RayCast3D

# Damage dealt per bullet hit.
const DAMAGE = 9


func _physics_process(_delta: float) -> void:
var distance := 0.0

# The collision check must be performed in `_physics_process()`, not `_ready()` or `_process()`.
# Otherwise, it may be performed before the RayCast has time to update its collisions.
if is_colliding():
# Bullet hit something (enemy or solid surface).
$HitLocation.global_position = get_collision_point()
$HitLocation/GPUParticles3D.emitting = true

if get_collider() is Enemy:
var enemy := get_collider() as Enemy
enemy.damage(DAMAGE)

if get_collider() is Player:
var player := get_collider() as Player
player.health -= DAMAGE
# Push player away from the bullet's direction.
player.velocity -= transform.basis.z * 1.5

if get_collider() is Box:
var box := get_collider() as Box
# Push box away from the player's shot.
box.apply_central_impulse(-transform.basis.z)
# Apply small upwards motion to make the box slide more with horizontal shots.
box.apply_central_impulse(Vector3.UP * 0.5)

distance = global_position.distance_to($HitLocation.global_position)
else:
# Bullet missed.
distance = abs(target_position.z)

# Set up tracer round visual effect.
# The tracer is faded over time using an autoplaying AnimationPlayer.
$Tracer.scale.y = distance
$Tracer.position.z -= distance * 0.5

# We've hit or missed something, no need to keep checking.
# However, keep the bullet present in the scene for wall decals and particle effects.
enabled = false
set_physics_process(false)


func _on_expire_timer_timeout() -> void:
# Bullet is no longer needed as its sound and particles have fully played.
queue_free()
159 changes: 159 additions & 0 deletions 3d/first_person_shooter/bullet.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
[gd_scene load_steps=14 format=3 uid="uid://cfb66sviolbur"]

[ext_resource type="Script" path="res://bullet.gd" id="1_2qff5"]

[sub_resource type="Gradient" id="Gradient_ebjhe"]
interpolation_mode = 2
offsets = PackedFloat32Array(0, 0.779221, 1)
colors = PackedColorArray(1, 0.780392, 0, 1, 0.752941, 0, 0, 0.12549, 0, 0, 0, 0)

[sub_resource type="GradientTexture1D" id="GradientTexture1D_uyrlj"]
gradient = SubResource("Gradient_ebjhe")

[sub_resource type="Curve" id="Curve_40aca"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2

[sub_resource type="CurveTexture" id="CurveTexture_iycpa"]
curve = SubResource("Curve_40aca")

[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_n2t5r"]
emission_shape = 1
emission_sphere_radius = 0.1
direction = Vector3(0, 1, 0)
spread = 90.0
initial_velocity_min = 1.5
initial_velocity_max = 1.5
scale_min = 0.15
scale_max = 0.15
scale_curve = SubResource("CurveTexture_iycpa")
color_ramp = SubResource("GradientTexture1D_uyrlj")
collision_mode = 1
collision_friction = 0.0
collision_bounce = 0.5
collision_use_scale = true

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_36rcs"]
transparency = 1
shading_mode = 0
vertex_color_use_as_albedo = true
billboard_mode = 1
billboard_keep_scale = true

[sub_resource type="QuadMesh" id="QuadMesh_af28p"]
material = SubResource("StandardMaterial3D_36rcs")

[sub_resource type="CylinderMesh" id="CylinderMesh_a6ysu"]
top_radius = 0.04
bottom_radius = 0.0
height = 1.0
radial_segments = 6
rings = 1
cap_top = false
cap_bottom = false

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_0twnr"]
transparency = 1
shading_mode = 0
albedo_color = Color(1, 0.94902, 0.85098, 0.0627451)
proximity_fade_distance = 0.1
distance_fade_mode = 1
distance_fade_max_distance = 5.0

[sub_resource type="Animation" id="Animation_kkxjh"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Tracer:scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(1, 1, 1)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Tracer:sorting_offset")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [2.0]
}

[sub_resource type="Animation" id="Animation_q06vx"]
resource_name = "fade"
length = 0.6
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Tracer:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.6),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [1.0, 0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Tracer:scale:z")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.6),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [1.0, 0.0]
}

[sub_resource type="AnimationLibrary" id="AnimationLibrary_0tdae"]
_data = {
"RESET": SubResource("Animation_kkxjh"),
"fade": SubResource("Animation_q06vx")
}

[node name="Bullet" type="RayCast3D"]
target_position = Vector3(0, 0, -100)
collision_mask = 7
script = ExtResource("1_2qff5")

[node name="HitLocation" type="Node3D" parent="."]

[node name="GPUParticles3D" type="GPUParticles3D" parent="HitLocation"]
layers = 2
cast_shadow = 0
emitting = false
amount = 20
one_shot = true
explosiveness = 1.0
fixed_fps = 0
interpolate = false
process_material = SubResource("ParticleProcessMaterial_n2t5r")
draw_pass_1 = SubResource("QuadMesh_af28p")

[node name="Tracer" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0)
sorting_offset = 2.0
mesh = SubResource("CylinderMesh_a6ysu")
surface_material_override/0 = SubResource("StandardMaterial3D_0twnr")

[node name="ExpireTimer" type="Timer" parent="."]
wait_time = 1.2
autostart = true

[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
autoplay = "fade"
libraries = {
"": SubResource("AnimationLibrary_0tdae")
}

[connection signal="timeout" from="ExpireTimer" to="." method="_on_expire_timer_timeout"]
Binary file added 3d/first_person_shooter/crosshair.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions 3d/first_person_shooter/crosshair.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://d0h6we75g3l0h"
path="res://.godot/imported/crosshair.png-b7f9cb2e0fc32a087972fec001d3421e.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://crosshair.png"
dest_files=["res://.godot/imported/crosshair.png-b7f9cb2e0fc32a087972fec001d3421e.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
8 changes: 8 additions & 0 deletions 3d/first_person_shooter/default_bus_layout.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[gd_resource type="AudioBusLayout" load_steps=2 format=3 uid="uid://det8rjoq2avpd"]

[sub_resource type="AudioEffectLowPassFilter" id="AudioEffectLowPassFilter_mtohd"]
resource_name = "LowPassFilter"

[resource]
bus/0/effect/0/effect = SubResource("AudioEffectLowPassFilter_mtohd")
bus/0/effect/0/enabled = false
Loading
Loading