Saturday, November 3, 2018

The silence of the Lambs

Completed the work on Countryside Simulator 2018, and added the possibility to walk in the world (I reused the Mouselook and FPSInput components from the previous post).

Walking Simulation 101

Nothing original, it's just the program from the second chapter of Unity in Action re-typed (yes, I too am a victim of tsundoku).

Thursday, November 1, 2018

Unity Animator, the Finite State Machines and AI for Fun and Profit

I tried to follow the suggestion of several books and articles, including the second chapter of Unity AI Game Programming and "Don’t Re-invent Finite State Machines: How to Repurpose Unity’s Animator" by Darren Tsung about repurposing the Unity Animator for a State Machine that could be used in creating some basic AI for my game.

Unfortunately I still had problems in understanding what came with the Unity framework and what I needed to write for myself in order to have a GameObject which was driven by the Finite State Machine, so I went and built up a small sample, a world full of sheeps who eat grass and wolves who hunt sheeps from scratch, and I think I managed to begin to understand quite a bit of the stuff I needed to do versus the stuff I needed to just use.

Tl;Dr;

This is the short list for creating an AI which uses the Animator component as a finite state machine.
  • Give the GameObject you want to give an AI a MonoBehaviour
  • Create an AnimatorController
  • Create the variables in the AnimatorController you need to check to drive the transition between states.
  • Assign the AnimatorController to the MonoBehaviour
  • Update the AnimatorController variables from MonoBehaviour
  • Create the AnimatorController States
  • Link the states with the transition rules
  • Create the StateMachineBehaviour objects in each one of the States
  • Make the StateMachineBehaviour classes call the methods you defined on the MonoBehaviour
Remember that this is not strictly an ordered list, most of the time during software development you iterate over different versions of your GameObjects, their MonoBehaviours, therefore you will have to follow these steps out of sequence.

The example

In this example I will create a scene with two cubes. One cube will be "dumb" and spin on command around a circle, and a second cube instead will have a simple AI that will just make it change its color based on the distance from the first cube.

Setting the scene

The scene itself is rather simple. I put a floor, a couple of cubes, and created four materials, one for the floor, two for the cube that changes color, and another one for the "bold" cube. This is nothing different from what is taught in the roll-a-ball tutorial. Note that there is an additional empty GameObject (called Orbit) that is the parent of SpinCube and will be used for the function that makes it orbit around the scene. SpinCube is also tagged as Player, this is to let us find it again without assigning it to the ShyCube's MonoBehaviour.



Making the Spin Cube move

Here I will just copy this answer from Michael House from the Gamedev Stackexchange. So here I created a MonoBehaviour that I will assign to the SpinCube. The only modification I made is to put a boolean variable I will drive from the script that reads from the keyboard.

using UnityEngine;

public class SpincubeMotion : MonoBehaviour
{

    public bool MustOrbit = false;

    // following code by Michael House
    // https://gamedev.stackexchange.com/a/62002/92294
    public float OrbitDegrees = 10.0f;

    public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion angle)
    {
        return angle * (point - pivot) + pivot;
    }

    // Update is called once per frame
    void Update()
    {
        if (MustOrbit)
        {
            transform.position = RotatePointAroundPivot(transform.position, transform.parent.position,
                               Quaternion.Euler(0, OrbitDegrees * Time.deltaTime, 0));
        }
    }
}

To keep this example simple I will assign the following MonoBehaviour to the SpinCube. It's a script that when the player presses "F" switches on and off the MustOrbit variable on SpincubeMotion. Note that it fetches the SpincubeMotion component of the related GameObject and just switches on and off its MustOrbit property.

using UnityEngine;

public class InputBehaviour : MonoBehaviour {

// Update is called once per frame
void Update () {
        if (Input.GetKeyDown(KeyCode.F))
        {
            SpincubeMotion component = GetComponent<SpincubeMotion>();
            component.MustOrbit = !component.MustOrbit;
        }
    }
}

And this is the result if I build and run the project.




Making the shy cube react.

To make the Shy Cube react I created an AnimatorController.
The AnimatorController will have two states, Bold and Shy, and one variable, a float variable called DangerDistance.



The two states will be connected by two transitions, one that is triggered when the DangerDistance is less than 5, and the other that will be triggered when the DangerDistance is more than 5.




Now I will assign an Animator component to ShyCube and the new AnimatorController to it.



Now I will create a MonoBehaviour, called ReactionBehaviour. The ReactionBehaviour's Update() method will calculate the distance between ShyCube (read by the gameObject's own transform) and SpinCube (that we fetch via the Player tag) and will update Animator's variable. Note the two other methods, BeBrave() and BeCoward(), which update the gameObject's material.

using UnityEngine;

public class ReactionBehaviour : MonoBehaviour {

    public Material Bold;

    public Material Shy;

    private Animator animator;

// Use this for initialization
void Start () {
        animator = GetComponent<Animator>() as Animator;
}

// Update is called once per frame
void Update () {
        GameObject danger = GameObject.FindGameObjectWithTag("Player");
        float dangerDistance = 
                Vector3.Distance(danger.GetComponent<Transform>().position, transform.position);
        animator.SetFloat("DangerDistance", dangerDistance);

        GameObject[] targets = GameObject.FindGameObjectsWithTag(tag);
    }

    public void BeBrave()
    {
        GetComponent<MeshRenderer>().material = Bold;
    }

    public void BeCoward()
    {
        GetComponent<MeshRenderer>().material = Shy;
    }
}

And now I will create two small C# classes and assign them to the States inside the Animator, like this...

using UnityEngine;

public class BoldBehaviour : StateMachineBehaviour {

override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
        animator.gameObject.GetComponent<ReactionBehaviour>().BeBrave();
    }

}

...and...

using UnityEngine;

public class ShyBehaviour : StateMachineBehaviour {
    
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        animator.gameObject.GetComponent<ReactionBehaviour>().BeCoward();
    }

}

Notice that I used the OnStateEnter method. For behaviours that repeat while the animator is in a state you might want to use the OnStateUpdate method instead, or rather the OnStateExit if you want to trigger something when the animator leaves that state (think the bomb in the Speed movie).

But now let's test drive it.


Conclusion.

Like working with every framework, the problem with working with Unity is not it's complexity, it's knowing what it provides and what you need to provide to make your software work. Working with the Animator component Finite State Machine is the same.

I also published a Github repository with the code on Github: https://github.com/limacat76/Shycube

Saturday, October 27, 2018

The Lamb lies down on Broadway

Wolf (the black square) and sheep (white sphere) prancing on a field. Grass and water are using the same data structure from the cellular automata. I still have to implement the AIs and the rules.

Conway's Game of life

An experiment implementing a cellular automata system to support a world with resources.

Saturday, October 13, 2018

Redout Shader

The Skybox for this week wasn't ready for this #screenshotsaturday (I could not manage to create the fsm to turn around the camera in time, never mind to stitch the images), so instead I worked a bit on a redout shader instead.

Friday, October 12, 2018

How to access the GameObject from a StateMachineBehaviour.

Let's say that you are using Unity's Animator/Mecanim component as a Finite State Machine and you don't need to use it for Animations, and let's say you have a Foo MonoBehaviour object with a Bar() method attached to your AnimationController. How do you access it from any method in any one of your StateMachineBehaviour?
animator.gameObject.GetComponent<Foo>().Bar();
doggone it...

Friday, October 5, 2018

A Floating Point Origin Test

After not being able to wrap my head over Unity ECS I got back to my plan to create a world with Floating Point Origin. This is the first test: it's just 4 zones that get displaced and looped over, while the camera gets translated to 0 every time it gets over a new zone. It's still not interactive.

Saturday, September 29, 2018

Drawing Distance Test

Just a dumb thing for testing drawing distances in Unity.