Game Maker Community YoYo Games

Welcome Guest ( Log In | Register )

> Tutorials Rules

This forum is for clear and concise tutorials and well documented examples. Tutorials and examples posted here must follow the format in the pinned Rules and Guidelines.

All new topics must be approved by a moderator before they appear. Don't post twice!

 
Reply to this topicStart new topic
Arc Of Sight, Included in guard-like A.I. example
gmXpert2000
post Apr 28 2008, 05:30 AM
Post #1


The Game Boy
Group Icon

Group: GMC Member
Posts: 2117
Joined: 9-March 04
From: Yes
Member No.: 6861



QUOTE (Local Moderators)
Replies to topics in this forum are held to a high standard. Reviews must have a critique or suggestion for use to be approved. Read the rules here.


Author: SoulRed12 (my name on YYG)
Title: Arc of Sight
Partner file: Link
Version: GM7, either Pro or Lite. However, if you have the Lite version, go to the constants section of the game settings and set "drawrotated" to 0. That will turn off the use of draw_sprite_ext. The sprites won't rotate as the objects move in different directions, but that's okay because it doesn't have to do with what's actually being described in the tutorial. As long as you keep the drawarc constant set to 1, you'll still be able to easily observe the intended effect inside this example.

Updates to example:
V2 (5/17/08):
Example now supports multiple players and multiple instances of a single player. Because the enemies search for an instance of "GoodGuy", which is now the parent of "Player1" and "Player2", no matter how many players you have, as long as they all have "GoodGuy" as their parent object, the enemies will still search for all of them.

Comments:

Hello everyone, I've written up a new tutorial in which you will learn how to make an object test for another object inside an "arc of sight". The first thing that probably comes to your mind is the similarity to "line of sight". A line of sight is a line usually drawn from one object to another object to make sure nothing is in the way. For example, an enemy must shoot towards the player, but only if a wall is not between them.

There's collision_line() for that, and collision_* for other types of tests. But we want to use more of a circle sector shape. Essentially, an arc of sight.

This is very useful for effects such as a guard carrying around a flashlight; if the light shines on you, you're seen and the guard chases after you. But you can't get that effect without being a little creative.

I have included a partner gmk file, so you can see the effect visually. In this file you play as a little blue guy walking around trying to find the diamonds. Also in the area are red guys sauntering around, guarding these diamonds. If they see you, they chase after you and if they catch you, you die. Run out of their sight and they continue pacing around again.

First I'd like you to download and run this file to see the final effect.

Note: this tutorial assumes you have at least a basic knowledge of geometry. More specifically, you must know what a circle's radius is.


The partner file includes most of the direct info for a practical example, but here I will describe the concept behind this arc of sight. The basic theory is that we need a different shape than can be provided by Game Maker's functions. This is the circle sector.

The orange portion is the shape I'm referring to:


How do we achieve that? Quite simply, we fake it.

What do you notice about this circle, and circles in general? The length of any line starting from their center (marked with a black dot in the picture) and ending on the circle's rim is equal to the circle's radius. Therefore, no matter where the object is inside the circle sector, the distance from the object to the center point must always be smaller than the radius of the full circle. Imagine the small red circle is the object, and the "r"s marks the circle's radii (plural of radius):



Then there's the issue of being inside the circle, but not being outside the desired orange region. We only want to "see" the object when it's inside the orange region. If the distance from the object to the center point is smaller than the radius, then the object is inside the "circle" somewhere, but not necessarily in the region we want. It could be like this:



As you can see, the object is still inside the full circle (the distance from the object to the centerpoint is smaller than the circle's radius), but the object is not inside the orange region.

So we have to make sure the direction does not stray too far from the direction that would point straight through the center of the circle segment. Shown by the white line here:

Diagram 4
(the board won't let me post any more images)


There is an equal amount of space on each side of the white line, so the angles formed at the center point on either side of the white line are also equal. Thus we need to define an angle offset. The greater this value, the more of the circle is included in the sector.



So now we have the basic theory. For the object to be inside the circle at all, the distance from the object to the circle's centerpoint must be less than the circle's radius. For the object to then be also inside the circle sector, we must make sure the angle from the center point to the object does not differ from the center angle (white line) by more than the offset value. Note that the circle sector does not have to be in the top part of the circle. The "center angle" could point to the right and the same idea still comes into play:



I encourage you now to examine the .gmk file which explains how all this is done in GML coding. Basically we use point_distance() and point_direction() to "make" the arc of sight as described above.

I think I did a good job of over-commenting, so you should be able to find your way around it. There are some other goodies in the gmk that I hope will also be helpful to you. These include:

-A single script for testing an arc of sight which can be used in any game, including non-topdown games
-Another script which draws a circle sector on the screen
-Topdown guard-like A.I.

Enjoy the tutorial. If you use it, some credit would be cool. Thanks.

This post has been edited by gmXpert2000: May 17 2008, 10:53 PM
Go to the top of the page
 
+Quote Post
AR_85
post May 13 2008, 01:22 PM
Post #2


GMC Member
Group Icon

Group: GMC Member
Posts: 20
Joined: 4-November 07
Member No.: 92240



Hi, I really like this and have implemented it in my test game.

A problem which I found is that the "bad guy" will only kill 1 object of the "good guy".

What I was experimenting was to put serveral "good guys" in the level and the "bad guy" would kill all of them if it can see them. However the "bad guy" can only see the first created object of the "good guy" and not the others...
Go to the top of the page
 
+Quote Post
Overman
post May 14 2008, 01:12 AM
Post #3


GMC Member


Group: Banned Users
Posts: 1121
Joined: 13-April 08
Member No.: 104330



Loop through all existing instances of the "good guy."
Go to the top of the page
 
+Quote Post
gmXpert2000
post May 14 2008, 10:38 PM
Post #4


The Game Boy
Group Icon

Group: GMC Member
Posts: 2117
Joined: 9-March 04
From: Yes
Member No.: 6861



This example was built under the basis of a basic standard game; a single player and enemies. However, it can be easily adapted to having multiple players.

If you want to have more than one player object, for example so you can have "objPlayer1" and "objPlayer2", or "GoodGuy1" and "GoodGuy2" for the case of this example, simply use the function twice. The first time use it with argument0=GoodGuy1, and the second time with argument0=GoodGuy2. If either one returns true, then store that object index in a variable.

CODE
var followobj; //Tells the bad guy which player to follow
followobj=noone; //Initialize to "noone"

//Pick an object to follow, depending on which one we can see
if (ArcOfSight(GoodGuy1,sight_maxdist,dir,sight_angleoff)) followobj=GoodGuy1;
else if (ArcOfSight(GoodGuy2,sight_maxdist,dir,sight_angleoff)) followobj=GoodGuy2;


Finally change "if (cansee)" to "if (followobj!=noone)", because we've switched methods.

Then, in inside that code block, find the line with point_direction() and change it to use your variable instead of just "GoodGuy".

So that whole part would now look like this:

CODE
//Now check if we can see the player.
var followobj; //Tells the bad guy which player to follow
followobj=noone; //Initialize to "noone"

//Pick an object to follow, depending on which one we can see
if (ArcOfSight(GoodGuy1,sight_maxdist,dir,sight_angleoff)) followobj=GoodGuy1;
else if (ArcOfSight(GoodGuy2,sight_maxdist,dir,sight_angleoff)) followobj=GoodGuy2;

if (followobj!=noone)
{
    running=1;
    move=1;
    dirto=point_direction(x,y,followobj.x,followobj.y);  //<--This line is changed

[...then the rest of the code...]


----------------------------------------------------------------------------------

I can't think of any reasons why in a game there would be more than one instance of a particular player object in the room, but if you do need to do so, try something like this:

Loop through each instance of "Good Guy", as overman stated. Use instance_nearest(), then the script, and finally move the instance of Good Guy far away so it won't be found with instance_nearest() until we're done. You're going to use the same principle as above (using a variable to control who the enemy follows) but this time the variable is going to contain an instance id.

CODE
//Now check if we can see the player.
followobj=noone;

//Go through each Good Guy and test if we can "see" them
while (instance_exists(GoodGuy)) //Repeat endlessly (we use "break" later)
{
    //Get an instance.  This will return noone when
    //all instances are at the same position.
    inst=instance_nearest(x,y,GoodGuy);
    if (inst==noone) break;

    //If we can "see" this instance of player, store it's id and break from the loop
    if (ArcOfSight(inst,sight_maxdist,dir,sight_angleoff)) {followobj=inst; break;}
    
    //Store instance's old x value and then move it far away
    with (inst)
    {
        oldx=x;
        x=-999999;
    }
}

//Now reset all the Good Guy's x positions
with (GoodGuy) x=oldx;


This whole code snippet would replace the old "cansee=ArcOfSight(...)" line, just as the first method I mentioned in this post did.

Finally, again, instead of "if (cansee)" you would use "if (followobj!=noone)" and "followobj" instead of "GoodGuy" inside the point_direction().

Hope that helps.

EDIT: Removed unnecessary part of while loop expression =p (shouldn't actually change anything)
EDIT 2: Ok, through personal messages, AR_85 has alerted me of a bug in the multiple-instances-of-one-player-object code that I wrote inside this post. I have changed it so that it works for any instances of the player. I also updated the main GMK in the main post because I realized this code can be used with parents to let an object search for multiple instances of one player object and multiple player objects. "GoodGuy" is now the parent of all the player objects (Player1, Player2, and however many more players you want), therefore the enemies will search for any and all of them.

This post has been edited by gmXpert2000: May 17 2008, 10:55 PM
Go to the top of the page
 
+Quote Post
NAVgames
post May 16 2008, 10:40 PM
Post #5


GMC Member
Group Icon

Group: GMC Member
Posts: 71
Joined: 24-April 08
Member No.: 105196



You explain this better than my maths teacher tongue.gif. Good job.

NAVg
Go to the top of the page
 
+Quote Post
j0nteh
post Jul 22 2008, 07:21 PM
Post #6


GMC Member
Group Icon

Group: GMC Member
Posts: 51
Joined: 20-July 08
Member No.: 111177



I love this!! smile.gif and i'm using it in my game.

But i do have some questions;

Question nr1:
Since i suck at scripting i used d&d to make the bot with the arc stop when it reaches object: obj_wall. But the stupid bot still tries to run thrue the wall so how can i make it turn when it reaches the wall?

Question nr2:
Is it possible to make the bot shoot while following the player?

Question nr3:
And how can i stop the bot from seeing the player thrue solid objects?

Go to the top of the page
 
+Quote Post
Squirmonkey
post Nov 15 2008, 11:42 PM
Post #7


GMC Member
Group Icon

Group: GMC Member
Posts: 290
Joined: 18-June 08
Member No.: 109188



This is cool! definately want to find a way to use it.

Ive noticed however, that when the enemies see the player, he sort of freezes up, stops moving.

Is there any way to avoid that happening, because ive looked through the code and cant find whats causing it
Go to the top of the page
 
+Quote Post
Mark13673
post Nov 16 2008, 07:34 AM
Post #8


Nocturne Games
Group Icon

Group: GMC Member
Posts: 3175
Joined: 25-April 08
From: Scotland/Spain
Member No.: 105300



This is an incredibly useful and well written tutorial... The code (for the arc) is simple, clean and fast, the comments perfectly written and the explanation in the first post one of the best I´ve seen...

Good job, and I will recommend this on the forums to all who ask about AI and line of sight... (and may even use it myself to replace my current collision triangle script!!!)
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 



RSS Lo-Fi Version Time is now: 21st November 2009 - 08:01 AM