Camera location and 3rd person movement

I have the movement for my project sorted (not complete though) and i have a camera that is 3rd person. The camera is parented to an empty and the empty has mouselook. The current control scheme is Global absolute but i would like for it to be Screen absolute (pushing up on controller makes you go forward respective to camera). This is something i wouldn’t even know where to begin with so i was hoping you masterminds could figure this out.NewAnimation.blend (4.0 MB)
The project is quite messy atm please excuse it.
Also thanks to anyone who helps (and have helped in the past)

I’m sorry, did somebody say quaternions?

I don’t have a controller handy to test your file properly. But if I’m not misunderstanding you want to align the player to the empty that the camera is pivoting around.

That you can bruteforce pretty easily.

from mathutils import Vector
own.worldOrientation = [0, 0, Vector(pivot.worldOrientation.to_euler()).z]

But that’s boring. Plus it looks horrible.

And of course, one problem you’re going to have here right away is the camera depends on the player through the parent hierarchy. That’s a hard no-no, all dissidents come at me. A lot of people do it like that including past me, but truth be told it is bad practice. The camera should depend on nothing but it’s pivot point(s). And the pivot is better off snapping itself to the player and moving a given offset every frame, than being a child of the player and being trapped in that kind of toxic relationship where it can only really move relative to him.

So, consider the following

from mathutils import Vector

pivot_offset = Vector([0,0,2]);

# - -- - -- - -- - -- -
# worldPosition snapping to avoid doing actual parenting

def fauxParenting(cont):
    own = cont.owner;
    own.worldPosition = own.scene.objects["Player"].worldPosition.copy() + pivot_offset;
    # - -- - -- - -- - -- -
    # if the camera rotated this frame, reset the interpolation timer    
    if own.sensors["Mouse"].positive:
        own["lerp_time"] = 0.0;

Well, now you can rotate the player without rotating the camera. So why not,

# assuming own == player

pivot  = own.scene.objects["Pivot"];
if pivot["lerp_time"] >= 0:

    camRot    = pivot.worldOrientation.to_quaternion();
    playerRot = own.worldOrientation.to_quaternion();

    factor = 0.05 + pivot["lerp_time"];
    if factor >= 1:
        factor = 1;
        pivot["lerp_time"] = -1;

    else: pivot["lerp_time"] += 0.005;

    aligned = Vector(playerRot.rotation_difference(camRot).to_euler()) * factor;

    # of course player would only rotate on Z, the way God intended
    aligned.xy = [0, 0];

    own.worldOrientation = Vector(playerRot.to_euler()) + aligned

Whoa, what’s going on? It’s easy. We take the difference between the two orientations, multiply it by our interpolation factor, then add it to our current orientation. It’s black magic. And it looks smooth, maan.

I don’t remember if there was a method to do this exact thing in fewer lines, perhaps a guru of the pythonic micro-optimization will save the day and inform us all of it’s existence. Meanwhile, this works well enough.

Here, have a blend file. FACING.blend (878.8 KB)


The player already moves freely without rotating the camera. I am unable to get your blend file working for some odd reason. I think the best way for me to explain it is like so…

camera facing X+
Up stickes makes the chr move forward along the X+

camera has rotated right by 90 degrees
the whole control scheme rotates 90.

You can see the cube under him right? That cube copies the chr location, then ad the co-ord of the controller to make it move in a direction relitive to chr. then chr points at cube. Im wondering if i can pivot this cube arount the chr when the camera moves.

PS. Thanks for the help. This has been stumping me for a while now

Should i make a keyboard compatible file? Might make things easier.

Oh oopsie, I made it in UPBGE which isn’t retrocompatible. Tell me which version of Blender you’re using and I’ll redo it. There’s minor differences between the APIs but the math is the same.

wow. i just tried to use upbg today funy enough. im in 2.79

I’m too lazy to type it all out again, but here is a link to a way I do it using logic bricks:

It works, but it is not the best solution (it is what I always do for a character like that)

My player uses a target box that is connected to a controller so this doesn’t work.

I might be able to apply the camera rotation (from the handle that guides the camera) after the controller is applied but because im using the track to actuator it just overwrites any changes i make to the z.

OK done it! i replaced my track to actuator with align axis to vector. then once the stick was above the set deadzone it would align axis then apply rotation for the handle that was controlling the camera. However for some strange reason im getting really bad performance now.

(pre formatted text doesn’t want to work, sorry)

import bge
from math import pi

cont = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()
own = cont.owner
self = scene.objects[]
sensor = cont.sensors[“Keyboard”]
walk = cont.actuators[‘Walk’]
Run = cont.actuators[‘Run’]
Nokey = cont.sensors[“Nokey”]
Idle = cont.actuators[“Idle”]
WalkBack = cont.actuators[“WalkBack”]
WalkTurnLeft = cont.actuators[“WalkTurnLeft”]
size = len(bge.logic.joysticks);
joy = None;
x, y ,z= 0, 0, 0

Handle = scene.objects[‘Handle’]
rx, ry, rz = Handle.worldOrientation.to_euler()

target = scene.objects [“StickCube”]
vec = target.worldPosition - own.worldPosition

for index in range(size):
joy = bge.logic.joysticks[index];

if joy != None: break;

if joy != None:
av = joy.axisValues
btns = joy.activeButtons
left_analog_x = av[0]
left_analog_y = av[1]
right_analog_x = av[2]
right_analog_y = av[3]

if av[0] > 0.5 or av[0] < -0.5 or av[1] > 0.5 or av[1] < -0.5:

    if left_analog_y < -0.5 or left_analog_y > 0.5:
        x = .016
    if left_analog_x < -0.5 or left_analog_x > 0.5:
        x = 0.016
  #  if left_analog_y < -0.2 and left_analog_x < -0.1:
   #     cont.activate(WalkTurnLeft)
    #    x = .016
     #   rz = left_analog_y

    if left_analog_y < -0.9 or left_analog_y > 0.9:
        x = 0.043
    if left_analog_x > 0.9 or left_analog_x < -0.9:
        x = 0.043

if left_analog_x > 0.05:


x = 0.043

if 0 in btns and len(btns) == 1:

print(“No controller”)

cont.owner.applyMovement([x, y, 0], True)

if Nokey.positive:



I will upload the full blend for anyone in the future who wants to see

I know isnt quite on topic but i didnt want to make a new thread, Im using this for camera control. How would i lock the Y axis GLOBAL not local. This makes the camera rotate in a super awkward manner.

import bge

cont = bge.logic.getCurrentController()
own = cont.owner

size = len(bge.logic.joysticks);
joy = None;

x = 0
y = 0

for index in range(size):
joy = bge.logic.joysticks[index];

if joy != None: break;

if joy != None:
av = joy.axisValues
btns = joy.activeButtons
x = av[3]
y = av[4]

if av[3] > 0.5 or av[3] < -0.5 or av[4] > 0.5 or av[4] < -0.5:
    if av[3] > 0.5 or av[3] < -0.5:
        setx = x
    if av[4] > 0.5 or av[4] < -0.5:
        sety = y
    own.applyRotation((y/10,0,x/10), True)


You solved it, then? Well done.

After all rotations are applied, assign a constant to the global y.

lock_y = 45;
rot = Vector(camera.worldOrientation.to_euler());
camera.worldOrientation = [rot.x, lock_y, rot.z];

Note: when posting code,

(your code here) { space over tabs; }

(your code here)             { space over tabs;                       }

or you can also

(your code here) { space over tabs; }

(your code here)             { space over tabs;                       }

You keep formatting that way. Makes things easier on the eyes.

1 Like