diff --git a/assets/shaders/orbit.gdshader b/assets/shaders/orbit.gdshader new file mode 100644 index 0000000..6a7d2ac --- /dev/null +++ b/assets/shaders/orbit.gdshader @@ -0,0 +1,9 @@ +shader_type canvas_item; + +uniform float radius = 0.0; +uniform float speed = 0.0; + +void vertex() { + VERTEX.x += cos(TIME * speed) * radius; + VERTEX.y += sin(TIME * speed) * radius; +} \ No newline at end of file diff --git a/assets/shaders/orbit.gdshader.uid b/assets/shaders/orbit.gdshader.uid new file mode 100644 index 0000000..cba262d --- /dev/null +++ b/assets/shaders/orbit.gdshader.uid @@ -0,0 +1 @@ +uid://bc8j5jyjynbca diff --git a/maps/level_z.tscn b/maps/level_z.tscn index c264a6b..114f917 100644 --- a/maps/level_z.tscn +++ b/maps/level_z.tscn @@ -25,7 +25,9 @@ tile_map_data = PackedByteArray("AAAAAAsAAAAAAAAAAAABAAsAAAAAAAAAAAACAAsAAAAAAAA tile_set = ExtResource("2_dj7w1") [node name="Lashy" parent="." instance=ExtResource("4_gykx6")] -position = Vector2(264, 72) +position = Vector2(160, 160) +launch_power = 300.0 +idle_radius = 6.0 [node name="Player" parent="." instance=ExtResource("5_5v7mr")] position = Vector2(120, 144) @@ -34,4 +36,4 @@ position = Vector2(120, 144) position = Vector2(96, 144) [node name="RotoBoy" parent="." instance=ExtResource("7_j1102")] -position = Vector2(152, 104) +position = Vector2(240, 40) diff --git a/objects/enemies/lashy/lashy.gd b/objects/enemies/lashy/lashy.gd index 4991781..7b9d57e 100644 --- a/objects/enemies/lashy/lashy.gd +++ b/objects/enemies/lashy/lashy.gd @@ -2,82 +2,59 @@ extends Node2D -enum State { - IDLE, - WINDING_UP, - ATTACK, - RECOVER, -} +@export var launch_power: float +@export var idle_radius: float +@export var draw_back_distance: float +@export var smash_distance: float -@export var idle_distance: float -@export_custom(PROPERTY_HINT_NONE, "radians_as_degrees") -var idle_rotate_speed: float - -@export var wind_up_time: float -@export var wind_up_distance: float - -@export var attack_time: float -@export var attack_distance: float - -@export var recover_time: float - -@export_range(0,1,1,"or_greater") var detection_radius: float: - set(value): - detection_radius = value - if not is_inside_tree(): - await tree_entered - var shape = detector_shape.shape as CircleShape2D - if shape: - shape.radius = detection_radius +@export_group("Timing", "time_") +@export var time_draw_back: float +@export var time_smash: float +@export var time_recover: float @export_group("Internal References") @export var head: Node2D -@export var head_pivot: Node2D -@export var detector_shape: CollisionShape2D -var state: State = State.IDLE -var tween: Tween = null -var tracked_player: Player = null -var idle_angle: float = 0.0 -var target_pos: Vector2 = Vector2.ZERO -var progress: float = 0.0 - - -func _process(delta: float) -> void: - if Engine.is_editor_hint(): - return - - match state: - State.IDLE: - idle_angle = fposmod(idle_angle + idle_rotate_speed * delta, TAU) - head_pivot.rotation = idle_angle - State.WINDING_UP: - target_pos = tracked_player.global_position - var angle = (global_position - target_pos).angle() - progress += delta / wind_up_time - head_pivot.rotation = lerp_angle(head_pivot.rotation, angle, progress) - State.RECOVER: - progress += delta / recover_time - head_pivot.rotation = lerp_angle(head_pivot.rotation, idle_angle, progress) - - -func _start_attack() -> void: - state = State.WINDING_UP - if tween: - tween.kill() - tween = create_tween().set_process_mode(Tween.TWEEN_PROCESS_PHYSICS) - tween.tween_property(head, ^"position:x", wind_up_distance, wind_up_time) - tween.tween_property(self, ^"state", State.ATTACK, 0.0) - tween.tween_property(head, ^"position:x", -attack_distance, attack_time).set_trans(Tween.TRANS_BOUNCE) - tween.tween_property(self, ^"state", State.RECOVER, 0.0) - tween.tween_property(head, ^"position:x", idle_distance, recover_time) - tween.tween_property(self, ^"state", State.IDLE, 0.0) +var _player: Player = null +var _tween: Tween = null func _on_player_detector_body_entered(body: Node2D) -> void: if body is Player: - if state == State.IDLE: - tracked_player = body - _start_attack() + _player = body + _start_attack() + + +func _on_head_body_entered(body: Node2D) -> void: + if body is Player: + body.launch(global_position.direction_to(body.global_position) * launch_power) + + +func _start_attack() -> void: + # do nothing if already doing animation + if _tween and _tween.is_running(): + return + + _tween = create_tween().set_process_mode(Tween.TWEEN_PROCESS_PHYSICS) + # draw back away from player + _tween.tween_property(head.material, ^"shader_parameter/radius", 0.0, 0.1) + _tween.parallel().tween_method(_move_facing_player, 0.0, -draw_back_distance, time_draw_back)\ + .set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC) + # smash towards player + _tween.tween_method(_move_facing_player, -draw_back_distance, smash_distance, time_smash)\ + .set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_ELASTIC) + # return to center + _tween.tween_method(_move_facing_player, smash_distance, 0.0, time_recover)\ + .set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BOUNCE) + _tween.tween_property(head.material, ^"shader_parameter/radius", idle_radius, 0.1) + + +## move head this disatance from (0,0) towards (or away if negative) from player +func _move_facing_player(distance: float) -> void: + if not _player: + return + + var dir = global_position.direction_to(_player.global_position) + head.position = dir * distance diff --git a/objects/enemies/lashy/lashy.tscn b/objects/enemies/lashy/lashy.tscn index 6124073..faaf6b9 100644 --- a/objects/enemies/lashy/lashy.tscn +++ b/objects/enemies/lashy/lashy.tscn @@ -1,31 +1,35 @@ -[gd_scene load_steps=8 format=3 uid="uid://c8r040r4glui4"] +[gd_scene load_steps=10 format=3 uid="uid://c8r040r4glui4"] [ext_resource type="Script" uid="uid://lxppe1op1240" path="res://objects/enemies/lashy/lashy.gd" id="1_34o54"] [ext_resource type="Texture2D" uid="uid://cm4cantqbhkwx" path="res://assets/textures/enemies/lashy/lashy_anchor.png" id="2_8ls3k"] [ext_resource type="Script" uid="uid://bt4bdjyekgh53" path="res://scripts/ball_snake/ball_snake.gd" id="3_8edxw"] [ext_resource type="Texture2D" uid="uid://gnkke4rr0wcr" path="res://assets/textures/enemies/lashy/lashy_body.png" id="4_xnkdk"] [ext_resource type="Texture2D" uid="uid://bf0i64q2dw0au" path="res://assets/textures/enemies/lashy/lashy_head.png" id="5_b5g1y"] +[ext_resource type="Shader" uid="uid://bc8j5jyjynbca" path="res://assets/shaders/orbit.gdshader" id="5_dkkk6"] [sub_resource type="CircleShape2D" id="CircleShape2D_woon5"] resource_local_to_scene = true -radius = 64.0 +radius = 32.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_42oum"] +resource_local_to_scene = true +shader = ExtResource("5_dkkk6") +shader_parameter/radius = 6.0 +shader_parameter/speed = 3.142 [sub_resource type="CircleShape2D" id="CircleShape2D_g5iss"] radius = 6.0 -[node name="Lashy" type="Node2D" node_paths=PackedStringArray("head", "head_pivot", "detector_shape")] +[node name="Lashy" type="Node2D" node_paths=PackedStringArray("head")] script = ExtResource("1_34o54") -idle_distance = 16.0 -idle_rotate_speed = 1.5708 -wind_up_time = 2.0 -wind_up_distance = 32.0 -attack_time = 0.5 -attack_distance = 64.0 -recover_time = 2.0 -detection_radius = 64.0 -head = NodePath("HeadPivot/Head") -head_pivot = NodePath("HeadPivot") -detector_shape = NodePath("PlayerDetector/DetectorShape") +launch_power = 240.0 +idle_radius = 4.0 +draw_back_distance = 24.0 +smash_distance = 32.0 +time_draw_back = 0.2 +time_smash = 0.2 +time_recover = 0.1 +head = NodePath("Head") [node name="PlayerDetector" type="Area2D" parent="."] collision_layer = 0 @@ -39,34 +43,29 @@ debug_color = Color(0.752941, 0.196078, 1, 0.25098) [node name="Anchor" type="Sprite2D" parent="."] texture = ExtResource("2_8ls3k") -[node name="BallSnake" type="Node2D" parent="." node_paths=PackedStringArray("target")] +[node name="BallSnake" type="Node2D" parent="Anchor" node_paths=PackedStringArray("target")] script = ExtResource("3_8edxw") texture = ExtResource("4_xnkdk") -target = NodePath("../HeadPivot/Head") -segments = 2 +target = NodePath("../../Head") +segments = -1 head_segment = false -tail_segment = true auto_density = true pixels_per_segment = 5.0 -[node name="HeadSprite" type="Sprite2D" parent="."] -position = Vector2(-5.79198e-05, -16) -texture = ExtResource("5_b5g1y") - -[node name="HeadPivot" type="Node2D" parent="."] -rotation = -1.5708 - -[node name="Head" type="Area2D" parent="HeadPivot"] -position = Vector2(16, 0) +[node name="Head" type="Area2D" parent="."] +material = SubResource("ShaderMaterial_42oum") +collision_layer = 0 +collision_mask = 16 +monitorable = false metadata/_edit_group_ = true -[node name="CollisionShape2D" type="CollisionShape2D" parent="HeadPivot/Head"] +[node name="HeadSprite" type="Sprite2D" parent="Head"] +use_parent_material = true +texture = ExtResource("5_b5g1y") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Head"] shape = SubResource("CircleShape2D_g5iss") debug_color = Color(1, 0, 0, 0.419608) -[node name="RemoteTransform2D" type="RemoteTransform2D" parent="HeadPivot/Head"] -remote_path = NodePath("../../../HeadSprite") -update_rotation = false -update_scale = false - [connection signal="body_entered" from="PlayerDetector" to="." method="_on_player_detector_body_entered"] +[connection signal="body_entered" from="Head" to="." method="_on_head_body_entered"]