From f0b89e3137b2f1ba8de198f074ec0dc17220c326 Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Fri, 17 May 2019 18:53:13 +0900 Subject: [PATCH 1/5] Add a switch to toggle ledge detect on and off --- prototypes/1.hook-movement/LedgeDetector.gd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/prototypes/1.hook-movement/LedgeDetector.gd b/prototypes/1.hook-movement/LedgeDetector.gd index 717a197..843488e 100644 --- a/prototypes/1.hook-movement/LedgeDetector.gd +++ b/prototypes/1.hook-movement/LedgeDetector.gd @@ -4,6 +4,8 @@ extends Position2D onready var ray_bottom: = $RayBottom onready var ray_top: = $RayTop +export var active: = true setget set_active + export var ray_length: = 30.0 setget set_ray_length var _ray_cast_to_x: float = ray_length setget _set_ray_cast_to_x @@ -16,6 +18,9 @@ func _ready() -> void: func is_against_ledge(look_direction:int) -> bool: + if not active: + return false + self._ray_cast_to_x = ray_length * look_direction ray_bottom.force_raycast_update() ray_top.force_raycast_update() @@ -36,3 +41,7 @@ func _set_ray_cast_to_x(value:float) -> void: ray_top.cast_to = cast_to ray_bottom.position.x = sign(value) * _offset ray_top.position.x = sign(value) * _offset + + +func set_active(value:bool) -> void: + active = value From 28d34c022df65f28e2354ace6c6b5ee7543f4ba9 Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Sun, 19 May 2019 14:45:34 +0900 Subject: [PATCH 2/5] Add refining core movement prototype plan --- GAME_CONCEPT.org | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/GAME_CONCEPT.org b/GAME_CONCEPT.org index 00ab7d9..5596a2c 100644 --- a/GAME_CONCEPT.org +++ b/GAME_CONCEPT.org @@ -318,6 +318,19 @@ Use a new node branch for each chunk, and separate collision bodies so we can save the best ones as reusable scenes. +** Refining the core movement + + After testing the level design work from Henrique, some problems with the game's controls are clearer: + + - It's hard to catch a hook at times, as the character falls fast. You have to be too reactive with the input. + - The camera doesn't help enough in seeing the challenges ahead, especially with vertical level design. + + This prototype is an attempt at solving these issues: + + - Camera design: the lookahead based on the mouse's position makes the camera jiggle too much. We've got to experiment working with the input direction and move direction of the character. Using the velocity alone makes it so the camera lags behind, while updating based on input instantly almost gives motion sickness. + - Store input: jump after fall start, or hook + - Allow the player to jump right after a fall started, and to hook if the input was right before actually the snap detector detected the hooking point. Maybe only when falling? + ** TODO Predicting player motion :PROPERTIES: :EFFORT: 3:00 From 5f720924f7afea84ee394f6ca314e78df2ccfbe1 Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Sun, 19 May 2019 16:08:10 +0900 Subject: [PATCH 3/5] Improve camera horizontal movement uses both the input direction and character velocity --- prototypes/1.hook-movement/CameraRig.gd | 6 +- prototypes/1.hook-movement/CameraRig.tscn | 17 ++++++ prototypes/1.hook-movement/Character.tscn | 17 ++---- prototypes/1.hook-movement/Game.tscn | 67 ++++++++++++++++------- 4 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 prototypes/1.hook-movement/CameraRig.tscn diff --git a/prototypes/1.hook-movement/CameraRig.gd b/prototypes/1.hook-movement/CameraRig.gd index aad4105..1a0645a 100644 --- a/prototypes/1.hook-movement/CameraRig.gd +++ b/prototypes/1.hook-movement/CameraRig.gd @@ -20,4 +20,8 @@ func update_position(velocity:Vector2) -> void: _camera.position = distance_ratio * mouse_position.normalized() * offset Settings.GAMEPAD: - _camera.position = ControlUtils.get_aim_joystick_direction() * offset + var joystick_direction: = get_aim_joystick_direction() + if Input.is_action_pressed("move_right") or Input.is_action_pressed("move_left"): + _camera.position.x = sign(velocity.x) * offset.x + _camera.position.y = joystick_direction.y * offset.y + diff --git a/prototypes/1.hook-movement/CameraRig.tscn b/prototypes/1.hook-movement/CameraRig.tscn new file mode 100644 index 0000000..67533e9 --- /dev/null +++ b/prototypes/1.hook-movement/CameraRig.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://CameraRig.gd" type="Script" id=1] + +[node name="CameraRig" type="Position2D"] +position = Vector2( 0, -30 ) +script = ExtResource( 1 ) + +[node name="Camera2D" type="Camera2D" parent="."] +current = true +process_mode = 0 +drag_margin_h_enabled = false +drag_margin_v_enabled = false +smoothing_enabled = true +smoothing_speed = 4.0 +editor_draw_drag_margin = true + diff --git a/prototypes/1.hook-movement/Character.tscn b/prototypes/1.hook-movement/Character.tscn index 5eb7e74..5fcab60 100644 --- a/prototypes/1.hook-movement/Character.tscn +++ b/prototypes/1.hook-movement/Character.tscn @@ -6,7 +6,7 @@ [ext_resource path="res://FloorDetector.gd" type="Script" id=4] [ext_resource path="res://Skin.gd" type="Script" id=5] [ext_resource path="res://Body.gd" type="Script" id=6] -[ext_resource path="res://CameraRig.gd" type="Script" id=7] +[ext_resource path="res://CameraRig.tscn" type="PackedScene" id=7] [sub_resource type="RectangleShape2D" id=1] extents = Vector2( 30, 30 ) @@ -22,6 +22,8 @@ shape = SubResource( 1 ) position = Vector2( 0, -30 ) [node name="LedgeDetector" parent="." instance=ExtResource( 3 )] +active = true +ray_length = 30.0 [node name="FloorDetector" type="RayCast2D" parent="."] position = Vector2( 0, -30 ) @@ -42,16 +44,5 @@ color_outline = Color( 0.231373, 0.145098, 0.584314, 1 ) [node name="Tween" type="Tween" parent="Skin"] -[node name="CameraRig" type="Position2D" parent="Skin"] -position = Vector2( 0, -30 ) -script = ExtResource( 7 ) - -[node name="Camera2D" type="Camera2D" parent="Skin/CameraRig"] -current = true -process_mode = 0 -drag_margin_h_enabled = false -drag_margin_v_enabled = false -smoothing_enabled = true -smoothing_speed = 6.0 -editor_draw_drag_margin = true +[node name="CameraRig" parent="Skin" instance=ExtResource( 7 )] diff --git a/prototypes/1.hook-movement/Game.tscn b/prototypes/1.hook-movement/Game.tscn index f54465c..6eb0f5f 100644 --- a/prototypes/1.hook-movement/Game.tscn +++ b/prototypes/1.hook-movement/Game.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=9 format=2] +[gd_scene load_steps=10 format=2] -[ext_resource path="res://src/theme/gdquest.theme" type="Theme" id=1] -[ext_resource path="res://src/theme/Components/Title.tscn" type="PackedScene" id=2] -[ext_resource path="res://InfoLabel.gd" type="Script" id=3] -[ext_resource path="res://Character.tscn" type="PackedScene" id=4] -[ext_resource path="res://HookTarget.tscn" type="PackedScene" id=5] +[ext_resource path="res://Game.gd" type="Script" id=1] +[ext_resource path="res://src/theme/gdquest.theme" type="Theme" id=2] +[ext_resource path="res://src/theme/Components/Title.tscn" type="PackedScene" id=3] +[ext_resource path="res://InfoLabel.gd" type="Script" id=4] +[ext_resource path="res://Character.tscn" type="PackedScene" id=5] +[ext_resource path="res://HookTarget.tscn" type="PackedScene" id=6] [sub_resource type="GDScript" id=1] script/source = "extends Label @@ -33,6 +34,7 @@ func _on_Settings_controls_changed(controls:int) -> void: radius = 401.995 [node name="Game" type="Node2D"] +script = ExtResource( 1 ) [node name="TopLayer" type="CanvasLayer" parent="."] @@ -40,7 +42,7 @@ radius = 401.995 anchor_right = 1.0 anchor_bottom = 1.0 mouse_filter = 2 -theme = ExtResource( 1 ) +theme = ExtResource( 2 ) __meta__ = { "_edit_group_": true, "_edit_lock_": true @@ -60,7 +62,7 @@ margin_top = 40.0 margin_right = -40.0 margin_bottom = -40.0 -[node name="Title" parent="TopLayer/UI/Panel/Column" instance=ExtResource( 2 )] +[node name="Title" parent="TopLayer/UI/Panel/Column" instance=ExtResource( 3 )] anchor_left = 0.0 anchor_top = 0.0 anchor_right = 0.0 @@ -76,7 +78,7 @@ margin_top = 44.0 margin_right = 280.0 margin_bottom = 70.0 text = "Player info" -script = ExtResource( 3 ) +script = ExtResource( 4 ) [node name="InfoLabel2" type="Label" parent="TopLayer/UI/Panel/Column"] margin_top = 78.0 @@ -92,7 +94,7 @@ margin_bottom = 138.0 text = "Controls" script = SubResource( 2 ) -[node name="Character" parent="." instance=ExtResource( 4 )] +[node name="Character" parent="." instance=ExtResource( 5 )] position = Vector2( 2080, -80 ) collision_mask = 6 @@ -186,31 +188,55 @@ collision_mask = 0 [node name="1" type="CollisionPolygon2D" parent="HookableWall"] polygon = PoolVector2Array( -40, -160, 0, -160, 0, 120, -40, 120 ) -[node name="HookTarget1" parent="." instance=ExtResource( 5 )] +[node name="HookTarget1" parent="." instance=ExtResource( 6 )] position = Vector2( 1680, 480 ) -[node name="HookTarget4" parent="." instance=ExtResource( 5 )] +[node name="HookTarget4" parent="." instance=ExtResource( 6 )] position = Vector2( 3360, 720 ) -[node name="HookTarget6" parent="." instance=ExtResource( 5 )] +[node name="HookTarget6" parent="." instance=ExtResource( 6 )] position = Vector2( 3880, 440 ) -[node name="HookTarget5" parent="." instance=ExtResource( 5 )] +[node name="HookTarget5" parent="." instance=ExtResource( 6 )] position = Vector2( 2964, 440 ) -[node name="HookTarget7" parent="." instance=ExtResource( 5 )] +[node name="HookTarget7" parent="." instance=ExtResource( 6 )] position = Vector2( 2560, -212 ) -[node name="HookTarget8" parent="." instance=ExtResource( 5 )] +[node name="HookTarget8" parent="." instance=ExtResource( 6 )] position = Vector2( 2960, -360 ) -[node name="HookTarget9" parent="." instance=ExtResource( 5 )] -position = Vector2( 3400, -400 ) +[node name="HookTarget9" parent="." instance=ExtResource( 6 )] +position = Vector2( 3360, -400 ) -[node name="HookTarget2" parent="." instance=ExtResource( 5 )] +[node name="HookTarget10" parent="." instance=ExtResource( 6 )] +position = Vector2( 3240, -720 ) + +[node name="HookTarget11" parent="." instance=ExtResource( 6 )] +position = Vector2( 2800, -720 ) + +[node name="HookTarget12" parent="." instance=ExtResource( 6 )] +position = Vector2( 2400, -760 ) + +[node name="HookTarget13" parent="." instance=ExtResource( 6 )] +position = Vector2( 2680, -1080 ) + +[node name="HookTarget14" parent="." instance=ExtResource( 6 )] +position = Vector2( 3000, -960 ) + +[node name="HookTarget15" parent="." instance=ExtResource( 6 )] +position = Vector2( 3440, -1000 ) + +[node name="HookTarget16" parent="." instance=ExtResource( 6 )] +position = Vector2( 3680, -720 ) + +[node name="HookTarget17" parent="." instance=ExtResource( 6 )] +position = Vector2( 4000, -960 ) + +[node name="HookTarget2" parent="." instance=ExtResource( 6 )] position = Vector2( 1960, 190 ) -[node name="HookTarget3" parent="." instance=ExtResource( 5 )] +[node name="HookTarget3" parent="." instance=ExtResource( 6 )] position = Vector2( 1640, -240 ) [node name="Flag" type="Node2D" parent="."] @@ -233,6 +259,7 @@ begin_cap_mode = 2 end_cap_mode = 2 [node name="ZeroGravityArea" type="Area2D" parent="."] +visible = false position = Vector2( 2720, -280 ) __meta__ = { "_edit_group_": true From 75b5a979a7a620dfc412c094f2bc18b1001959fb Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Sun, 19 May 2019 16:09:21 +0900 Subject: [PATCH 4/5] Add a button to restart the scene --- prototypes/1.hook-movement/Game.gd | 5 +++++ prototypes/1.hook-movement/project.godot | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 prototypes/1.hook-movement/Game.gd diff --git a/prototypes/1.hook-movement/Game.gd b/prototypes/1.hook-movement/Game.gd new file mode 100644 index 0000000..90b620b --- /dev/null +++ b/prototypes/1.hook-movement/Game.gd @@ -0,0 +1,5 @@ +extends Node2D + +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("restart"): + get_tree().reload_current_scene() diff --git a/prototypes/1.hook-movement/project.godot b/prototypes/1.hook-movement/project.godot index 7e2393e..50ffdb0 100644 --- a/prototypes/1.hook-movement/project.godot +++ b/prototypes/1.hook-movement/project.godot @@ -115,6 +115,11 @@ aim={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":false,"script":null) ] } +restart={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":82,"unicode":0,"echo":false,"script":null) + ] +} [layer_names] From ae54ee9139f9ebc02dc38913acc471bd3d03eed9 Mon Sep 17 00:00:00 2001 From: Nathan Lovato Date: Sun, 19 May 2019 16:55:44 +0900 Subject: [PATCH 5/5] Add delayed hook input detection See InputContinuous, stores a hook input and generates input events for a few frames, so the player can hook on a target even if they miss it by a small margin --- prototypes/1.hook-movement/Hook.gd | 31 +++++++++++++++---- prototypes/1.hook-movement/InputContinuous.gd | 30 ++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 prototypes/1.hook-movement/InputContinuous.gd diff --git a/prototypes/1.hook-movement/Hook.gd b/prototypes/1.hook-movement/Hook.gd index 9a7efcd..204f0b3 100644 --- a/prototypes/1.hook-movement/Hook.gd +++ b/prototypes/1.hook-movement/Hook.gd @@ -23,11 +23,7 @@ var aim_mode: = false setget set_aim_mode func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("hook") and _can_hook(): - cooldown.start() - arrow.hook_position = snap_detector.target.global_position if snap_detector.target else ray.get_collision_point() - if aim_mode: - self.aim_mode = false - emit_signal("hooked_onto_target", _get_hook_position()) + _hook() get_tree().set_input_as_handled() if event.is_action_pressed("aim"): @@ -35,6 +31,14 @@ func _unhandled_input(event: InputEvent) -> void: get_tree().set_input_as_handled() +func _hook() -> void: + cooldown.start() + arrow.hook_position = snap_detector.target.global_position if snap_detector.target else ray.get_collision_point() + if aim_mode: + self.aim_mode = false + emit_signal("hooked_onto_target", _get_hook_position()) + + func set_aim_mode(value:bool) -> void: aim_mode = value @@ -73,6 +77,21 @@ func _get_hook_position() -> Vector2: func _get_aim_direction() -> Vector2: match Settings.controls: Settings.GAMEPAD: - return ControlUtils.get_aim_joystick_direction() + return get_aim_joystick_direction() Settings.KBD_MOUSE, _: return (get_global_mouse_position() - global_position).normalized() + + +# FIXME +static func get_aim_joystick_direction() -> Vector2: + var use_right_stick: bool = ProjectSettings.get_setting('debug/testing/controls/use_right_stick') + + # FIXME: axes should be 2 and 3, or RX and RY, is there a calibration issue with the gamepad? + if use_right_stick: + return Vector2( + Input.get_joy_axis(0, JOY_AXIS_3), + Input.get_joy_axis(0, JOY_AXIS_4)).normalized() + else: + return Vector2( + Input.get_joy_axis(0, JOY_AXIS_0), + Input.get_joy_axis(0, JOY_AXIS_1)).normalized() diff --git a/prototypes/1.hook-movement/InputContinuous.gd b/prototypes/1.hook-movement/InputContinuous.gd new file mode 100644 index 0000000..99a1b77 --- /dev/null +++ b/prototypes/1.hook-movement/InputContinuous.gd @@ -0,0 +1,30 @@ +""" +Generates input events continuously based on a timer +Allows the player to hook onto a target even if they pressed the hook key before the hook was in range +""" +extends Node + +export var timer_duration: = 0.05 + +const ACTION_HOOK: = 'hook' + +var _action_names: = [] + +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed(ACTION_HOOK) and not ACTION_HOOK in _action_names: + _action_names.append(ACTION_HOOK) + _remove_delayed(_action_names.size() - 1, timer_duration) + + +func _physics_process(delta: float) -> void: + for action_name in _action_names: + var event: = InputEventAction.new() + event.action = action_name + event.pressed = true + Input.parse_input_event(event) + + +# Coroutine +func _remove_delayed(action_id:int, delay:float) -> void: + yield(get_tree().create_timer(delay), "timeout") + _action_names.remove(action_id)