• Godot Help
  • Checking the right/left side of a character in 3D space

Hi, I'm trying to achieve the effect of having one character turn to look at another in 3D. Rather than just pointing them like a model, though, my characters are billboarded AnimatedSprite3Ds, so they'll simply flip to reflect whether the player character is on their left or right. Think Paper Mario, I suppose, but with a free-roaming camera. The characters always face the camera and only have side-facing sprites, so they flip horizontally to look left or right.

I'm new to Godot and 3D in general, so I might be missing something obvious about 3D transforms or dot matrices. I achieved this effect in a Unity project with C# fairly simply, but Godot handles transforms differently from what I've read. This is the functional code from the old Unity project:

Vector3 dir = (target.transform.position - gameObject.transform.position).normalized;
        float direction = Vector3.Dot(dir, gameObject.transform.right);
        if (direction < 0)
        {
            gameObject.transform.localScale = new Vector3(-1, 1, 1);
        }
        if (direction > 0)
        {
            gameObject.transform.localScale = new Vector3(1, 1, 1);
        }

I can't call "transform.right" the same way in GDScript, as far as I know. I'm no expert at this, so I've kind of been throwing things at the wall and seeing what sticks... but I'm close. I have this code at the moment, but it's working on the Z-axis. The character's sprite flips only when passing them from front to back instead of left to right. I'm not entirely sure why.

	var dir := Vector3.ZERO
	dir.x = sign(player.transform.origin.x - transform.origin.x)
	dir.z = sign(player.transform.origin.z - transform.origin.z)
	dir = dir.rotated(Vector3.UP, cam.rotation.y)
	var direction = dir.dot(transform.origin)
	if direction > 0:
		sprite.set_flip_h(false)
	elif direction < 0:
		sprite.set_flip_h(true)

"cam" references the camera and "sprite" references the AnimatedSprite3D

I'm sure there's an obvious explanation that I could figure out with more tinkering, but I thought I'd ask for help because understanding this problem should help me understand 3D better in the long run. I read the documentation - it's gotten me this far, but I'm stumped as of right now. Any advice is greatly appreciated. Thanks!

I'm not really sure what the code is doing, but there should be an easier way.

You can get the angle of the Spatial (a base classes of Sprite3D) using rotation which is a Vector3. Then maybe project that into camera space to get the screen rotation (using xform).

Then use dot to see where it is relative to the global UP vector (y-axis). If is 1.0, it is straight up, -1.0 will be down, and at the border of 0.0 will be either to the right or left.

var sprite_rot = cam.transform.xform(player.rotation)
var boundary = sprite_rot.dot(Vector3.UP)
sprite.set_flip_h(boundary < 0.0)

You might also replace Vector3.UP with Vector3.RIGHT, I'm not sure how your game is setup.

    cybereality Firstly, thanks for the tip on checking the condition in the set flip h call. Much cleaner.

    The problem with checking the player's rotation versus its position is that, since it's a billboarded sprite, it always keeps the same rotation as the NPC character, who is also billboarded. Your method managed to get the NPC to turn at certain camera angles, but isn't accounting for the player's position, which can move without the camera turning.

    I realize I may need to provide a better example, but I'm not at liberty to share screenshots of the project I'm working on, so I've made a crappy MS Paint mockup to help illustrate the effect I want.

    I want red guy to turn to face green guy wherever green guy is on the screen. This was achieved in Unity because I could use "gameObject.transform.right" which returned whatever the object's X-axis was relative to its rotation. I've read on old forum posts that transform.right was equal to "transform.TransformDirection(Vector3.right)" - I guess I'm searching for GDScript equivalents, or ways of recreating this calculation.

    My original method was an attempt at aping my movement code, which translates directional presses to the angle of the camera. This is slightly more complicated than static forward/back inputs, though, so I'm struggling.

      spacecloud Brilliant! I saw basis in the documentation but discarded it too quickly when it didn't work. The key I was missing was that, like I said, my rotation wasn't changing at all (it just stayed 0 as the sprite billboarded itself to the camera). In Unity, I achieved this through code rather than Godot's handy built-in feature, so in Unity it was already actually rotating the object to match the camera.

      So, after adding a line to actually turn my object, it works perfectly as it did in Unity.

      	look_at(cam.transform.origin, Vector3.UP)
      	var dir = player.transform.origin - transform.origin;
      	var direction = dir.dot(transform.basis.x)
      	sprite.set_flip_h(direction > 0.0)

      Thank you both for your help!

      Okay. I still don't understand the diagram or what you are doing, but I'm happy it works.