@tool class_name EntityMovement extends Node const FRICTION = 0.01 enum FacingDirection { SOUTH = 0, EAST = 1, NORTH = 2, WEST = 3 }; const FacingDirWalkAnimations = { FacingDirection.SOUTH: "walk_south", FacingDirection.EAST: "walk_east", FacingDirection.NORTH: "walk_north", FacingDirection.WEST: "walk_west" }; const FacingDirAngle = { FacingDirection.SOUTH: 0.0, FacingDirection.EAST: PI / 2, FacingDirection.NORTH: PI, FacingDirection.WEST: -PI / 2 }; var _inputDir:Vector2 = Vector2.ZERO var _facingDir:FacingDirection = FacingDirection.SOUTH var _running:bool = false @export var body:CharacterBody3D @export var rotate:Node3D @export var sprite:AnimatedSprite3D @export var walkSpeed:float = 48.0 @export var runSpeed:float = 64.0 @export var facingDir:FacingDirection = FacingDirection.SOUTH: set(value): _facingDir = value _updateSprite() get: return _facingDir # # Private Methods # func _updateSprite() -> void: if !sprite || sprite.animation == FacingDirWalkAnimations[facingDir]: return sprite.animation = FacingDirWalkAnimations[facingDir] func _applyFacingDir() -> void: if !sprite || _inputDir.length() <= 0.01: return if _inputDir.y > 0: if facingDir != FacingDirection.NORTH && _inputDir.x != 0: if _inputDir.x > 0 && facingDir == FacingDirection.EAST: facingDir = FacingDirection.EAST elif _inputDir.x < 0 && facingDir == FacingDirection.WEST: facingDir = FacingDirection.WEST else: facingDir = FacingDirection.NORTH else: facingDir = FacingDirection.NORTH elif _inputDir.y < 0: if facingDir != FacingDirection.SOUTH && _inputDir.x != 0: if _inputDir.x > 0 && facingDir == FacingDirection.EAST: facingDir = FacingDirection.EAST elif _inputDir.x < 0 && facingDir == FacingDirection.WEST: facingDir = FacingDirection.WEST else: facingDir = FacingDirection.SOUTH else: facingDir = FacingDirection.SOUTH elif _inputDir.x > 0: facingDir = FacingDirection.EAST else: facingDir = FacingDirection.WEST func _applyGravity() -> void: if !body.is_on_floor(): body.velocity += PHYSICS.GRAVITY * get_process_delta_time() func _applyMovement() -> void: if !canMove(): return var cameraCurrent = get_viewport().get_camera_3d() if !cameraCurrent: return # Use camera orientation for movement direction var camBasis = cameraCurrent.global_transform.basis # Forward and right vectors, ignore vertical component var forward = -camBasis.z forward.y = 0 forward = forward.normalized() var right = camBasis.x right.y = 0 right = right.normalized() var directionAdjusted = ( forward * _inputDir.y + right * _inputDir.x ).normalized() if directionAdjusted.length() <= 0.01: return if rotate: var targetRot = atan2(directionAdjusted.x, directionAdjusted.z) rotate.rotation.y = targetRot var speed = walkSpeed if _running: speed = runSpeed body.velocity.x = directionAdjusted.x * speed body.velocity.z = directionAdjusted.z * speed func _applyFriction(delta:float) -> void: body.velocity.x *= delta * FRICTION body.velocity.z *= delta * FRICTION # # Protected Methods # func canMove() -> bool: return true # # Callbacks # func _enter_tree() -> void: _updateSprite() func _physics_process(delta:float) -> void: if Engine.is_editor_hint(): return if !body: return _applyGravity() _applyFriction(delta) _applyMovement() _applyFacingDir() body.move_and_slide()