diff options
| -rw-r--r-- | player.gd | 251 | ||||
| -rw-r--r-- | ps_base.gd | 5 | ||||
| -rw-r--r-- | ps_base.gd.uid | 1 | ||||
| -rw-r--r-- | ps_curve.gd | 37 | ||||
| -rw-r--r-- | ps_curve.gd.uid | 1 | ||||
| -rw-r--r-- | ps_forced.gd | 46 | ||||
| -rw-r--r-- | ps_forced.gd.uid | 1 | ||||
| -rw-r--r-- | ps_hanging.gd | 90 | ||||
| -rw-r--r-- | ps_hanging.gd.uid | 1 | ||||
| -rw-r--r-- | ps_normal.gd | 64 | ||||
| -rw-r--r-- | ps_normal.gd.uid | 1 |
11 files changed, 266 insertions, 232 deletions
@@ -7,42 +7,9 @@ extends CharacterBody2D @export var jump_held_coef: float @export var hook_distance_max: float -## float (-1.0 .. 1.0) or null -var forced_input_x: Variant - -var curve_t: float = 0 -var curve_target: Curve2D -var curve_speed: float -var curve_dir: int - -var hanging_target: HookHanging -var hanging_t: float -var hanging_L: float -var hanging_theta0: float -var hanging_line2D: Line2D - -enum MovementType { - NORMAL, - FORCED, - CURVE, - HANG_TWEEN, - HANGING, -} -var movement_type = MovementType.NORMAL -var movement_handler: Callable = _movement_normal -var movement_handlers = { - MovementType.NORMAL: _movement_normal, - MovementType.FORCED: _movement_forced, - MovementType.CURVE: _movement_curve, -} +var state: PlayerState var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") -var prev_dir: float = 0.0 -var jump_was_released: bool = true - -# coyote = 0 indicates on floor -# coyote > 0 indicates seconds since on floor -var coyote: float = 0 # Called when the node enters the scene tree for the first time. func _ready() -> void: @@ -55,216 +22,36 @@ func get_facing() -> int: return 1 # Called every frame. 'delta' is the elapsed time since the previous frame. -func _process(_delta: float) -> void: - if velocity.x > 0: - $AnimatedSprite2D.flip_h = false - elif velocity.x < 0: - $AnimatedSprite2D.flip_h = true - - if not is_on_floor(): - $AnimatedSprite2D.set_animation("jump") - elif abs(velocity.x) > 0.0: - $AnimatedSprite2D.set_animation("walk") - else: - $AnimatedSprite2D.set_animation("idle") +func _process(delta: float) -> void: + state._handle(self, delta) func _physics_process(delta: float): - movement_handler.call(delta) + state._handle_physics(self, delta) #region movement type setters func set_movement_normal(): - movement_type = MovementType.NORMAL - movement_handler = _movement_normal + state = PlayerStateNormal.new() func set_movement_forced(dir: String): - if movement_type == MovementType.FORCED: return - - movement_type = MovementType.FORCED - movement_handler = _movement_forced - - var jump_coef = 1.3 - - if dir == "LEFT": - forced_input_x = -1.0 - elif dir == "JUMP_LEFT": - velocity.y = -jump_coef * jump_strength - forced_input_x = -1.0 - elif dir == "RIGHT": - forced_input_x = 1.0 - elif dir == "JUMP_RIGHT": - velocity.y = -jump_coef * jump_strength - forced_input_x = 1.0 - elif dir == "JUMP": - velocity.y = -jump_strength - forced_input_x = 0.0 - gravity = 0.0 - elif dir == "DROP": - forced_input_x = 0.0 - -func set_movement_curve(curve: Curve2D, speed: float, reverse: bool): - if movement_type == MovementType.CURVE: return - - movement_type = MovementType.CURVE - movement_handler = _movement_curve - - curve_target = curve - curve_speed = speed - if reverse: - curve_dir = -1 - curve_t = 1 - else: - curve_dir = 1 - curve_t = 0 - -func set_movement_hanging(target: Node2D, hang_dist: float): - if movement_type == MovementType.HANGING: return - - hanging_t = 0 - hanging_L = hang_dist - hanging_target = target - - # Add tongue line - hanging_line2D = Line2D.new() - hanging_line2D.width = 2 - hanging_line2D.add_point(Vector2.ZERO) - hanging_line2D.add_point(Vector2.ZERO) # set in movement handler - target.add_child(hanging_line2D) - - var hyp = target.global_position - global_position - var sign_x = sign(hyp.x) - var angle_below_horiz = 15 - var angle_below_horiz_r = deg_to_rad(angle_below_horiz) - var tweening_target_point = hanging_target.global_position \ - - Vector2(sign_x * hanging_L * cos(angle_below_horiz_r), - -hanging_L * sin(angle_below_horiz_r)) - - # if player is above the hook, we need to tween to horizontal - if tweening_target_point.y - global_position.y > 0: - movement_type = MovementType.HANG_TWEEN - movement_handler = _movement_hang_tween - return - - movement_type = MovementType.HANGING - movement_handler = _movement_hanging - - hanging_theta0 = atan2(-hyp.y, hyp.x) - PI/2 - if hanging_theta0 < -PI: hanging_theta0 += 2*PI - print_debug("hyp: ", hyp, ", hanging_theta0: ", hanging_theta0) -#endregion - -#region movement handlers -func _movement_normal(delta: float): - var grav_coef = 1.0 - if velocity.y < 0 and Input.is_action_pressed("jump"): - grav_coef = 1.0 + (jump_held_coef * velocity.y / jump_strength) + if state is PlayerStateForced: return - velocity.y += grav_coef * gravity * delta - if velocity.y > jump_strength: - velocity.y = jump_strength + var psf: PlayerStateForced = PlayerStateForced.new() + psf.initialize(self, dir) + state = psf - if is_on_floor(): - coyote = 0 - else: - coyote += delta +func set_movement_curve(curve: Curve2D, c_speed: float, c_reverse: bool): + if state is PlayerStateCurve: return - var can_jump = coyote < (coyote_max_ms * 0.001) and jump_was_released - if Input.is_action_pressed("jump") and can_jump: - velocity.y = -jump_strength - # Force coyote time to expire to forbid double jumps - coyote = coyote_max_ms - jump_was_released = false + var psc: PlayerStateCurve = PlayerStateCurve.new() + psc.initialize(self, curve, c_speed, c_reverse) + state = psc - if not Input.is_action_pressed("jump"): - jump_was_released = true - - if Input.is_action_just_pressed("action"): - handle_action_input() - - var direction: float - if Input.is_action_pressed("move_left") and Input.is_action_pressed("move_right"): - # This is a fix to the stalled movement issue - # where the player stops if both x inputs are pressed at the same time. - # To fix it, we store the previous direction the player was moving in, and change - # the direction when both buttons are held. This is what the player wants nearly always. - direction = -prev_dir - else: - direction = Input.get_axis("move_left", "move_right") - prev_dir = direction - - velocity.x = direction * speed - - move_and_slide() - -func _movement_forced(delta: float): - velocity.y += gravity * delta - if velocity.y > jump_strength: - velocity.y = jump_strength - - velocity.x = forced_input_x * speed - - move_and_slide() - -func _movement_curve(delta: float): - curve_t += curve_dir * curve_speed * delta - var finished: bool - if curve_dir == 1: - finished = curve_t >= 1.0 - else: - finished = curve_t <= 0.0 - - var new_position = curve_target.sample_baked(curve_t * curve_target.get_baked_length(), true) - velocity = (new_position - position) / delta - - if finished: - set_movement_normal() - curve_target = null - return - - position = new_position - -func _movement_hanging(delta: float): - hanging_t += delta - - if Input.is_action_just_pressed("jump"): - velocity.y = -jump_strength - # Force coyote time to expire to forbid double jumps - coyote = coyote_max_ms - jump_was_released = false - hanging_line2D.queue_free() - set_movement_normal() - - var omega = sqrt(gravity / hanging_L) - var theta = hanging_theta0 * cos(hanging_t * omega) - global_position.x = hanging_target.global_position.x + hanging_L * sin(theta) - global_position.y = hanging_target.global_position.y + hanging_L * cos(theta) - - hanging_line2D.points[1] = global_position - hanging_target.global_position - -func _movement_hang_tween(delta: float): - var tween_speed = jump_strength - var hyp = hanging_target.global_position - global_position - var sign_x = sign(hyp.x) - - var angle_below_horiz = 15 - var angle_below_horiz_r = deg_to_rad(angle_below_horiz) - var tweening_target_point = hanging_target.global_position \ - - Vector2(sign_x * hanging_L * cos(angle_below_horiz_r), - -hanging_L * sin(angle_below_horiz_r)) - - var tween_hyp = tweening_target_point - global_position - var tween_dir: Vector2 = tween_hyp.normalized() - var v: Vector2 = tween_dir * tween_speed - var s: Vector2 = v * delta +func set_movement_hanging(target: Node2D, hang_dist: float): + if state is PlayerStateHanging: return - if tween_hyp.length() < s.length(): - movement_type = MovementType.HANGING - movement_handler = _movement_hanging - hanging_theta0 = deg_to_rad(-sign_x * (90 - angle_below_horiz)) - else: - position += s - - hanging_line2D.points[1] = global_position - hanging_target.global_position - pass + var psh: PlayerStateHanging = PlayerStateHanging.new() + psh.initialize(self, target, hang_dist) + state = psh #endregion func handle_action_input(): diff --git a/ps_base.gd b/ps_base.gd new file mode 100644 index 0000000..32bcdfc --- /dev/null +++ b/ps_base.gd @@ -0,0 +1,5 @@ +@abstract class_name PlayerState +extends RefCounted + +@abstract func _handle(p: Player, delta: float) +@abstract func _handle_physics(p: Player, delta: float) diff --git a/ps_base.gd.uid b/ps_base.gd.uid new file mode 100644 index 0000000..2daf6bc --- /dev/null +++ b/ps_base.gd.uid @@ -0,0 +1 @@ +uid://y4ksf6fnhf68 diff --git a/ps_curve.gd b/ps_curve.gd new file mode 100644 index 0000000..bf903bd --- /dev/null +++ b/ps_curve.gd @@ -0,0 +1,37 @@ +class_name PlayerStateCurve +extends PlayerState + +var t: float = 0 +var target: Curve2D +var speed: float +var dir: int + +func initialize(_p: Player, c_target: Curve2D, c_speed: float, c_reverse: bool): + target = c_target + speed = c_speed + if c_reverse: + dir = -1 + t = 1 + else: + dir = 1 + t = 0 + +func _handle(_p: Player, _delta: float): + pass + +func _handle_physics(p: Player, delta: float): + t += dir * speed * delta + var finished: bool + if dir == 1: + finished = t >= 1.0 + else: + finished = t <= 0.0 + + var new_position = target.sample_baked(t * target.get_baked_length(), true) + p.velocity = (new_position - p.position) / delta + + if finished: + p.set_movement_normal() + return + + p.position = new_position diff --git a/ps_curve.gd.uid b/ps_curve.gd.uid new file mode 100644 index 0000000..2262bf0 --- /dev/null +++ b/ps_curve.gd.uid @@ -0,0 +1 @@ +uid://2fdqe6rj4lnq diff --git a/ps_forced.gd b/ps_forced.gd new file mode 100644 index 0000000..4c4c3dc --- /dev/null +++ b/ps_forced.gd @@ -0,0 +1,46 @@ +class_name PlayerStateForced +extends PlayerState + +var input_x: float +var jump_coef = 1.3 + +func initialize(p: Player, dir: String): + if dir == "LEFT": + input_x = -1.0 + elif dir == "JUMP_LEFT": + p.velocity.y = -jump_coef * p.jump_strength + input_x = -1.0 + elif dir == "RIGHT": + input_x = 1.0 + elif dir == "JUMP_RIGHT": + p.velocity.y = -jump_coef * p.jump_strength + input_x = 1.0 + elif dir == "JUMP": + p.velocity.y = -p.jump_strength + input_x = 0.0 + p.gravity = 0.0 + elif dir == "DROP": + input_x = 0.0 + +func _handle(p: Player, _delta: float): + var as2d = p.get_node("AnimatedSprite2D") + if p.velocity.x > 0: + as2d.flip_h = false + elif p.velocity.x < 0: + as2d.flip_h = true + + if not p.is_on_floor(): + as2d.set_animation("jump") + elif abs(p.velocity.x) > 0.0: + as2d.set_animation("walk") + else: + as2d.set_animation("idle") + +func _handle_physics(p: Player, delta: float): + p.velocity.y += p.gravity * delta + if p.velocity.y > p.jump_strength: + p.velocity.y = p.jump_strength + + p.velocity.x = input_x * p.speed + + p.move_and_slide() diff --git a/ps_forced.gd.uid b/ps_forced.gd.uid new file mode 100644 index 0000000..42ac8b6 --- /dev/null +++ b/ps_forced.gd.uid @@ -0,0 +1 @@ +uid://1lkv8h66oxmp diff --git a/ps_hanging.gd b/ps_hanging.gd new file mode 100644 index 0000000..3533348 --- /dev/null +++ b/ps_hanging.gd @@ -0,0 +1,90 @@ +class_name PlayerStateHanging +extends PlayerState + +var target: HookHanging +var t: float +var L: float +var theta0: float +var line2D: Line2D + +var tweening: bool + +func initialize(p: Player, h_target: HookHanging, h_dist: float): + t = 0 + L = h_dist + target = h_target + + # Add tongue line + line2D = Line2D.new() + line2D.width = 2 + line2D.add_point(Vector2.ZERO) + line2D.add_point(Vector2.ZERO) # set in movement handler + target.add_child(line2D) + + var hyp = target.global_position - p.global_position + var sign_x = sign(hyp.x) + var angle_below_horiz = 15 + var angle_below_horiz_r = deg_to_rad(angle_below_horiz) + var tweening_target_point = target.global_position \ + - Vector2(sign_x * L * cos(angle_below_horiz_r), + -L * sin(angle_below_horiz_r)) + + # if player is above the hook, we need to tween to horizontal + if tweening_target_point.y - p.global_position.y > 0: + tweening = true + return + + tweening = false + + theta0 = atan2(-hyp.y, hyp.x) - PI/2 + if theta0 < -PI: theta0 += 2*PI + print_debug("hyp: ", hyp, ", theta0: ", theta0) + +func _handle(_p: Player, _delta: float): + pass + +func _handle_physics(p: Player, delta: float): + if tweening: + _movement_hang_tween(p, delta) + else: + _movement_hanging(p, delta) + +func _movement_hanging(p: Player, delta: float): + t += delta + + if Input.is_action_just_pressed("jump"): + p.velocity.y = -p.jump_strength + line2D.queue_free() + p.set_movement_normal() + + var omega = sqrt(p.gravity / L) + var theta = theta0 * cos(t * omega) + p.global_position.x = target.global_position.x + L * sin(theta) + p.global_position.y = target.global_position.y + L * cos(theta) + + line2D.points[1] = p.global_position - target.global_position + +func _movement_hang_tween(p: Player, delta: float): + var tween_speed = p.jump_strength + var hyp = target.global_position - p.global_position + var sign_x = sign(hyp.x) + + var angle_below_horiz = 15 + var angle_below_horiz_r = deg_to_rad(angle_below_horiz) + var tweening_target_point = target.global_position \ + - Vector2(sign_x * L * cos(angle_below_horiz_r), + -L * sin(angle_below_horiz_r)) + + var tween_hyp = tweening_target_point - p.global_position + var tween_dir: Vector2 = tween_hyp.normalized() + var v: Vector2 = tween_dir * tween_speed + var s: Vector2 = v * delta + + if tween_hyp.length() < s.length(): + tweening = false + theta0 = deg_to_rad(-sign_x * (90 - angle_below_horiz)) + else: + p.position += s + + line2D.points[1] = p.global_position - target.global_position + pass diff --git a/ps_hanging.gd.uid b/ps_hanging.gd.uid new file mode 100644 index 0000000..36c2d60 --- /dev/null +++ b/ps_hanging.gd.uid @@ -0,0 +1 @@ +uid://82c7ylyq0b3u diff --git a/ps_normal.gd b/ps_normal.gd new file mode 100644 index 0000000..e0ca9b5 --- /dev/null +++ b/ps_normal.gd @@ -0,0 +1,64 @@ +class_name PlayerStateNormal +extends PlayerState + +var jump_was_released: bool = true +# coyote = 0 indicates on floor +# coyote > 0 indicates seconds since on floor +var coyote: float = 10000 +var prev_dir: float = 0.0 + +func _handle(p: Player, _delta: float): + var as2d = p.get_node("AnimatedSprite2D") + if p.velocity.x > 0: + as2d.flip_h = false + elif p.velocity.x < 0: + as2d.flip_h = true + + if not p.is_on_floor(): + as2d.set_animation("jump") + elif abs(p.velocity.x) > 0.0: + as2d.set_animation("walk") + else: + as2d.set_animation("idle") + +func _handle_physics(p: Player, delta: float): + var grav_coef = 1.0 + if p.velocity.y < 0 and Input.is_action_pressed("jump"): + grav_coef = 1.0 + (p.jump_held_coef * p.velocity.y / p.jump_strength) + + p.velocity.y += grav_coef * p.gravity * delta + if p.velocity.y > p.jump_strength: + p.velocity.y = p.jump_strength + + if p.is_on_floor(): + coyote = 0 + else: + coyote += delta + + var can_jump = coyote < (p.coyote_max_ms * 0.001) and jump_was_released + if Input.is_action_pressed("jump") and can_jump: + p.velocity.y = -p.jump_strength + # Force coyote time to expire to forbid double jumps + coyote = p.coyote_max_ms + jump_was_released = false + + if not Input.is_action_pressed("jump"): + jump_was_released = true + + if Input.is_action_just_pressed("action"): + p.handle_action_input() + + var direction: float + if Input.is_action_pressed("move_left") and Input.is_action_pressed("move_right"): + # This is a fix to the stalled movement issue + # where the player stops if both x inputs are pressed at the same time. + # To fix it, we store the previous direction the player was moving in, and change + # the direction when both buttons are held. This is what the player wants nearly always. + direction = -prev_dir + else: + direction = Input.get_axis("move_left", "move_right") + prev_dir = direction + + p.velocity.x = direction * p.speed + + p.move_and_slide() diff --git a/ps_normal.gd.uid b/ps_normal.gd.uid new file mode 100644 index 0000000..2534612 --- /dev/null +++ b/ps_normal.gd.uid @@ -0,0 +1 @@ +uid://cf1jdvgtqxg3q |
