Monday, 9 September 2013

Simple AI in Unity3D

Here's a short writeup on how we're doing the enemy AI for our most recent game, Slinki. I recommend you read up on C# delegates and Unity coroutines if you're not familiar with either.

We decided early on that the enemies should have really simple behaviours. For instance, here's an example of how the Shorg works on the newest version:
  1. Wait until Slinki comes close
  2. Appear and jump out once he's close enough
  3. Wait for 2 seconds
  4. Jump towards slinki a certain small distance
  5. Go to 3
Things like these are really easy to implement using easy AI techniques such as state machines.

The method

Create a new MonoBehaviour for your AI. It should look like this:
//this is like a template for all our states
public delegate IEnumerator State();

//the current state
private State stateFunc; 

//seconds between AI updates
public float AIupdateFrequency = 0.1f; 

void OnEnable() {
 //Sets the first State
 stateFunc = StateWait;
 StartCoroutine (Action());
}

public IEnumerator Action() {
    while (true) {
        //Waits untill stateFunc is done before proceeding
        yield return StartCoroutine(stateFunc());
    
        //Waits for AIupdateFrequency so we don't busy-run our AI logic
        yield return new WaitForSeconds(AIupdateFrequency);
    }
}

Here's an example of a possible state method. This one just makes Shorg stand still for 2 seconds before changing to the "Hop" state.
IEnumerator StateWait() {
    yield return new WaitForSeconds(2);
    
    //next state
    stateFunc = StateHop; 
    yield return null;
}

Here's another state method, this one applies gravity while Shorg is airborne. Once he lands he goes back to waiting.
IEnumerator StateAirborne() {
    kinController.jumpSpeed = -kinController.gravity * AIupdateFrequency;

    if (kinController.isGrounded) {
        stateFunc = StateWait;
    }
    
    yield return null;
}

Guidelines

The "rules" for writting new states are simple: Following the delegate - they should be functions that return IEnumerator , they should end in yield return null,  and they need to have one or more conditions for changing the stateFunc variable (i.e. change to another state).

 

Summary

Advantages of this method:
  • Simple
  • Easy to create new states
  • Very easy to debug just using prints
  • Self destructs cleanly if the GameObject is destroyed or disabled
Disadvantages
  • Can get complicated as the AI gets more complex
  • No way to enforce the guidelines due to the way Unity and C# work
  • Coroutines can get a bit funky to use for an inexperienced programmer
  • State is lost if the gameObject is temporarily disabled

Special thanks to David Craft for this amazing hack that allows code syntax highlighting on blogger


Note: If you need to temporarily run the AI logic more often than AIupdateFrequency will allow, all you need to do is yield inside a cycle. This probably sounds confusing so here's another version of the Wait state. In this one Shorg turns towards the player before waiting:

IEnumerator StateWait() {
 
    //Facing wrong way?
    while (Vector3.Dot(transform.forward, slinkiDirection.x * Vector3.right) < 0) {
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 100);
        Debug.Log("Turning around");
        yield return null;
    }
 
    //Debug.Log("Waiting for 1 second");
    yield return new WaitForSeconds(1);
    stateFunc = StateHop;
}

That while cycle will make the coroutine run every frame update. Without it, Shorg would only try to face the player once every 0.1 seconds, producing a very jerky animation.

Monday, 22 July 2013

Poultron: Bonus

Since a new version of unity came out today with realtime shadows I figured I might as well make a new version with them on.

Shadows!

EDIT: Now actually with shadows as of the 22nd of August

Sunday, 21 July 2013

Poultron: Final day


And to think it looks so small from up here...

The main problem with making a game that's designed to be frustrating to play is that playtesting is hell. In the end I think I made it too easy and removed most of the ramps as I couldn't figure out if they were actually climbable. Oh well...

The credits sequence ended up taking up most of my spare time. It was basically just my name and a list of assets used but a neat little thing to figure out nonetheless.

In the end I think this was a neat little experiment and I might do a post-mortem soonish. I might even compile a list of issues I had with Unity (like the fact prefabs are not clearly marked, which might result in you changing a lot more things than you'd hope when tweaking certain values).

Here's hoping you'll enjoy it.

Saturday, 20 July 2013

Poultron: Day 6

  Been very busy, can't remember most of the things I've done today.


Features:
  • More slight graphical tweaks
  • New flying enemy! (took up most of my time)
  • Landscape is slightly more interesting
  • Keep walking and you'll find new enemies (eventually)

Current issues:
  • Still no endgame :(
  • Enemies pop up in front of you. The level will be tweaked tomorrow.

Tomorrow:
  • Ending and credits
  • Finish the level
 Abandon all hope ye who enter here.

Poultron: Day 5


I just noticed my pilot looks eerily similar to the default minecraft dude

Time's starting to run out! I still have no flying enemies and the level's still mostly empty! That being said, the game now has a menu of sorts and a second robot to choose from.

 Features:

  • An interactive menu and intro sequence
  • A new robot to fall in love with (not recommended for people with low self-esteem)
  • LASERS!
  • Slight graphical tweaks here and there
  • Wobbly feet
  • I'm starting to lose track to be honest

Current issues:
  • Footstep detection is a bit wonky
  • We need an endgame 
  • You can hear the tank trying to shoot outside the hangar (will be fixed tomorrow)

Tomorrow:
  • Make the level!
  • Hopefully put in the flying enemy

Embark on an adventure of a lifetime by clicking this link

Thursday, 18 July 2013

Poultron: Day 4



The closest you'll ever be to his pretty face

I tried to go with no health system and I like the results. However, this made me spend most of the day rewriting the projectile system into something a lot more physics-based. I also spent some time polishing up the robot and some other features.

 Features:

  • Enemy bullets can now hit you and will make it harder for you to keep walking
  • Footstep sounds!
  • Rate of fire limitations on your own mech (the huge cannon is no longer semi-automatic)
  • The robot looks 20% cooler and features a tiny pilot (which I might animate if I feel like it)
  • Dings!

Current issues:
  • Footstep detection is a bit wonky

Tomorrow:
  • I might make the "shoot" button a keyboard key if it doesn't make the game too easy. I intended to keep it on the screen as an added challenge.
  • Maybe make the feet separate parts to make walking seem smoother and more realistic (also should help polish the footstep sounds)
  • Begginings of robot number 2 (aka hard mode)
  • Make the menu.
  • Put flying enemy into the game if we're very lucky

This link over here should take you on today's adventure.



Wednesday, 17 July 2013

Poultron: Day 3


My gun is slightly bigger


Spent most of the day doing other non-game related things like going to the dentist and such. Despite this I think I made quite a bit of progress. Most of it can be summed up as "cleaning up" the messy code and fixing lesser issues.

Features:

  • Cool special effects for the bullets!
  • Graphical tweaks, sort of... Well at least everything should look shinier now .
  • The tank now runs away from you!
  • It also shoots back (bullets hitting the robot may not actually do anything yet. Batteries sold separately.)
  • Made the gun mount more "rigid" (it now doesn't wobble every time you shoot). This means it's now harder to use the recoil as an improvised jetpack (sorry)
  • Did you know the tank had moving tracks? Well now you'll notice they exist.

Current issues:
  • THE BULLETS, THEY DO NOTHING. Should I make the mech have a health bar or just make the shots move it back a bit and make it harder to move?
  • The tank dies in one shot
  • The tank explosion's effect disappears suddenly. This won't be trivial to fix unfortunately.
  • Bullets still do nothing when they hit the ground
  • As you can see from the screenshot, that shell might be slightly bigger than it should
Tomorrow:
  • Consider implementing a health system or not.
  • Make a menu?
  • Tweak the interface?
  • More graphical tweaks?
  • Model a flying enemy.
 As usual, amazing fun awaits thee here.