Custom RotateTowards Ability for UCC AI Agent

wuche

Member
Hi,

When applying the basic RotateTowards task on a Third Person Controller-powered AI Agent, it doesn't do anything because the rotation of the agent is controlled by TPC. I asked Justin on Discord on how to get around this, and he responded that a new custom UCC ability that rotates the agent that could then be used as a task in a behavior tree should do the trick.

I wanted to post this question here so that others who have or might run into this same issue can look back on this for guidance.

I'm currently coding the new ability and am having trouble figuring out how to accomplish this. My goal is to have the agent rotate towards a given Target GameObject at a variable rotation speed whenever this ability is called upon, until it is completely facing / looking at the target.

Is there some sort of method that's part of UCC's API that I should be calling upon / overriding in order to manually apply rotation? I toyed around with overriding ApplyRotation but wasn't able to get my desired effect. Or should I be manually applying a Quaternion Slerp on the Agent's transform?

Any sort of help would be greatly appreciated. Loving Behavior Designer and Third Person Controller so far, and can tell that it'll be very powerful once I get through some of these newbie hurdles.

Thanks!
 
You should be able to override the UpdateRotation method. With this you can then assign a DeltaYawRotation which then rotates the character (assuming you are not using root motion rotation). This is actually really similar to the Aim ability - take a look at that for reference.
 
Resurrecting a old thread, but did you manage to do it?
I am not quite sure how to proceed: if I make a custom UCC ability that overrides UpdateRotation, what's the proper way to get target of the rotation
from the behavior tree? Is it going to work if I do behaviorTree.GetVariable("MyVariable") from within the UCC ability script?
 
Funny you should ask, I'm actually back on the forum to follow up on this exact issue.

I think I'm close, but for some reason the Agent is sometimes looking astray by like.... 10-45 degrees off to the side? Only at certain transform positions does it ever look straight at the Target Game Object (the target game object being a child attached to the SteamVR camera game object). 90% of the time, it's looking off to the side.

For example, lets say I manage to find the rare transform where it's looking directly at me, and I move to the left. The agent will overcorrect and be looking too much to the left. The more my target gameobject is left from that point, the more the undesired rotation offset to the left is. Same goes if I go right... the more right I go, the more extreme the undesired rotation offset is to the right.

It's really peculiar and I can't quite figure it out. I think it may have to do with something about normalizing the look direction, because I remember running into this issue before, and ended up resolving it by either normalizing the direction or converting it to world space. But I'm not sure how to do that here. Please see code below, any help would be greatly appreciated!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateCharacterController.Character.Abilities;
using Opsive.UltimateCharacterController.Utility;

public class RotateToFaceAbil : Ability {

public GameObject targetGO;

protected float m_MaxRotationAngle = 100f; // turnSpeed

public override void ApplyRotation()
{
var angle = Quaternion.Angle(Quaternion.identity, m_CharacterLocomotion.Torque);
if (angle > m_MaxRotationAngle)
{
m_CharacterLocomotion.Torque = Quaternion.Slerp(Quaternion.identity, m_CharacterLocomotion.Torque, m_MaxRotationAngle / angle);
}
}

public override void UpdateRotation()
{
//// If the character can look independently then the character does not need to rotate to face the look direction.
//if (m_CharacterLocomotion.ActiveMovementType.UseIndependentLook(true))
//{
// return;
//}

// Determine the direction that the character should be facing.

var lookDirection = targetGO.transform.position;
var localLookDirection = m_Transform.InverseTransformDirection(lookDirection);
localLookDirection.y = 0;
m_CharacterLocomotion.DeltaYawRotation = MathUtility.ClampInnerAngle(Quaternion.LookRotation(localLookDirection.normalized, m_CharacterLocomotion.Up).eulerAngles.y);

base.UpdateRotation();
}
}
 
Last edited:
Hello!

Actually I realized that I don't need a RotateTowards ability, because (when it works) the StartStopUse task is already assigning the target to the localLookSource of the agent, and then the Character Controller is supposed to rotate based on this localLookSource.

The problem happens because I want to do a Point&Click movement for the player character.
Because of this, the player character has a NavMeshMovement ability. And as soon as this ability is added, the AI agent does not face the player
anymore. It behaves like if the player transform was horizontally offset a litle bit.

I'll make a new post about that.
 
I solved the issue! You have to subtract the agent's transform from the target transform when establishing the look direction. If you don't do that, it causes the undesired offset. If someone smarter than me could explain why it is this solved the issue, I would really appreciate it. It's something I want to fundamentally understand, but I can't really wrap my head around why it fixes the issue.

public override void UpdateRotation()
{

// var lookDirection = targetGO.transform.position; // original code
var agentPos = agentGO.transform.position; // newcode line 1
var lookDirection = targetGO.transform.position - agentPos; // newcode line 2
var localLookDirection = m_Transform.InverseTransformDirection(lookDirection);
localLookDirection.y = 0;
m_CharacterLocomotion.DeltaYawRotation = MathUtility.ClampInnerAngle(Quaternion.LookRotation(localLookDirection.normalized, m_CharacterLocomotion.Up).eulerAngles.y);

base.UpdateRotation();
}
 
Trying to do this myself. How were you setting your target parameter at runtime, @wuche? is it better to use LookSource for this like the way that Aim/Attack seems to work? I tried to use SetAimTarget BD task, and it seemed to make my agent spin in circles.

I feel like there should be a UCC compatible "LookAt" Node in Behavior designer, seems base feature worthy to me @Justin. But somehow I presume there is a reason for this omission that I am missing?
 
Last edited:
Take a look at the UCC integration sample for an example of the agent looking at the target. If all that you want the agent to do is look at the target while aiming/shooting then you can use the existing abilities/tasks since the LocalLookSource will set the correct look direction. You do need a new ability though if you want the character to rotate towards a different target while not aiming. I can add that to my request list.
 
That would be awesome @Justin. It would be helpful to be able to rotate towards a target without Aiming. I tried to implement myself, I could get the rotate, but couldn't figure out how to deliver the Target (Game Object) as a parameter to the Ability at run-time.
 
I'm back trying to figure this one out. I got further than last time. I'm creating a "RotateTowardsAbility" based on the "Aim" ability. This ability also uses the same LocalLookSource pattern as the "Aim" ability. It seems that I am correctly calculating the look vector, however "DeltaRotation" is not updating the actual players deltaRotation. Is m_CharacterLocomotion.DeltaRotation the incorrect variable to update for AI characters?? DeltaYawRotation no longer seems to exist.

Be forever grateful if someone could help me out with this.

Here is my code which works perfectly for regular UCC characters but NOT AI Navmesh characters:


C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateCharacterController.Character.Abilities;
using Opsive.UltimateCharacterController.Utility;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Events;

public class RotateTowardsAbility : Ability
{

    private ILookSource m_LookSource;
    public override void Awake()
    {
        base.Awake();
        EventHandler.RegisterEvent<ILookSource>(m_GameObject, "OnCharacterAttachLookSource", OnAttachLookSource);

        // The look source may have already been assigned if the ability was added to the character after the look source was assigned.
        m_LookSource = m_CharacterLocomotion.LookSource;
    }
    private void OnAttachLookSource(ILookSource lookSource)
    {

        m_LookSource = lookSource;

    }
    public override void UpdateRotation()
    {

        // The look source may be null if a remote player is still being initialized.
        if (m_LookSource == null)
        {
            return;
        }

        var localLookSourcePos = m_GameObject.GetComponent<LocalLookSource>().Target.position;
        // Determine the direction that the character should be facing.
        var lookDirection = m_LookSource.LookDirection(m_LookSource.LookPosition(), true, m_CharacterLayerManager.IgnoreInvisibleCharacterLayers, false);
        var rotation = m_Transform.rotation * Quaternion.Euler(m_CharacterLocomotion.DeltaRotation);
        var localLookDirection = MathUtility.InverseTransformDirection(lookDirection, rotation);
        localLookDirection.y = 0;
        lookDirection = MathUtility.TransformDirection(localLookDirection, rotation);
        var targetRotation = Quaternion.LookRotation(lookDirection, rotation * Vector3.up);
        Debug.Log((Quaternion.Inverse(m_Transform.rotation) * targetRotation).eulerAngles);
        m_CharacterLocomotion.DeltaRotation = (Quaternion.Inverse(m_Transform.rotation) * targetRotation).eulerAngles;

    }
}
 
Last edited:
Top