gun system + minigun

This commit is contained in:
Michael Campbell 2026-02-19 00:04:31 -05:00
parent 84415d5aca
commit f73f093ae2
13 changed files with 84 additions and 24 deletions

View file

@ -3,6 +3,8 @@ extends CharacterBody3D
@export var dot_curve: Curve
var health := 10.
func _physics_process(delta: float) -> void:
%NavAgent.target_position = Player.instance.global_position
@ -15,8 +17,11 @@ func _physics_process(delta: float) -> void:
rotation.y = Vector2(velocity.x, -velocity.z).angle()
func hit(_proj: PlayerProjectile) -> bool:
queue_free()
func hit(_proj: PlayerProjectile, damage: float) -> bool:
health -= damage
if health <= 0:
queue_free()
return true

View file

@ -6,7 +6,7 @@ func _ready() -> void:
assert(get_parent() is PoliceCar)
parent = get_parent()
func hit(projectile: PlayerProjectile) -> bool:
func hit(projectile: PlayerProjectile, _damage: float) -> bool:
if projectile == null: return false
var angle := projectile.velocity.angle_to(-parent.velocity)

View file

@ -2,7 +2,7 @@ extends Area3D
func _on_collision(node: Node3D):
if node.has_method("hit"):
node.hit(null)
node.hit(null, INF)
func _ready() -> void:
body_entered.connect(_on_collision)

11
player/guns/base_gun.gd Normal file
View file

@ -0,0 +1,11 @@
@abstract
class_name Gun
extends Node3D
var fire_clock := 0.
@export_custom(PROPERTY_HINT_NONE, "suffix:rounds per minute")
var fire_rate := 125.
@abstract
func fire(aim_angle: float) -> void

View file

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

10
player/guns/basic_gun.gd Normal file
View file

@ -0,0 +1,10 @@
class_name BasicGun
extends Gun
func fire(aim_angle: float) -> void:
var dir := Vector3.RIGHT.rotated(Vector3.UP, aim_angle)
var player_projectile: PlayerProjectile = preload("res://player/projectile/player_projectile.tscn").instantiate()
player_projectile.init(dir)
player_projectile.global_position = global_position + Vector3.UP * 0.5
get_tree().current_scene.add_child(player_projectile)
fire_clock = 60. / fire_rate

View file

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

13
player/guns/mini_gun.gd Normal file
View file

@ -0,0 +1,13 @@
class_name MiniGun
extends Gun
func fire(aim_angle: float) -> void:
var dir := Vector3.RIGHT.rotated(Vector3.UP, aim_angle)
var player_projectile: PlayerProjectile = preload("res://player/projectile/player_projectile.tscn").instantiate()
player_projectile.init(dir)
player_projectile.damage = 5
player_projectile.global_position = global_position + Vector3.UP * 0.5
get_tree().current_scene.add_child(player_projectile)
fire_clock = 60. / fire_rate
Player.instance.velocity += -dir * 10.

View file

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

View file

@ -28,6 +28,8 @@ var state := State.NORMAL
var dash_direction: Vector2
var stamina := 1.0
@onready var gun: Gun = %MiniGun
func _init() -> void:
instance = self
@ -38,10 +40,14 @@ func _process_stamina(delta: float) -> void:
stamina = move_toward(stamina, 1., delta * 0.2)
%StaminaBar.value = stamina
func _process_movement() -> void:
func exp_lerp(a: Variant, b: Variant, decay: float, dt: float) -> Variant:
return lerp(a, b, 1 - exp(-decay * dt))
func _process_movement(delta: float) -> void:
var input = Input.get_vector("move_left", "move_right", "move_up", "move_down")
input.normalized()
velocity = Vector3(input.x, 0., input.y) * MOVE_SPEED
var desired_velocity = Vector3(input.x, 0., input.y) * MOVE_SPEED
velocity = exp_lerp(velocity, desired_velocity, 20, delta)
move_and_slide()
if Input.is_action_just_pressed("dash") and stamina >= 1. and not input.is_zero_approx():
@ -76,7 +82,6 @@ func _process_aim() -> void:
%Reticle.position = to_mouse_pos
var fire_clock := 0.
func _process_shoot(delta: float) -> void:
var clock_mul := 1.
@ -84,14 +89,9 @@ func _process_shoot(delta: float) -> void:
State.DASHING: clock_mul = 2.5
State.LOCKED: clock_mul = 2.5
fire_clock -= delta * clock_mul
if Input.is_action_pressed("fire") and fire_clock <= 0.:
var dir := Vector3.RIGHT.rotated(Vector3.UP, aim_angle)
var player_projectile: PlayerProjectile = preload("res://player/projectile/player_projectile.tscn").instantiate()
player_projectile.init(dir)
player_projectile.global_position = global_position + Vector3.UP * 0.5
add_sibling(player_projectile)
fire_clock = 60. / 125.
gun.fire_clock -= delta * clock_mul
if Input.is_action_pressed("fire") and gun.fire_clock <= 0.:
gun.fire(aim_angle)
func _process_cam_shake(delta: float) -> void:
shake_duration -= delta
@ -110,7 +110,7 @@ func _process_cam_shake(delta: float) -> void:
func _process(delta: float) -> void:
match state:
State.NORMAL: _process_movement()
State.NORMAL: _process_movement(delta)
State.DASHING: _process_dash()
State.LOCKED: pass

View file

@ -2,6 +2,8 @@
[ext_resource type="Script" uid="uid://dhin7ux2njfja" path="res://player/player.gd" id="1_4flbx"]
[ext_resource type="Script" uid="uid://cjdxibg3pnans" path="res://player/score_label.gd" id="2_onrkg"]
[ext_resource type="Script" uid="uid://fv1n5noqdfg2" path="res://player/guns/basic_gun.gd" id="3_g1dw6"]
[ext_resource type="Script" uid="uid://b87cyvu2m5b1k" path="res://player/guns/mini_gun.gd" id="4_yw30f"]
[sub_resource type="FastNoiseLite" id="FastNoiseLite_onrkg"]
@ -86,3 +88,14 @@ offset_top = -27.0
offset_right = 196.0
offset_bottom = -4.0
text = "Stamina"
[node name="Guns" type="Node3D" parent="." unique_id=2018109083]
[node name="BasicGun" type="Node3D" parent="Guns" unique_id=1677055720]
unique_name_in_owner = true
script = ExtResource("3_g1dw6")
[node name="MiniGun" type="Node3D" parent="Guns" unique_id=416187827]
unique_name_in_owner = true
script = ExtResource("4_yw30f")
fire_rate = 800.0

View file

@ -9,10 +9,11 @@ var health: int = 1:
health = v
if v == 0:
queue_free()
var damage := 10.
func _on_collision(node: Node3D):
if node.has_method("hit"):
if node.hit(self):
if node.hit(self, damage):
health -= 1
func _ready() -> void:

View file

@ -1,16 +1,20 @@
class_name Building
extends CollisionObject3D
var health := 10.
func _ready() -> void:
scale.y = randf_range(.5, 1.25)
func hit(_proj: PlayerProjectile) -> bool:
print("boom!")
SignalBus.building_destroyed.emit(self)
collision_layer = 1 # World collision only
func hit(_proj: PlayerProjectile, damage: float) -> bool:
health -= damage
if health <= 0.:
print("boom!")
SignalBus.building_destroyed.emit(self)
collision_layer = 1 # World collision only
%Mesh.hide()
%DestroyedMesh.show()
%GPUParticles3D.preprocess = randf()
%Mesh.hide()
%DestroyedMesh.show()
%GPUParticles3D.preprocess = randf()
return true