Creating a Smart Enemy
As a game gets more complex, it is important to add more comprehensive enemy types to challenge the player. A simple way to start adding more depth is to make an enemy “aware” of its surroundings. Although this article will be focusing on the simpler worlds of 2D gaming, this can be considered a predecessor to more elaborate AI functions.
Objective: Create an enemy that can detect when the player is behind them and fire accordingly.
Detecting the player
In order to detect the player, we will be using a CircleCast. A Circle Cast is similar to firing a sonar from a specific point in a given direction. If any objects intercept the sonar-like structure, they will be detected and reported.
To implement a CircleCast, we need to specify the origin from where the circle is being cast, the direction and distance it will travel. Lastly, we can also add a specification via layer masks so that we can tell the sonar exactly what we are looking for and ignore any other objects it comes in contact with.
Let’s take a look at this step-by-step:
- Declaring our global variables
_rayDistance: As the name suggests, this is how far the ray will travel. You can tweak this value to better suit your needs.
_rayCastRad: The radius of our circle. Determines how wide of an area we will be covering as our detection zone. This variable is a SeirlizeField so that it can be visible and manipulable in the inspector as needed.
Note: If you are unsure on the specific numbers you should be using, you can always do a quick Debug.DrawRay, which is explained in this article.
2. Coding our CircleCast into existence
Create a new void method to handle the enemy’s attack behavior. You can name this whatever you like, in my case, this is the backAttack method. The CircleCast will be placed here. The syntax goes as follows:
The origin: We are using the position of the object to which the script is attached, in this case, an Enemy object.
The direction variable uses the in-library Vectory2.up as it already comes with the preset vectorial data we need to go in said direction.
Note: You can use any of the vectorial directions you need for your game, in this article I use Vector2.up because of the way my 2D game is oriented.
LayerMask.GetMask(“Player”): Returns the specified layer. This will make it so all other objects outside of this assigned layer will be ignored. You can assign layers from the inspector as follows:
Attacking the Player from Behind
Now that we have a way to detect the player, we can create a firing method that executes when the player is detected from behind.
- To create a firing method, you can simply instantiate your projectile. We will be coding this to execute when the player is detected later on.
Note: Quaternion.Euler can be left as the Quternion.rotation, which will leave the instantiated object in its defaul rotation. In my code, I am using Euler to manipulte the rotation of my object in order to match the direction in which I want it to go due to its default rotaion being the opposite.
Personally, my code(the image below) might look more complicated but that is because my projectile has child objects attached to it which require a bit more thought.
Furthermore, the first two yellow variables are part of a cooldown system which makes it so that the firing is more controlled rather than a continuous and seemingly endless stream of projectiles. You can learn more about this cooldown system here.
Now that we have a firing method, we can make it execute once the player is detected. Let’s go back to the method we created before for the attack behavior.
2. First, we check if there are existing colliders.
3. Compare the hit variable with the desired tag. Remember, this hit variable is the return value for the CircleCast, which is set to ignore all objects that are not within the assigned “Player” layer mask.
Note: Once again, the yellow variables here are part of the aforementioned cooldown sytem.
Note: Reremeber that a tag is not the same as a layer. The tag will need to be assigned in the inspector as well.
4. Lastly, call the method once if the return value hit is the same as the desired tag.
Note: Here I added a debug line of code to make sure the player is being detected. This is completely optional but useful.
By now you should have an enemy that can fire at the player when they are detected behind the enemy.
Unfortunately, I had been unable to post articles for quite some time but I’m excited to be back! Lots of interesting cool stuff coming soon!