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:
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:
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:
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):
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):
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:
    /**
     * 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:
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:
// 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:
    if (intersects(targetMetal))
You can change the getDistance method in MyWorld to:
    /**
     * 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.