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

2017/4/7

Location of Objects

ironphoenix20 ironphoenix20

2017/4/7

#
Hi, guys. I posted a discussion about this simulation a while ago but I have a different problem now. I want the guy (Scavenger) to only collect the metal objects that are on the world (which is getX() > 400 since the width of my world is 800. but it isnt working right now. can you see how i would do that or any other errors i have? I will post all the classes just in case. Scavenger (Collector) 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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.ArrayList;
 
/**
 * Write a description of class Scavenger here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class Scavenger extends Actor
{
    private Metal targetMetal;
    int numMetals;
    private ArrayList<Metal> metals;
    private int metalCount;
     
    private int metalCollect = 0;
    private int mySpeed = 2;
    private int count = 0;
     
    private int offset;
    //int initialX = this.getX();
    //int initialY = this.getY();
     
    /**
     * Act - do whatever the Scavenger wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        {
            //hp -= DesertWorld.BUG_DEATH_RATE;
            if (count % 8 == 0) // Only run every 8 acts to avoid lag
            {
                targetClosestMetal ();
                //hpBar.update(hp);
            }
            // If my current target Metal exists, move toward it
            if (targetMetal != null && targetMetal.getWorld() != null)
            {
                moveTowardOrCollectMetal();
            }
            else if (getWorld().getObjects(Metal.class).size()==0)
            this.setRotation(0);
            // If I can't find anything to eat, move in a random direction
            /*else
            {
                moveRandomly();
            }*/
        }
        count++;
        //collectMetal();
    }
     
    //Constructor that initializes a new metal collector guy
    public Scavenger()
    {
        GreenfootImage guy = new GreenfootImage("scav guy.png");
        guy.scale(100,100);
        this.setImage(guy);
    }
     
    public void targetClosestMetal()
    {
        double closestTargetDistance = 0;
        double distanceToActor;
        numMetals = getWorld().getObjects(Metal.class).size();
        metals = (ArrayList)getWorld().getObjects(Metal.class);
         
        if (metals.size() > 0)
        {
            // set the first one as my target
            targetMetal = metals.get(0);
            // Use method to get distance to target. This will be used
            // to check if any other targets are closer
            closestTargetDistance = MyWorld.getDistance (this, targetMetal);
 
            // Loop through the objects in the ArrayList to find the closest target
            for (Metal o : metals)
            {
                // Cast for use in generic method
                Actor a = (Actor) o;
                // Measure distance from me
                distanceToActor = MyWorld.getDistance(this, a);
                // If I find a Flower closer than my current target, I will change
                // targets
                if (distanceToActor < closestTargetDistance && a.getX()>400)
                {
                    targetMetal = o;
                    closestTargetDistance = distanceToActor;
                }
            }
        }
    }
     
    /**
     * Private method, called by act(), that moves toward the target,
     * or eats it if within range.
     */
    private void moveTowardOrCollectMetal ()
    {
        turnTowards(targetMetal.getX(), targetMetal.getY());
        
        if (this.getNeighbours (30, true, Metal.class).size() > 0)
            {
            // If I was able to eat, increase by life by flower's nibble power
            getWorld().removeObject(targetMetal);
            /*if (tryToCollect > 0)
            * {
            metalCollect += tryToCollect;
            }*/
            }
            else
            {
            move (mySpeed);
        }
    }
    
    private void randomMove()
    {
        if (Greenfoot.getRandomNumber (100) == 50)
        {
            turn (Greenfoot.getRandomNumber(360));
        }
        else
            move (mySpeed);
    }
}
Metal 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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.ArrayList;
 
/**
 * Write a description of class Metal here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class Metal extends Actor
{
    //A list of all the metals that has been collected by a team
    public static ArrayList<Integer> collectedMetals = new ArrayList<Integer>();
    public static int numMetals = 0;
     
    /**
     * Act - do whatever the Metal wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
         
    
     
    public Metal()
    {
        GreenfootImage metal = new GreenfootImage("metal.png");
        metal.scale(100,100);
        this.setImage(metal);
    }
}
World 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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.ArrayList;
 
/**
 * Write a description of class MyWorld here.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class MyWorld extends World
{
    private int actCounter = 0;
    private boolean scoreShowing;
    private int metalCount;
    private int initialX;
    private int initialY;
     
    private ScoreBar scoreBar;   
    private TextButton metalButton;
    private Scavenger g1;
     
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public MyWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(800, 600, 1);
        GreenfootImage bg = new GreenfootImage("white back.png");
        bg.scale(getWidth(), getHeight());
        setBackground(bg);
         
        Metal.numMetals = 0;
         
        Metal m1 = new Metal();
        addObject(m1,300,300);
         
        Metal m2 = new Metal();
        addObject(m2,350,350);
         
        g1 = new Scavenger();
        addObject(g1, Greenfoot.getRandomNumber(400), Greenfoot.getRandomNumber(480)+120);
        initialX = g1.getX();
        initialY = g1.getY();
         
        ScoreBar scoreBar = new ScoreBar (800);
        addObject(scoreBar, 400, 15);
        scoreBar.update("Hi!");
         
        // Add the spawn button to the world
        metalButton = new TextButton ("  ->  Metals  <-  ", 24);
        addObject (metalButton, 400, 550);
         
        spawnMetal();
    }
     
    public void act()
    {
        actCounter++;
        if (actCounter % 30 == 0)
        {
            //statUpdates();
            //if (scoreShowing)
            //{
                //scoreBar.update(metals);
            //}       
        }
         
        if (Greenfoot.mouseClicked(metalButton))
        {
            spawnMetal();
        }
        returnHome();
    }
     
    /**
     * Method to spawn a single bug at a random location
     */
    private void spawnMetal ()
    {
        addObject (new Metal (), Greenfoot.getRandomNumber(800), Greenfoot.getRandomNumber(480)+120);
        scoreShowing = true;
    }
     
    /**
     * Static method that gets the distance between the x,y coordinates of two Actors
     * using Pythagorean Theorum.
     *
     * @param a     First Actor
     * @param b     Second Actor
     * @return float
     */
    public static float getDistance (Actor a, Actor b)
    {
        double distance;
        double xLength = a.getX() - b.getX();
        double yLength = a.getY() - b.getY();
        distance = Math.sqrt(Math.pow(xLength, 2) + Math.pow(yLength, 2));
        return (float)distance;
    }
     
     
    private void returnHome()
    {
      metalCount = getObjects(Metal.class).size(); 
       
      if (metalCount == 0)
      {
          g1.setLocation(initialX, initialY);
      }
    }
}
ScoreBar Class (for displaying number of metals collected):
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 // Need Color and Font for drawing ScoreBar
 
 
/**
 * ScoreBar is a Greenfoot Actor that displays text.
 * The text is white on a reddish brown
 * background and designed to work in a scenario that is
 * 800x600 pixels.
 * <p>
 * Display any String that fits, or send
 * game info straight to customized method.
 * @author Jordan Cohen
 * @version April 2013
 */
public class ScoreBar extends Actor
{
    // Declare Objects
    private GreenfootImage scoreBoard;
    private Color background;
    private Color foreground;
    private Font textFont;
    private String text;
 
    // Declare Variables:
    private int width;
    private int totalMetals = 0;
    /**
     * Construct a ScoreBar of the appropriate size.
     *
     * @param width     width of the World where the
     *                  ScoreBar will be placed
     */
    public ScoreBar (int width)
    {
        scoreBoard = new GreenfootImage (width, 30);
        background = new Color (175, 20, 23);
        foreground = new Color (255, 255, 255);
        textFont = new Font ("Courier", true, false, 24);
        scoreBoard.setColor(background);
        scoreBoard.fill();
        this.setImage (scoreBoard);
        scoreBoard.setFont(textFont);
 
        this.width = width;
    }
     
    public void act()
    {
        update(totalMetals);
    }
 
    /**
     * Updates this ScoreBar with game stats. This method should be
     * re-written to work with your specific labels/values
     *
     * @param alive     current number of living Bugs
     * @param maxAlive  largest number of bugs alive during game
     * @param dead      number of dead bugs
     * @param averageLifespan  
     */
    public void update (int metalCollected)
    {
        // In order to make uniform sizes and preceding zeros:
        String metalString;
        // If there is only one digit
 
        metalString = zeroAdder (metalCollected, 3);
 
         
        text = "Metal pieces: " + Metal.numMetals;
        // Now that we have built the text to output...
        // this.update (String) calls the other version of update(), in this case
        // update(String) - see below
        this.update (text);
    }
 
 
    /**
     * Takes a String and displays it centered to the screen.
     *
     * @param output    Text for displaying.
     */
    public void update (String output)
    {
        // Refill the background with background color
        scoreBoard.setColor(background);
        scoreBoard.fill();
 
        // Write text over the solid background
        scoreBoard.setColor(foreground); 
        // Smart piece of code that centers text
        int centeredY = (width/2) - ((output.length() * 14)/2);
        // Draw the text onto the image
        scoreBoard.drawString(output, centeredY, 22);
    }
 
    /**
     * Method that aids in the appearance of the scoreboard by generating
     * Strings that fill in zeros before the score. For example:
     *
     * 27 ===> to 5 digits ===> 00027
     *
     * @param   value   integer value to use for score output
     * @param   digits   number of zeros desired in the return String
     * @return  String  built score, ready for display
     */
    public static String zeroAdder (int value, int digits)
    {
        // Figure out how many digits the number is
        int numDigits = digitCounter(value);
 
        // If not extra digits are needed
        if (numDigits >= digits)
            return Integer.toString(value);
 
        else // Build the number with zeroes for extra place values:
        {
            String zeroes = "";
            for (int i = 0; i < (digits - numDigits); i++)
            {
                zeroes += "0";
            }
            return (zeroes + value);
        }
 
    }
     
    /**
     * Useful private method that counts the digit in any integer.
     *
     * @param number    The number whose digits you want to count
     * @return  int     The number of digits in the given number
     */
    private static int digitCounter (int number)
    {
        if (number < 10) {
            return 1;
        }
        int count = 0;
        while (number > 0) {
            number /= 10;
            count++;
        }
        return count;
    }
 
}
TextButton Class (which is used by ScoreBar 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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * A Generic Button to display text that is clickable.
 *
 * This should be added to, and controlled by, a world.
 *
 * @author Jordan Cohen
 * @version v0.1.5
 */
public class TextButton extends Actor
{
 
    // Declare variables
    private GreenfootImage myImage;
    private GreenfootImage myAltImage;
    private String buttonText;
    private int textSize;
 
    /**
     * Construct a TextButton given only a String
     * @param String    String to be displayed
     */
    public TextButton (String text)
    {
        this(text, 20);
    }
     
    /**
     * Construct a TextButton given a String and a text size
     * @param String    String to be displayed
     */
    public TextButton (String text, int textSize)
    {
        // Assign value to my internal String
        buttonText = text;
        this.textSize = textSize;
        // Draw a button with centered text:
         
        updateMe (text);
    }
 
    public void act ()
    {
        if (Greenfoot.mousePressed(this))
        {
            setImage (myAltImage);
        }
        else
        {
            setImage (myImage);
        }
    }
     
    /**
     * Update current TextButton text
     */
    public void updateMe (String text)
    {
        buttonText = text;
        GreenfootImage tempTextImage = new GreenfootImage (text, textSize, Color.RED, Color.WHITE);
        myImage = new GreenfootImage (tempTextImage.getWidth() + 8, tempTextImage.getHeight() + 8);
        myImage.setColor (Color.WHITE);
        myImage.fill();
        myImage.drawImage (tempTextImage, 4, 4);
 
        myImage.setColor (Color.BLACK);
        myImage.drawRect (0,0,tempTextImage.getWidth() + 7, tempTextImage.getHeight() + 7);
        setImage(myImage);
         
        tempTextImage = new GreenfootImage (text, textSize, Color.WHITE, Color.RED);
        myAltImage = new GreenfootImage(tempTextImage.getWidth() + 8, tempTextImage.getHeight() + 8);
        myAltImage.setColor (Color.WHITE);
        myAltImage.fill();
        myAltImage.drawImage (tempTextImage, 4, 4);
 
        myAltImage.setColor (Color.BLACK);
        myAltImage.drawRect (0,0,tempTextImage.getWidth() + 7, tempTextImage.getHeight() + 7);
    }
     
     
     
}
Thanks for any help you can provide in advance. And if anyone's wondering, this code is to be used as part of a bigger war simulation. im just coding one portion of it for now.
Nosson1459 Nosson1459

2017/4/7

#
Things were getting complicated so I just made a whole new method. Add the following method into the Scavenger 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
/**
 * Returns the closest metal in the world, but only if it's past 400 on the
 * x-axis.
 *
 * @return the nearest Metal past 400
 */
public Metal closestMetal() {
    // make a list of all the Metals in the World
    java.util.List metalsList = getWorld().getObjects(Metal.class);
    /**
     * Remove all the Metals that are before 400.
     */
    for (int i = 0; i < metalsList.size(); i++) {
        if (((Actor) metalsList.get(i)).getX() <= 400) {
            metalsList.remove(metalsList.get(i));
            i--;
        }
    }
    if (!metalsList.isEmpty()) { // only continue if there are Metals past 400
        Metal closestMetal = (Metal) metalsList.get(0); // initialize anymetal as closest
        int closestMetalDistance = (int) MyWorld.getDistance(this, (Actor) metalsList.get(0));
        // find the distance of our closest metal
        int metalsDistance;
        for (Object metal : metalsList) {
            metalsDistance = (int) MyWorld.getDistance(this, (Actor) metal);
            if (metalsDistance < closestMetalDistance) {
                // if there is a closer metal then set it to be the closest
                closestMetalDistance = metalsDistance;
                closestMetal = (Metal) metal;
            }
        }
        return closestMetal; // return the closest metal
    }
    return null; // return 'null' since there weren't any Metals past 400
}
And then change line 35 to:
1
targetMetal = closestMetal();
With the given code the Scavenger will move towards the nearest Metal if it's past 400 pixels on the x-axis, if there isn't any then it won't move at all (that's how the coding always was - I just didn't add anything so it does the same it always did when there were no more). An explanation of the problem with the way you were doing it is:
1
2
3
4
5
6
7
8
9
10
11
// only if there are metals in the world
// choose any Metal as the closest
// set the closest distance as the distance between the Scavenger and the random metal
// for each Metal that's in the world
// find the distance of it
 
/* Here is the problem */
 
// if it's closer than the last closest AND past 400
// you want to make sure that it's past 400, BUT why should a Metal that's past 400 be closer than one that's by 100 when you're at 50
// so the metal stayed as the first initialized one and the Scavenger went there no matter where it is since targetMetal isn't null
I have two questions about your code:
  • 1) Why are you using an ArrayList as opposed to a List which is the class that getObjects (and other methods) returns?
  • 2) Why, in your Scavenger class on line 104 are you checking for the target metal like that? You can change that if to:
    1
    if (intersects(targetMetal))
You can change the getDistance method in MyWorld to:
1
2
3
4
5
6
7
8
9
10
11
/**
 * Static method that gets the distance between the x,y coordinates of two
 * Actors using the distance formula.
 *
 * @param a the first actor
 * @param b the second actor
 * @return the distance between the first actor and the second
 */
public static double getDistance(Actor a, Actor b) {
    return Math.sqrt(Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2));
}
It's the same formula that you used just shortened (you called it the Pythagorean Theorem, but it's not since that is a^2+b^2=c^2 - a squared plus b squared equals c squared).
You need to login to post a reply.