An important part of most AI’s is line of sight. This article describes an original technique, which uses no detectors at all, is fast and the vision angle can be easily adjusted. This article is quite long, if you prefer to read it, split up in pages, goto http://www.crobasoft.com/tools/article/line-of-sight/1
Note: This article is not intended for any specific multimedia tool or language, this is a general article, and the techniques I explain here, can be implemented in most multimedia tools and languages.
The usual approach to line of sight is simply shooting out a detector in the direction of the enemy, and if it hits the player, the enemy can see him. There quite a few problems with approach:
• The enemy can only see what’s immediately in front of him, if the player is slightly to the right, or the left, the enemy wont be able to see him
• This method usually slow. Say you the enemy shoots out a detector every second, and it was looking straight at you, it might take a second for the detector to be shot, and even more time before it hits you, and the enemy becomes aware of you.
• And then there’s the obvious problem with too many objects. If you had 10 enemies each shooting out a detector every second, you could end up with a lot of objects, which might slow the game down considerably.
But of course, this method might work for some simple games, however, in most situations, you will need your enemy to have a broad line of sight, and you want it to see the player instantly. Therefore I have developed a method, which works by comparing the angle and distance between the player and enemy. In this article, I will walk you through the steps of creating an enemy that has a sight similar to this:
This is how our ideal enemy will see. The red ellipse is the enemy, and the area shown pink is the part of the map it scans. Scanning such a large area seems slow, however, we will not scan that entire area at once, only the parts that’s needed, which makes it relatively fast. Looking at this diagram for a while, you will notice that there are 3 things that limit the line of sight: Distance, Angle and obstacles. The latter isn’t made clear here, but I will go in to more detail further down. These 3 limitations will be our conditions. All 3 of these conditions must be true before the enemy see the player. I’ll refer to these conditions as flags that can either be turned on or off; this is the same as a Boolean value that can either be TRUE or FALSE, but I will stick to flags as it’s easier.
In this image, our red ellipse is the enemy, and the blue is the player. In this example, an obstacle have been added, that the enemy cannot see through. Thus, in this case, the enemy cannot see the player, as he is hiding behind a wall. So, before the enemy can see the player, the path between them, must be obstacle-free. When the path is obstacle free, we turn ON the obstacle flag, and when it is not obstacle free, we turn it OFF. I will explain one way to check for obstacles later in the article.
In this diagram, the enemy does not see the player either, because the enemy cannot see that much to the right. So, before the enemy can see the player, it must be within a certain angle from the enemy. We call this flag within_angle; it must be turned ON when the player is within the angle, and off when he’s not. I’ll explain how to get the angle between them, later in this article.
And last, the enemy cannot see for miles, so we must have a limit, as to how long it can see. In this case, it can see 200 pixels straight ahead. So, when the player is within 200 pixels, the distance flag is turned ON, and when he’s not, it’s turned OFF. I’ll explain how to get the distance later in this article.
Obstacle recognition
The first step in the line of sight algorithm is to recognize obstacles, which might be between the player and enemy. This step can either be very hard, or very easy, depending on what tool you use. What we will want to do is shoot a detector from the enemy, towards the player. (Note: A detector doesn’t have to be another object; it could just be an X and a Y value) When this detector moves from the enemy towards the player, you check if it hit any obstacle on its path. If it hits anything on the way, you know there’s an obstacle in the way, and you turn the obstacle flag OFF, but if it reaches the player without hitting an obstacle on the way, it means there are no obstacles in the way, and you turn the obstacle flag ON.
The hard part here (In some languages/multimedia tools) is moving the detector. I’ll assume there are no built-in features in the tool you use to help you with this. We first need to figure out the angle between the player and enemy, so we know which direction to move the detector in. This can (in most languages) be done by:
If you don’t have the Atan2 function available, you’ll have to find another way to get the angle between the points.
After that, you have to move the detector in a loop, one pixel at a time.
X = Enemy.X + Cos(angle) * Loop step/index
Y = Enemy.Y + Sin(angle) * Loop step/index
You run this loop until you hit an obstacle, or reach the player.
Distance comparison
As explained in the introduction, our enemy’s line of sight must have 3 restrictions: Distance, obstacles, and angles. In this section, I will be focusing on distance.
In real life, a person would have a limited viewing distance. For example, must people wouldn’t be able to see another person a mile away, even if there’s no obstacles in between, so we need to add a similar system to our line of sight algorithm. First, we need to figure out the distance between our enemy and player.
Finding out the distance is relatively easy, I’ll explain how to do it using Pythagoras, which is the standard way of getting the exact distance between two points.
a^2+b^2=c^2
Pythagoras works on the principle, that in any right triangle, the length of the longest side, or the side that is opposite to the right angle (The Hypotenuse) increased by a power of 2, is equal to the length of the 2 shortest sides or the sides that is not opposite to the right angle, increased by power of 2.
In the above equation, the Hypotenuse is expressed a c, and the other two sides are expressed as a and b. From these facts, we can conclude that to get the distance between two positions (in this case the Player and enemy), we must use the following equation:
Squareroot((X1-X2)^2+(Y1-Y2)^2)
After you have figured out the distance, you simply need to compare it to the enemy’s maximum viewing distance. In this case, its 200, so simply check if the distance is lower than 200; if it is, turn the distance flag ON, if it’s not, turn it OFF.
Angle Comparison
The last step is to make sure the player doesn’t stand out of the enemy’s viewing angle. For example, if you were looking forward, and someone was standing to the immediate right or left of you, you wouldn’t be able to see him, as illustrated in diagram 2.B
First thing first, we need to figure out the angle between our enemy and player. This was covered in the distance comparison section.
When you have figured out the angle between the player and enemy, you compare it to the angle the enemy is facing. In diagram 2.B, the enemy can see 45 degrees to the left, and the same to the right. So, we need to figure out if the player is within this area.
Abs(Angle-Enemy_viewing_angle)
Now you simply compare if the value is lower than the maximum angle, in this case; 45. If it is, turn the angle flag ON, if it’s greater than 45, turn it OFF.
That’s pretty much it; we now have 3 flags, all you have to do is check whether all 3 of these are turned on, If they are, you know the player is within the enemy’s sight.
. Spread a value through all objects that are on-screen (using the 'Is object getting closer than -32px from Window's edge' condition)
. Loop through these checking the angle between each enemy and the player.
. If the angle is acceptable, Move Safely 2 positions a detector first at the Player, then at the enemy. It checks for obstacles in between.
. If there are no obstacles, trigger this object's AI to 'Chase' mode.
. If there are obstacles, set the AI mode to 'don't Chase' mode (or whatever you want to happen)
. Optionally, throw in another check to see if the enemy could see you on the previous event loop.
Then code how the enemies should act in each 'mode' of their AI.
excellent article, but yeah, you should check for obstacles after distance and angle because MMF doesn't have collision masks for active objects so you need to shoot an active object to test collision.
omfg you are a bit retarded man u dont need 2 use a graphical parent to check over lap its like the stupidest idea u would get so many ploblems such all 2 many enemys set as seeing u at once as well as it u use a lot of vertual memory u can use a maths equasion 2 work out if there in frpunt of you o well for beginers its an ok idea
Not that what you said made much sense, but you obviously didn't read more than the first few paragraphs. I am using math equations, not a "graphical parent" to check if they're in front of you, I only added the pink area on the diagrams to make it obvious what part it scans. Next time, try reading an article before calling it's author retarded, moron.
no sorry i was buisssy i cauldent finish wat i was writing u dont actually need any ectra objects ill upload a sample showing u how to do it in like 2 command lines
Definitely more efficient if you only take obstacle recognition if the angle and distance flags are on. In fact with multiple enemies in a game it can quickly become unplayably slow if you don't do this.
Good article though.
You can be sneaky and use queing to optimise this technique greatly. I've had 30 objects all in the same place, ALL testing for obstacles between each and every other and running at a perfect frame rate too.