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
- AI colliding with AI
- Optimizing the Collisions
- Making the AI move and attack, only when they are within a reasonable distance
- Making the AI move and attack, when they are alive.
- Monitor the AI's health so that when they have no health left, they die.
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.
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.

