Monster vs Doty Exercise

Copy the 3 classes from the "Code Listing" section to the template project. You'll notice a few things about this program:

  1. The monster can be controlled by the arrow keys on the keyboard. Make sure you understand how the run animation is called.
  2. If you press the "S" key, the swipe animation will run and a ball will fly out of the monster.
  3. You notice also that you only get 1 ball, if you examine the Game class, you'll see that the same Ball object is used over and over again.

Exercise 1

Your challenge is to create a list of balls in game, instead of just 1 ball. Then, make it possible for the monster to fire multiple balls. The potential balls that can be fired is unlimited.

HINT: For this exercise, do not worry about removing the balls from the list or the environment.

Exercise 2

Create a Doty class and place a few Doty objects in the room, the constructor for Game looks like this:

public Game()
{ 
    // Initial monster location
    m = new Monster(5,1,1);
    balls = new ArrayList<Ball>();
    dotys = new ArrayList<Doty>();
    dotys.add(new Doty(1, 1, 3));
    dotys.add(new Doty(8, 1, 6));
    dotys.add(new Doty(4, 1, 8));
}

Make it so that the balls that the monster fire will be able to eliminate the various dotys.


Code Listing

Game class

// Import the Env class to work with the 3D Environment 
import env3d.Env;
import org.lwjgl.input.Keyboard;

/**
 * Game class, the main controller class
 */
public class Game
{
    // This game controlls exactly 1 Doty object
    private Monster m;
    private Ball b;
    
    /**
     * Put the doty at default location
     */ 
    public Game()
    { 
        // Initial monster location
        m = new Monster(5,1,1);
        // Initial ball location - hidden
        b = new Ball(100, 0, 0);
    }
    
    /**
     * The play method is the main entry point of the entire 
     * game.  It contains some setup code and a game loop 
     */
    public void play()
    {
        // Initial setup code
        boolean finished;
        finished = false;
        
        Env env;
        // Create the environment.  This next line will cause a 
        // blank window to appear
        env = new Env();
        
        // Add objects to the environment
        env.addObject(m);
        env.addObject(b);        
        
        // Camera control.  We fix the camera and
        // not allow users to change it.
        env.setCameraXYZ(5,5,13);
        env.setCameraPitch(-25);
        env.setDefaultControl(false);
        
        // The main game loop
        while (finished == false)
        {
            // --- Process user input ---
            
            // Keyboard input
            // Track a complete key press (key up)
            int key = env.getKey();
            // Track a key down only
            int keyDown = env.getKeyDown();
            
            if (key == Keyboard.KEY_ESCAPE) {
                finished = true;
            } else if (key == Keyboard.KEY_S) {
                m.swipe();
                b.setDir(m.getRotateY());
                b.setXYZ(m.getX(), m.getY(), m.getZ());                
            } 
            
            // Use key down to move the monster
            if (keyDown == Keyboard.KEY_RIGHT) {
                m.moveX(0.1);
            } else if (keyDown == Keyboard.KEY_LEFT) {
                m.moveX(-0.1);
            } else if (keyDown == Keyboard.KEY_UP) {
                m.moveZ(-0.1);
            } else if (keyDown == Keyboard.KEY_DOWN) {
                m.moveZ(0.1);
            } else if (keyDown == Keyboard.CHAR_NONE) {
                if (m.getState().equals("run")) {
                    m.idle();
                }
            }
            
           
            // --- finish user input ---
            
            // Change the object state
            m.move();
            b.move();
            
            // Update display
            env.advanceOneFrame();
        }

        // Cleanup code
        env.exit();
    }   
}

Monster class

import java.util.ArrayList;

/**
 * A monster with various states
 * 
 */
public class Monster
{

    private double x;
    private double y;
    private double z;
    private String texture;
    private String model;
    private double scale;
    private double rotateX;
    private double rotateY;
    private double rotateZ;
    
    // Keeps tract of the current frame
    private int frame;
    
    // Keeps track of the monster's current state
    private String state;
    
    // Lists to keep track of all the models
    private ArrayList<String> idleModels;    
    private ArrayList<String> swipeModels;
    private ArrayList<String> runModels;
    
    /**
     * Constructor for objects of class Monster
     */
    public Monster(double x, double y, double z)
    {
        // initialise instance variables
        this.x = x;
        this.y = y;
        this.z = z;
        texture = "models/Boss/boss.jpg";
        scale = 1;
        rotateY = 0;
        
        frame = 0;
        
        // Start state
        state = "idle";
        loadModels();
        model = idleModels.get(0);
    }
    
    /**
     * Load all the models into the proper ArrayList
     */
    public void loadModels() 
    {
        idleModels = new ArrayList<String>();
        idleModels.add("models/Boss/Idle/bossidle00.obj");
        idleModels.add("models/Boss/Idle/bossidle01.obj");
        idleModels.add("models/Boss/Idle/bossidle02.obj");
        idleModels.add("models/Boss/Idle/bossidle03.obj");
        idleModels.add("models/Boss/Idle/bossidle04.obj");
        idleModels.add("models/Boss/Idle/bossidle05.obj");
        idleModels.add("models/Boss/Idle/bossidle06.obj");
        idleModels.add("models/Boss/Idle/bossidle07.obj");
        idleModels.add("models/Boss/Idle/bossidle08.obj");
        idleModels.add("models/Boss/Idle/bossidle09.obj");
        idleModels.add("models/Boss/Idle/bossidle10.obj");
        idleModels.add("models/Boss/Idle/bossidle11.obj");
        idleModels.add("models/Boss/Idle/bossidle12.obj");
        idleModels.add("models/Boss/Idle/bossidle13.obj");
        idleModels.add("models/Boss/Idle/bossidle14.obj");
        idleModels.add("models/Boss/Idle/bossidle15.obj");
        idleModels.add("models/Boss/Idle/bossidle16.obj");
        idleModels.add("models/Boss/Idle/bossidle17.obj");
        idleModels.add("models/Boss/Idle/bossidle18.obj");
        idleModels.add("models/Boss/Idle/bossidle19.obj");
                
        swipeModels = new ArrayList<String>();
        swipeModels.add("models/Boss/Swipe/bossswipe00.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe01.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe02.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe03.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe04.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe05.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe06.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe07.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe08.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe09.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe10.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe11.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe12.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe13.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe14.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe15.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe16.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe17.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe18.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe19.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe20.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe21.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe22.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe23.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe24.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe25.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe26.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe27.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe28.obj");
        swipeModels.add("models/Boss/Swipe/bossswipe29.obj");
        
        runModels = new ArrayList<String>();
        runModels.add("models/Boss/Run/bossrun00.obj");
        runModels.add("models/Boss/Run/bossrun01.obj");
        runModels.add("models/Boss/Run/bossrun02.obj");
        runModels.add("models/Boss/Run/bossrun03.obj");
        runModels.add("models/Boss/Run/bossrun04.obj");
        runModels.add("models/Boss/Run/bossrun05.obj");
        runModels.add("models/Boss/Run/bossrun06.obj");
        runModels.add("models/Boss/Run/bossrun07.obj");
        runModels.add("models/Boss/Run/bossrun08.obj");
        runModels.add("models/Boss/Run/bossrun09.obj");
        runModels.add("models/Boss/Run/bossrun10.obj");
        runModels.add("models/Boss/Run/bossrun11.obj");
        runModels.add("models/Boss/Run/bossrun12.obj");
        runModels.add("models/Boss/Run/bossrun13.obj");
        runModels.add("models/Boss/Run/bossrun14.obj");
        runModels.add("models/Boss/Run/bossrun15.obj");
        runModels.add("models/Boss/Run/bossrun16.obj");
        runModels.add("models/Boss/Run/bossrun17.obj");
        runModels.add("models/Boss/Run/bossrun18.obj");
        runModels.add("models/Boss/Run/bossrun19.obj");
        runModels.add("models/Boss/Run/bossrun20.obj");
        runModels.add("models/Boss/Run/bossrun21.obj");
        runModels.add("models/Boss/Run/bossrun22.obj");
        runModels.add("models/Boss/Run/bossrun23.obj");
        runModels.add("models/Boss/Run/bossrun24.obj");
        runModels.add("models/Boss/Run/bossrun25.obj");
        runModels.add("models/Boss/Run/bossrun26.obj");
        runModels.add("models/Boss/Run/bossrun27.obj");
        runModels.add("models/Boss/Run/bossrun28.obj");
        runModels.add("models/Boss/Run/bossrun29.obj");
        
    }
    
    /**
     * The move method is called every frame.
     * It's purpose is to check the different state and 
     * mutate monster accordingly
     */
    public void move()
    {
        frame++;
        if (state.equals("idle")) {
            idleAction();
        } else if (state.equals("swipe")) {
            swipeAction();
        } else if (state.equals("run")) {
            runAction();
        }
    }
    
    /**
     * Change the state to idle
     */
    public void idle() 
    {
        frame = 0;
        state = "idle";
    }
    
    /**
     * Change the state to swipe
     */
    public void swipe()
    {
        frame = 0;
        state = "swipe";
    }
    
    
    /**
     * Load the correct model when monster is idle
     */
    public void idleAction()
    {

        if (frame < idleModels.size()) {
            model = idleModels.get(frame);
        } else {
            frame = 0;
        }
    }
    
    /**
     * Load the correct model when monster is swiping
     */
    public void swipeAction()
    {
        if (frame < swipeModels.size()) {
            model = swipeModels.get(frame);            
        } else {
            idle();
        }
    }
    
    /**
     * Load the correct model when monster is running
     */
    public void runAction()
    {
        if (frame < runModels.size()) {            
            model = runModels.get(frame);
        } else {
            idle();
        }        
    }    
    
    /**
     * Move monster in the x direction, change model as needed
     */
    public void moveX(double delta)
    {
        if (!state.equals("run")) {
            state = "run";
            frame = 0;
        }

        x += delta;
        if (delta < 0) {
            rotateY = -90;
        } else {
            rotateY = 90;
        }
    }
    
    /**
     * Move monster in the z direction, change model as needed
     */
    public void moveZ(double delta) 
    {
        if (!state.equals("run")) {
            state = "run";
            frame = 0;
        }        
        z += delta;
        if (delta < 0) {
            rotateY = 180;
        } else {
            rotateY = 0;
        }
        
    }

    /**
     * Accessor for x, y, z, and rotateY, and state
     */
    public double getX()
    {
        return x;
    }
    
    public double getY()
    {
        return y;
    }
    
    public double getZ()
    {
        return z;
    }
    
    public double getRotateY()
    {
        return rotateY;
    }
    
    public String getState()
    {        
        return state;
    }
}

Ball class

public class Ball
{

    private double x, y, z, scale;
    private double dir;
    
    /**
     * Constructor for objects of class Ball
     */
    public Ball(double x, double y, double z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
        scale = 0.2f;
    }

    public void setXYZ(double x, double y, double z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    
    public double getX()
    {
        return x;
    }
    
    public double getY()
    {
        return y;
    }
    
    public double getZ()
    {
        return z;
    }
    
    /**
     * Set the direction of the ball
     */
    public void setDir(double angle) 
    {
        dir = angle;
    }
    
    /**
     * Move the ball based on it's direction
     */
    public void move()
    {
        if (dir == -90) {
            x -= 0.1;
        } else if (dir == 90) {
            x += 0.1;
        } else if (dir == 180) {
            z -= 0.1;
        } else if (dir == 0) {
            z += 0.1;
        }
    }   
}