The Legend of the Tomato Quest Tutorial - Enemies II

From SwinGame

As discussed in the previous tutorial, this tutorial will be used to optimize the AI Handling that we have in place.

Some improvements include

  1. AI colliding with AI
  2. Optimizing the Collisions
  3. Making the AI move and attack, only when they are within a reasonable distance
  4. Making the AI move and attack, when they are alive.
  5. Monitor the AI's health so that when they have no health left, they die.
This page contains a Tutorial. Tutorials are designed to walk you through the development of a small game.

Contents

Optimizing our Characters

First, add this Accessor to the Character class in Character.cs

//Returns if the Character is Alive
public bool Alive
{
    get 
    {
        if (Health > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

This will tell our game if our characters are alive or not.

Now edit the Character() constructor method in the Characters class within the Character.cs source file, and add the following line of code:

...
 
    CanMove = canMove;
    CanAttack = canAttack;
    CanInteract = canInteract;
    
    //ADD THIS LINE OF CODE
    _Sprite.UsePixelCollision = false;
}

What this will do, is turn off pixel level collision. Checking collisions pixel by pixel is a slow process, which will slow down our game. Pixel level collision should only be used if absolutely necessary.

Calculating Distance

We need to write a method that will calculate the distance between any 2 characters, so we can use it to optimize all of our collision and updating routines. We want to do this because theres no sense checking for collisions between the Player and an AI, if they are inches away from each other.

Add the following method to the AIController class in the AIController.cs source file:

public static int CalculateDistance(Character character1, Character character2)
{
    double distancex = character1.Sprite.xPos - character2.Sprite.xPos;
    double distancey = character1.Sprite.yPos - character2.Sprite.yPos;
 
    return (int)Math.Sqrt(Math.Pow(distancex, 2) + Math.Pow(distancey, 2));
}

Using simple trigonometry, we calculate the distance between the 2 characters, and return a rounded off value representing that approximate distance. We can now use this in our AI routines to optimize them.

Optimizing the Player vs AI Collision

Modify the PlayerCollideWithAI() method in the AIController class within the AIController.cs source file to look like the following:

public void PlayerCollideWithAI(Character thePlayer, List<Character> theAI)
{
    //Goes through all the AI
    for (int i = 0; i < theAI.Count; i++)
    {
        if (!Graphics.IsSpriteOffscreen(theAI[i].Sprite) && theAI[i].Alive && CalculateDistance(thePlayer, theAI[i]) < 120)
        {
            //Checks if the Player has collided with an AI
            if (Physics.HaveSpritesCollided(thePlayer.Sprite, theAI[i].Sprite))
            {
                //Moves the Player back
                Graphics.MoveSprite(thePlayer.Sprite, Physics.InvertVector(thePlayer.Sprite.Movement));
                //We only want to move back once, to avoid glitches where
                //we bounce between 2 AI.
                return;
            }
        }
    }
}

Not much has changed in the routine, except for 1 thing. We've added an additional if statement before we check if the 2 character's have collided.

This if statement, first checks that the AI is Not Off the Screen, since if it is off the screen, its pointless checking for collisions. Next we check if the AI is alive, if it isn't alive, we obviously shouldn't be able to collide with it. Finally it checks that the Player and AI are within a reasonable distance to be checking for collisions, I've chosen a distance of 120.

Creating the AI vs AI Collision method

Add the following method to the AIController class in the AIController.cs source file:

public void AICollideWithAI(Character theAI, List<Character> allAI, int index)
{
    //Go through all the AI
    for (int i = 0; i < allAI.Count; i++)
    {
        //Skip the AI that is checking the other AI
        if (i != index)
        {
            //Check that the AI is alive, Onscreen and within distance
            if (!Graphics.IsSpriteOffscreen(allAI[i].Sprite) && allAI[i].Alive && CalculateDistance(theAI, allAI[i]) < 120)
            {
                //Check if the Sprites have Collided
                if (Physics.HaveSpritesCollided(theAI.Sprite, allAI[i].Sprite))
                {
                    //Move the AI Back 1 Step
                    Graphics.MoveSprite(theAI.Sprite, Physics.InvertVector(theAI.Sprite.Movement));
                }
            }
        }
    }
}

This new method takes in 3 parameters, the first is the AI that is checking if its colliding with any other AI. The second parameter is all the AI in the game, and the third is the index of the AI from the first parameter.

We get this index, so when we go through each AI to see if they collide, we skip the check that would otherwise result in the AI checking if it collides with itself.

Just with the Player vs AI collision, we check if they are on the screen, and are alive and within a reasonable distance. And if there is a collision, the AI character is moved backwards 1 step.

Optimizing the UpdateAI Method

This is the biggest change in this tutorial, since we'll be optimizing almost every aspect of the AI Handling routines.

Modify the UpdateAI() method in the AIController class within the AIController.cs source file to look like the following.

public void UpdateAI(List<Character> theAI, Character thePlayer, Map theMap)
{
    //Go through each AI
    for (int i = 0; i < theAI.Count; i++)
    {
        //If the AI is Onscreen and alive, contine.
        if (!Graphics.IsSpriteOffscreen(theAI[i].Sprite) && theAI[i].Alive)
        {
 
            //If the AI can move, and is within sight distance, move
            if (theAI[i].CanMove && CalculateDistance(theAI[i], thePlayer) < 300 && CalculateDistance(theAI[i], thePlayer) > 30)
            {
                //Moves the AI
                MoveAI(theAI[i], thePlayer, theMap);
                //Checks if the AI has collided with the player, and if so
                //Moves the back
 
                //Check for collisions with the Player
                AICollideWithPlayer(theAI[i], thePlayer);
 
                //Check for collisions with other AI
                AICollideWithAI(theAI[i], theAI, i);
            }
            //Otherwise the AI just points towards the player
            else if (CalculateDistance(theAI[i], thePlayer) < 300)
            {
                //Points the AI towards the Player
                PointAI(theAI[i], thePlayer);
            }
 
            //If the AI can attack, and is within attacking distance, attack
            if (theAI[i].CanAttack && CalculateDistance(theAI[i], thePlayer) < 45)
            {
                theAI[i].InitiateAttack();
            }
 
            //Updates the AI's Walking Animation
            theAI[i].UpdateCharacterAnimation();
 
            //Draws the AI
            Graphics.DrawSprite(theAI[i].Sprite);
        }
    }
}

Some big changes to this method, we'll walk through each of them.

Next we've added an if statement which checks that the AI is both on the screen and is alive, if either of these conditions are false, we skip over and continue on to the next AI.

The good thing about this is, is now the AI won't animate, draw, move, point, or collide unless its on the screen and alive.

With the moving of the AI, we've included a 2 checks in the if statement that checks if the AI is within a reasonable distance to see the player, and so will start moving towards him. We've also set a minimum distance, so that the enemy doesn't get too close, and result in the AI and the Player getting stuck.

We've also included the AIColideWithAI() method in the movement section of the code.

Pointing the AI has also changed, if the AI is within a reasonable distance, the AI will point towards the Player.

Also we've added the attack section to the UpdateAI() method. If the AI canAttack and is within attacking distance, the AI will attack.

And as usual, we update the animation, and draw the sprite.

Image:RPGss8.PNG

The Project so far

Source code for the tutorial up until this point can be found Here.

It is strongly recommended that you read through the code while going through the tutorials, to get a better understanding of how it works.

Tutorial Table of Contents

  1. The Legend of the Tomato Quest Tutorial
  2. Loading the level
  3. The Player
    1. Loading and Drawing the Player
    2. Moving the Player
  4. Player Stats
  5. User Interface
  6. Attacking Animation
  7. AI
    1. The Healer
    2. The Healer II
    3. Enemies
    4. Enemies II
  8. Interaction
  9. Combat
  10. Items and End Game Conditions
  11. Additions and Improvements