Build a 2D Top Down Game – Zero to Published – Part 4

Welcome to part 4!  In this part we are going to cover creating an AI Enemy Character for our game.  We will build the character up very similar to how we did in part 1 and 2.  We will also be adding an AI Script.  The material covered in parts 1 and 2 will be gone over quickly.

Part 1:  Getting Started

Part 2: Animations

Part 3: Prefabs

Part 4: Enemies

Part 5: Modular Scripting

Part 6: Timed Prefab Instantiation

Part 7: Health Bars

Part 8: Finishing Touches

Part 9: Publishing

Create a Skeleton

  1. Pick your favorite skeleton image and drop it onto the scene.
  2. Add a circle collider and a rigid body 2D just like we did for the spider.
  3. Add an animator component, just like we did for the spider.
  4. Navigate to the animations folder, right click and add a new animatorController call “SkeletonController”.
  5. Drag the controller onto the animator
  6. Open the animator and the animation tabs while having the skeleton highlighted.
  7. Create 2 new clips just like we did for the spider. Name them SkeletonIdle and SkeletonRun.
  8. For SkeletonIdle, drag a single one of your favorite images onto the animation area and set the sample to 1. This is just like we did with the spider.
  9. For SkeletonRun, drag all of the animations on to the area and set the sample to 10.
  10. In the Animator, add a new parameter called ‘Speed”, just like with the spider.
  11. Create your to and from transitions just like with the spider. Where speed > .01 transitions from idle to run and speed < .01 transitions from run to idle.

Add some components to the skeleton.

Our Skeleton is going to be an AI that deals some damage to the player.  As such, we need to think about a few things AI Characters usually do.  They usually search around, find the player, chase after the player and do some damage!

  1. Add a Circle Collider to the Skeleton and make sure the collider is the size of the “Follow Me” activation zone.  Whenever the player walks into this zone, the AI will follow him, if the player walks outside of this zone, the AI will stop following.  Make sure you select the IsTrigger flag on this collider.
  2. colliderAsTrigger
  3. Drag the CharacterScript onto the skeleton as we will use that for movement.  Make sure you adjust the initial heading variable accordingly such that as the skeleton moves, he points in the correct direction.  This is 90.

Add some code to the skeleton.

  1. Create a new script in the scripts folder called “EnemyAI”.
  2. We will need to require the Character Script, as we will be moving using calls to that.

usingUnityEngine;

usingSystem.Collections;

[RequireComponent(typeof(CharacterScript))]

publicclassEnemyAI : MonoBehaviour {

publicenumEnemyState

{

searching,

following

}

//time in seconds till the character determines a new heading.

publicfloattimeToChangeDirection=5.0f;

//amount of damage to do to the player

publicintdamage=5;

//how fast this can deal damage

publicfloatdamageFrequency=1.0f;

//internal timer to track last time dealt damage.

privatefloatdamageTimer;

//reference to the character script.

privateCharacterScriptcharacter;

//internal timer to keep track of elapsed time.

privatefloatinternalTimer=0.0f;

//direction relative to the character to head.

privateVector2heading;

privateEnemyStatecurrentState;

privateGameObjecttarget;

/// <summary>

/// Used for initializing your script component

/// </summary>

privatevoidAwake () {

//cache the character script

this.character=this.GetComponent<CharacterScript>();

//set an initial heading.

this.createNewHeading();

//set internal timer for damagefrequency

this.damageTimer=this.damageFrequency;

this.currentState=EnemyState.searching;

}

/// <summary>

/// called after all awakes are called.

/// </summary>

privatevoidStart()

{

this.character.SetDeathFunction( () => {

//destroy the game object.

Destroy(this.gameObject);

});

}

 

/// <summary>

/// Update that is called once per frame.

/// </summary>

privatevoidUpdate () {

//increment our damage timer by the time it took to complete the last frame.

this.damageTimer+=Time.deltaTime;

}

/// <summary>

/// Update that is kept in sync with physics.

/// </summary>

privatevoidFixedUpdate()

{

//if we are searching, call the search function

if(this.currentState==EnemyState.searching)

{

this.Search();

}elseif(this.currentState==EnemyState.following)

{

//if we are following, call the follow function

this.Follow();

}

}

/// <summary>

/// The enemy follows the player.

/// </summary>

privatevoidFollow()

{

//vector math says position to go minus current position = vector we should head in.

Vector3nTransform=this.target.transform.positionthis.transform.position;

//make sure its of magnitude 1.

nTransform.Normalize();

//move that direction

character.Move(nTransform);

}

/// <summary>

/// The enemy searches for the player.

/// </summary>

privatevoidSearch()

{

//increase the internal timer by

//the amount of time that has passed.

this.internalTimer+=Time.deltaTime;

//determine if we need to create a new heading.

if (this.internalTimer>this.timeToChangeDirection)

{

//create a new heading

this.createNewHeading();

//reset the timer.

this.internalTimer=0.0f;

}

//move the character in the direction of its heading

character.Move(heading);

}

/// <summary>

/// Generates a new random heading for the character

/// and applies that heading immediately.

/// </summary>

privatevoidcreateNewHeading()

{

//I used -100 to 100 to give an equal chance for any direction

floatxCoord=Random.Range(100.0f, 100.0f);

floatyCoord=Random.Range(100.0f, 100.0f);

//set our heading to the random headings.

heading=newVector2(xCoord, yCoord);

//make sure the magnitude of our heading is 1

heading.Normalize();

}

/// <summary>

/// Called when a collision happens between the character

/// and something else.

/// </summary>

/// <param name=“other”>What you collided with.</param>

privatevoidOnCollisionEnter2D(Collision2Dother)

{

//make sure we don’t do too much damage to the player

if (other.gameObject.tag==“Player”

&&this.damageTimer>this.damageFrequency)

{

//deal some damage!

other.gameObject.GetComponent<CharacterScript>().AdjustHealth(1*this.damage);

//reset the timer so we don’t damage too much again.

this.damageTimer=0.0f;

}

//create a new heading, you ran into something.

this.createNewHeading();

}

publicvoidSetSpawnedBy(GameObjecto)

{

this.spawnedBy=o;

}

publicGameObjectGetSpawnedBy()

{

returnthis.spawnedBy;

}

/// <summary>

/// Occurs when something enters its trigger zone.

/// </summary>

/// <param name=“other”></param>

privatevoidOnTriggerEnter2D(Collider2Dother)

{

//check to see what this thing is

if (other.gameObject.tag==“Player”)

{

//Its the player! FOLLOW IT!

this.currentState=EnemyState.following;

//set the target.

this.target=other.gameObject;

}

}

/// <summary>

/// Occurs when something enters its trigger zone.

/// </summary>

/// <param name=“other”></param>

privatevoidOnTriggerExit2D(Collider2Dother)

{

//Check to see what it is

if (other.gameObject.tag==“Player”)

{

//Its the player, I can’t see it anymore, stop following.

this.currentState=EnemyState.searching;

}

}

}

 

 

Finally

Create a new prefab and drag your skeleton onto it.

9 thoughts on “Build a 2D Top Down Game – Zero to Published – Part 4

  1. So here is some strangeness on the part of Unity.

    Because by this part of the tutorial you’ve added the lambda for SetDeathFunction, but haven’t added the function itself to CharacterScript, the build fails.

    If you do not add it (I poked through the source and put it together), you cannot add EnemyAI to any prefab. You get an error “Cannot add EnemyAI because it does not exist” or something to that effect.

    Once the compile errors are fixed, the script can be added.

    • I’ll take a look at it tomorrow. It looks like I may have also missed this particular article for the new code formatter as well, which may be causing some issues.

  2. So here is some strangeness on the part of Unity.

    Because by this part of the tutorial you’ve added the lambda for SetDeathFunction, but haven’t added the function itself to CharacterScript, the build fails.

    If you do not add it (I poked through the source and put it together), you cannot add EnemyAI to any prefab. You get an error “Cannot add EnemyAI because it does not exist” or something to that effect.

    Once the compile errors are fixed, the script can be added.

  3. Pingback: Build a 2D Top Down Game - Zero to Published - Dave Crook's Dev Blog - Site Home - MSDN Blogs

  4. Pingback: Build a 2D Top Down Game - Zero to Published - Dave Crook's Dev Blog - Site Home - MSDN Blogs

  5. The code formatter on here would indeed be a big help rather than having to go through it.

    I think i have managed it but get the following errors:
    http://i.gyazo.com/33b9739f1ff2f150d97c753abc844d62.png

    Having said that, these seem to be as mentioned above due to missing out the function in the Character Script. Adding these would be much appreciated I’m sure, but off to google i go for now.

    Thanks again.

  6. The code formatter on here would indeed be a big help rather than having to go through it.

    I think i have managed it but get the following errors:
    http://i.gyazo.com/33b9739f1ff2f150d97c753abc844d62.png

    Having said that, these seem to be as mentioned above due to missing out the function in the Character Script. Adding these would be much appreciated I’m sure, but off to google i go for now.

    Thanks again.

Leave a Reply

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