Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am using an ArrayList to handle objects and at each interval of 120 frames, I am adding a new object of the same type at a random location along the z-axis of 60. The problem is, it doesn't add just 1. It depends on how many are in the list. If I kill the Fox before the time interval when one is supposed to spawn comes, then no Fox will be spawned. If I don't kill any foxes, it grows exponentially. I only want one Fox to be added every 120 frames. This problem never happened before when I created new ones and added them to the environment. Any insights?

Here is my code:

/**** FOX CLASS ****/
import env3d.EnvObject;
import java.util.ArrayList;

public class Fox extends Creature
{
    private int frame = 0;
    public Fox(double x, double y, double z)
    {
        super(x, y, z);

        // Must use the mutator as the fields have private access
        // in the parent class
        setTexture("models/fox/fox.png");
        setModel("models/fox/fox.obj");

        setScale(1.4);
    }

    public void move(ArrayList<Creature> creatures, ArrayList<Creature> dead_creatures, ArrayList<Creature> new_creatures)
    {
        frame++;
        setX(getX()-0.2);
        setRotateY(270);

        if (frame > 120) {
            Fox f = new Fox(60, 1, (int)(Math.random()*28)+1);
            new_creatures.add(f);
            frame = 0;
        }

        for (Creature c : creatures) {
            if (this.distance(c) < this.getScale()+c.getScale() && c instanceof Tux) {
                    dead_creatures.add(c);
            }
        }

        for (Creature c : creatures) {
            if (c.getX() < 1 && c instanceof Fox) {
                dead_creatures.add(c);
            }
        }
    }
}

import env3d.Env;
import java.util.ArrayList;
import org.lwjgl.input.Keyboard;

/**
 * A predator and prey simulation.  Fox is the predator and Tux is the prey.
 */
public class Game
{
    private Env env;    
    private boolean finished;
    private ArrayList<Creature> creatures;
    private KingTux king;
    private Snowball ball;
    private int tuxcounter;
    private int kills;

    /**
     * Constructor for the Game class. It sets up the foxes and tuxes.
     */
    public Game()
    {
        // we use a separate ArrayList to keep track of each animal. 
        // our room is 50 x 50.
        creatures = new ArrayList<Creature>();

        for (int i = 0; i < 10; i++) {
            creatures.add(new Tux((int)(Math.random()*10)+1, 1, (int)(Math.random()*28)+1));
        }

        for (int i = 0; i < 1; i++) {
            creatures.add(new Fox(60, 1, (int)(Math.random()*28)+1));
        }

        king = new KingTux(25, 1, 35);

        ball = new Snowball(-400, -400, -400);
    }

    /**
     * Play the game
     */
    public void play()
    {

        finished = false;

        // Create the new environment.  Must be done in the same
        // method as the game loop
        env = new Env();

        // Make the room 50 x 50.
        env.setRoom(new Room());

        // Add all the animals into to the environment for display
        for (Creature c : creatures) {
            env.addObject(c);
        }

        for (Creature c : creatures) {
            if (c instanceof Tux) {
                tuxcounter++;
            }
        }

        env.addObject(king);
        env.addObject(ball);

        // Sets up the camera
        env.setCameraXYZ(30, 50, 55);
        env.setCameraPitch(-63);

        // Turn off the default controls
        env.setDefaultControl(false);

        // A list to keep track of dead tuxes.
        ArrayList<Creature> dead_creatures = new ArrayList<Creature>();
        ArrayList<Creature> new_creatures = new ArrayList<Creature>();

        // The main game loop
        while (!finished) {            

            if (env.getKey() == 1 || tuxcounter == 0)  {
                finished = true;
            }

            env.setDisplayStr("Tuxes: " + tuxcounter, 15, 0);
            env.setDisplayStr("Kills: " + kills, 140, 0);

            processInput(); 

            ball.move();

            king.check();

            // Move each fox and tux.
            for (Creature c : creatures) {
                c.move(creatures, dead_creatures, new_creatures);
            }

            for (Creature c : creatures) {
                if (c.distance(ball) < c.getScale()+ball.getScale() && c instanceof Fox) {
                    dead_creatures.add(c);
                    ball.setX(-400);
                    ball.setY(-400);
                    ball.setZ(-400);
                    kills++;
                }
            }

            // Clean up of the dead tuxes.
            for (Creature c : dead_creatures) {
                if (c instanceof Tux) {
                    tuxcounter--;
                }
                env.removeObject(c);
                creatures.remove(c);
            }

            for (Creature c : new_creatures) {
                creatures.add(c);
                env.addObject(c);
            }

            // we clear the ArrayList for the next loop.  We could create a new one 
            // every loop but that would be very inefficient.

            dead_creatures.clear();
            new_creatures.clear();

            // Update display
            env.advanceOneFrame();
        }

        // Just a little clean up
        env.exit();
    }

    private void processInput()
    {
        int keyDown = env.getKeyDown();
        int key = env.getKey();

        if (keyDown == 203) {
            king.setX(king.getX()-1);
        } else if (keyDown == 205) {
            king.setX(king.getX()+1);
        }

        if (ball.getX() <= -400 && key == Keyboard.KEY_S) { 
            ball.setX(king.getX());
            ball.setY(king.getY());
            ball.setZ(king.getZ());
        }
    }

    /**
     * Main method to launch the program.
     */
    public static void main(String args[]) {
        (new Game()).play();
    }
}

/**** CREATURE CLASS ****/
/* (Parent class to Tux, Fox, and KingTux) */
import env3d.EnvObject;
import java.util.ArrayList;

abstract public class Creature extends EnvObject
{
    private int frame;
    private double rand;
    /**
     * Constructor for objects of class Creature
     */
    public Creature(double x, double y, double z)
    {
        setX(x);
        setY(y);
        setZ(z);
        setScale(1);
        rand = Math.random();
    }

    private void randomGenerator()
    {
        rand = Math.random();
    }

    public void move(ArrayList<Creature> creatures, ArrayList<Creature> dead_creatures, ArrayList<Creature> new_creatures)
    {        
        frame++;
        if (frame > 12) {
            randomGenerator();
            frame = 0;
        }

//         if (rand < 0.25) {
//                 setX(getX()+0.3);
//                 setRotateY(90);
//         } else if (rand < 0.5) {
//                 setX(getX()-0.3);
//                 setRotateY(270);
//         } else if (rand < 0.75) {
//                 setZ(getZ()+0.3);
//                 setRotateY(0);
//         } else if (rand < 1) {
//                 setZ(getZ()-0.3);
//                 setRotateY(180);
//         }

        if (rand < 0.5) {
            setRotateY(getRotateY()-7);
        }   else if (rand < 1) {
            setRotateY(getRotateY()+7);
        }

        setX(getX()+Math.sin(Math.toRadians(getRotateY()))*0.5);
        setZ(getZ()+Math.cos(Math.toRadians(getRotateY()))*0.5);

        if (getX() < getScale()) setX(getScale());
        if (getX() > 50-getScale()) setX(50 - getScale());
        if (getZ() < getScale()) setZ(getScale());
        if (getZ() > 50-getScale()) setZ(50 - getScale());

        // The move method now handles collision detection
        if (this instanceof Fox) {
            for (Creature c : creatures) {
                if (c.distance(this) < c.getScale()+this.getScale() && c instanceof Tux) {
                    dead_creatures.add(c);
                }
            }
        }
    }        
}

The rest of the classes are a bit trivial to this specific problem.

share|improve this question
4  
I removed the link to pastebin, as they tend to expire and this question would then be more difficult for others who stumble across it with a similar problem to read. Also, if one of the answers below helped you, you should mark it as the answer. –  Josh Petrie Feb 10 '11 at 2:11

2 Answers 2

It may be my lack of understanding the underlying objects here, but I think the definition of 'frame' is the cause of the problems.

You are incrementing the counter once per creature so the more creatures you have the faster new ones will spawn. This likely should be incremented before the call to move() instead of inside the call that happens once per creature.

If this isn't the case, please clarify in comments and I'll try again. :)

EDIT: This is the line that is the culprit; you are ++frame PER FOX, not per update loop as mentioned in the comments section:

public void move(ArrayList<Creature> creatures, ArrayList<Creature> dead_creatures, ArrayList<Creature> new_creatures)
{
    frame++;

This should be done in the loop that is calling move() or the like, not by the move method, unless this frame variable is, as suggested, part of the fox object itself. In that case, every fox has its own unique frame counter to spawn off of; that also gives you your exponential growth.

share|improve this answer
    
Is the frame variable per instance or is it in the scope of all instances? (I'm not a java guy) I was wondering what he meant by exponential. (fast spawning or multiple spawning simultaneously) –  Michael Coleman Feb 10 '11 at 1:09
    
the frame variable is updated every time ENV3D advances one frame. With the amount of objects I am using in this game it's about 20-40 FPS. The reason it is growing exponentially is because the move() method is being called every frame for EVERY instance of Fox. The more Foxes in the ArrayList, the more will spawn when it comes time to spawn a Fox. From a set theory point of view, there are n elements in the list. It will grow by n elements every time it is time to spawn. nxn = n^2. –  user5326 Feb 10 '11 at 1:15
    
Yes, but there is a frame field in the fox class that is updated in the move method. Anyway, I can see that you understand the problem with the code. Problem solved? –  Michael Coleman Feb 10 '11 at 1:38

If I understand your code, your fox class is the one that spawns new fox objects inside the move method. By putting this logic in the move method, multiple fox instances are adding more foxes to the list. Also, if no foxes exist, this logic will never occur because the move method will not execute (no instances to call upon). Thus, if there are no more foxes (no fox objects are being updated) then foxes will not be spawned.

You should move this spawning code outside of the fox class. That way only one fox is added every 120 frames (no exponential growth) and a fox will always respawn, regardless of whether there are no more foxes.

Keep track of the frames and spawn foxes outside of this move method. Do not put it inside the fox class, make it independent of fox instances so that this logic is only at least and no more than once, regardless of the number of fox instances. That's the only reason why your seeing this erratic behavior. It's because you've got some game logic in the wrong place, making it dependent on whether there are fox instances being moved (updated) when this spawning logic should be independent and outside of the move method, and perhaps the fox class altogether.

share|improve this answer
    
That's very strange. We had a very similar situation to this in our earlier exercises. We would spawn a Fox randomly when it ate a Tux and add it into the list from there. –  user5326 Feb 10 '11 at 0:54
    
OOOH. I get it. The other situation we used a for loop to iterate the list and added a new Fox based on what a particular instance of Fox did. I should have read your first paragraph more carefully. THANK YOU. –  user5326 Feb 10 '11 at 1:00
    
Questions that heavily involve code like this might be more suitable for SO. Anyway, I'm glad you understand the problem now. –  Michael Coleman Feb 10 '11 at 1:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.