redrc How are feet detecting ground now?
Character3D has is_on_floor() function.
redrc Have you tried 2D in Godot? It is highly recommended first project.
Im not interested in 2D
redrc Raycast is necessary in 3.x, but in 4.x character body has "Grounded" in inspector option.
This would solve something, maybe.. not sure.
redrc Honestly, I think forward K. is the hard way to go.
Not sure what you mean "hard way". Forward kinematics is how you animate character/mesh. In my case i use boths kinematics - FK and IK to animate the animation in blender and then export the animation to godot to use in animation_player to play.
Problem is that when i need to jump. In jump animation root bone stays ar 0,0,0 vector.
redrc I haven't yet tried Godot 3D , but (from Lightwave) I believe firmly if you can go IK, you can set up collision for the feet against the sloping ground, and apply usual physics. There are several IK videos for Godot online.
I dont need this. this is not the problems im having.
redrc Can you explain a bit about your rig setup?
I dont even know what you need to know. Its just a rig. Basically this setup:
But here only blender part is shown. As you export to godot, In my case, i export only deformation bones + root bone.
This plugin helps to rename and reparent existing rigify rig so that your bones stem from one root bone (which is a must in root motion).
Here is my problem. When im changing states from run/wak which are root motion, to jump-midair (falling) which is not root motion, there is a milisecond of root motion animation slipping away from the character because the root motion track is reset. And sometimes it glitches out and my character mesh moves further in scene on its own without the character body moving.

In my character script at the end of the
extends CharacterBody3D
class_name Player
@onready var player_camera: PlayerCamera = $PlayerCamera
@onready var coyote_timer: Timer = $CoyoteTimer
@export var anim_player:AnimationPlayer
@onready var debug_ui: DebugUI = $DebugUI
# For root motion
@onready var character_node: Node3D = $"mogjun-anim_1_human5finger"
@onready var state_machine: StateMachine = $StateMachine
@onready var input_gatherer : InputGatherer = InputGatherer.new()
var jump_velocity = 9
var coyote_time = 0.15
var jump_count: int = 0
var max_jump_count = 2
var is_sprinting = false
var is_root_motion = true
#--------- NEW ----------
var input_dir :Vector2
var direction :Vector3
var speed = 1.0
var acceleration = 3.0
var fps: float
var is_crouching = false
var is_ascending = false
var is_falling = false
var is_midair = false
var is_onwall = false
var is_attacking = false
var jump_request = false
var save_path = "user://savegame.save"
var player_tranny:Transform3D
var global_basis_wanted:Basis #= global_basis
var current_rotation
var default_root_motion_track = NodePath("Armature/Skeleton3D:root")
func _physics_process(delta: float) -> void:
var input_package:InputPackage = input_gatherer.gather_input()
if not is_on_floor():
is_midair = true
else:
is_midair = false
is_falling = velocity.y < 0 and !is_on_floor()
is_ascending = velocity.y > 0 and !is_on_floor()
is_onwall = is_on_wall()
input_dir = Input.get_vector("k_left", "k_right", "k_forward", "k_backward")
direction = (player_camera.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
var up := Vector3.UP
# For root motion
var forward := direction
var right := up.cross(forward).normalized()
var corrected_up := forward.cross(right).normalized()
var target_basis := Basis(right, corrected_up, forward).orthonormalized()
var a = transform.basis.get_rotation_quaternion()
var b = target_basis.get_rotation_quaternion()
var c = a.slerp(b,0.1)
transform.basis = Basis(c).orthonormalized()
else:
# For root motion
if is_root_motion:
var _velocity = get_quaternion() * anim_player.get_root_motion_position() / delta
set_velocity(_velocity)
else:
velocity.x = move_toward(velocity.x, 0.0, delta)
velocity.z = move_toward(velocity.z, 0.0, delta)
apply_weight(delta)
process_input(input_package)
func process_input(input_package:InputPackage):
var switch_state: State.Type = state_machine.current_state.switch_state(input_package)
if not state_machine.current_state.is_blocking:
if self.is_midair:
if state_machine.current_state.state_type != State.Type.JUMP_MIDAIR:
var new_input_pkg = input_gatherer.build_input_package(State.Type.JUMP_MIDAIR)
state_machine.change_state(new_input_pkg,state_machine.current_state)
return
if switch_state != State.Type._NONE:
var new_input_pkg = input_gatherer.build_input_package(switch_state)
state_machine.change_state(new_input_pkg,state_machine.current_state)
else:
if input_package.requested_state_type != state_machine.current_state.state_type:
state_machine.change_state(input_package,state_machine.current_state)
return
Not the cleanest code...
So basically when i switch states from Run to Jump because the character is_midair = True i set root motion to false.
func set_root_motion(_is_root_motion:bool):
if _is_root_motion:
anim_player.root_motion_track = default_root_motion_track
else:
anim_player.root_motion_track = NodePath("")
is_root_motion=_is_root_motion
This is my jump midair state:
extends State
var char_speed :float = 3.0
func _ready() -> void:
anim_name = "Jump_midair_90"
anim_blend = 0.1
state_type = Type.JUMP_MIDAIR
func enter(_previous_state:State=null) -> void:
previous_state = _previous_state
is_blocking = true
player.anim_player.play(anim_name,anim_blend)
player.set_root_motion(false)
print_debug("Jump_midair")
func physics_update(_delta:float) ->void:
if player.is_on_floor():
is_blocking = false
#player.move(player.speed,player.acceleration,_delta)
#player.move_root(_delta)
player.move_and_slide()
func anim_finished(_animation_name):
player.anim_player.animation_finished.disconnect(anim_finished)
is_blocking = false
func switch_state(_input_package:InputPackage) -> State.Type:
if not is_blocking:
return Type.JUMP_LANDED
return State.Type._NONE
On change state by statemachine, the new state runs enter() function.
Maybe i need to redo statemachine....?