How can you move inverse kinematics via code when the character turns?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Andrew Strauch

We have our player with cutout animation, and it uses inverse kinematics and bones for the limbs. Particularly, we are looking at the arms here.

We are attempting to add aiming to the gun. When facing to the right and aiming, it looks great. When facing to the left, however, it gets wonky and the arms don’t match up at all.

We are setting the parent Body sprite.scale.x to -1 when the player turns to the left. Tested it out in the editor and this works as expected.

For some reason, the rotations applied get messed up due to the flipping.

Code below:

var rotation_offsets
var transforms

Place player hands on Position2Ds on gun

func place_hand(base_node, ik_node, is_front):
	transforms = []
	rotation_offsets = []
	var target = front_hand_position if is_front else back_hand_position
	_calc_ik(base_node, ik_node, target, true)
	_apply_ik(base_node, ik_node)

calculate

  func _calc_ik(node, ik_node, target_node, is_forward, index = 0):
    	var length = 0
    	var target = null
    	transforms.append(null)
    	rotation_offsets.append(null)
    	if node.get_child_count() > 0 and node != ik_node:
    		target = node.get_child(0).global_position
    		rotation_offsets[index] = node.get_child(0).position.angle()
    		length = (target - node.global_position).length()
    		_calc_ik(node.get_child(0), ik_node, target_node, is_forward, index + 1)
    	if node == ik_node:
    		var rot = 0
    		var pos = target_node.global_position
    		transforms[index] = Transform2D(rot, pos)
    	else:
    		var rot = (transforms[index+1].get_origin() - node.global_position).angle()
    		var pos = (transforms[index+1].get_origin() - (Vector2.RIGHT * length).rotated(rot))
    		transforms[index] = Transform2D(rot, pos)

apply

func _apply_ik(node, ik_node, index = 0):
	if node != ik_node:
		var rotation_to_apply = transforms[index].get_rotation() - rotation_offsets[index]
		_apply_ik(node.get_child(0), ik_node, index + 1)
		node.global_rotation = rotation_to_apply

Hi!
First, i’m courious about is_forward variable… it seems to be always true, and never used… is reserved for future use?
Second, i think the problem might be when rotating pos:

var pos = (transforms[index+1].get_origin() - (Vector2.RIGHT * length).rotated(rot))

I made a (more simple) aiming animation here and i had to add 180 when rotating if flipped.
I didn’t go through all the ik chain, just rotated one part of the body, so idk if this apply to your case… but may be it helps.

Also, let me ask you if the gun is flipped with the body (as it is its child or something like that) or not… cause rotation in gun seems to work fine.

p7f | 2019-08-27 12:32

or perhaps you should change the Vector2.RIGHT when flipped.

p7f | 2019-08-27 12:33

Tried the vector.RIGHT, that’s just getting the normal though.

Here is a repo with the simplified version of the code that can be run in Godot. Left click for moving, and right click to process movement every tick. It mirrors the same IK chain.

enter link description here

Andrew Strauch | 2019-08-27 18:59

Did you ever get this figured out? I’m attempting to the exact same thing now - aiming a gun in my character’s hands using cutout animation, auto-gen bones, and IK. Any help you could give would be greatly appreciated.

dillydadally | 2021-10-11 05:03