From 33cab8574c16c76bcb609946f7e6fa7070bee58b Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 25 May 2025 04:30:39 +0200 Subject: [PATCH] Implement platform and ship movement types; refactor player movement logic and add switching mechanism --- objects/entities/brick_player.tscn | 33 ++++- project.godot | 5 + scripts/components/brick_throw.gd | 6 +- scripts/components/flip_player.gd | 8 +- scripts/components/jump_pad_component.gd | 8 +- scripts/components/platform_movement.gd | 106 +++++++++++++++++ scripts/components/platform_movement.gd.uid | 1 + scripts/components/player_movement.gd | 17 +++ scripts/components/player_movement.gd.uid | 1 + scripts/components/ship_movement.gd | 6 +- scripts/components/stomp_damage_component.gd | 4 +- scripts/player.gd | 119 ++++++------------- 12 files changed, 215 insertions(+), 99 deletions(-) create mode 100644 scripts/components/platform_movement.gd create mode 100644 scripts/components/platform_movement.gd.uid create mode 100644 scripts/components/player_movement.gd create mode 100644 scripts/components/player_movement.gd.uid diff --git a/objects/entities/brick_player.tscn b/objects/entities/brick_player.tscn index 6c1bca2..a98df3a 100644 --- a/objects/entities/brick_player.tscn +++ b/objects/entities/brick_player.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=40 format=3 uid="uid://bqi5s710xb1ju"] +[gd_scene load_steps=42 format=3 uid="uid://bqi5s710xb1ju"] [ext_resource type="Script" uid="uid://ccuddyoakg04u" path="res://scripts/player.gd" id="1_8j4h4"] [ext_resource type="Texture2D" uid="uid://b7gp0gqvkv8j4" path="res://sprites/MrBrick_base.png" id="2_bc55y"] [ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="2_lgb3u"] [ext_resource type="Texture2D" uid="uid://jl1gwqchhpdc" path="res://sprites/left_eye.png" id="3_2srrh"] +[ext_resource type="Script" uid="uid://b3mrdvre1y567" path="res://scripts/components/ship_movement.gd" id="3_p4n66"] [ext_resource type="Texture2D" uid="uid://iiawtnwmeny3" path="res://sprites/right_eye.png" id="4_ccn81"] [ext_resource type="Script" uid="uid://oxeqvxkgj87j" path="res://scripts/components/flip_player.gd" id="5_geu10"] [ext_resource type="Script" uid="uid://qeu80jy4vmuf" path="res://scripts/components/score.gd" id="6_fowa2"] @@ -30,6 +31,7 @@ [ext_resource type="PackedScene" uid="uid://bg76mtpcmfm2j" path="res://objects/ui/charging_bar_layer.tscn" id="28_3f5nm"] [ext_resource type="PackedScene" uid="uid://b12tppjkkqpt4" path="res://objects/fxs/hit_particles.tscn" id="28_jh5m0"] [ext_resource type="Script" uid="uid://ceq8n7yw7qxpi" path="res://scripts/components/hit_component.gd" id="29_jh5m0"] +[ext_resource type="Script" uid="uid://c1wtrgw0x77xo" path="res://scripts/components/platform_movement.gd" id="31_xoue7"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_xoue7"] shader = ExtResource("2_lgb3u") @@ -75,12 +77,31 @@ scale_curve = SubResource("CurveTexture_xoue7") color = Color(0.764706, 0.443137, 0, 1) color_ramp = SubResource("GradientTexture1D_lgb3u") -[node name="Brick Player" type="CharacterBody2D" node_paths=PackedStringArray("jump_sfx", "rotation_target") groups=["player"]] +[node name="Brick Player" type="CharacterBody2D" groups=["player"]] collision_layer = 4 collision_mask = 43 script = ExtResource("1_8j4h4") -jump_sfx = NodePath("sfx_jump") -rotation_target = NodePath("Root/Base") +movement_types = { +"platform": NodePath("Movements/PlatformMovement"), +"ship": NodePath("Movements/ShipMovement") +} + +[node name="Movements" type="Node" parent="."] + +[node name="PlatformMovement" type="Node" parent="Movements" node_paths=PackedStringArray("jump_sfx", "rotation_target", "body")] +script = ExtResource("31_xoue7") +jump_sfx = NodePath("../../sfx_jump") +rotation_target = NodePath("../../Root/Base") +body = NodePath("../..") +type = "platform" + +[node name="ShipMovement" type="Node" parent="Movements" node_paths=PackedStringArray("body")] +script = ExtResource("3_p4n66") +acceleration = 800.0 +friction = 600.0 +body = NodePath("../..") +type = "ship" +metadata/_custom_type_script = "uid://b3mrdvre1y567" [node name="Root" type="Node2D" parent="."] @@ -103,11 +124,11 @@ visible = false position = Vector2(0, 0.5) shape = SubResource("RectangleShape2D_hdsg1") -[node name="FlipPlayerComponent" type="Node2D" parent="." node_paths=PackedStringArray("eye_left", "eye_right", "player_controller")] +[node name="FlipPlayerComponent" type="Node2D" parent="." node_paths=PackedStringArray("eye_left", "eye_right", "platform_movement")] script = ExtResource("5_geu10") eye_left = NodePath("../Root/Left Eye") eye_right = NodePath("../Root/Right Eye") -player_controller = NodePath("..") +platform_movement = NodePath("../Movements/PlatformMovement") [node name="StompDamageArea" type="Area2D" parent="."] collision_layer = 0 diff --git a/project.godot b/project.godot index d77517c..0dc192d 100644 --- a/project.godot +++ b/project.godot @@ -147,6 +147,11 @@ pause={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":false,"script":null) ] } +switch_movement={ +"deadzone": 0.2, +"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":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [layer_names] diff --git a/scripts/components/brick_throw.gd b/scripts/components/brick_throw.gd index 4a04f43..ddeebbf 100644 --- a/scripts/components/brick_throw.gd +++ b/scripts/components/brick_throw.gd @@ -42,15 +42,15 @@ func on_timer_timeout() -> void: func throw_brick(power_multiplier: float = 1.0) -> void: var instance: Node2D = brick_scene.instantiate() var init := instance.get_node_or_null("ProjectileInitComponent") as ProjectileInitComponent - if init: + if init and player_controller.current_movement is PlatformMovement: init.initialize({ "position": player_controller.global_position, "rotation": player_controller.rotation, - "direction": player_controller.last_direction, + "direction": player_controller.current_movement.last_direction, "power_multiplier": power_multiplier }) get_tree().current_scene.add_child(instance) can_throw = false - timer.start() \ No newline at end of file + timer.start() diff --git a/scripts/components/flip_player.gd b/scripts/components/flip_player.gd index d578475..2471365 100644 --- a/scripts/components/flip_player.gd +++ b/scripts/components/flip_player.gd @@ -3,10 +3,14 @@ extends Node2D @export var eye_left: Sprite2D @export var eye_right: Sprite2D -@export var player_controller: PlayerController +@export var platform_movement: PlatformMovement + func _process(_delta: float) -> void: - var velocity := player_controller.last_direction + if not platform_movement: + return + + var velocity := platform_movement.last_direction if velocity.x < 0: eye_left.frame = 1 eye_right.frame = 1 diff --git a/scripts/components/jump_pad_component.gd b/scripts/components/jump_pad_component.gd index 43eb80a..8257ffa 100644 --- a/scripts/components/jump_pad_component.gd +++ b/scripts/components/jump_pad_component.gd @@ -25,11 +25,11 @@ func _on_body_entered(body: Node2D) -> void: if not can_be_launched: return - if body is PlayerController: + if body is PlayerController and body.current_movement is PlatformMovement: handle_launchpad_animation() body.velocity.y = -jump_force - if body.jump_sfx: - body.jump_sfx.play() + if body.current_movement.jump_sfx: + body.current_movement.jump_sfx.play() func handle_launchpad_animation() -> void: @@ -39,4 +39,4 @@ func handle_launchpad_animation() -> void: var timer := get_tree().create_timer(animation_duration) sprite2d.frame = start_animation_index + 1 await timer.timeout - sprite2d.frame = start_animation_index \ No newline at end of file + sprite2d.frame = start_animation_index diff --git a/scripts/components/platform_movement.gd b/scripts/components/platform_movement.gd new file mode 100644 index 0000000..f991998 --- /dev/null +++ b/scripts/components/platform_movement.gd @@ -0,0 +1,106 @@ +class_name PlatformMovement +extends PlayerMovement + +@export var speed: float = 300.0 +@export var jump_height: float = 100 +@export var jump_time_to_peak: float = 0.5 +@export var jump_time_to_descent: float = 0.4 +@export var coyote_frames: int = 6 +@export var jump_sfx: AudioStreamPlayer2D +@export var rotation_target: Node2D +@export var body: CharacterBody2D + +var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") +var was_last_floor := false +var coyote_mode := false +var coyote_timer: Timer +var last_direction := Vector2.RIGHT + +@onready var jump_velocity: float = ((2.0 * jump_height) / jump_time_to_peak) * -1.0 +@onready var jump_gravity: float = ((-2.0 * jump_height) / (jump_time_to_peak * jump_time_to_peak)) * -1.0 +@onready var fall_gravity: float = ((-2.0 * jump_height) / (jump_time_to_descent * jump_time_to_descent)) * -1.0 + + +func _ready() -> void: + if not body: + return + + coyote_timer = Timer.new() + coyote_timer.one_shot = true + coyote_timer.wait_time = coyote_frames / 60.0 + coyote_timer.timeout.connect(on_coyote_timer_timeout) + add_child(coyote_timer) + + +func _process(_delta: float) -> void: + if not body or not enabled: + return + + if body.velocity.x > 0.0: + rotation_target.rotation = deg_to_rad(-10) + elif body.velocity.x < 0.0: + rotation_target.rotation = deg_to_rad(10) + else: + rotation_target.rotation = 0 + + +func _physics_process(delta) -> void: + if not body or not enabled: + return + + if body.is_on_floor(): + was_last_floor = true + coyote_mode = false # Reset coyote mode when back on the floor + coyote_timer.stop() # Stop timer when grounded + else: + if was_last_floor: # Start coyote timer only once + coyote_mode = true + coyote_timer.start() + was_last_floor = false + + if not body.is_on_floor(): + body.velocity.y += calculate_gravity() * delta + + if Input.is_action_pressed("jump") and (body.is_on_floor() or coyote_mode): + jump() + + if Input.is_action_just_pressed("down"): + body.position.y += 1 + + var direction := Input.get_axis("left", "right") + if direction != 0: + last_direction = handle_direction(direction) + + if direction: + body.velocity.x = direction * speed + else: + body.velocity.x = move_toward(body.velocity.x, 0, speed) + + previous_velocity = body.velocity + body.move_and_slide() + + +func jump() -> void: + if not body: + return + + body.velocity.y = jump_velocity + coyote_mode = false + if jump_sfx: + jump_sfx.play() + + +func calculate_gravity() -> float: + return jump_gravity if body.velocity.y < 0.0 else fall_gravity + + +func on_coyote_timer_timeout() -> void: + coyote_mode = false + + +func handle_direction(input_dir: float) -> Vector2: + if input_dir > 0: + return Vector2.RIGHT + elif input_dir < 0: + return Vector2.LEFT + return last_direction diff --git a/scripts/components/platform_movement.gd.uid b/scripts/components/platform_movement.gd.uid new file mode 100644 index 0000000..80f7a5f --- /dev/null +++ b/scripts/components/platform_movement.gd.uid @@ -0,0 +1 @@ +uid://c1wtrgw0x77xo diff --git a/scripts/components/player_movement.gd b/scripts/components/player_movement.gd new file mode 100644 index 0000000..2b37f0c --- /dev/null +++ b/scripts/components/player_movement.gd @@ -0,0 +1,17 @@ +class_name PlayerMovement +extends Node + +@export var type: String = "" + +var enabled: bool = true +var previous_velocity: Vector2 = Vector2.ZERO + + +func _process(_delta: float) -> void: + if not enabled: + return + + +func _physics_process(_delta: float) -> void: + if not enabled: + return \ No newline at end of file diff --git a/scripts/components/player_movement.gd.uid b/scripts/components/player_movement.gd.uid new file mode 100644 index 0000000..1e45c17 --- /dev/null +++ b/scripts/components/player_movement.gd.uid @@ -0,0 +1 @@ +uid://bqtc3sxew0sau diff --git a/scripts/components/ship_movement.gd b/scripts/components/ship_movement.gd index 90759b9..965c40f 100644 --- a/scripts/components/ship_movement.gd +++ b/scripts/components/ship_movement.gd @@ -1,5 +1,5 @@ class_name ShipMovement -extends Node +extends PlayerMovement @export var max_speed: float = 200.0 @export var acceleration: float = 100.0 @@ -10,6 +10,9 @@ var velocity: Vector2 = Vector2.ZERO func _physics_process(delta: float) -> void: + if not body or not enabled: + return + var input_vector := Vector2( Input.get_action_strength("right") - Input.get_action_strength("left"), Input.get_action_strength("down") - Input.get_action_strength("up") @@ -22,5 +25,6 @@ func _physics_process(delta: float) -> void: velocity = velocity.limit_length(max_speed) body.velocity = velocity + previous_velocity = body.velocity body.move_and_slide() \ No newline at end of file diff --git a/scripts/components/stomp_damage_component.gd b/scripts/components/stomp_damage_component.gd index e1bcc8a..08774f8 100644 --- a/scripts/components/stomp_damage_component.gd +++ b/scripts/components/stomp_damage_component.gd @@ -3,7 +3,7 @@ extends Node @export var damage: float = 0.25 @export var area2d: Area2D -@export var root: Node2D +@export var root: PlayerController func _ready() -> void: @@ -34,7 +34,7 @@ func on_area2d_body_entered(body: Node2D) -> void: if root.global_position.y < body.global_position.y: if root is PlayerController: - var velocity: Vector2 = root.previous_velocity + var velocity: Vector2 = root.current_movement.previous_velocity if velocity.y > 0.0: deal_damage(health_component) diff --git a/scripts/player.gd b/scripts/player.gd index fbc8006..d672531 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -1,94 +1,51 @@ class_name PlayerController extends CharacterBody2D -@export var speed: float = 300.0 +@export var default_movement_type: String = "platform" +@export var movement_types: Dictionary = {} -var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") -var last_direction: Vector2 = Vector2.RIGHT -var previous_velocity: Vector2 = Vector2.ZERO - -@onready var root = $Root -@onready var coyote_timer: Timer = $CoyoteTimer - -@export var jump_height: float = 100 -@export var jump_time_to_peak: float = 0.5 -@export var jump_time_to_descent: float = 0.4 -@export var coyote_frames: int = 6 -@export var coyote_mode: bool = false -@export var was_last_floor: bool = false -@export var jump_sfx: AudioStreamPlayer2D -@export var rotation_target: Node2D - -@onready var jump_velocity: float = ((2.0 * jump_height) / jump_time_to_peak) * -1.0 -@onready var jump_gravity: float = ((-2.0 * jump_height) / (jump_time_to_peak * jump_time_to_peak)) * -1.0 -@onready var fall_gravity: float = ((-2.0 * jump_height) / (jump_time_to_descent * jump_time_to_descent)) * -1.0 +var current_movement: PlayerMovement = null func _ready() -> void: - coyote_timer.timeout.connect(on_coyote_timer_timeout) - coyote_timer.wait_time = coyote_frames / 60.0 + for movement_type in movement_types: + var movement_node: Node = get_node_or_null(movement_types[movement_type]) + if movement_node and movement_node is PlayerMovement: + movement_node.enabled = false + + switch_movement(default_movement_type) -func _process(_delta): - if velocity.x > 0.0: - rotation_target.rotation = deg_to_rad(-10) - elif velocity.x < 0.0: - rotation_target.rotation = deg_to_rad(10) +func _unhandled_input(event: InputEvent) -> void: + if event is InputEventKey: + if event.is_action_pressed("switch_movement"): + var next_movement_type: String = get_next_movement_type() + switch_movement(next_movement_type) + + +func switch_movement(movement_type: String) -> void: + if current_movement: + current_movement.enabled = false + + if movement_type in movement_types: + current_movement = get_node_or_null(movement_types[movement_type]) + if not current_movement: + push_error("Movement type '%s' not found in movement_types." % movement_type) + return + current_movement.enabled = true else: - rotation_target.rotation = 0 + push_error("Movement type '%s' not found in movement_types." % movement_type) + + if not current_movement: + push_error("No current movement set after switching.") -func _physics_process(delta): - if is_on_floor(): - was_last_floor = true - coyote_mode = false # Reset coyote mode when back on the floor - coyote_timer.stop() # Stop timer when grounded - else: - if was_last_floor: # Start coyote timer only once - coyote_mode = true - coyote_timer.start() - was_last_floor = false +func get_next_movement_type() -> String: + var keys: Array = movement_types.keys() + print("Available movement types: ", keys) + var current_index: int = keys.find(current_movement.type) + if current_index == -1: + return default_movement_type - if not is_on_floor(): - velocity.y += calculate_gravity() * delta - - if Input.is_action_pressed("jump") and (is_on_floor() or coyote_mode): - jump() - - if Input.is_action_just_pressed("down"): - position.y += 1 - - var direction := Input.get_axis("left", "right") - if direction != 0: - last_direction = handle_direction(direction) - - if direction: - velocity.x = direction * speed - else: - velocity.x = move_toward(velocity.x, 0, speed) - - previous_velocity = velocity - move_and_slide() - - -func jump(): - velocity.y = jump_velocity - coyote_mode = false - if jump_sfx: - jump_sfx.play() - - -func calculate_gravity() -> float: - return jump_gravity if velocity.y < 0.0 else fall_gravity - - -func on_coyote_timer_timeout(): - coyote_mode = false - - -func handle_direction(input_dir: float) -> Vector2: - if input_dir > 0: - return Vector2.RIGHT - elif input_dir < 0: - return Vector2.LEFT - return last_direction + current_index = (current_index + 1) % keys.size() + return keys[current_index]