Basic platformer with rope

This commit is contained in:
ASOwnerYT
2026-07-25 16:10:50 +12:00
commit 4818002bbd
503 changed files with 16081 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
extends "res://addons/pinjoint-ropephysics/path_3d_rope.gd"
## Tether variant of the pinjoint rope.
##
## Adds two things the base addon does not do:
## - endpoints can be any PhysicsBody3D (the addon only exports RigidBody3D,
## and its `rigidbody_attached_to_start` path overwrites node_b instead of
## setting node_a, which pins the body to world space rather than to the rope)
## - every segment is locked to the X-Y plane so the rope behaves as a 2D rope
##
## Expects to be a direct child of an untransformed parent: the base addon bakes
## its own local `position` into the segment/joint positions and zeroes the node.
## Bodies the two rope ends pin to. NodePath rather than a typed node export so
## the value resolves reliably when set from a .tscn instance override.
@export var attach_start_path : NodePath
@export var attach_end_path : NodePath
var attach_start : PhysicsBody3D
var attach_end : PhysicsBody3D
## Extra rope length as a fraction of the gap between the two endpoints.
## 0.0 spawns the rope taut (pin joints will fight); ~0.15 gives a natural sag.
@export_range(0.0, 1.0, 0.01) var slack := 0.15
## Constrain segments to the X-Y plane (linear Z, angular X and Y).
@export var plane_lock_z := true
@export_flags_3d_physics var segment_collision_layer := 4
@export_flags_3d_physics var segment_collision_mask := 1
# The base script zeroes `position` and bakes it into child positions, so keep
# our own copy to place the end joint we add.
var _origin_offset := Vector3.ZERO
func _ready() -> void:
attach_start = get_node_or_null(attach_start_path) as PhysicsBody3D
attach_end = get_node_or_null(attach_end_path) as PhysicsBody3D
_origin_offset = position
_fit_curve_to_endpoints()
super()
_apply_plane_lock()
_wire_endpoints()
# Rebuild the curve as a sagging arc between the two endpoints so the rope
# always spans wherever the players actually spawn.
func _fit_curve_to_endpoints() -> void:
if attach_start == null or attach_end == null:
return
var a := to_local(attach_start.global_position)
var b := to_local(attach_end.global_position)
var span := a.distance_to(b)
if span < 0.001:
return
# Bow the spawn arc *upward*. A downward sag can start inside level geometry
# (a rope between two grounded players dips below the floor), and segments
# that spawn embedded tunnel straight through it. Starting high is always
# clear, and gravity drapes the rope into place within a few frames.
var mid := (a + b) * 0.5 + Vector3(0, span * slack, 0)
var fitted := Curve3D.new()
# The scene's curve ships with a huge bake_interval, which would make
# sample_baked() miss the midpoint entirely.
fitted.bake_interval = maxf(span / float(number_of_segments) * 0.25, 0.05)
fitted.add_point(a)
fitted.add_point(mid)
fitted.add_point(b)
curve = fitted
# `distance` is an @onready in the base script; recompute it explicitly so
# the value is correct regardless of when that initializer runs.
distance = curve.get_baked_length()
func _apply_plane_lock() -> void:
for segment in segments:
segment.collision_layer = segment_collision_layer
segment.collision_mask = segment_collision_mask
segment.continuous_cd = true
if plane_lock_z:
segment.axis_lock_linear_z = true
segment.axis_lock_angular_x = true
segment.axis_lock_angular_y = true
# The base script offsets look_at by (0.001, 0, -0.001) to dodge a
# degenerate up vector, which nudges segments off the plane.
segment.position.z = 0.0
func _wire_endpoints() -> void:
if attach_start != null:
# node_b first: assigning node_a while node_b is still segments[0] would
# briefly join the body to itself.
joints[0].node_b = attach_start.get_path()
joints[0].node_a = segments[0].get_path()
if attach_end != null and not fixed_end_point:
var end_joint := PinJoint3D.new()
add_child(end_joint)
end_joint.position = curve_points[-1] + _origin_offset
end_joint.node_a = segments[-1].get_path()
end_joint.node_b = attach_end.get_path()
end_joint.set_param(PinJoint3D.PARAM_BIAS, joint_bias_or_stiffness)
end_joint.set_param(PinJoint3D.PARAM_IMPULSE_CLAMP, max_impulse)
joints.append(end_joint)