Friends, food, and flourishing

MalberS magic: trot and run

So far, Carl the corgi can laze round, and stroll between waypoints. He looks natural doing it.

Why don’t we give him some urgency? If my doggos hear the clink of food hitting a bowl, they don’t stroll.

We’ll have to do a few things:

  • Change the locomotion blend tree to add trotting and running.
  • Add speeds to AC.
  • Change the AI control code to use those new speeds.

Changing the locomotion blend tree

Recall we’re using a 2D Cartesian blend tree. It chooses animations based on values for horizontal (left/right) and vertical (forward/back) movement. Here is what we have so far:

Locomotion blend tree

The values in the Pos X and Pos Y are movement changes (deltas). If both are 0, Carl is not moving, so do some idling. If he’s moving forward but not left or right, use the Walk_F animation. And so on.

If delta is 0, no movement. Delta 1 means walking. Let’s use 2 for trotting, and 3 for running. I’m using root motion animations, so 1, 2 and 3 are not actual doggo speeds, just values for selecting animations. Er, I think. I could be wrong.

Red Deer includes many animations for corgis. Like, a lot. We’ll use these:

Animation list

Here’s the new blend tree.

New blend tree

It’s much the same, but with trot and run animations, and their Pos X and Y values.

Speeds

Recall states are mutually exclusive. If Carl is flying, he can’t be swimming.

Within each state, Carl can move at different speeds. AC’s terminology is a bit tricky when it comes to speeds, so we need to be careful.

Animations for animals have different gaits, like walk, trot, and run. You can use different animations for different speeds. When walking, you might use walking animations that come with the model.

When flying, there might be hover, slow, cruise, and dash speeds. Flying is a speed set. When swimming, there might be floating, slow, and fast. That’s another speed set.

It’s possible for different states to use the same speed set. For example, the swimming and space walk states might have the same speed sets. To allow for this, speed sets are separate from states and are linked to them, rather than being fully contained in them. Er, I think.

Here’s how the Ground speed set is linked to the Locomotion state in AC:

States linked to speed sets

So, Locomotion has tabs called General, Tags, Limits, etc. One tab is Speeds. It lists the speed sets Carl has, just one in this case.

Here’s the Ground speed set:

Speed sets

They’re the same, except for the vertical speed.

Problem?

One issue I had, that seemed like a bug, but maybe isn’t, just my lack of understanding. Here’s the states tab from before:

States linked to speed sets

The state tab is selected, the Locomotion state chosen, and there’s the speed set Ground. Earlier we saw the Speed tab under the Ground speed set, but there’s another tab, General:

State speed general tab

So far, so good. Take a look at the AC again. As well as a states tab, there’s a speed tab:

Speed general tab

Both are showing settings for Ground. The same categories and fields: Start Index, Top Index, etc. Makes sense. It should be the same data. Just getting to Ground data in different ways. Via the states tab, or directly with the speeds tab.

However, if you look at Start Index in both screen shots, you’ll see they’re different. Experimenting, it seems the Start Index when going through the state tab is working, but the one shown using AC’s speed tab isn’t used. Again, I could be wrong.

Using the new speeds

We have some waypoints set up from last time.

Waypoints

Carl wanders between them, choosing the next one randomly. I want the speed to be random as well, either walk, trot, or run.

The AI control has an event that seems right: On New Target Set.

AI event

I wrote some simple code for it.

public void OnNewTargetSet(Transform target)
{
int speedIndex = Random.Range(1, 4);
animal.Speed_CurrentIndex_Set(speedIndex);
}

Hmm. It didn’t work.

Here’s what I think’s going on. The MalberS AI control (AIC) works with the state system. To get Carl moving, AIC switches him to Locomotion. When he arrives at a waypoint, AIC switches Carl to Idle. It waits a while, then switches Carl to Locomotion to get him moving again.

On New Target Set fires when AIC picks a new waypoint. Makes sense. That happens when Carl’s state is Idle, just before changing it to Locomotion. Idle does not have walk, trot, and run speeds, so setting speed on the current state, which is what Speed_CurrentIndex_Set() does, is useless.

Poo!

I added one line, to force the state to Locomotion:

public void OnNewTargetSet(Transform target)
{
animal.State_Activate(1);
int speedIndex = Random.Range(1, 4);
animal.Speed_CurrentIndex_Set(speedIndex);
}

Recall that 1 is the Id of the Locomotion state.

It worked, but I didn’t like it. It could be forcing a state change before AIC is ready for it. Like it’s interfering with AIC’s logic.

Another solution was to use an event on the waypoints: On Target Arrived.

Waypoint event: on target arrived

public void OnTargetArrived(GameObject target)
{
  int speedIndex = Random.Range(1, 4);
  animal.Speed_CurrentIndex_Set(speedIndex);
}

That worked. But, you guessed it, another problem arose. Sigh.

A friend for Carl

I duplicated Carl, creating June. She’s got a hat, and some different colors.

Jimbo

She looks like a walking banana.

She didn’t work, though. When she arrived at a target, the waypoint ran On Target Arrived, and called Carl’s CorgiController method, not hers. Why? That’s how the waypoint is configured:

Waypoint event: on target arrived

Hmm. What to do? Unfortunately, On Target Arrived passes the waypoint GameObject, not the waypoint of the target that arrived there.

Umm. OK, how about in Update(), when the doggo reaches a waypoint, choose a new speed?

private void Update()
{
    if (animalAIControl.HasArrived)
    {
        int speedIndex = Random.Range(1, 4);
        animal.Speed_CurrentIndex_Set(speedIndex);
    }
}

That would work, but it picks a new speed for every frame the doggo was at the destination. It only needs to pick a speed once. There wouldn’t be a problem with 10 doggos, but what about 1,000? I don’t know whether the game could slow down or not.

Best to avoid the issue. Set a new speed only on the first frame after a doggo reaches a waypoint. That worked out:

private bool _hasArrivedPreviousFrame;

private void Update()
{
    if (!_hasArrivedPreviousFrame && animalAIControl.HasArrived)
    {
        int speedIndex = Random.Range(1, 4);
        animal.Speed_CurrentIndex_Set(speedIndex);
    }
    _hasArrivedPreviousFrame = animalAIControl.HasArrived;
}

The test is true when the doggo hadn’t reached the waypoint in the previous frame, but has in the current frame.

Weehoo! It worked. Carl and June wandered about freely. 

In-place animations

Root motion (RM) animations change their transform’s Z when they walk, trot, or run forward. In-place (IP) animations do not. Some other code needs to change the transform.

Set animations to loop. Open the FBX model, click edit to make it read/write, go to animation tab, choose an animation, loop, apply. Do this for all the animations you want to change.

What's next?

The animations aren’t quite right. There’s some sliding. Not much, but noticable. It might be in the transition from turning to moving forward when trotting and running, but I’m not sure.

MalberS has an interesting AI system based on pluggable scriptable objects. I want to give that a try, particularly following the player around.

Leave a Reply

Your email address will not be published. Required fields are marked *

css.php