From 9db4deba05c1934efb30669171d67e80946b5339 Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 28 Apr 2025 20:54:39 -0400 Subject: [PATCH] Week 3: input buffer, acceleration, hardgravity, state machine --- bullet.gd | 5 + project.godot | 9 ++ scenes/bullet.tscn | 9 +- scenes/crate.tscn | 9 +- scenes/game.tscn | 70 +++++++++++++- scenes/player.tscn | 227 ++++++++++++++++++++++++++++++++++++++++++++- scripts/player.gd | 95 +++++++++++++++++-- 7 files changed, 407 insertions(+), 17 deletions(-) diff --git a/bullet.gd b/bullet.gd index d21576f..7a840ab 100644 --- a/bullet.gd +++ b/bullet.gd @@ -1,5 +1,6 @@ class_name Bullet extends Area2D var speed = 700 +@onready var bullet_image: Sprite2D = $BulletImage signal bulletHit(body, bullet) @@ -8,6 +9,10 @@ func _physics_process(delta: float) -> void: func setSpeed(value): speed = value + if speed < 0: + bullet_image.flip_h = true + if speed > 0: + bullet_image.flip_h = false func _on_body_entered(body: Node2D) -> void: diff --git a/project.godot b/project.godot index 7dc2674..a01456d 100644 --- a/project.godot +++ b/project.godot @@ -15,9 +15,18 @@ run/main_scene="res://scenes/game.tscn" config/features=PackedStringArray("4.4", "Forward Plus") config/icon="res://icon.svg" +[display] + +window/size/viewport_width=320 +window/size/viewport_height=180 +window/size/window_width_override=960 +window/size/window_height_override=540 +window/stretch/mode="viewport" + [file_customization] folder_colors={ +"res://assets/": "purple", "res://scripts/": "red" } diff --git a/scenes/bullet.tscn b/scenes/bullet.tscn index f1a8d2a..5ed74c9 100644 --- a/scenes/bullet.tscn +++ b/scenes/bullet.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=3 format=3 uid="uid://cy02yf4v8q5v6"] +[gd_scene load_steps=4 format=3 uid="uid://cy02yf4v8q5v6"] [ext_resource type="Script" uid="uid://wmy17yj81te8" path="res://bullet.gd" id="1_mkf8s"] +[ext_resource type="Texture2D" uid="uid://dwn36dqvridjp" path="res://assets/graphics/projectiles/pixel_bullet.png" id="2_y25gk"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_y25gk"] size = Vector2(34, 8) @@ -12,4 +13,10 @@ script = ExtResource("1_mkf8s") shape = SubResource("RectangleShape2D_y25gk") debug_color = Color(0.931576, 2.76271e-05, 0.652218, 0.42) +[node name="BulletImage" type="Sprite2D" parent="."] +texture_filter = 1 +position = Vector2(-8, 5) +scale = Vector2(-0.25, 0.225) +texture = ExtResource("2_y25gk") + [connection signal="body_entered" from="." to="." method="_on_body_entered"] diff --git a/scenes/crate.tscn b/scenes/crate.tscn index f17eef3..f16805b 100644 --- a/scenes/crate.tscn +++ b/scenes/crate.tscn @@ -1,15 +1,20 @@ -[gd_scene load_steps=3 format=3 uid="uid://ch3ncwkrhv38n"] +[gd_scene load_steps=4 format=3 uid="uid://ch3ncwkrhv38n"] [ext_resource type="Script" uid="uid://d2pjirchmmx8w" path="res://scripts/crate.gd" id="1_b66cd"] +[ext_resource type="Texture2D" uid="uid://2nsjo0lmroq6" path="res://assets/graphics/objects/crate.png" id="2_dwt0d"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_uwrxv"] size = Vector2(144, 44) [node name="Crate" type="RigidBody2D" groups=["pushables"]] -rotation = 1.08962 script = ExtResource("1_b66cd") metadata/_edit_group_ = true [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_uwrxv") debug_color = Color(0.82094, 0.38733, 0.296015, 0.42) + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(1, 3) +scale = Vector2(0.520002, 0.16) +texture = ExtResource("2_dwt0d") diff --git a/scenes/game.tscn b/scenes/game.tscn index 18243dd..c9563e9 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -1,12 +1,68 @@ -[gd_scene load_steps=9 format=3 uid="uid://bvs868ncmeoj5"] +[gd_scene load_steps=12 format=4 uid="uid://bvs868ncmeoj5"] [ext_resource type="Script" uid="uid://dtod1rsvbxyti" path="res://gamecontroller.gd" id="1_lnu2h"] [ext_resource type="Script" uid="uid://bijvpac45v6s1" path="res://scripts/scene_manager.gd" id="2_lbhrr"] [ext_resource type="PackedScene" uid="uid://c5a5u2c250g6" path="res://scenes/player.tscn" id="2_lnu2h"] [ext_resource type="PackedScene" uid="uid://dgbqoys4mm8j" path="res://scenes/trigger.tscn" id="3_lnu2h"] +[ext_resource type="Texture2D" uid="uid://35cnnia85y3f" path="res://assets/graphics/terrain/Terrain (32x32).png" id="3_u5sy4"] [ext_resource type="PackedScene" uid="uid://ch3ncwkrhv38n" path="res://scenes/crate.tscn" id="4_iywne"] [ext_resource type="PackedScene" uid="uid://cy02yf4v8q5v6" path="res://scenes/bullet.tscn" id="6_p57ef"] +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_gee14"] +texture = ExtResource("3_u5sy4") +texture_region_size = Vector2i(32, 32) +0:0/0 = 0 +1:0/0 = 0 +2:0/0 = 0 +4:0/0 = 0 +6:0/0 = 0 +7:0/0 = 0 +9:0/0 = 0 +10:0/0 = 0 +12:0/0 = 0 +13:0/0 = 0 +15:0/0 = 0 +0:1/0 = 0 +1:1/0 = 0 +2:1/0 = 0 +4:1/0 = 0 +6:1/0 = 0 +7:1/0 = 0 +9:1/0 = 0 +10:1/0 = 0 +12:1/0 = 0 +13:1/0 = 0 +15:1/0 = 0 +16:1/0 = 0 +0:2/0 = 0 +1:2/0 = 0 +2:2/0 = 0 +4:2/0 = 0 +6:3/0 = 0 +7:3/0 = 0 +9:3/0 = 0 +10:3/0 = 0 +12:3/0 = 0 +13:3/0 = 0 +15:3/0 = 0 +16:3/0 = 0 +0:4/0 = 0 +1:4/0 = 0 +2:4/0 = 0 +4:4/0 = 0 +6:4/0 = 0 +7:4/0 = 0 +9:4/0 = 0 +10:4/0 = 0 +12:4/0 = 0 +13:4/0 = 0 +15:4/0 = 0 +16:4/0 = 0 + +[sub_resource type="TileSet" id="TileSet_0tnpc"] +tile_size = Vector2i(32, 32) +sources/0 = SubResource("TileSetAtlasSource_gee14") + [sub_resource type="RectangleShape2D" id="RectangleShape2D_8cj0n"] size = Vector2(160, 48) @@ -19,20 +75,28 @@ script = ExtResource("1_lnu2h") unique_name_in_owner = true script = ExtResource("2_lbhrr") +[node name="TileMapLayer" type="TileMapLayer" parent="."] +texture_filter = 1 +tile_map_data = PackedByteArray("AAANAAgAAAAEAAQAAAAOAAgAAAAEAAQAAAAPAAgAAAAEAAQAAAAQAAgAAAAEAAQAAAARAAgAAAAEAAQAAAASAAgAAAAEAAQAAAAUAAgAAAAEAAQAAAAVAAgAAAAEAAQAAAAWAAgAAAAEAAQAAAAXAAgAAAAEAAQAAAAYAAgAAAAEAAQAAAA=") +tile_set = SubResource("TileSet_0tnpc") + [node name="CharacterBody2D" parent="." instance=ExtResource("2_lnu2h")] position = Vector2(489, 45) +bump_power = 50.0 [node name="Level" type="Node2D" parent="."] [node name="Plat 0" type="StaticBody2D" parent="Level"] -position = Vector2(510, 276) +position = Vector2(511, 271) +scale = Vector2(1.21285, 0.681287) metadata/_edit_group_ = true [node name="CollisionShape2D" type="CollisionShape2D" parent="Level/Plat 0"] shape = SubResource("RectangleShape2D_8cj0n") [node name="Plat 1" type="StaticBody2D" parent="Level"] -position = Vector2(715, 276) +position = Vector2(720, 272) +scale = Vector2(1, 0.72) metadata/_edit_group_ = true [node name="CollisionShape2D" type="CollisionShape2D" parent="Level/Plat 1"] diff --git a/scenes/player.tscn b/scenes/player.tscn index d2dc95e..c6c14b5 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,10 +1,217 @@ -[gd_scene load_steps=3 format=3 uid="uid://c5a5u2c250g6"] +[gd_scene load_steps=33 format=3 uid="uid://c5a5u2c250g6"] [ext_resource type="Script" uid="uid://c3vqmu6nc4gb0" path="res://scripts/player.gd" id="1_3vyb7"] +[ext_resource type="Texture2D" uid="uid://bd3w6vo31snc" path="res://assets/graphics/player/jump/player jump 48x48.png" id="2_dqkch"] +[ext_resource type="Texture2D" uid="uid://c3oyc8ergtwia" path="res://assets/graphics/player/idle/Player Idle 48x48.png" id="2_g2els"] +[ext_resource type="Texture2D" uid="uid://cv23jrdj8e5gv" path="res://assets/graphics/player/run/player run 48x48.png" id="3_qhqgy"] +[ext_resource type="Texture2D" uid="uid://bw2te4kygoarq" path="res://assets/graphics/player/melee/Player Punch 64x64.png" id="4_qlg0r"] [sub_resource type="CircleShape2D" id="CircleShape2D_8cj0n"] radius = 21.2132 +[sub_resource type="AtlasTexture" id="AtlasTexture_i4ail"] +atlas = ExtResource("2_dqkch") +region = Rect2(96, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qhqgy"] +atlas = ExtResource("2_g2els") +region = Rect2(0, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dqkch"] +atlas = ExtResource("2_g2els") +region = Rect2(48, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qlg0r"] +atlas = ExtResource("2_g2els") +region = Rect2(96, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_tuyoq"] +atlas = ExtResource("2_g2els") +region = Rect2(144, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fjrip"] +atlas = ExtResource("2_g2els") +region = Rect2(192, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_smehm"] +atlas = ExtResource("2_g2els") +region = Rect2(240, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ur7pv"] +atlas = ExtResource("2_g2els") +region = Rect2(288, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_y4r1p"] +atlas = ExtResource("2_g2els") +region = Rect2(336, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_d2wvv"] +atlas = ExtResource("2_g2els") +region = Rect2(384, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3v2ag"] +atlas = ExtResource("2_g2els") +region = Rect2(432, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_a38lo"] +atlas = ExtResource("2_dqkch") +region = Rect2(0, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_4ni07"] +atlas = ExtResource("2_dqkch") +region = Rect2(48, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_l71n6"] +atlas = ExtResource("2_dqkch") +region = Rect2(96, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ke2ow"] +atlas = ExtResource("4_qlg0r") +region = Rect2(128, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ujl30"] +atlas = ExtResource("4_qlg0r") +region = Rect2(192, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_31cv2"] +atlas = ExtResource("4_qlg0r") +region = Rect2(256, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jej6c"] +atlas = ExtResource("3_qhqgy") +region = Rect2(0, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_f1ej7"] +atlas = ExtResource("3_qhqgy") +region = Rect2(48, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_oprun"] +atlas = ExtResource("3_qhqgy") +region = Rect2(96, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_a8ls1"] +atlas = ExtResource("3_qhqgy") +region = Rect2(144, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qfm1y"] +atlas = ExtResource("3_qhqgy") +region = Rect2(192, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fulsm"] +atlas = ExtResource("3_qhqgy") +region = Rect2(240, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_4r5pv"] +atlas = ExtResource("3_qhqgy") +region = Rect2(288, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_60mlk"] +atlas = ExtResource("3_qhqgy") +region = Rect2(336, 0, 48, 48) + +[sub_resource type="SpriteFrames" id="SpriteFrames_jej6c"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_i4ail") +}], +"loop": false, +"name": &"fall", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_qhqgy") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_dqkch") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qlg0r") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_tuyoq") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fjrip") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_smehm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ur7pv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_y4r1p") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_d2wvv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3v2ag") +}], +"loop": true, +"name": &"idle", +"speed": 12.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_a38lo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_4ni07") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_l71n6") +}], +"loop": false, +"name": &"jump", +"speed": 12.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_ke2ow") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ujl30") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_31cv2") +}], +"loop": false, +"name": &"melee", +"speed": 12.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_jej6c") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_f1ej7") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_oprun") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_a8ls1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qfm1y") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fulsm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_4r5pv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_60mlk") +}], +"loop": true, +"name": &"run", +"speed": 12.0 +}] + [node name="CharacterBody2D" type="CharacterBody2D"] script = ExtResource("1_3vyb7") metadata/_edit_group_ = true @@ -25,3 +232,21 @@ position = Vector2(22, -9) [node name="LeftSpawn" type="Node2D" parent="."] position = Vector2(-30, -10) + +[node name="jumpBufferTimer" type="Timer" parent="."] +wait_time = 0.4 +one_shot = true + +[node name="PlayerGraphic" type="AnimatedSprite2D" parent="."] +texture_filter = 1 +position = Vector2(1, -1) +sprite_frames = SubResource("SpriteFrames_jej6c") +animation = &"melee" +autoplay = "idle" + +[node name="Camera2D" type="Camera2D" parent="."] +offset = Vector2(0, -20) + +[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."] + +[connection signal="animation_finished" from="PlayerGraphic" to="." method="_on_animation_finished"] diff --git a/scripts/player.gd b/scripts/player.gd index 70ec84b..318f6c9 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -1,29 +1,49 @@ class_name Player extends CharacterBody2D +const GRASS_RUNNING = preload("res://assets/sounds/Grass Running.wav") + +@onready var audio_stream_player_2d: AudioStreamPlayer2D = $AudioStreamPlayer2D + @onready var right_cast: RayCast2D = $RightCast @onready var left_cast: RayCast2D = $LeftCast @onready var right_spawn: Node2D = $RightSpawn @onready var left_spawn: Node2D = $LeftSpawn +@onready var jump_buffer_timer: Timer = $jumpBufferTimer +@onready var player_graphic: AnimatedSprite2D = $PlayerGraphic const SPEED = 300.0 const JUMP_VELOCITY = -400.0 @export var bump_power = 60.0 @export var shove_power = 600.0 +@export var acceleration = 50 +@export var hard_gravity = 5 + +enum State {IDLE, RUN, JUMP, FALLING, MELEE} +var current_state = State.IDLE enum FaceDirection {LEFT, RIGHT} var facing: FaceDirection = FaceDirection.LEFT +var direction: float = 0 var pushTarget: RigidBody2D var pushEnable: bool = false +var upJump: bool = false func _physics_process(delta: float) -> void: # Add the gravity. - if not is_on_floor(): + if current_state == State.JUMP: velocity += get_gravity() * delta + else: + velocity += get_gravity() * hard_gravity * delta # Handle input handle_input() + update_movement() + + # Handle states and animation + update_states() + update_animation() # Move and handle player collisions move_and_slide() @@ -38,6 +58,7 @@ func handle_input() -> void: %SceneManager.makeBullet(right_spawn.global_transform, 700) if Input.is_action_just_pressed("shove") and pushEnable: + current_state = State.MELEE if facing == FaceDirection.LEFT: print("Shove attack LEFT!!!") pushTarget.apply_central_impulse(Vector2(-1,0) * shove_power) @@ -48,23 +69,69 @@ func handle_input() -> void: pushEnable = false # Handle jump. - if Input.is_action_just_pressed("ui_accept") and is_on_floor(): + if Input.is_action_just_pressed("ui_accept"): + jump_buffer_timer.start() + + if is_on_floor() and jump_buffer_timer.time_left > 0: velocity.y = JUMP_VELOCITY + current_state = State.JUMP + upJump = true + jump_buffer_timer.stop() # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. - var direction := Input.get_axis("ui_left", "ui_right") - if direction: - velocity.x = direction * SPEED - facing = FaceDirection.LEFT if direction < 0 else FaceDirection.RIGHT - else: - velocity.x = move_toward(velocity.x, 0, SPEED) + direction = Input.get_axis("ui_left", "ui_right") + func update_movement() -> void: - pass + if direction: + velocity.x = move_toward(velocity.x, SPEED*direction, acceleration) + #facing = FaceDirection.LEFT if direction < 0 else FaceDirection.RIGHT + if direction < 0: + facing = FaceDirection.LEFT + player_graphic.flip_h = true + if direction > 0: + facing = FaceDirection.RIGHT + player_graphic.flip_h = false + else: + velocity.x = move_toward(velocity.x, 0, acceleration) func update_states() -> void: - pass + match current_state: + #describe all the rules for the state + State.IDLE when velocity.x != 0: + current_state = State.RUN + State.RUN: + if velocity.x == 0: + current_state = State.IDLE + if !is_on_floor() and velocity.y > 0: + current_state = State.FALLING + + State.JUMP when velocity.y > 0: + current_state = State.FALLING + + State.FALLING when is_on_floor(): + if velocity.x == 0: + current_state = State.IDLE + else: + current_state = State.RUN + +func update_animation()->void: + match current_state: + State.IDLE: + player_graphic.play("idle") + State.RUN: + player_graphic.play("run") + if not audio_stream_player_2d.playing: + audio_stream_player_2d.stream = GRASS_RUNNING + audio_stream_player_2d.playing = true + State.JUMP: + if upJump: + player_graphic.play("jump") + State.FALLING: + player_graphic.play("fall") + State.MELEE: + player_graphic.play("melee") func handle_collisions() -> void: for i in get_slide_collision_count(): @@ -90,3 +157,11 @@ func handle_collisions() -> void: if not right_cast.is_colliding() and not left_cast.is_colliding(): pushEnable = false + + +func _on_animation_finished() -> void: + match current_state: + State.JUMP: + upJump = false + State.MELEE: + current_state = State.IDLE