Merge pull request #9 from idbrii/gamepad

Work with gamepads (and some cleanup)
pull/10/head
Kenney 2023-10-16 09:57:58 +07:00 committed by GitHub
commit 75b487e579
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 199 additions and 280 deletions

2
.gitignore vendored

@ -16,3 +16,5 @@ mono_crash.*.json
# Kenney ignores # Kenney ignores
build/ build/
# https://github.com/godotengine/godot/issues/82270
*~*.TMP

@ -13,12 +13,12 @@ var destroyed := false
# When ready, save the initial position # When ready, save the initial position
func _ready():
func _ready():
target_position = position target_position = position
func _process(delta):
func _process(delta):
self.look_at(player.position + Vector3(0, 0.5, 0), Vector3.UP, true) # Look at player self.look_at(player.position + Vector3(0, 0.5, 0), Vector3.UP, true) # Look at player
target_position.y += (cos(time * 5) * 1) * delta # Sine movement (up and down) target_position.y += (cos(time * 5) * 1) * delta # Sine movement (up and down)
@ -26,10 +26,11 @@ func _process(delta):
position = target_position position = target_position
# Take damage from player # Take damage from player
func damage(amount):
func damage(amount):
Audio.play("sounds/enemy_hurt.ogg") Audio.play("sounds/enemy_hurt.ogg")
health -= amount health -= amount
@ -37,27 +38,27 @@ func damage(amount):
if health <= 0 and !destroyed: if health <= 0 and !destroyed:
destroy() destroy()
# Destroy the enemy when out of health # Destroy the enemy when out of health
func destroy():
func destroy():
Audio.play("sounds/enemy_destroy.ogg") Audio.play("sounds/enemy_destroy.ogg")
destroyed = true destroyed = true
queue_free() queue_free()
# Shoot when timer hits 0 # Shoot when timer hits 0
func _on_timer_timeout():
func _on_timer_timeout():
raycast.force_raycast_update() raycast.force_raycast_update()
if raycast.is_colliding(): if raycast.is_colliding():
var collider = raycast.get_collider() var collider = raycast.get_collider()
if collider.has_method("damage"): # Raycast collides with player if collider.has_method("damage"): # Raycast collides with player
# Play muzzle flash animation(s) # Play muzzle flash animation(s)
muzzle_a.frame = 0 muzzle_a.frame = 0

@ -2,5 +2,6 @@ extends AnimatedSprite3D
# Remove this impact effect after the animation has completed # Remove this impact effect after the animation has completed
func _on_animation_finished(): func _on_animation_finished():
queue_free() queue_free()

@ -2,7 +2,8 @@ extends CharacterBody3D
@export_subgroup("Properties") @export_subgroup("Properties")
@export var movement_speed = 5 @export var movement_speed = 5
@export var jump_strength = 7 @export var jump_strength = 8
@export var max_look_speed := Vector2.ONE * 5.0 # in radians per second
@export_subgroup("Weapons") @export_subgroup("Weapons")
@export var weapons: Array[Weapon] = [] @export var weapons: Array[Weapon] = []
@ -10,13 +11,13 @@ extends CharacterBody3D
var weapon: Weapon var weapon: Weapon
var weapon_index := 0 var weapon_index := 0
var mouse_sensitivity = 700 var gamepad_sensitivity := 0.05 # max radians we try to move per physics frame
var mouse_sensitivity = 700 # 1/max radians we try to move per mouse input
var mouse_captured := true var mouse_captured := true
var movement_velocity: Vector3 var movement_velocity: Vector3
var rotation_target: Vector3 var rotation_delta: Vector2 # x: horizontal, y: vertical
var input: Vector3
var input_mouse: Vector2 var input_mouse: Vector2
var health: int = 100 var health: int = 100
@ -44,14 +45,30 @@ signal health_updated
# Functions # Functions
func _ready():
func _ready():
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
rotation_delta = Vector2.ZERO
weapon = weapons[weapon_index] # weapon must never be nil
initiate_change_weapon(weapon_index) initiate_change_weapon(weapon_index)
func _physics_process(delta):
func angle_difference(from: float, to: float) -> float:
var diff := fmod(to - from, TAU)
return fmod(2.0 * diff, TAU) - diff
func rotate_toward(from: float, to: float, delta: float) -> float:
# Not yet in gdscript: https://github.com/godotengine/godot/pull/80225
var diff := angle_difference(from, to)
var diff_abs := absf(diff)
# When `delta < 0` move no further than to PI radians away from `p_to` (as
# PI is the max possible angle distance).
return from + clamp(delta, diff_abs - PI, diff_abs) * signf(diff)
func _physics_process(delta: float):
# Handle functions # Handle functions
handle_controls(delta) handle_controls(delta)
@ -71,12 +88,23 @@ func _physics_process(delta):
# Rotation # Rotation
# Roll a bit when turning horizontally.
camera.rotation.z = lerp_angle(camera.rotation.z, -input_mouse.x * 25 * delta, delta * 5) camera.rotation.z = lerp_angle(camera.rotation.z, -input_mouse.x * 25 * delta, delta * 5)
camera.rotation.x = lerp_angle(camera.rotation.x, rotation_target.x, delta * 25) var look_amount := max_look_speed * delta
rotation.y = lerp_angle(rotation.y, rotation_target.y, delta * 25) # pitch
var pitch := rotate_toward(
container.position = lerp(container.position, container_offset - (applied_velocity / 30), delta * 10) camera.rotation.x, camera.rotation.x + rotation_delta.y, look_amount.x
)
pitch = clamp(pitch, -TAU * 0.23, TAU * 0.23)
camera.rotation.x = pitch
# yaw
rotation.y = rotate_toward(rotation.y, rotation.y + rotation_delta.x, look_amount.y)
rotation_delta = Vector2.ZERO
container.position = lerp(
container.position, container_offset - (applied_velocity / 30), delta * 10
)
# Movement sound # Movement sound
@ -101,18 +129,19 @@ func _physics_process(delta):
if position.y < -10: if position.y < -10:
get_tree().reload_current_scene() get_tree().reload_current_scene()
# Mouse movement # Mouse movement
func _input(event): func _input(event):
if event is InputEventMouseMotion and mouse_captured: if event is InputEventMouseMotion and mouse_captured:
input_mouse = event.relative / mouse_sensitivity input_mouse = event.relative / mouse_sensitivity
rotation_target.y -= event.relative.x / mouse_sensitivity rotation_delta.x -= event.relative.x / mouse_sensitivity
rotation_target.x -= event.relative.y / mouse_sensitivity rotation_delta.y -= event.relative.y / mouse_sensitivity
func handle_controls(_delta):
func handle_controls(_delta):
# Mouse capture # Mouse capture
if Input.is_action_just_pressed("mouse_capture"): if Input.is_action_just_pressed("mouse_capture"):
@ -127,20 +156,15 @@ func handle_controls(_delta):
# Movement # Movement
input.x = Input.get_axis("move_left", "move_right") var input := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
input.z = Input.get_axis("move_forward", "move_back") movement_velocity = Vector3(input.x, 0, input.y).normalized() * movement_speed
movement_velocity = input.normalized() * movement_speed
# Rotation # Rotation
var rotation_input := Vector3.ZERO var rotation_input := Input.get_vector(
"camera_right", "camera_left", "camera_down", "camera_up"
rotation_input.y = Input.get_axis("camera_left", "camera_right") )
rotation_input.x = Input.get_axis("camera_up", "camera_down") / 2 rotation_delta += rotation_input * gamepad_sensitivity
rotation_target -= rotation_input.limit_length(1.0) * 5
rotation_target.x = clamp(rotation_target.x, deg_to_rad(-90), deg_to_rad(90))
# Shooting # Shooting
@ -149,48 +173,49 @@ func handle_controls(_delta):
# Jumping # Jumping
if Input.is_action_just_pressed("jump"): if Input.is_action_just_pressed("jump"):
if jump_single or jump_double: if jump_single or jump_double:
Audio.play("sounds/jump_a.ogg, sounds/jump_b.ogg, sounds/jump_c.ogg") Audio.play("sounds/jump_a.ogg, sounds/jump_b.ogg, sounds/jump_c.ogg")
if jump_double: if jump_double:
gravity = -jump_strength gravity = -jump_strength
jump_double = false jump_double = false
if(jump_single): action_jump() if jump_single:
action_jump()
# Weapon switching # Weapon switching
action_weapon_toggle() action_weapon_toggle()
# Handle gravity # Handle gravity
func handle_gravity(delta):
func handle_gravity(delta):
gravity += 20 * delta gravity += 20 * delta
if gravity > 0 and is_on_floor(): if gravity > 0 and is_on_floor():
jump_single = true jump_single = true
gravity = 0 gravity = 0
# Jumping # Jumping
func action_jump():
func action_jump():
gravity = -jump_strength gravity = -jump_strength
jump_single = false; jump_single = false
jump_double = true; jump_double = true
# Shooting # Shooting
func action_shoot():
func action_shoot():
if Input.is_action_pressed("shoot"): if Input.is_action_pressed("shoot"):
if !blaster_cooldown.is_stopped():
if !blaster_cooldown.is_stopped(): return # Cooldown for shooting return # Cooldown for shooting
Audio.play(weapon.sound_shoot) Audio.play(weapon.sound_shoot)
@ -211,13 +236,13 @@ func action_shoot():
# Shoot the weapon, amount based on shot count # Shoot the weapon, amount based on shot count
for n in weapon.shot_count: for n in weapon.shot_count:
raycast.target_position.x = randf_range(-weapon.spread, weapon.spread) raycast.target_position.x = randf_range(-weapon.spread, weapon.spread)
raycast.target_position.y = randf_range(-weapon.spread, weapon.spread) raycast.target_position.y = randf_range(-weapon.spread, weapon.spread)
raycast.force_raycast_update() raycast.force_raycast_update()
if !raycast.is_colliding(): continue # Don't create impact when raycast didn't hit if !raycast.is_colliding():
continue # Don't create impact when raycast didn't hit
var collider = raycast.get_collider() var collider = raycast.get_collider()
@ -238,21 +263,22 @@ func action_shoot():
impact_instance.position = raycast.get_collision_point() + (raycast.get_collision_normal() / 10) impact_instance.position = raycast.get_collision_point() + (raycast.get_collision_normal() / 10)
impact_instance.look_at(camera.global_transform.origin, Vector3.UP, true) impact_instance.look_at(camera.global_transform.origin, Vector3.UP, true)
# Toggle between available weapons (listed in 'weapons') # Toggle between available weapons (listed in 'weapons')
func action_weapon_toggle():
func action_weapon_toggle():
if Input.is_action_just_pressed("weapon_toggle"): if Input.is_action_just_pressed("weapon_toggle"):
weapon_index = wrap(weapon_index + 1, 0, weapons.size()) weapon_index = wrap(weapon_index + 1, 0, weapons.size())
initiate_change_weapon(weapon_index) initiate_change_weapon(weapon_index)
Audio.play("sounds/weapon_change.ogg") Audio.play("sounds/weapon_change.ogg")
# Initiates the weapon changing animation (tween) # Initiates the weapon changing animation (tween)
func initiate_change_weapon(index):
func initiate_change_weapon(index):
weapon_index = index weapon_index = index
tween = get_tree().create_tween() tween = get_tree().create_tween()
@ -260,10 +286,11 @@ func initiate_change_weapon(index):
tween.tween_property(container, "position", container_offset - Vector3(0, 1, 0), 0.1) tween.tween_property(container, "position", container_offset - Vector3(0, 1, 0), 0.1)
tween.tween_callback(change_weapon) # Changes the model tween.tween_callback(change_weapon) # Changes the model
# Switches the weapon model (off-screen) # Switches the weapon model (off-screen)
func change_weapon():
func change_weapon():
weapon = weapons[weapon_index] weapon = weapons[weapon_index]
# Step 1. Remove previous weapon model(s) from container # Step 1. Remove previous weapon model(s) from container
@ -289,8 +316,8 @@ func change_weapon():
raycast.target_position = Vector3(0, 0, -1) * weapon.max_distance raycast.target_position = Vector3(0, 0, -1) * weapon.max_distance
crosshair.texture = weapon.crosshair crosshair.texture = weapon.crosshair
func damage(amount):
func damage(amount):
health -= amount health -= amount
health_updated.emit(health) # Update health on HUD health_updated.emit(health) # Update health on HUD

@ -61,7 +61,7 @@ move_back={
jump={ jump={
"deadzone": 0.5, "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) "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(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null)
] ]
} }
camera_left={ camera_left={

@ -1,112 +0,0 @@
; 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="Starter Kit FPS"
config/tags=PackedStringArray("starterkit")
run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.1", "Forward Plus")
boot_splash/bg_color=Color(0.92549, 0.92549, 0.960784, 1)
boot_splash/image="res://splash-screen.png"
config/icon="res://icon.png"
[autoload]
Audio="*res://scripts/audio.gd"
[display]
window/size/viewport_width=1280
window/size/viewport_height=720
[editor]
movie_writer/mjpeg_quality=1.0
movie_writer/movie_file="D:/Godot/test.avi"
[input]
move_right={
"deadzone": 0.25,
"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)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
]
}
move_left={
"deadzone": 0.25,
"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)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
]
}
move_forward={
"deadzone": 0.25,
"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)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
]
}
move_back={
"deadzone": 0.25,
"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)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"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(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null)
]
}
camera_left={
"deadzone": 0.5,
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":2,"axis_value":-1.0,"script":null)
]
}
camera_right={
"deadzone": 0.5,
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":2,"axis_value":1.0,"script":null)
]
}
camera_up={
"deadzone": 0.5,
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":-1.0,"script":null)
]
}
camera_down={
"deadzone": 0.5,
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":3,"axis_value":1.0,"script":null)
]
}
mouse_capture={
"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":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
mouse_capture_exit={
"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(92, 12),"global_position":Vector2(96, 55),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":5,"axis_value":1.0,"script":null)
]
}
weapon_toggle={
"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":69,"key_label":0,"unicode":101,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null)
]
}
[rendering]
anti_aliasing/quality/screen_space_aa=1

@ -8,8 +8,8 @@ var bus = "master"
var available = [] # The available players. var available = [] # The available players.
var queue = [] # The queue of sounds to play. var queue = [] # The queue of sounds to play.
func _ready():
func _ready():
for i in num_players: for i in num_players:
var p = AudioStreamPlayer.new() var p = AudioStreamPlayer.new()
add_child(p) add_child(p)
@ -24,14 +24,14 @@ func _ready():
func _on_stream_finished(stream): func _on_stream_finished(stream):
available.append(stream) available.append(stream)
func play(sound_path): # Path (or multiple, separated by commas) func play(sound_path): # Path (or multiple, separated by commas)
var sounds = sound_path.split(",") var sounds = sound_path.split(",")
queue.append("res://" + sounds[randi() % sounds.size()].strip_edges()) queue.append("res://" + sounds[randi() % sounds.size()].strip_edges())
func _process(_delta):
func _process(_delta):
if not queue.is_empty() and not available.is_empty(): if not queue.is_empty() and not available.is_empty():
available[0].stream = load(queue.pop_front()) available[0].stream = load(queue.pop_front())
available[0].play() available[0].play()
available[0].pitch_scale = randf_range(0.9, 1.1) available[0].pitch_scale = randf_range(0.9, 1.1)

@ -1,5 +1,5 @@
extends CanvasLayer extends CanvasLayer
func _on_health_updated(health):
func _on_health_updated(health):
$Health.text = str(health) + "%" $Health.text = str(health) + "%"