This site requires JavaScript, please enable it in your browser!
Greenfoot back
jimboweb
jimboweb wrote ...

2014/6/24

A new way to determine if one actor can "see" another

jimboweb jimboweb

2014/6/24

#
Instead of a question, I have an answer to a common question! I've seen a number of people on the discussion board asking about a way to determine if one actor can "see" another with no obstacles blocking them. The usual solution to this is an "invisible projectile" which is stopped by obstacles. This isn't the ideal solution, because it doesn't happen instantaneously, and the faster the projectile goes the more likely it is to "skip over" the object to be seen. Below is a new solution. This is the entire code for an actor called LineOfSight that draws a line between two characters. It has a boolean method called "clearLineOfSight" that returns "true" if the character can be seen. In the clearLineOfSight method you will want to change the class of the object from Wall.class to whatever class obstructs the actor's vision. To use this, simple create a new actor with no image named LineOfSight and replace its code with the code below. When you create it in your world, pass the two actors it should connect, as in:
1
LineOfSight los = new LineOfSight (actor1, actor2)
You'll need to make the code available to the object that wants to "see" by passing it as an argument or returning it through a method in the world. Here is the complete code of the class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.awt.Color;
/**
 * Write a description of class LineOfSight here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class LineOfSight extends Actor
{
    Actor mouse;
    Actor cat;
    int mouseX;
    int mouseY;
    int catX;
    int catY;
    GreenfootImage myImage;
    /**
     * Act - do whatever the LineOfSight wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public LineOfSight (Actor c, Actor m)
    {
        cat = c;
        mouse = m;
        setImage(new GreenfootImage(1,1));
    }
    public void act()
    {
        if(mouse.getWorld()!=null && cat.getWorld()!=null)
        {
            mouseX = mouse.getX();
            mouseY = mouse.getY();
            catX = cat.getX();
            catY = cat.getY();
            int myX = (catX + mouseX)/2;
            int myY = (catY + mouseY)/2;
            setLocation(myX, myY);
            turnTowards(mouseX, mouseY);
            int d = (int)Math.sqrt(Math.pow((catX-mouseX),2)+Math.pow((catY-mouseY),2));
            setImage(new GreenfootImage(d,1));
            /* commented out section makes line of sight visible
            myImage = getImage();
            myImage.setColor(Color.BLACK);
            myImage.drawLine(0,0,d,0);
            */
        }
    }   
    public boolean clearLineOfSight()
    {
        return(getOneIntersectingObject(Wall.class)==null);
    }
}
jimboweb jimboweb

2014/6/24

#
Obviously in my example it was a cat seeing a mouse, but "cat" and "mouse" could represent any two actors you pass to it.
jimboweb jimboweb

2014/6/24

#
If you want to see the line, just uncomment the commented section!
jimboweb jimboweb

2014/6/24

#
A video lesson showing how this was created can be seen at: http://www.youtube.com/watch?v=qvjvqRzCqow&list=UUHLduiuV8Sz5KlLtnjmaBCA&feature=share
jimboweb jimboweb

2014/6/24

#
I also just published the full scenario so you can see it: http://www.greenfoot.org/scenarios/11737
jimboweb jimboweb

2014/6/24

#
lordhershey lordhershey

2014/6/24

#
That is a neat solution. I think I would change the setImage(new GreenfootImage(1,1)); refer to a class static instance like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*Static Class Member*/
static GreenfootImage SinglePelImage
static
{
  //This static block executed once upon class load
  SinglePelImage = new GreenfootImage(1,1);
}
 
 
/*To initialize the image later in the code*/
Constructor()
{
.
.
.
setImage(SinglePelImage); 
.
.
.
}
danpost danpost

2014/6/24

#
I think I would use 'Math.hypot' instead of the combination of 'Math.sqrt' and multiple 'Math.pow's. Also, instead of just not running the act method if one or both actors (cat and mouse) are not in the world, I would have the LineOfSight object remove itself from the world by appending the following 'else' block:
1
else getWorld().removeObject(this);
jimboweb jimboweb

2014/6/25

#
Thanks for the suggestions danpost and lordhershey. Of course line 40 should use Math.hypot. I had forgotten that that was a method in Java; that will make it much neater. And making the line of sight remove itself when one of the actors is not in the world is also a good idea. Lordhershey, just out of curiosity, what is the advantage of declaring the single pixel image as a static before I use it? I'm not saying it's not, I just don't know what the benefit is and I'd like to. Does it save memory, or is it so that I can use it later?
lordhershey lordhershey

2014/6/25

#
So you do not rapidly create and then destroy an object, doing this over and over again can cause lag at odd times since the Garbage Collector will constantly be trying to reclaim all of the unreachable objects. Once the setImage is set again the reference to the original object is lost and the GC has to go get it (when it can), It might not matter too much in this case since the number of objects made is not very large.
danpost danpost

2014/6/25

#
I think I see where lordhershey is coming from; though, his original post does not seem to have much of anything to do with this issue (a LineOfSight object is only being created once in a blue moon and the pixel-sized image is used once at that time). However, you are creating a new GreenfootImage object each act cycle for the LineOfSight actor at line 41. You could change that line to the following:
1
getImage().scale(d, 1);
to avoid overhead on the GC.
You need to login to post a reply.