Browse Source

Add Squash the Creeps (3D) demo

This is a 3D version of Dodge the Creeps, originally made by GDQuest
and updated to 4.0 by myself.

Some changes are included for more polish:

- Invisible walls were added so that you can't exit the playable area anymore.
- Pillars have a different color to be more visible.
- Physical keys are now used, so that you can use WASD movement
  on any keyboard layout.
- The right mouse button can now be used to jump.
- Visual settings were tweaked to match other 3D demos.
- Project icon was upscaled to 128×128 using Waifu2x, so that it
  looks better on hiDPI displays.
- `.blend` files were resaved with compression enabled.

Co-authored-by: Nathan Lovato <[email protected]>
Hugo Locurcio 2 years ago
parent
commit
71d29dcf3b
33 changed files with 912 additions and 0 deletions
  1. 2 0
      3d/squash_the_creeps/.gitignore
  2. 36 0
      3d/squash_the_creeps/Main.gd
  3. 185 0
      3d/squash_the_creeps/Main.tscn
  4. 35 0
      3d/squash_the_creeps/Mob.gd
  5. 66 0
      3d/squash_the_creeps/Mob.tscn
  6. 7 0
      3d/squash_the_creeps/MusicPlayer.tscn
  7. 73 0
      3d/squash_the_creeps/Player.gd
  8. 73 0
      3d/squash_the_creeps/Player.tscn
  9. 40 0
      3d/squash_the_creeps/README.md
  10. 7 0
      3d/squash_the_creeps/ScoreLabel.gd
  11. BIN
      3d/squash_the_creeps/art/House In a Forest Loop.ogg
  12. 19 0
      3d/squash_the_creeps/art/House In a Forest Loop.ogg.import
  13. 12 0
      3d/squash_the_creeps/art/body.tres
  14. 14 0
      3d/squash_the_creeps/art/eye.tres
  15. BIN
      3d/squash_the_creeps/art/mob.blend
  16. BIN
      3d/squash_the_creeps/art/mob.glb
  17. 32 0
      3d/squash_the_creeps/art/mob.glb.import
  18. 7 0
      3d/squash_the_creeps/art/mob_body.tres
  19. 14 0
      3d/squash_the_creeps/art/mob_eye.tres
  20. BIN
      3d/squash_the_creeps/art/player.blend
  21. BIN
      3d/squash_the_creeps/art/player.glb
  22. 32 0
      3d/squash_the_creeps/art/player.glb.import
  23. 16 0
      3d/squash_the_creeps/art/pupil.tres
  24. 43 0
      3d/squash_the_creeps/fonts/LICENSE.txt
  25. BIN
      3d/squash_the_creeps/fonts/Montserrat-Medium.ttf
  26. 33 0
      3d/squash_the_creeps/fonts/Montserrat-Medium.ttf.import
  27. BIN
      3d/squash_the_creeps/icon.webp
  28. 34 0
      3d/squash_the_creeps/icon.webp.import
  29. 93 0
      3d/squash_the_creeps/project.godot
  30. 0 0
      3d/squash_the_creeps/screenshots/.gdignore
  31. 33 0
      3d/squash_the_creeps/screenshots/README.md
  32. BIN
      3d/squash_the_creeps/screenshots/squash_the_creeps.webp
  33. 6 0
      3d/squash_the_creeps/ui_theme.tres

+ 2 - 0
3d/squash_the_creeps/.gitignore

@@ -0,0 +1,2 @@
+.import
+logs/

+ 36 - 0
3d/squash_the_creeps/Main.gd

@@ -0,0 +1,36 @@
+extends Node
+
+@export var mob_scene: PackedScene
+
+
+func _ready():
+	$UserInterface/Retry.hide()
+
+
+func _unhandled_input(event):
+	if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
+		# warning-ignore:return_value_discarded
+		get_tree().reload_current_scene()
+
+
+func _on_mob_timer_timeout():
+	# Create a new instance of the Mob scene.
+	var mob = mob_scene.instantiate()
+
+	# Choose a random location on the SpawnPath.
+	var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
+	mob_spawn_location.progress_ratio = randf()
+
+	# Communicate the spawn location and the player's location to the mob.
+	var player_position = $Player.position
+	mob.initialize(mob_spawn_location.position, player_position)
+
+	# Spawn the mob by adding it to the Main scene.
+	add_child(mob)
+	# We connect the mob to the score label to update the score upon squashing a mob.
+	mob.squashed.connect($UserInterface/ScoreLabel._on_Mob_squashed)
+
+
+func _on_player_hit():
+	$MobTimer.stop()
+	$UserInterface/Retry.show()

+ 185 - 0
3d/squash_the_creeps/Main.tscn

@@ -0,0 +1,185 @@
+[gd_scene load_steps=15 format=3 uid="uid://2y3ar4s86yra"]
+
+[ext_resource type="Script" path="res://Main.gd" id="1"]
+[ext_resource type="PackedScene" uid="uid://dp478jyugrn7o" path="res://Player.tscn" id="2"]
+[ext_resource type="PackedScene" uid="uid://ha0ar5s2c3m4" path="res://Mob.tscn" id="3"]
+[ext_resource type="Theme" uid="uid://cqquurjk1i7yw" path="res://ui_theme.tres" id="4_gnyca"]
+[ext_resource type="Script" path="res://ScoreLabel.gd" id="6"]
+
+[sub_resource type="BoxShape3D" id="1"]
+size = Vector3(60, 2, 60)
+
+[sub_resource type="BoxMesh" id="2"]
+size = Vector3(60, 2, 60)
+
+[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_i5n5q"]
+
+[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_m3njk"]
+sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
+ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1)
+
+[sub_resource type="Sky" id="Sky_yutuu"]
+sky_material = SubResource("ProceduralSkyMaterial_m3njk")
+
+[sub_resource type="Environment" id="Environment_0tcge"]
+background_mode = 2
+sky = SubResource("Sky_yutuu")
+tonemap_mode = 2
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_cr5ed"]
+albedo_color = Color(0.635294, 0.211765, 0.0235294, 1)
+
+[sub_resource type="CylinderMesh" id="3"]
+material = SubResource("StandardMaterial3D_cr5ed")
+radial_segments = 24
+rings = 1
+
+[sub_resource type="Curve3D" id="5"]
+_data = {
+"points": PackedVector3Array(0, 0, 0, 0, 0, 0, 14, 0, -15, 0, 0, 0, 0, 0, 0, -13, 0, -15, 0, 0, 0, 0, 0, 0, -13, 0, 16, 0, 0, 0, 0, 0, 0, 14, 0, 16, 0, 0, 0, 0, 0, 0, 14, 0, -15),
+"tilts": PackedFloat32Array(0, 0, 0, 0, 0)
+}
+point_count = 5
+
+[node name="Main" type="Node"]
+script = ExtResource("1")
+mob_scene = ExtResource("3")
+
+[node name="Ground" type="StaticBody3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
+collision_layer = 4
+collision_mask = 0
+
+[node name="CollisionShape" type="CollisionShape3D" parent="Ground"]
+shape = SubResource("1")
+
+[node name="MeshInstance" type="MeshInstance3D" parent="Ground"]
+mesh = SubResource("2")
+
+[node name="Walls" type="StaticBody3D" parent="."]
+editor_description = "These invisible walls are used to prevent the player from escaping the playable area.
+
+These walls use WorldBoundaryShape3D collision shapes, which are infinitely large planes
+(even though they look finite in the editor)."
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 5)
+collision_layer = 4
+collision_mask = 0
+
+[node name="CollisionShape3D" type="CollisionShape3D" parent="Walls"]
+transform = Transform3D(1, 0, 0, 0, 1.31134e-07, 1, 0, -1, 1.31134e-07, 0, 10, 11)
+shape = SubResource("WorldBoundaryShape3D_i5n5q")
+
+[node name="CollisionShape3D2" type="CollisionShape3D" parent="Walls"]
+transform = Transform3D(-4.37114e-08, 1, -1.31134e-07, 0, 1.31134e-07, 1, 1, 4.37114e-08, -5.73206e-15, -13, 10, -4.76837e-07)
+shape = SubResource("WorldBoundaryShape3D_i5n5q")
+
+[node name="CollisionShape3D3" type="CollisionShape3D" parent="Walls"]
+transform = Transform3D(4.37114e-08, -1, 2.18557e-07, 3.82137e-15, -2.18557e-07, -1, 1, 4.37114e-08, -5.73206e-15, 14, 10, -4.76837e-07)
+shape = SubResource("WorldBoundaryShape3D_i5n5q")
+
+[node name="CollisionShape3D4" type="CollisionShape3D" parent="Walls"]
+transform = Transform3D(1, 5.96046e-08, -1.42109e-14, 3.82137e-15, -2.18557e-07, -1, -5.96046e-08, 1, -2.18557e-07, 9.53674e-07, 10, -20)
+shape = SubResource("WorldBoundaryShape3D_i5n5q")
+
+[node name="DirectionalLight" type="DirectionalLight3D" parent="."]
+transform = Transform3D(0.5, -0.777049, 0.382355, 0, 0.441506, 0.897258, -0.866025, -0.448629, 0.220753, 0, 12.5592, 14.7757)
+shadow_enabled = true
+shadow_bias = 0.04
+shadow_blur = 1.5
+directional_shadow_mode = 0
+directional_shadow_fade_start = 1.0
+directional_shadow_max_distance = 40.0
+
+[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
+environment = SubResource("Environment_0tcge")
+
+[node name="Player" parent="." instance=ExtResource("2")]
+
+[node name="CameraPivot" type="Marker3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0.183514, 0, 0)
+
+[node name="Camera" type="Camera3D" parent="CameraPivot"]
+transform = Transform3D(1, 0, 0, 0, 1, -2.98023e-08, 0, 2.98023e-08, 1, 0, 9.53674e-07, 19)
+projection = 1
+fov = 48.6
+size = 24.0
+far = 40.0
+
+[node name="Cylinders" type="Node3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 0, 0)
+
+[node name="MeshInstance" type="MeshInstance3D" parent="Cylinders"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 1, -15)
+mesh = SubResource("3")
+
+[node name="MeshInstance3" type="MeshInstance3D" parent="Cylinders"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -21, 1, -15)
+mesh = SubResource("3")
+
+[node name="MeshInstance2" type="MeshInstance3D" parent="Cylinders"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -21, 1, 16)
+mesh = SubResource("3")
+
+[node name="MeshInstance4" type="MeshInstance3D" parent="Cylinders"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 1, 16)
+mesh = SubResource("3")
+
+[node name="SpawnPath" type="Path3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
+curve = SubResource("5")
+
+[node name="SpawnLocation" type="PathFollow3D" parent="SpawnPath"]
+transform = Transform3D(-4.37113e-08, 0, -0.999999, 0, 1, 0, 0.999999, 0, -4.37113e-08, 14, 0, -15)
+rotation_mode = 1
+cubic_interp = false
+loop = false
+
+[node name="MobTimer" type="Timer" parent="."]
+wait_time = 0.5
+autostart = true
+
+[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
+theme = ExtResource("4_gnyca")
+
+[node name="ScoreLabel" type="Label" parent="UserInterface"]
+layout_mode = 0
+offset_left = 16.0
+offset_top = 16.0
+offset_right = 77.0
+offset_bottom = 42.0
+theme_override_colors/font_color = Color(0, 0, 0, 1)
+theme_override_font_sizes/font_size = 32
+text = "Score: 0"
+script = ExtResource("6")
+
+[node name="Retry" type="ColorRect" parent="UserInterface"]
+visible = false
+layout_mode = 0
+anchor_right = 1.0
+anchor_bottom = 1.0
+color = Color(0, 0, 0, 0.447059)
+
+[node name="Label" type="Label" parent="UserInterface/Retry"]
+layout_mode = 1
+anchors_preset = 8
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -75.0
+offset_top = -13.0
+offset_right = 75.0
+offset_bottom = 13.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_font_sizes/font_size = 32
+text = "Press Space or Enter to retry"
+
+[connection signal="hit" from="Player" to="." method="_on_player_hit"]
+[connection signal="timeout" from="MobTimer" to="." method="_on_mob_timer_timeout"]

+ 35 - 0
3d/squash_the_creeps/Mob.gd

@@ -0,0 +1,35 @@
+extends CharacterBody3D
+
+# Emitted when the player jumped on the mob.
+signal squashed
+
+## Minimum speed of the mob in meters per second.
+@export var min_speed = 10
+## Maximum speed of the mob in meters per second.
+@export var max_speed = 18
+
+
+func _physics_process(_delta):
+	move_and_slide()
+
+
+func initialize(start_position, player_position):
+	look_at_from_position(start_position, player_position, Vector3.UP)
+	rotate_y(randf_range(-PI / 4, PI / 4))
+
+	var random_speed = randf_range(min_speed, max_speed)
+	# We calculate a forward velocity first, which represents the speed.
+	velocity = Vector3.FORWARD * random_speed
+	# We then rotate the vector based on the mob's Y rotation to move in the direction it's looking.
+	velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+	$AnimationPlayer.speed_scale = random_speed / min_speed
+
+
+func squash():
+	squashed.emit()
+	queue_free()
+
+
+func _on_visible_on_screen_notifier_screen_exited():
+	queue_free()

+ 66 - 0
3d/squash_the_creeps/Mob.tscn

@@ -0,0 +1,66 @@
+[gd_scene load_steps=6 format=3 uid="uid://ha0ar5s2c3m4"]
+
+[ext_resource type="Script" path="res://Mob.gd" id="1"]
+[ext_resource type="PackedScene" uid="uid://bicorb7we351t" path="res://art/mob.glb" id="2"]
+
+[sub_resource type="BoxShape3D" id="1"]
+size = Vector3(1.35822, 1.08835, 2.20058)
+
+[sub_resource type="Animation" id="2"]
+length = 1.2
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Pivot/Character:position")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0.22, 0.7, 1.18),
+"transitions": PackedFloat32Array(0.435275, 2.21914, 1),
+"update": 0,
+"values": [Vector3(0, 0.329753, 0), Vector3(0, 0.660351, 0), Vector3(0, 0.349734, 0)]
+}
+tracks/1/type = "value"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("Pivot/Character:rotation_degrees")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"times": PackedFloat32Array(0.1, 0.46, 1.18),
+"transitions": PackedFloat32Array(0.482968, 0.535887, 1),
+"update": 0,
+"values": [Vector3(-5.0326, 0, 0), Vector3(10, 0, 0), Vector3(-10, 0, 0)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_5n6vs"]
+_data = {
+"float": SubResource("2")
+}
+
+[node name="Mob" type="CharacterBody3D" groups=["mob"]]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0114588, 0)
+collision_layer = 2
+collision_mask = 2147483648
+script = ExtResource("1")
+
+[node name="Pivot" type="Node3D" parent="."]
+
+[node name="Character" parent="Pivot" instance=ExtResource("2")]
+transform = Transform3D(1, 0, 0, 0, 0.996145, 0.0877225, 0, -0.0877225, 0.996145, 0, 0.329753, 0)
+
+[node name="CollisionShape" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.52793, 0)
+shape = SubResource("1")
+
+[node name="VisibleOnScreenNotifier3D" type="VisibleOnScreenNotifier3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.062134, 0.331645)
+aabb = AABB(-1.19986, 0.251327, -1.57098, 2.41047, 1.09305, 3.17223)
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+autoplay = "float"
+libraries = {
+"": SubResource("AnimationLibrary_5n6vs")
+}
+
+[connection signal="screen_exited" from="VisibleOnScreenNotifier3D" to="." method="_on_visible_on_screen_notifier_screen_exited"]

+ 7 - 0
3d/squash_the_creeps/MusicPlayer.tscn

@@ -0,0 +1,7 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://art/House In a Forest Loop.ogg" type="AudioStream" id=1]
+
+[node name="MusicPlayer" type="AudioStreamPlayer"]
+stream = ExtResource( 1 )
+autoplay = true

+ 73 - 0
3d/squash_the_creeps/Player.gd

@@ -0,0 +1,73 @@
+extends CharacterBody3D
+
+signal hit
+
+## How fast the player moves in meters per second.
+@export var speed = 14
+## Vertical impulse applied to the character upon jumping in meters per second.
+@export var jump_impulse = 20
+## Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+@export var bounce_impulse = 16
+## The downward acceleration when in the air, in meters per second.
+@export var fall_acceleration = 75
+
+
+func _physics_process(delta):
+	var direction = Vector3.ZERO
+	if Input.is_action_pressed("move_right"):
+		direction.x += 1
+	if Input.is_action_pressed("move_left"):
+		direction.x -= 1
+	if Input.is_action_pressed("move_back"):
+		direction.z += 1
+	if Input.is_action_pressed("move_forward"):
+		direction.z -= 1
+
+	if direction != Vector3.ZERO:
+		# In the lines below, we turn the character when moving and make the animation play faster.
+		direction = direction.normalized()
+		$Pivot.look_at(position + direction, Vector3.UP)
+		$AnimationPlayer.speed_scale = 4
+	else:
+		$AnimationPlayer.speed_scale = 1
+
+	velocity.x = direction.x * speed
+	velocity.z = direction.z * speed
+
+	# Jumping.
+	if is_on_floor() and Input.is_action_just_pressed("jump"):
+		velocity.y += jump_impulse
+
+	# We apply gravity every frame so the character always collides with the ground when moving.
+	# This is necessary for the is_on_floor() function to work as a body can always detect
+	# the floor, walls, etc. when a collision happens the same frame.
+	velocity.y -= fall_acceleration * delta
+	move_and_slide()
+
+	# Here, we check if we landed on top of a mob and if so, we kill it and bounce.
+	# With move_and_slide(), Godot makes the body move sometimes multiple times in a row to
+	# smooth out the character's motion. So we have to loop over all collisions that may have
+	# happened.
+	# If there are no "slides" this frame, the loop below won't run.
+	for index in range(get_slide_collision_count()):
+		var collision = get_slide_collision(index)
+		if collision.get_collider().is_in_group("mob"):
+			var mob = collision.get_collider()
+			if Vector3.UP.dot(collision.get_normal()) > 0.1:
+				mob.squash()
+				velocity.y = bounce_impulse
+				# Prevent this block from running more than once,
+				# which would award the player more than 1 point for squashing a single mob.
+				break
+
+	# This makes the character follow a nice arc when jumping
+	$Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
+
+
+func die():
+	emit_signal("hit")
+	queue_free()
+
+
+func _on_MobDetector_body_entered(_body):
+	die()

+ 73 - 0
3d/squash_the_creeps/Player.tscn

@@ -0,0 +1,73 @@
+[gd_scene load_steps=7 format=3 uid="uid://dp478jyugrn7o"]
+
+[ext_resource type="Script" path="res://Player.gd" id="1"]
+[ext_resource type="PackedScene" uid="uid://d0ypm0v45pwdv" path="res://art/player.glb" id="2"]
+
+[sub_resource type="SphereShape3D" id="1"]
+radius = 0.792278
+
+[sub_resource type="CylinderShape3D" id="2"]
+height = 0.1438
+radius = 0.907607
+
+[sub_resource type="Animation" id="3"]
+length = 1.2
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Pivot/Character:position")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0.22, 0.7, 1.18),
+"transitions": PackedFloat32Array(0.435275, 2.21914, 1),
+"update": 0,
+"values": [Vector3(0, 0.329753, 0), Vector3(0, 0.660351, 0), Vector3(0, 0.349734, 0)]
+}
+tracks/1/type = "value"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("Pivot/Character:rotation_degrees")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"times": PackedFloat32Array(0.1, 0.46, 1.18),
+"transitions": PackedFloat32Array(0.482968, 0.535887, 1),
+"update": 0,
+"values": [Vector3(-5.0326, 0, 0), Vector3(10, 0, 0), Vector3(-10, 0, 0)]
+}
+
+[sub_resource type="AnimationLibrary" id="AnimationLibrary_aq6tr"]
+_data = {
+"float": SubResource("3")
+}
+
+[node name="Player" type="CharacterBody3D"]
+collision_mask = 2147483654
+script = ExtResource("1")
+
+[node name="Pivot" type="Node3D" parent="."]
+
+[node name="Character" parent="Pivot" instance=ExtResource("2")]
+transform = Transform3D(1, 0, 0, 0, 0.996145, 0.0877225, 0, -0.0877225, 0.996145, 0, 0.329753, 0)
+
+[node name="CollisionShape" type="CollisionShape3D" parent="."]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.771765, 0)
+shape = SubResource("1")
+
+[node name="MobDetector" type="Area3D" parent="."]
+collision_layer = 0
+collision_mask = 2
+monitorable = false
+
+[node name="CollisionShape" type="CollisionShape3D" parent="MobDetector"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.06491, 0)
+shape = SubResource("2")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+autoplay = "float"
+libraries = {
+"": SubResource("AnimationLibrary_aq6tr")
+}
+
+[connection signal="body_entered" from="MobDetector" to="." method="_on_MobDetector_body_entered"]

+ 40 - 0
3d/squash_the_creeps/README.md

@@ -0,0 +1,40 @@
+# Squash the Creeps (3D)
+
+This is a simple game where your character must move
+and jump to squash as many enemies for as long as possible.
+You gain 1 point for every enemy squashed.
+
+Controls:
+
+- <kbd>Up Arrow</kbd>, <kbd>W</kbd>, <kbd>Gamepad Left Stick</kbd>: Move up
+- <kbd>Down Arrow</kbd>, <kbd>S</kbd>, <kbd>Gamepad Left Stick</kbd>: Move down
+- <kbd>Left Arrow</kbd>, <kbd>Gamepad Left Stick</kbd>: Move left
+- <kbd>Right Arrow</kbd>, <kbd>Gamepad Left Stick</kbd>: Move right
+- <kbd>Space</kbd>, <kbd>Right Mouse Button</kbd>, <kbd>Gamepad A/Cross</kbd>: Jump
+
+This is a finished version of the game featured in the
+["Your first 3D game"](https://docs.godotengine.org/en/latest/getting_started/first_3d_game/index.html)
+tutorial in the documentation. For more details,
+consider following the tutorial in the documentation.
+
+This demo covers:
+
+- Handling player input.
+- Moving and jumping in a 3D game.
+- Spawning enemies randomly around the game arena.
+- Using the built-in animation editor to breathe life to your game.
+- Creating a complete game loop with lose conditions, score, and the ability to restart.
+
+Language: GDScript
+
+Renderer: Forward+
+
+## Screenshots
+
+![Screenshot](screenshots/squash_the_creeps.webp)
+
+## Copying
+
+`art/House In a Forest Loop.ogg` Copyright &copy; 2012 [HorrorPen](https://opengameart.org/users/horrorpen), [CC-BY 3.0: Attribution](http://creativecommons.org/licenses/by/3.0/). Source: https://opengameart.org/content/loop-house-in-a-forest
+
+Font is "Montserrat Medium". Copyright 2011 [The Montserrat Project Authors](https://github.com/JulietaUla/Montserrat). Details are in `fonts/LICENSE.txt`.

+ 7 - 0
3d/squash_the_creeps/ScoreLabel.gd

@@ -0,0 +1,7 @@
+extends Label
+
+var score = 0
+
+func _on_Mob_squashed():
+	score += 1
+	text = "Score: %s" % score

BIN
3d/squash_the_creeps/art/House In a Forest Loop.ogg


+ 19 - 0
3d/squash_the_creeps/art/House In a Forest Loop.ogg.import

@@ -0,0 +1,19 @@
+[remap]
+
+importer="oggvorbisstr"
+type="AudioStreamOggVorbis"
+uid="uid://c5qpnfga41t4p"
+path="res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"
+
+[deps]
+
+source_file="res://art/House In a Forest Loop.ogg"
+dest_files=["res://.godot/imported/House In a Forest Loop.ogg-1a6a72ae843ad792b7039931227e8d50.oggvorbisstr"]
+
+[params]
+
+loop=true
+loop_offset=0
+bpm=0
+beat_count=0
+bar_beats=4

+ 12 - 0
3d/squash_the_creeps/art/body.tres

@@ -0,0 +1,12 @@
+[gd_resource type="SpatialMaterial" format=2]
+
+[resource]
+resource_name = "body"
+params_cull_mode = 2
+albedo_color = Color( 0.906332, 0.353653, 0, 1 )
+roughness = 0.5
+emission_enabled = true
+emission = Color( 0, 0, 0, 1 )
+emission_energy = 1.0
+emission_operator = 0
+emission_on_uv2 = false

+ 14 - 0
3d/squash_the_creeps/art/eye.tres

@@ -0,0 +1,14 @@
+[gd_resource type="SpatialMaterial" format=2]
+
+[resource]
+resource_name = "eye"
+params_cull_mode = 2
+albedo_color = Color( 0.858824, 0.858824, 0.858824, 1 )
+metallic = 0.05
+metallic_specular = 1.0
+roughness = 0.1
+emission_enabled = true
+emission = Color( 0.254902, 0.254902, 0.27451, 1 )
+emission_energy = 1.0
+emission_operator = 0
+emission_on_uv2 = false

BIN
3d/squash_the_creeps/art/mob.blend


BIN
3d/squash_the_creeps/art/mob.glb


+ 32 - 0
3d/squash_the_creeps/art/mob.glb.import

@@ -0,0 +1,32 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://bicorb7we351t"
+path="res://.godot/imported/mob.glb-3afb43c03b9d1598b6af5154e2543eac.scn"
+
+[deps]
+
+source_file="res://art/mob.glb"
+dest_files=["res://.godot/imported/mob.glb-3afb43c03b9d1598b6af5154e2543eac.scn"]
+
+[params]
+
+nodes/root_type="Spatial"
+nodes/root_name="Scene Root"
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+meshes/ensure_tangents=true
+meshes/generate_lods=false
+meshes/create_shadow_meshes=true
+meshes/light_baking=0
+meshes/lightmap_texel_size=0.1
+skins/use_named_skins=true
+animation/import=true
+animation/fps=15
+animation/trimming=false
+animation/remove_immutable_tracks=true
+import_script/path=""
+_subresources={}
+gltf/embedded_image_handling=1

+ 7 - 0
3d/squash_the_creeps/art/mob_body.tres

@@ -0,0 +1,7 @@
+[gd_resource type="SpatialMaterial" format=2]
+
+[resource]
+resource_name = "mob_body"
+params_cull_mode = 2
+albedo_color = Color( 0.0588235, 0.266667, 0.490196, 1 )
+roughness = 0.43

+ 14 - 0
3d/squash_the_creeps/art/mob_eye.tres

@@ -0,0 +1,14 @@
+[gd_resource type="SpatialMaterial" format=2]
+
+[resource]
+resource_name = "mob_eye"
+params_cull_mode = 2
+albedo_color = Color( 0.760784, 0.113725, 0.188235, 1 )
+metallic = 0.48
+metallic_specular = 1.0
+roughness = 0.25
+emission_enabled = true
+emission = Color( 0.74902, 0.0784314, 0.0784314, 1 )
+emission_energy = 1.0
+emission_operator = 0
+emission_on_uv2 = false

BIN
3d/squash_the_creeps/art/player.blend


BIN
3d/squash_the_creeps/art/player.glb


+ 32 - 0
3d/squash_the_creeps/art/player.glb.import

@@ -0,0 +1,32 @@
+[remap]
+
+importer="scene"
+importer_version=1
+type="PackedScene"
+uid="uid://d0ypm0v45pwdv"
+path="res://.godot/imported/player.glb-08dcfb373480a049995065542e37637b.scn"
+
+[deps]
+
+source_file="res://art/player.glb"
+dest_files=["res://.godot/imported/player.glb-08dcfb373480a049995065542e37637b.scn"]
+
+[params]
+
+nodes/root_type="Spatial"
+nodes/root_name="Scene Root"
+nodes/apply_root_scale=true
+nodes/root_scale=1.0
+meshes/ensure_tangents=true
+meshes/generate_lods=false
+meshes/create_shadow_meshes=true
+meshes/light_baking=0
+meshes/lightmap_texel_size=0.1
+skins/use_named_skins=true
+animation/import=true
+animation/fps=15
+animation/trimming=false
+animation/remove_immutable_tracks=true
+import_script/path=""
+_subresources={}
+gltf/embedded_image_handling=1

+ 16 - 0
3d/squash_the_creeps/art/pupil.tres

@@ -0,0 +1,16 @@
+[gd_resource type="SpatialMaterial" format=2]
+
+[resource]
+resource_name = "pupil"
+params_cull_mode = 2
+albedo_color = Color( 0, 0, 0, 1 )
+metallic_specular = 1.0
+roughness = 0.3
+emission_enabled = true
+emission = Color( 0, 0, 0, 1 )
+emission_energy = 1.0
+emission_operator = 0
+emission_on_uv2 = false
+rim_enabled = true
+rim = 1.0
+rim_tint = 0.5

+ 43 - 0
3d/squash_the_creeps/fonts/LICENSE.txt

@@ -0,0 +1,43 @@
+Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+—————————————————————————————-
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+—————————————————————————————-
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
+
+“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
+
+“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
+
+“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
+
+“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
+
+5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
3d/squash_the_creeps/fonts/Montserrat-Medium.ttf


+ 33 - 0
3d/squash_the_creeps/fonts/Montserrat-Medium.ttf.import

@@ -0,0 +1,33 @@
+[remap]
+
+importer="font_data_dynamic"
+type="FontFile"
+uid="uid://cyxhh7ddkn62q"
+path="res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata"
+
+[deps]
+
+source_file="res://fonts/Montserrat-Medium.ttf"
+dest_files=["res://.godot/imported/Montserrat-Medium.ttf-e832861e4ad4110e172112dc430c04b0.fontdata"]
+
+[params]
+
+Rendering=null
+antialiasing=1
+generate_mipmaps=false
+multichannel_signed_distance_field=false
+msdf_pixel_range=8
+msdf_size=48
+allow_system_fallback=true
+force_autohinter=false
+hinting=1
+subpixel_positioning=1
+oversampling=0.0
+Fallbacks=null
+fallbacks=[]
+Compress=null
+compress=true
+preload=[]
+language_support={}
+script_support={}
+opentype_features={}

BIN
3d/squash_the_creeps/icon.webp


+ 34 - 0
3d/squash_the_creeps/icon.webp.import

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

+ 93 - 0
3d/squash_the_creeps/project.godot

@@ -0,0 +1,93 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+;   [section] ; section goes between []
+;   param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="Squash the Creeps (3D)"
+config/description="In this game, your goal is to chase and kick out the creeps!
+
+This is a finished version of the game featured in the \"Your first 3D game\"
+tutorial in the official documentation."
+run/main_scene="res://Main.tscn"
+config/features=PackedStringArray("4.0")
+config/icon="res://icon.webp"
+
+[autoload]
+
+MusicPlayer="*res://MusicPlayer.tscn"
+
+[display]
+
+window/stretch/mode="canvas_items"
+window/stretch/aspect="expand"
+window/size/width=720
+window/size/height=540
+window/size/test_width=480
+window/size/test_height=360
+
+[filesystem]
+
+import/blender/enabled=false
+
+[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":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
+, 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":113,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"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":4194321,"key_label":0,"unicode":0,"echo":false,"script":null)
+, 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)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null)
+]
+}
+move_forward={
+"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":4194320,"key_label":0,"unicode":0,"echo":false,"script":null)
+, 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":122,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":-1.0,"script":null)
+]
+}
+move_back={
+"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":4194322,"key_label":0,"unicode":0,"echo":false,"script":null)
+, 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)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":1,"axis_value":1.0,"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)
+, 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":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"pressed":false,"double_click":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
+]
+}
+
+[layer_names]
+
+3d_physics/layer_1="player"
+3d_physics/layer_2="enemies"
+3d_physics/layer_3="world"
+
+[physics]
+
+common/physics_ticks_per_second=120
+
+[rendering]
+
+anti_aliasing/quality/msaa_3d=2
+quality/driver/driver_name="GLES2"
+vram_compression/import_etc=true
+vram_compression/import_etc2=false
+quality/filters/msaa=3

+ 0 - 0
3d/squash_the_creeps/screenshots/.gdignore


+ 33 - 0
3d/squash_the_creeps/screenshots/README.md

@@ -0,0 +1,33 @@
+# Dodge the Creeps
+
+This is a simple game where your character must move
+and avoid the enemies for as long as possible.
+
+This is a finished version of the game featured in the
+["Your first 2D game"](https://docs.godotengine.org/en/latest/getting_started/first_2d_game/index.html)
+tutorial in the documentation. For more details,
+consider following the tutorial in the documentation.
+
+Language: GDScript
+
+Renderer: Vulkan Mobile
+
+Note: There is a C# version available [here](https://github.com/godotengine/godot-demo-projects/tree/master/mono/dodge_the_creeps).
+
+Note: There is a GDNative C++ version available [here](https://github.com/godotengine/gdnative-demos/tree/master/cpp/dodge_the_creeps).
+
+Check out this demo on the asset library: https://godotengine.org/asset-library/asset/515
+
+## Screenshots
+
+![GIF from the documentation](https://docs.godotengine.org/en/latest/_images/dodge_preview.gif)
+
+![Screenshot](screenshots/dodge.png)
+
+## Copying
+
+`art/House In a Forest Loop.ogg` Copyright &copy; 2012 [HorrorPen](https://opengameart.org/users/horrorpen), [CC-BY 3.0: Attribution](http://creativecommons.org/licenses/by/3.0/). Source: https://opengameart.org/content/loop-house-in-a-forest
+
+Images are from "Abstract Platformer". Created in 2016 by kenney.nl, [CC0 1.0 Universal](http://creativecommons.org/publicdomain/zero/1.0/). Source: https://www.kenney.nl/assets/abstract-platformer
+
+Font is "Xolonium". Copyright &copy; 2011-2016 Severin Meyer <[email protected]>, with Reserved Font Name Xolonium, SIL open font license version 1.1. Details are in `fonts/LICENSE.txt`.

BIN
3d/squash_the_creeps/screenshots/squash_the_creeps.webp


+ 6 - 0
3d/squash_the_creeps/ui_theme.tres

@@ -0,0 +1,6 @@
+[gd_resource type="Theme" load_steps=2 format=3 uid="uid://cqquurjk1i7yw"]
+
+[ext_resource type="FontFile" uid="uid://cyxhh7ddkn62q" path="res://fonts/Montserrat-Medium.ttf" id="1"]
+
+[resource]
+default_font = ExtResource("1")