implement basic power hit

This commit is contained in:
yuki 2025-11-17 21:45:45 -03:00
parent 1c3b7b6f32
commit c2e3fa2ac2
14 changed files with 173 additions and 38 deletions

View file

@ -111,6 +111,16 @@ p2_hit_right={
"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":4194441,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
p1_power_hit={
"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":73,"key_label":0,"unicode":105,"location":0,"echo":false,"script":null)
]
}
p2_power_hit={
"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":4194439,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[layer_names]

View file

@ -17,9 +17,9 @@ func _ready() -> void:
launch()
func launch(angle: float = 0) -> void:
func launch(angle: float = 0, speed_override: float = 0) -> void:
angle = randf_range(-PI/3, PI/3) + PI * float(randi()%2) if angle == 0 else angle
linear_velocity = Vector2(cos(angle), sin(angle)) * randf_range(launch_speed.x, launch_speed.y)
linear_velocity = Vector2(cos(angle), sin(angle)) * (randf_range(launch_speed.x, launch_speed.y) if speed_override == 0 else speed_override)
var length: float = linear_velocity.length()/100
play_animation(length)
decelerate_to_stop(length*2)
@ -45,15 +45,17 @@ func decelerate_to_stop(duration: float = 0.3) -> void: # Call this after colli
)
func _on_hit(hitbox: Area2D) -> void:
func _on_hit(hitbox: Hit) -> void:
print("area detected")
if not is_hit:
if hitbox.is_in_group("hit"):
assert(hitbox.power_hit != null, "hit doesnt have correct metadata, is it a hit?")
print("ball not hit yet")
var player: Player = hitbox.get_parent()
var timer: Timer = player.hit_timer
var angle: float = randf_range(3*PI/4, PI/4) * (-1 if player.id == 1 else 1)
launch(angle)
var speed: float = randf_range(300, 350) if hitbox.power_hit else 0.0 # o.o
launch(angle, speed)
print("ball hit")
if not timer.is_connected("timeout", _on_hit_end):
timer.connect("timeout", _on_hit_end)

3
scenes/classes/hit.gd Normal file
View file

@ -0,0 +1,3 @@
class_name Hit extends Area2D
@export var power_hit: bool = false

View file

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

14
scenes/classes/hit.tscn Normal file
View file

@ -0,0 +1,14 @@
[gd_scene load_steps=2 format=3 uid="uid://d04legmj3c42r"]
[ext_resource type="Script" uid="uid://b440ri3521uio" path="res://scenes/classes/hit.gd" id="1_udkr8"]
[node name="Hit2D" type="Area2D" groups=["hit"]]
collision_layer = 64
collision_mask = 0
script = ExtResource("1_udkr8")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
rotation = 1.5707964
debug_color = Color(0.6205005, 0.42311785, 0.8432588, 0.41960785)

View file

@ -4,7 +4,7 @@
## Emitted when the state finishes and wants to transition to another state.
@warning_ignore("unused_signal")
signal finished(next_state_path: String, data: Dictionary[String, Variant])
signal finished(next_state_path: String, data: Dictionary)
## For 'ghost frame' update fix

View file

@ -1,13 +1,17 @@
[gd_scene load_steps=3 format=3 uid="uid://d0c57c37o75b0"]
[gd_scene load_steps=4 format=3 uid="uid://d0c57c37o75b0"]
[ext_resource type="Texture2D" uid="uid://d3qem75thpant" path="res://scenes/hit/assets/power_hit.png" id="1_g005q"]
[ext_resource type="Script" uid="uid://b440ri3521uio" path="res://scenes/classes/hit.gd" id="1_kwsj5"]
[sub_resource type="CircleShape2D" id="CircleShape2D_g005q"]
radius = 21.023796
[node name="Hit" type="Area2D" groups=["hit"]]
[node name="PowerHit2D" type="Area2D" groups=["hit"]]
collision_layer = 64
collision_mask = 0
script = ExtResource("1_kwsj5")
power_hit = true
metadata/_custom_type_script = "uid://b440ri3521uio"
metadata/power_hit = true
[node name="Sprite2D" type="Sprite2D" parent="."]

View file

@ -1,19 +1,22 @@
[gd_scene load_steps=3 format=3 uid="uid://px67runjx6ex"]
[gd_scene load_steps=4 format=3 uid="uid://px67runjx6ex"]
[ext_resource type="Texture2D" uid="uid://c6a62gvw7218s" path="res://scenes/hit/assets/hit.png" id="1_wjo4f"]
[ext_resource type="Script" uid="uid://b440ri3521uio" path="res://scenes/classes/hit.gd" id="1_dk3vy"]
[ext_resource type="Texture2D" uid="uid://c6a62gvw7218s" path="res://scenes/hit/assets/hit.png" id="1_rmj1p"]
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_fxeki"]
radius = 11.0
height = 24.0
[node name="Hit" type="Area2D" groups=["hit"]]
[node name="SmallHit2D" type="Area2D" groups=["hit"]]
collision_layer = 64
collision_mask = 0
script = ExtResource("1_dk3vy")
metadata/_custom_type_script = "uid://b440ri3521uio"
metadata/power_hit = false
[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(7, -4)
texture = ExtResource("1_wjo4f")
texture = ExtResource("1_rmj1p")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(4, 1)

View file

@ -20,14 +20,18 @@ metadata/_edit_lock_ = true
[node name="Player1" parent="." instance=ExtResource("2_r0du0")]
position = Vector2(141, 184)
id = 1
max_speed = 90.0
[node name="Player2" parent="." instance=ExtResource("2_r0du0")]
position = Vector2(167, 77)
id = 2
max_speed = 90.0
[node name="Ball" parent="." instance=ExtResource("3_r0du0")]
position = Vector2(162, 115)
launch_speed = Vector2(150, 300)
speed_mult = null
[node name="LevelCamera2D" parent="." instance=ExtResource("4_jyhfs")]
ball_weight = 0.4

View file

@ -9,19 +9,36 @@ const DEADZONE: float = 0.1
@onready var anim_player: AnimationPlayer = $AnimationPlayer
@onready var state_machine: StateMachine = $StateMachine
@onready var hit_timer: Timer = $HitTimer
@onready var power_timer: Timer = $PowerTimer
var anim_dir: String
var def_dir: String
var power_bar: float
func _ready() -> void:
assert(id == 1 or id == 2, "id ("+str(id)+") is invalid")
anim_dir = 'up' if id == 1 else 'down'
def_dir = 'idle_up' if id == 1 else 'idle_down'
power_bar = 0
hit_timer.connect("timeout", $StateMachine/Hitting._on_hit_end)
power_timer.connect("timeout", func() -> void:
print(power_bar)
)
func get_movement_vector() -> Vector2:
var x_mov: float = Input.get_action_strength('p'+str(id)+'_move_right') - Input.get_action_strength('p'+str(id)+'_move_left')
var y_mov: float = Input.get_action_strength('p'+str(id)+'_move_down') - Input.get_action_strength('p'+str(id)+'_move_up')
return Vector2(x_mov, y_mov)
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("p"+str(id)+"_power_hit") and power_bar == 100:
power_bar = 0
state_machine.state.finished.emit(PlayerState.HITTING, {"hit": "power"})
func _process(_delta: float) -> void:
if Input.is_action_pressed("p"+str(id)+"_hit_left") and Input.is_action_pressed("p"+str(id)+"_hit_right"):
if power_bar < 100: power_bar += 1

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=38 format=3 uid="uid://642nm6c4mpxx"]
[gd_scene load_steps=40 format=3 uid="uid://642nm6c4mpxx"]
[ext_resource type="Script" uid="uid://rdqmsvofiots" path="res://scenes/player/player.gd" id="1_v6fml"]
[ext_resource type="Texture2D" uid="uid://b8ptokcqwpdud" path="res://scenes/player/saffron.png" id="2_3li8b"]
@ -363,11 +363,69 @@ tracks/1/keys = {
"values": [0]
}
[sub_resource type="Animation" id="Animation_myrg7"]
resource_name = "hit_power_up"
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("AnimatedSprite2D:animation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [&"idle_up"]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("AnimatedSprite2D:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [2]
}
[sub_resource type="Animation" id="Animation_kvlxm"]
resource_name = "hit_power_down"
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("AnimatedSprite2D:animation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [&"idle_down"]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("AnimatedSprite2D:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [2]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_ugbui"]
_data = {
&"RESET": SubResource("Animation_gx1jg"),
&"hit_left_down": SubResource("Animation_fcs02"),
&"hit_left_up": SubResource("Animation_h17s1"),
&"hit_power_down": SubResource("Animation_kvlxm"),
&"hit_power_up": SubResource("Animation_myrg7"),
&"hit_right_down": SubResource("Animation_ugbui"),
&"hit_right_up": SubResource("Animation_je7p5"),
&"idle_down": SubResource("Animation_fm80t"),
@ -585,3 +643,7 @@ metadata/_custom_type_script = "uid://b2sr7p80gdjii"
[node name="HitTimer" type="Timer" parent="."]
wait_time = 0.5
one_shot = true
[node name="PowerTimer" type="Timer" parent="."]
wait_time = 0.847
autostart = true

View file

@ -6,12 +6,14 @@ var hit_type: String
var hit_node_ref: WeakRef
func _enter(_previous_state_path: String, _data: Dictionary[String, Variant] = {}) -> void:
func _enter(_previous_state_path: String, _data: Dictionary = {}) -> void:
print("entering hitting")
player.velocity = Vector2.ZERO
player.move_and_slide()
assert(
_data.get("hit") == "left" or _data.get("hit") == "right",
_data.get("hit") == "left" or
_data.get("hit") == "right" or
_data.get("hit") == "power",
"Invalid hit_type ("+str(_data.get("hit"))+")."
)
print("previous state path: "+_previous_state_path)
@ -28,28 +30,41 @@ func _enter(_previous_state_path: String, _data: Dictionary[String, Variant] = {
player.anim_player.current_animation == "hit_left_up" or
player.anim_player.current_animation == "hit_left_down" or
player.anim_player.current_animation == "hit_right_up" or
player.anim_player.current_animation == "hit_right_down",
player.anim_player.current_animation == "hit_right_down" or
player.anim_player.current_animation == "hit_power_up" or
player.anim_player.current_animation == "hit_power_down",
"invalid animation ("+player.anim_player.current_animation+")"
)
var sprite_texture: Texture2D = player.sprite.sprite_frames.get_frame_texture('up', 1)
var hit_node: Area2D = preload("res://scenes/hit/hit.tscn").instantiate()
var sprite_texture: Texture2D = player.sprite.sprite_frames.get_frame_texture(player.sprite.animation, 1)
var hit_node: Hit
# flip entire node horizontally if spawning left
# flip vertically if player 2
hit_node.scale = Vector2(
-1 if hit_type == "left" else 1,
1 if player.id == 1 else -1
)
player.add_child(hit_node)
# set position according to frame width and height
@warning_ignore("integer_division")
hit_node.global_position = player.global_position + Vector2(
(sprite_texture.get_width()/2)*(-1 if hit_type == "left" else 1),
(sprite_texture.get_height()*-1) if player.id == 1 else 4
)
if hit_type == "left" or hit_type == "right":
hit_node = preload("res://scenes/hit/small_hit.tscn").instantiate()
# flip entire node horizontally if spawning left
# flip vertically if player 2
hit_node.scale = Vector2(
-1 if hit_type == "left" else 1,
1 if player.id == 1 else -1
)
player.add_child(hit_node)
# set position according to frame width and height
@warning_ignore("integer_division")
hit_node.global_position = player.global_position + Vector2(
(sprite_texture.get_width()/2)*(-1 if hit_type == "left" else 1),
(sprite_texture.get_height()*-1) if player.id == 1 else 4
)
elif hit_type == "power":
hit_node = preload("res://scenes/hit/power_hit.tscn").instantiate()
player.add_child(hit_node)
@warning_ignore("integer_division")
hit_node.global_position = player.global_position + Vector2(0,(sprite_texture.get_height()/2)*-1)
assert(hit_node != null, "hit_node is null")
hit_node_ref = weakref(hit_node)
# cooldown
@ -59,7 +74,7 @@ func _on_hit_end() -> void:
print("cooldown end")
assert(hit_node_ref != null, "hit node is null")
var hit_node: Area2D = hit_node_ref.get_ref()
var hit_node: Hit = hit_node_ref.get_ref()
if hit_node and is_instance_valid(hit_node):
hit_node.queue_free()
@ -72,11 +87,11 @@ func _on_hit_end() -> void:
print("transitioning to idling")
finished.emit(IDLE)
return
elif Input.is_action_pressed('p'+str(player.id)+'_hit_left'):
elif Input.is_action_just_pressed('p'+str(player.id)+'_hit_left'):
print("transitioning to hitting left")
finished.emit(HITTING, {"hit": "left"})
return
elif Input.is_action_pressed('p'+str(player.id)+'_hit_right'):
elif Input.is_action_just_pressed('p'+str(player.id)+'_hit_right'):
print("transitioning to hitting right")
finished.emit(HITTING, {"hit": "right"})
return

View file

@ -1,7 +1,7 @@
extends PlayerState
func _enter(_previous_state_path: String, _data: Dictionary[String, Variant] = {}) -> void:
func _enter(_previous_state_path: String, _data: Dictionary = {}) -> void:
print("entering idle")
player.velocity = Vector2.ZERO
player.move_and_slide()
@ -21,9 +21,9 @@ func _state_physics_update(_delta: float) -> void:
if movement_vector.length() > player.DEADZONE:
finished.emit(RUNNING)
return
elif Input.is_action_pressed('p'+str(player.id)+'_hit_left'):
elif Input.is_action_just_pressed('p'+str(player.id)+'_hit_left'):
finished.emit(HITTING, {"hit": "left"})
return
elif Input.is_action_pressed('p'+str(player.id)+'_hit_right'):
elif Input.is_action_just_pressed('p'+str(player.id)+'_hit_right'):
finished.emit(HITTING, {"hit": "right"})
return

View file

@ -1,7 +1,7 @@
extends PlayerState
func _enter(_previous_state_path: String, _data: Dictionary[String, Variant] = {}) -> void:
func _enter(_previous_state_path: String, _data: Dictionary = {}) -> void:
print("entering running")
player.move_and_slide()