Game Engine walk along surface

Hi there!

I somehow managed to write a script for WASD camera movement along the terrain surface.

Here is the blend if anyone interested: align.blend (2.74 MB)

The setup uses 4 objects:

-sonic_hitbox
-the camera
-dir, which takes the direction of the camera.
-and cam, which aligns to the normal of the surface.

It works like this:

depending of the keys you press, sonic creates 8 possible direction vectors, which is called vec.
Then, vec is multiplied by the matrix of dir, which allows for camera relative movement.
and finally, it is multiplied by the matrix of cam, which allows to move along the surface.

It works pretty well, but the problem comes when sonic is standing on an edge of the surface, then he starts flickering (like sonic 06 :spin:), and, for some reason, cam rotates weirdly to the world x axis, which screws up the controls very badly.

Also, it only happens in an outer curve, in the inner ones, (like the loop-de-loop), I haven’t gotten any problems so far.

To be able to see cam rotating, you should play on wireframe mode.

Thanks in advance.

Is there maybe, a bug with alignAxisToVect?

If the ray cast to align to the surface does not hit anything,
you dont get a surface normal, and will need to resort to a stored
normal or world axis etc.

As noted, alignAxisToVect has some artifacts. All that you need do is build an orientation matrix from the appropriate vectors - maintaining the current direction, and then use quaternion slerp to smoothly move between the current orientation and target.


from mathutils import Matrix, Quaternion, Vector




def _align_to_surface(obj, distance=2, slerp_factor=0.5):
	orientation = obj.worldOrientation
	z_direction = orientation * Vector((0, 0, 1))
	
	source = obj.worldPosition
	target = source - z_direction
	hit_obj, hit_pos, hit_nor = obj.rayCast(target, source, distance)
	
	if hit_obj is None:
		return
		
	x_direction = orientation * Vector((1, 0, 0))
	y_direction = x_direction.cross(hit_nor)
	
	new_orientation = Matrix.Identity(3)
	new_orientation.col[0] = x_direction
	new_orientation.col[1] = y_direction
	new_orientation.col[2] = hit_nor
	
	new_quaternion = new_orientation.to_quaternion()
	current_quaternion = orientation.to_quaternion()
	
	slerped_orientation = current_quaternion.slerp(new_quaternion, slerp_factor)
	obj.worldOrientation = slerped_orientation
	


def any_positive(cont):
	for sens in cont.sensors:
		if sens.positive:
			return True
	
	return False


	
def align_to_surface(cont):
	if not any_positive(cont):
		return 
		
	own = cont.owner
	_align_to_surface(own)

submit a bug report? have them remove the artifacts using your method?

besides the controls screwing up, how can i fix the annoying edge flickering?

Yay I did it!

to orientate the object i just had to calculate the angle between ray.hitNormal and the world Z axis, and then rotate to that angle by the cross product of those two.

ang = Vector(ray.hitNormal).angle(Vector([0,0,1]), 0)


mat = Matrix.Rotation(ang, 3, Vector([0,0,1]).cross(ray.hitNormal))

And for the flickering I had to snap the object back to the ray.hitPosition.

if ray.positive:
    
    own.alignAxisToVect(Vector,2,1)
    ali.worldOrientation = mat
    own.worldPosition = Vector(ray.hitPosition) + own.worldOrientation*Vector([0,0,(distance from origin to bottom)])

blend:align.blend (2.65 MB)