0
$\begingroup$

I'm attempting to have a central turret object turn to point to a moving target. The turret is always in the center of the scene (un-moving). Using the Steering>Seek actuator is too fast and allows the turret to lock immediately to the target. I need to be able to have the turret slowly turn clockwise or counterclockwise (depending on which is shorter) at a set rotation speed until it eventually points to the target.

I found the getVectTo() Python function, which I can use to pull a local vector from the turret to the target. Unfortunately, I can't figure a way to use that vector to determine the shortest turning direction to reach the target; i.e. clockwise or counter-clockwise.

To clarify, the logic would look like this:

if shortestRotationAngleToTarget = Counterclockwise:
    rotateTurret(turretRotationStep)
else
    rotateTurret(-turretRotationStep)

EDIT: I've done more digging and tried to implement some of the suggestions provided so far. I believe I need to be using the .angleSigned() method applied to the turret's currently facing angle, but can't figure out how to get it working. The following screen shot shows the testing scene I'm using to figure this out. I am attempting to get the output of +90.0 returned where the error occurs.

I've posted the Blend here: GetVectTo.blend

Screenshot

Thanks for any suggestions!

$\endgroup$
0
+50
$\begingroup$

I downloaded your blend and imports mathutils Vector and math, angule_signed need a 2d vector while angle a 3d. Output (obj -- target):

enter image description here

Output changing target position (target -- obj)

enter image description here

Note that 2d would give negative indicating where you should turn.

Script:

import bge
from mathutils import Vector
import math
# get the controller
controller = bge.logic.getCurrentController()

# get game object controller attached to
obj = controller.owner

# get the current scene
scene = bge.logic.getCurrentScene()

# get a list of the objects in the scene
objList = scene.objects

# get game object named "Box"
box = objList["Target"]


# Get current facing direction of turret
currently_facing2d = Vector((obj.getAxisVect([0.0, 1.0,0.0]).xy))
currently_facing3d = obj.getAxisVect([0.0, 1.0,0.0])
print("currently_facing2d: ", currently_facing2d)
print("currently_facing3d: ", currently_facing3d)


# get the vector to target
vectTo2d = Vector((obj.getVectTo(box)[1].xy))
vectTo3d = obj.getVectTo(box)[1]
print("vectTo: ", vectTo2d)
print("vectTo: ", vectTo3d)

# Compute angle between turret facing direction and target
angle_between_facing_and_target2d = currently_facing2d.angle_signed(vectTo2d, None)
angle_between_facing_and_target3d = currently_facing3d.angle(vectTo3d, None)
print("Angle Between 2D(radians): ", angle_between_facing_and_target2d)
print("Angle Between 2D(degrees): ", 180 * angle_between_facing_and_target2d / math.pi)
print("Angle Between 3D(radians): ", angle_between_facing_and_target3d)
print("Angle Between 3D(degrees): ", 180 * angle_between_facing_and_target3d / math.pi)

Implementing rotation

Script:

from bge import logic
from random import choice
from mathutils import Vector
import math
from time import time

def change_objective():
    try:
        logic.objective = choice([obj for obj in logic.getCurrentScene().objects if 'enemy' in obj.getPropertyNames()])
    except Exception as e:
        add_enemies()

def add_enemies():
    for obj in logic.spawn_list:
        logic.getCurrentScene().addObject('enemy',obj)

if not hasattr(logic, 'spawn_list'):
    logic.spawn_list = [obj for obj in logic.getCurrentScene().objects if 'spawn_point' in obj.getPropertyNames()]
    add_enemies()
    logic.objective = None
    logic.time = 0

try:
    obj = logic.getCurrentController().owner
    currently_facing2d = Vector((obj.getAxisVect([0.0, 1,0.0]).xy))
    vectTo2d = Vector((obj.getVectTo(logic.objective)[1].xy))
    angle_between_facing_and_target2d = currently_facing2d.angle_signed(vectTo2d, None)
    rotation_speed = 0.03
    rotation = 180 * obj.localOrientation.to_euler().z / math.pi
    angle_to_rotate = 180 * angle_between_facing_and_target2d / math.pi
    angle_distance = rotation - angle_to_rotate

    if not round(rotation,4) == round(angle_to_rotate,4):
        if angle_to_rotate > 0:
            obj.applyRotation([0,0, -rotation_speed],False)
        else:
            obj.applyRotation([0,0, rotation_speed],False)
    else:
        obj.applyRotation([0,0,0],False)
except Exception as e:
    print('error')

if logic.objective is None:
    change_objective()

if logic.getCurrentController().sensors['Ray'].hitObject and logic.getCurrentController().sensors['Ray'].hitObject is logic.objective:
    logic.getCurrentController().sensors['Ray'].hitObject.endObject()
    logic.objective = None

$\endgroup$
  • $\begingroup$ This is the solution I needed! Thank you for putting so much effort into your answer! Cheers! $\endgroup$ – D. Waschow Nov 2 '17 at 12:15
0
$\begingroup$

Use a Edit Object actuator > Track to instead of Steering actutator.

KX_TrackToActuator Change the time to meet your requeriments of rotation speed time = 0 = instant track to. Between 10 and 30 feels ok.

I made a demo:

enter image description here

Script:

from bge import logic
from random import choice

def change_objective():
    try:
        return choice([obj for obj in logic.getCurrentScene().objects if 'enemy' in obj.getPropertyNames()])
    except Exception as e:
        add_enemies()

def add_enemies():
    for obj in logic.spawn_list:
        logic.getCurrentScene().addObject('enemy',choice(logic.spawn_list))

if not hasattr(logic, 'spawn_list'):
    logic.spawn_list = [obj for obj in logic.getCurrentScene().objects if 'spawn_point' in obj.getPropertyNames()]
    add_enemies()

if logic.getCurrentController().actuators['track'].object is None:
    logic.getCurrentController().actuators['track'].object = change_objective()
    logic.getCurrentController().activate(logic.getCurrentController().actuators['track'])

if logic.getCurrentController().sensors['Ray'].hitObject:
    logic.getCurrentController().sensors['Ray'].hitObject.endObject()
    logic.getCurrentController().actuators['track'].object = None

$\endgroup$
  • $\begingroup$ Unfortunately, your solution doesn't meet the criteria that the turret turns at a set speed. Looking at your demo, the turret eases in/out of its rotation. Thanks for the idea, though. $\endgroup$ – D. Waschow Oct 30 '17 at 12:14
0
$\begingroup$

The trick is to compare angles with the two vectors. While I don't have a full worked solution available, something like the following should set you on the right path:

ROT_SPEED = 0.1  # Radians per frame. 

player = some_game_object_that_turns_to_face_enemy
enemy = some_other_game_object_or_point_in_3d_space

_, to_target, _ = player.getVectTo(enemy)  # Get direction to enemy in worldSpace
current_facing = player.getAxisVect([0, 1, 0])  # Y axis is "forwards"

to_target_2d = to_target.xy
current_facing_2d = current_facing.xy

angle_difference = current_facing_2d.angle_signed(to_target_2d)

# Clamp the rotation speed
rotation_speed = min(max(angle_difference, -ROT_SPEED), ROT_SPEED) 

# Make object rotate
player.applyRotation([0, 0, rotation_speed])

This is for 2d - projected down from the top (ie it will only rotate around the Z axis. For 3D tracking, you need to use rotation matrices - but it's essentially the same process as above.

If it's a physics object, replace the applyRotation with setting the angular velocity (player.worldAngularVelocity = [0, 0, rotation_speed])

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.