Godot interaction system init

This commit is contained in:
2026-08-17 20:06:51 +03:00
commit a86839c8cd
53 changed files with 2134 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
extends Interactable
class_name Button3D
signal pressed(button: Button3D)
signal held(button: Button3D)
signal released(button: Button3D)
@export var pressDepth: float = 0.05
@export var pressSpeed: float = 10.0
@export var autoReset: bool = true
@export var buttonMesh: MeshInstance3D
@export var holdMode: bool = false
@export var pressArea: Area3D
var isPressed: bool = false
var offset: float = 0.0
var pressing_body: Node3D = null
func _ready() -> void:
if pressArea:
pressArea.monitoring = true
pressArea.monitorable = true
func beginInteract(player, hitPosition: Vector3):
if holdMode:
isPressed = true
offset = pressDepth
emit_signal("pressed", self)
else:
if isPressed:
return
isPressed = true
offset = pressDepth
emit_signal("pressed", self)
func updateInteract(player):
if holdMode and isPressed:
emit_signal("held", self)
func endInteract(player):
if holdMode:
isPressed = false
offset = 0.0
emit_signal("released", self)
else:
if autoReset:
offset = 0.0
func _physics_process(delta):
var target = offset
var current = buttonMesh.position.z
var newZ = lerp(current, -target, delta * pressSpeed)
buttonMesh.position.z = newZ
if autoReset and isPressed and abs(newZ) < 0.001:
isPressed = false
#func _physics_process(delta):
## Detect rigidbody overlap
#var bodies = pressArea.get_overlapping_bodies()
#
#if bodies.size() > 0:
#var body = bodies[0]
#
#if pressing_body == null:
#pressing_body = body
#isPressed = true
#offset = pressDepth
#emit_signal("pressed", self)
#else:
#if holdMode:
#emit_signal("held", self)
#else:
#if pressing_body != null:
#pressing_body = null
#if autoReset:
#offset = 0.0
#isPressed = false
#emit_signal("released", self)
#
## Animate button
#var target = offset
#var current = buttonMesh.position.z
#var newZ = lerp(current, -target, delta * pressSpeed)
#buttonMesh.position.z = newZ
#
#if autoReset and isPressed and abs(newZ) < 0.001:
#isPressed = false
+1
View File
@@ -0,0 +1 @@
uid://diphc7baic3px
+17
View File
@@ -0,0 +1,17 @@
extends CanvasLayer
class_name Crosshair
@export var normalTexture: Texture2D
@export var hoverTexture: Texture2D
@onready var normal = $NormalCrosshair
@onready var hover = $HoverCrosshair
func _ready():
normal.texture = normalTexture
hover.texture = hoverTexture
hover.visible = false
func setHover(active: bool):
normal.visible = !active
hover.visible = active
+1
View File
@@ -0,0 +1 @@
uid://dd8ys8xg3q3sw
+135
View File
@@ -0,0 +1,135 @@
extends Interactable
class_name Door
@export var body: RigidBody3D
@export var springStrength: float = 200.0
@export var springDamping: float= 50.0
@export var maxGrabDistance: float = 6.0
@export var mouseForce: float = 0.02
@export var hinge: HingeJoint3D
@export var hingeAngularUpperLimit: float = 120
@export var hingeAngularLowerLimit: float = -120
@export var snapAngleThreshold: float = 1
@export var snapVelocityThreshold: float = 5.0
@export var mouseHorizontalFactor: float = 1.0
@export var mouseVerticalFactor: float = 1.0
var dragging: bool = false
var grabLocal: Vector3 = Vector3.ZERO
var handOffset: Vector3 = Vector3.ZERO
func _ready() -> void:
hinge.set_param(HingeJoint3D.PARAM_LIMIT_LOWER, deg_to_rad(hingeAngularLowerLimit))
hinge.set_param(HingeJoint3D.PARAM_LIMIT_UPPER, deg_to_rad(hingeAngularUpperLimit))
func _physics_process(_delta):
if dragging:
return
var angle = get_hinge_angle()
var ang_vel = get_hinge_ang_vel()
if abs(rad_to_deg(angle)) > snapAngleThreshold:
return
if abs(ang_vel) > snapVelocityThreshold:
return
# Strong damping near closed position
var damping = springDamping * 4.0
# PD controller torque toward angle 0
var torque = -angle * springStrength - ang_vel * damping
var axis = hinge.global_transform.basis.y
body.apply_torque(axis * torque)
func beginInteract(player, hitPosition: Vector3):
dragging = true
handOffset = Vector3.ZERO
grabLocal = body.to_local(hitPosition)
player.setMouseFactors(mouseHorizontalFactor, mouseVerticalFactor)
func updateInteract(player):
if !dragging:
return
var worldGrab = body.to_global(grabLocal)
handOffset += player.camera.global_basis.x * player.interactionMouseDelta.x * mouseForce
handOffset += -player.camera.global_basis.y * player.interactionMouseDelta.y * mouseForce
handOffset = handOffset.limit_length(0.3)
var targetHand = player.hand.global_position + handOffset
var error = targetHand - worldGrab
var handDistance = player.hand.global_position.distance_to(worldGrab)
if handDistance > maxGrabDistance:
player.stopInteraction()
return
var offset = worldGrab - body.global_position
var pointVelocity = body.linear_velocity + body.angular_velocity.cross(offset)
var hingeAxis = body.global_basis.y
var tangent = hingeAxis.cross(offset)
if tangent.length_squared() < 0.0001:
return
tangent = tangent.normalized()
var tangentError = tangent * error.dot(tangent)
var tangentVelocity = tangent * pointVelocity.dot(tangent)
var force = tangentError * springStrength - tangentVelocity * springDamping
body.apply_force(force, offset)
func endInteract(player):
dragging = false
handOffset = Vector3.ZERO
player.setMouseFactors(1.0, 1.0)
#func get_hinge_angle() -> float:
#var body_basis = body.global_transform.basis
#var hinge_basis = hinge.global_transform.basis
#
## Body forward vector in world space
#var forward = body_basis.z
#
## Convert forward vector into hinge local space
#var local_forward = hinge_basis.inverse() * forward
#
## Angle around hinge axis (Y)
#return atan2(local_forward.x, local_forward.z)
func get_hinge_angle() -> float:
var hinge_axis = hinge.global_transform.basis.y.normalized()
# Door forward direction
var fwd = body.global_transform.basis.z.normalized()
# Reference forward direction (door closed direction)
var ref = hinge.global_transform.basis.z.normalized()
# Remove hinge axis component (project onto hinge plane)
var fwd_proj = (fwd - hinge_axis * fwd.dot(hinge_axis)).normalized()
var ref_proj = (ref - hinge_axis * ref.dot(hinge_axis)).normalized()
# Signed angle
var angle = atan2(
hinge_axis.dot(ref_proj.cross(fwd_proj)),
ref_proj.dot(fwd_proj)
)
return angle
func get_hinge_ang_vel() -> float:
var axis = hinge.global_transform.basis.y.normalized()
return body.angular_velocity.dot(axis)
+1
View File
@@ -0,0 +1 @@
uid://cyw8ng7alo12e
+19
View File
@@ -0,0 +1,19 @@
extends Node3D
class_name Interactable
static func find(node: Node) -> Interactable:
while node:
if node is Interactable:
return node
node = node.get_parent()
return null
func beginInteract(player, hitPosition: Vector3):
pass
func updateInteract(player):
pass
func endInteract(player):
pass
+1
View File
@@ -0,0 +1 @@
uid://boc1b3v02ssn5
+107
View File
@@ -0,0 +1,107 @@
extends Interactable
class_name Lever
@export var body: RigidBody3D
@export var hinge: HingeJoint3D
@export var angleOn: float = -45.0
@export var angleOff: float = 0.0
@export var snapStrength: float = 200.0
@export var maxGrabDistance: float = 4.0
@export var mouseForce: float = 0.015
var dragging := false
var grabLocal := Vector3.ZERO
var handOffset := Vector3.ZERO
var targetAngle := 0.0
func _ready():
hinge.set_param(HingeJoint3D.PARAM_LIMIT_LOWER, deg_to_rad(angleOn))
hinge.set_param(HingeJoint3D.PARAM_LIMIT_UPPER, deg_to_rad(angleOff))
targetAngle = angleOff
func _physics_process(delta):
if dragging:
return
var angle = rad_to_deg(get_hinge_angle())
# Snap to nearest end
var mid = (angleOn + angleOff) * 0.5
if angle < mid:
targetAngle = angleOn
else:
targetAngle = angleOff
var angleError = deg_to_rad(targetAngle) - get_hinge_angle()
# HARD SNAP — no spring
var axis = hinge.global_transform.basis.x
var torque = snapStrength * sign(angleError)
body.apply_torque(axis * torque)
func beginInteract(player, hitPosition: Vector3):
dragging = true
handOffset = Vector3.ZERO
grabLocal = body.to_local(hitPosition)
func updateInteract(player):
if !dragging:
return
var worldGrab = body.to_global(grabLocal)
# Only vertical mouse movement (Y)
handOffset += -player.camera.global_basis.y * player.interactionMouseDelta.y * mouseForce
handOffset = handOffset.limit_length(0.25)
var targetHand = player.hand.global_position + handOffset
var error = targetHand - worldGrab
var handDistance = player.hand.global_position.distance_to(worldGrab)
if handDistance > maxGrabDistance:
endInteract(player)
player.dragging = false
return
var offset = worldGrab - body.global_position
var pointVelocity = body.linear_velocity + body.angular_velocity.cross(offset)
# Correct hinge axis (X)
var hingeAxis = body.global_basis.x
var tangent = hingeAxis.cross(offset)
if tangent.length_squared() < 0.0001:
return
tangent = tangent.normalized()
var tangentError = tangent * error.dot(tangent)
var tangentVelocity = tangent * pointVelocity.dot(tangent)
var force = tangentError * snapStrength
body.apply_force(force, offset)
func endInteract(player):
dragging = false
handOffset = Vector3.ZERO
func get_hinge_angle() -> float:
var body_basis = body.global_transform.basis
var hinge_basis = hinge.global_transform.basis
# X-axis rotation → use UP vector
var up = body_basis.y
var local_up = hinge_basis.inverse() * up
return atan2(local_up.z, local_up.y)
func get_hinge_ang_vel() -> float:
var axis = hinge.global_transform.basis.x
return body.angular_velocity.dot(axis)
+1
View File
@@ -0,0 +1 @@
uid://2vyt44um2tty
+97
View File
@@ -0,0 +1,97 @@
extends Interactable
class_name Pickupable
@export var springStrength: float = 150.0
@export var springDamping: float = 50.0
@export var rotateStrength: float = 5.0
@export var rotateDamping: float = 5.0
@export var throwForce: float = 10.0
@export var maxDistance: float = 4.0
@export var body: RigidBody3D
@export var snapAnchor: Marker3D
@export_range(0.01, 1000.0, 0.01) var bodyMass: float = 1.0
var dragging: bool = false
var rotating: bool = false
var playerRef
var grabLocal: Vector3
var holdDistance: float
var rotationOffset: Basis
func _ready():
if body:
body.mass = bodyMass
func beginInteract(player, hitPosition: Vector3):
playerRef = player
dragging = true
grabLocal = body.to_local(hitPosition)
var holdPoint = player.hand
holdDistance = holdPoint.global_position.distance_to(hitPosition)
rotationOffset = holdPoint.global_basis.inverse() * body.global_basis
func updateInteract(player):
if !dragging:
return
# Toggle rotation mode
if Input.is_action_just_pressed("rotate"):
rotating = true
playerRef.setMouseFactors(0.0, 0.0)
if Input.is_action_just_released("rotate"):
rotating = false
playerRef.setMouseFactors(1.0, 1.0)
var holdPoint = player.hand
# Release if too far away
if body.global_position.distance_to(player.global_position) > maxDistance:
endInteract(player)
return
# Target position
var targetPosition = holdPoint.global_position + (-holdPoint.global_basis.z * holdDistance)
var offset = targetPosition - body.global_position
# Linear spring
body.apply_force(offset * springStrength - body.linear_velocity * springDamping, body.to_global(grabLocal) - body.global_position)
if rotating:
var delta = player.interactionMouseDelta
rotationOffset = Basis(Vector3.UP, deg_to_rad(-delta.x * 0.35)) * rotationOffset
rotationOffset = Basis(Vector3.RIGHT, deg_to_rad(delta.y * 0.35)) * rotationOffset
# Angular spring
var targetBasis = holdPoint.global_basis * rotationOffset
var currentQuat = body.global_basis.get_rotation_quaternion()
var targetQuat = targetBasis.get_rotation_quaternion()
var diff = targetQuat * currentQuat.inverse()
if diff.dot(Quaternion.IDENTITY) < 0:
diff = -diff
var angle = diff.get_angle()
if angle > 0.001:
body.apply_torque(diff.get_axis() * angle * rotateStrength - body.angular_velocity * rotateDamping)
func endInteract(player):
dragging = false
rotating = false
player.setMouseFactors(1.0, 1.0)
playerRef = null
func throw():
if !dragging:
return
var direction = -playerRef.camera.global_basis.z
body.apply_impulse(direction * throwForce)
+1
View File
@@ -0,0 +1 @@
uid://b143brnl7gf4f
+143
View File
@@ -0,0 +1,143 @@
extends CharacterBody3D
@export var speed: float = 6.0
@export var jumpVelocity: float = 5.0
@export var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") as float
@export var acceleration: float = 20.0
@export var airAcceleration: float = 4.0
@export var friction: float = 10.0
var mouseBaseSensitivity: float = 0.004
var mouseSensitivity: float = 0.0
@export var mouseSensitivityMultiplier: float = 0.2
@export var head: Node3D
@export var camera: Camera3D
@export var raycast: RayCast3D
@export var hand: Marker3D
@export var crosshair: CanvasLayer
var lateJumpTime: float = 0.15
var lateJumpTimer: float = 0.0
var currentInteractable: Interactable
var dragging: bool = false
var pitch: float = 0.0
var playerId: String
var displayName: String
var mouseDelta: Vector2 = Vector2.ZERO
var interactionMouseDelta: Vector2 = Vector2.ZERO
var mouseHorizontalFactor: float = 1.0
var mouseVerticalFactor: float = 1.0
var targetMouseHorizontalFactor: float = 1.0
var targetMouseVerticalFactor: float = 1.0
@export var mouseFactorLerpSpeed: float = 2.0
func _ready():
mouseSensitivity = mouseBaseSensitivity * mouseSensitivityMultiplier
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
return
if event is InputEventMouseMotion:
if dragging and currentInteractable:
interactionMouseDelta += event.relative
mouseDelta += event.relative
func _physics_process(delta):
mouseHorizontalFactor = lerp(mouseHorizontalFactor, targetMouseHorizontalFactor, delta * mouseFactorLerpSpeed)
mouseVerticalFactor = lerp(mouseVerticalFactor, targetMouseVerticalFactor, delta * mouseFactorLerpSpeed)
if mouseDelta != Vector2.ZERO:
rotate_y(-mouseDelta.x * mouseSensitivity * mouseHorizontalFactor)
pitch -= mouseDelta.y * mouseSensitivity * mouseVerticalFactor
pitch = clamp(pitch, deg_to_rad(-90), deg_to_rad(90))
head.rotation.x = pitch
mouseDelta = Vector2.ZERO
if !is_on_floor():
velocity.y -= gravity * delta
lateJumpTimer -= delta
else:
lateJumpTimer = lateJumpTime
# Interaction search
if !dragging:
if raycast.is_colliding():
currentInteractable = Interactable.find(raycast.get_collider())
crosshair.setHover(true)
else:
currentInteractable = null
crosshair.setHover(false)
# Start interaction
if !dragging and currentInteractable:
if Input.is_action_just_pressed("interact"):
dragging = true
currentInteractable.beginInteract(self, raycast.get_collision_point())
# Update interaction
if dragging and currentInteractable:
if Input.is_action_just_pressed("throw"):
if currentInteractable is Pickupable:
currentInteractable.throw()
stopInteraction()
return
currentInteractable.updateInteract(self)
interactionMouseDelta = Vector2.ZERO
# End interaction
if dragging and currentInteractable:
if Input.is_action_just_released("interact"):
stopInteraction()
# Late jump buffering
if Input.is_action_just_pressed("jump") and lateJumpTimer > 0.0:
velocity.y = jumpVelocity
# Movement
var inputDir := Input.get_vector("left", "right", "forward", "backward")
var direction := (transform.basis * Vector3(inputDir.x, 0, inputDir.y)).normalized()
if is_on_floor():
if direction != Vector3.ZERO:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = lerp(velocity.x, 0.0, delta * friction)
velocity.z = lerp(velocity.z, 0.0, delta * friction)
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * airAcceleration)
velocity.z = lerp(velocity.z, direction.z * speed, delta * airAcceleration)
move_and_slide()
func setMouseFactors(horizontal: float, vertical: float):
targetMouseHorizontalFactor = clamp(horizontal, 0.0, 1.0)
targetMouseVerticalFactor = clamp(vertical, 0.0, 1.0)
func stopInteraction():
if currentInteractable:
currentInteractable.endInteract(self)
currentInteractable = null
dragging = false
interactionMouseDelta = Vector2.ZERO
+1
View File
@@ -0,0 +1 @@
uid://c1r1jpfh3vsn2
+52
View File
@@ -0,0 +1,52 @@
class_name Socket
extends Node3D
@export var snapPoint: Marker3D
@export var snapDistance: float = 0.5
@export var lockOnSnap: bool = true
var candidate: Pickupable = null
var snappedObject: Pickupable = null
func is_in_range(object: Pickupable) -> bool:
if object == null:
return false
if object.snapAnchor == null:
return false
var distance := object.snapAnchor.global_position.distance_to(
snapPoint.global_position
)
return distance <= snapDistance
func getSnapTransform(object: Pickupable) -> Transform3D:
var objectTransform := object.body.global_transform
var anchorTransform := object.snapAnchor.global_transform
# Where is the SnapAnchor relative to the Rigidbody?
var anchorOffset := objectTransform.affine_inverse() * anchorTransform
# We want the SnapAnchor to have exactly the same
# transform as the Socket's SnapPoint.
return snapPoint.global_transform * anchorOffset.affine_inverse()
func snapObject(object: Pickupable):
if not is_in_range(object):
return
var targetTransform := getSnapTransform(object)
var body := object.body
body.linear_velocity = Vector3.ZERO
body.angular_velocity = Vector3.ZERO
body.global_transform = targetTransform
if lockOnSnap:
body.freeze = true
snappedObject = object
candidate = null
+1
View File
@@ -0,0 +1 @@
uid://xd03vepkva1l
+132
View File
@@ -0,0 +1,132 @@
extends AudioStreamPlayer3D
class_name SpatialAudioPlayer
@export var maxRaycastDistance: float = 30.0
@export var updateFrequencySeconds: float = 0.5
@export var maxReverbWetness: float = 0.5
@export var wallLowpassCutoffAmount: int = 600
var raycastArray: Array = []
var distanceArray: Array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
var lastUpdateTime: float = 0.0
var updateDistances: bool = true
var currentRaycastIndex: int = 0
var audioBusIndex = null
var audioBusName: String = ""
var reverbEffect: AudioEffectReverb
var lowpassFilterEffect: AudioEffectLowPassFilter
var targetLowpassCutoff: float = 20000
var targetReverbRoomSize: float = 0.0
var targetReverbWetness: float = 0.0
var targetVolumeDb: float = 0.0
func _ready() -> void:
audioBusIndex = AudioServer.bus_count
audioBusName = "SpatialBus#"+str(audioBusIndex)
AudioServer.add_bus(audioBusIndex)
AudioServer.set_bus_name(audioBusIndex, audioBusName)
AudioServer.set_bus_send(audioBusIndex, bus)
self.bus = audioBusName
AudioServer.add_bus_effect(audioBusIndex, AudioEffectReverb.new(), 0)
reverbEffect = AudioServer.get_bus_effect(audioBusIndex, 0)
AudioServer.add_bus_effect(audioBusIndex, AudioEffectLowPassFilter.new(), 1)
lowpassFilterEffect = AudioServer.get_bus_effect(audioBusIndex, 1)
targetVolumeDb = volume_db
volume_db = -60
$RayCastDown.target_position = Vector3(0, -maxRaycastDistance, 0)
$RayCastLeft.target_position = Vector3(maxRaycastDistance, 0, 0)
$RayCastRight.target_position = Vector3(-maxRaycastDistance, 0, 0)
$RayCastForward.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastForwardLeft.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastForwardRight.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastBackwardRight.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastBackwardLeft.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastBackward.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastUp.target_position = Vector3(0, maxRaycastDistance, 0)
raycastArray.append($RayCastDown)
raycastArray.append($RayCastLeft)
raycastArray.append($RayCastRight)
raycastArray.append($RayCastForward)
raycastArray.append($RayCastForwardLeft)
raycastArray.append($RayCastForwardRight)
raycastArray.append($RayCastBackwardRight)
raycastArray.append($RayCastBackwardLeft)
raycastArray.append($RayCastBackward)
raycastArray.append($RayCastUp)
play()
func onUpdateRaycastDistance(raycast: RayCast3D, raycastIndex: int) -> void:
raycast.force_raycast_update()
var collider = raycast.get_collider()
if collider != null:
distanceArray[raycastIndex] = self.global_position.distance_to(raycast.get_collision_point())
else:
distanceArray[raycastIndex] = -1
raycast.enabled = false
func onUpdateSpatialAudio(player: Node3D) -> void:
onUpdateReverb(player)
onUpdateLowpassFilter(player)
func onUpdateReverb(player: Node3D) -> void:
if reverbEffect != null:
var roomSize: float = 0.0
var wetness: float = 0.0
for distance in distanceArray:
if distance >= 0:
roomSize += (distance / maxRaycastDistance) / (float(distanceArray.size()))
roomSize = min(roomSize, 1.0)
else:
wetness -= 1.0 / float(distanceArray.size())
wetness = max(wetness, 0.0)
targetReverbWetness = wetness
targetReverbRoomSize = roomSize
func onUpdateLowpassFilter(player : Node3D):
if lowpassFilterEffect != null:
var query = PhysicsRayQueryParameters3D.create(self.global_position,self.global_position + (player.global_position - self.global_position).normalized() * maxRaycastDistance, $RayCastForward.get_collision_mask())
var spaceState = get_world_3d().direct_space_state
var result = spaceState.intersect_ray(query)
var lowpassCutoff = 20000
if !result.is_empty():
var rayDistance = self.global_position.distance_to(result["position"])
var distanceToPlayer = self.global_position.distance_to(player.global_position)
var wallToPlayerRatio = rayDistance / max(distanceToPlayer, 0.001)
if rayDistance < distanceToPlayer:
lowpassCutoff = wallLowpassCutoffAmount * wallToPlayerRatio
targetLowpassCutoff = lowpassCutoff
func lerpParameters(delta: float) -> void:
volume_db = lerp(volume_db, targetVolumeDb, delta)
lowpassFilterEffect.cutoff_hz = lerp(lowpassFilterEffect.cutoff_hz, targetLowpassCutoff, delta * 5.0)
reverbEffect.wet = lerp(reverbEffect.wet, targetReverbRoomSize * maxReverbWetness, delta * 5.0)
reverbEffect.room_size = lerp(reverbEffect.room_size, targetReverbRoomSize, delta * 5.0)
func _physics_process(delta: float) -> void:
lastUpdateTime += delta
if updateDistances:
onUpdateRaycastDistance(raycastArray[currentRaycastIndex], currentRaycastIndex)
currentRaycastIndex += 1
if currentRaycastIndex >= distanceArray.size():
currentRaycastIndex = 0
updateDistances = false
if lastUpdateTime > updateFrequencySeconds:
var playerCamera = get_viewport().get_camera_3d()
if playerCamera != null:
onUpdateSpatialAudio(playerCamera)
updateDistances = true
lastUpdateTime = 0.0
lerpParameters(delta)
print("Audio:", global_position)
print("Camera:", get_viewport().get_camera_3d().global_position)
+1
View File
@@ -0,0 +1 @@
uid://d07hgc6eewoup