Gaming in Java

Explaining game application in Java with focus on OOP

Setting Up Game Panel and Game Window Class

Game Panel Class

The GamePanel class serves as the central component for displaying the game environment. GamePanel extends JPanel and implements Runnable, allowing it to run in a separate thread for continuous updates and rendering.

Implementation of Runnable

Runnable is an interface that is part of the ‘java.lang’ package. It is used for creating threads for execution. It defines a method ‘run()’ which contains code for constituting new threads. GamePanel implements ‘Runnable’ so it can be executed by a thread.

public class GamePanel extends JPanel implements Runnable {
}
public void startGameThread() {
    // creates new Java Thread associated with the current instance of GamePanel
    gameThread = new Thread(this); 

    // starts thread's execution. runs 'run' method in a new thread
    gameThread.start();
}

Purpose of startGameThread is to initiate a new thread specifically for running the game loop in GamePanel class. This includes methods that will be described later, resposible for periodically updating and repainting the game components to keep the game animated and responsive.

Class Properties

  • originalTileSize: The original size of a tile.
  • scale: The scaling factor for the tiles.
  • tileSize: The size of each tile after scaling.
  • maxScreenCol: The maximum number of columns on the screen.
  • maxScreenRow: The maximum number of rows on the screen.
  • screenWidth: The total width of the screen.
  • screenHeight: The total height of the screen.
  • keyH: An instance of KeyHandler for handling user input. gameThread: The thread for running the game loop. player: An instance of the Player class representing the player - character.
  • playerX: The default X position of the player.
  • playerY: The default Y position of the player.
  • playerSpeed: The speed of the player character.
  • FPS: Frames per second for the game loop.
  • backgroundImage: The background image for the game panel.

Key Handler

The KeyHandler class is responsible for handling keyboard input events. It implements the KeyListener interface to listen for key events such as key presses and releases.

public class KeyHandler implements KeyListener{

    public boolean upPressed, downPressed, leftPressed, rightPressed; 
    @Override
    public void keyPressed(KeyEvent e) {
        /// WASD Controls 
        int code = e.getKeyCode();
        if(code == KeyEvent.VK_W) {
            upPressed = true; 
        }
        if(code == KeyEvent.VK_A) {
            leftPressed = true; 
        }
        if(code == KeyEvent.VK_S) {
            downPressed = true ;
        }
        if(code == KeyEvent.VK_D) {
            rightPressed = true; 
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int code = e.getKeyCode();
        if(code == KeyEvent.VK_W) {
            upPressed = false; 
        }
        if(code == KeyEvent.VK_A) {
            leftPressed = false; 
        }
        if(code == KeyEvent.VK_S) {
            downPressed = false ;
        }
        if(code == KeyEvent.VK_D) {
            rightPressed = false; 
        }
    }

    @Override
    public void keyTyped(KeyEvent arg0) {
        // TODO Auto-generated method stub
    }
    
    
}

Class Properties:

  • upPressed, downPressed, leftPressed, rightPressed: These boolean variables indicate whether the corresponding arrow keys (W, A, S, D) are currently pressed. They are used to track the state of these keys.

Methods:

  • keyPressed(KeyEvent e): This method is called when a key is pressed. It checks the keycode of the pressed key and updates the corresponding boolean variable to true if the arrow keys (W, A, S, D) are pressed.

  • keyReleased(KeyEvent e): This method is called when a key is released. Similar to keyPressed(), it checks the keycode of the released key and updates the corresponding boolean variable to false if the arrow keys (W, A, S, D) are released.

  • keyTyped(KeyEvent arg0): This method is not implemented in this class. It’s called when a key is typed, but it’s not relevant for this class’s purpose, so it remains empty.

Usage:

  • An instance of KeyHandler is used by the panel class to respond to keyboard input, in this case controlling player movement in a game.
  • When a key is pressed or released, the KeyHandler instance updates its boolean variables accordingly.
  • Panel part of the program can then check these variables to determine which keys are currently pressed and take appropriate actions based on that information.

Note: This implementation only handles basic keyboard input for the arrow keys (W, A, S, D). Depending on the requirements of your application, you may need to extend it to handle additional keys or more complex input logic.

Entity and Child Classes

The Entity class serves as a parent class for all character classes in the game. It encapsulates common properties and behaviors that are shared among different types of entities.

// Parent class for all character classes
public class Entity {
    public int x, y;  // position
    public int speed;  // speed 

    public String direction; 
}

Class Properties:

  • x, y: These integer variables represent the position of the entity on the game map. The x coordinate indicates the horizontal position, and the y coordinate indicates the vertical position.

  • speed: This integer variable specifies the speed of the entity’s movement. It determines how fast the entity can move across the game map.

  • direction: This string variable stores the direction in which the entity is currently moving or facing. It can be used to track the entity’s orientation or movement direction.

Methods:

The Entity class doesn’t contain any methods in the provided code snippet. However, subclasses of Entity will define additional methods (you will see this later) to implement specific behaviors for different types of entities, such as player characters or non-player characters.

Usage:

  • Subclasses of Entity, such as Player or Npc, inherit the properties and behaviors defined in the Entity class.

The Entity class basically just provides a foundation for implementing common functionalities shared among different types of entities…. we are now going to see it’s implementation and inheritance in child classes.

Player Class Explanation:

public class Player extends Entity{
    GamePanel gp; 
    KeyHandler keyH; 
    
    private int currentSpriteFrame = 0;
    private Map<String, Image[]> sprites;  // Map to store sprite animations
    private String currentDirection;  // To keep track of the current direction
    private int movementsCount = 0;

    public Player(GamePanel gp, KeyHandler keyH) {

        this.gp = gp; 
        this.keyH = keyH; 
        setDefaultValues();
        loadSprites();
    }

    //default value method 

    public void setDefaultValues() {
        x = 100; 
        y = 100; 
        speed = 4; 

    }



    public void update() {
        if (keyH.upPressed) {
            y -= speed;
            currentDirection = "up";
            updateSpriteFrame();
        } else if (keyH.downPressed) {
            y += speed;
            currentDirection = "down";
            updateSpriteFrame();
        } else if (keyH.leftPressed) {
            x -= speed;
            currentDirection = "left";
            updateSpriteFrame();
        } else if (keyH.rightPressed) {
            x += speed;
            currentDirection = "right";
            updateSpriteFrame();
        }
        movementsCount++;
        if (movementsCount >= 20) {
            movementsCount = 0;
            currentSpriteFrame = (currentSpriteFrame + 1) % 2;  // Alternating between 0 and 1
        }

        
    }

    private void updateSpriteFrame() {
        // Update the sprite frame based on the direction
        Image[] directionSprites = sprites.get(currentDirection);
        currentSpriteFrame = (currentSpriteFrame + 1) % directionSprites.length;
    }


    private void loadSprites() {
        sprites = new HashMap<>();
    
        sprites.put("up", new Image[]{
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_up_1.png")).getImage(),
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_up_2.png")).getImage()
        });
    
        sprites.put("down", new Image[]{
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_down_1.png")).getImage(),
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_down_2.png")).getImage()
        });
    
        sprites.put("left", new Image[]{
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_left_1.png")).getImage(),
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_left_2.png")).getImage()
        });
    
        sprites.put("right", new Image[]{
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_right_1.png")).getImage(),
                new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/boy_right_2.png")).getImage()
        });
    }
    


    public void draw(Graphics2D g2) {

        Image[] directionSprites = sprites.get(currentDirection);
        Image currentSprite = directionSprites[currentSpriteFrame];
        // Draw the appropriate sprite at the player's position
        g2.drawImage(currentSprite, x, y, gp.tileSize, gp.tileSize, null);

    }

}

Properties:

  • gp: Represents the associated GamePanel instance.
  • keyH: Represents the associated KeyHandler instance for handling keyboard input.
  • currentSpriteFrame: Tracks the current frame of the sprite animation.
  • sprites: A map that stores sprite animations for different directions.
  • currentDirection: Tracks the current direction of the player. movementsCount: Tracks the number of movements for sprite animation.

    Constructor:

  • Initializes the Player instance with the associated GamePanel and KeyHandler instances.
  • Calls setDefaultValues() to set default values.
  • Calls loadSprites() to load sprite images.

Methods:

  • setDefaultValues(): Sets default values for the player’s position and speed.
  • update(): Updates the player’s position based on keyboard input and updates the sprite frame accordingly.
  • updateSpriteFrame(): Updates the sprite frame based on the current direction.
  • loadSprites(): Loads sprite images for different directions.
  • draw(Graphics2D g2): Draws the player’s sprite on the GamePanel.

Npc Class Explanation:

public class Npc extends Entity implements MouseListener {
    private Image npcImage;
    private String speechText;
    private Boolean displaySpeech; 
    private GamePanel gamePanel;
    
    public Npc(int x, int y, String speechText, GamePanel gamePanel) {
        this.x = x;
        this.y = y;
        this.speechText = speechText;
        this.displaySpeech = false;
        this.gamePanel = gamePanel;  // Assign the GamePanel instance
        loadNPCImage();
        gamePanel.addMouseListener(this);  // Add MouseListener to the GamePanel
    }

    private void loadNPCImage() {
        npcImage = new ImageIcon(getClass().getResource("/com/nighthawk/spring_portfolio/mvc/collision/sprites/npc1.png")).getImage();
        npcImage = npcImage.getScaledInstance(npcImage.getWidth(null) / 2, npcImage.getHeight(null) / 2, Image.SCALE_DEFAULT);
    }

    public void draw(Graphics2D g2) {
        // Draw the NPC image at its position
        g2.drawImage(npcImage, x, y, null);

        if (displaySpeech) {
            // Draw speech bubble
            int bubbleWidth = 200;
            int bubbleHeight = 50;

            // Adjust the bubble position based on NPC position
            int bubbleX = x + npcImage.getWidth(null) + 5;
            int bubbleY = y - 10;

            // Draw the speech bubble
            g2.setColor(Color.WHITE);
            g2.fillRect(bubbleX, bubbleY, bubbleWidth, bubbleHeight);
            g2.setColor(Color.BLACK);
            g2.drawRect(bubbleX, bubbleY, bubbleWidth, bubbleHeight);

            // Draw the speech text
            g2.setFont(new Font("Arial", Font.PLAIN, 12));
            g2.drawString(speechText, bubbleX + 10, bubbleY + 20);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // Check if the click occurred on the NPC
        Rectangle npcBounds = new Rectangle(x, y, npcImage.getWidth(null), npcImage.getHeight(null));
        if (npcBounds.contains(e.getPoint())) {
            // Toggle the display of speech bubble
            displaySpeech = !displaySpeech;
            gamePanel.repaint();  // Trigger repaint to update the display
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

Properties:

  • npcImage: Represents the image of the NPC.
  • speechText: Represents the text displayed in the speech bubble.
  • displaySpeech: Indicates whether the speech bubble should be displayed.
  • gamePanel: Represents the associated GamePanel instance.

    Constructor:

  • Initializes the NPC with its position, speech text, and the associated GamePanel.
  • Loads the NPC image.
  • Adds itself as a MouseListener to the GamePanel.

    Methods:

  • loadNPCImage(): Loads the NPC image and scales it.
  • draw(Graphics2D g2): Draws the NPC image and speech bubble on the GamePanel.
  • mouseClicked(MouseEvent e): Toggles the display of the speech bubble when the NPC is clicked.

    Usage:

  • The Player class represents the player character and handles player movement based on keyboard input.
  • The Npc class represents non-player characters and displays speech bubbles when clicked.
  • Both classes interact with the GamePanel to draw themselves on the game screen and respond to user input.
  • Both classes are inherited from the Entity Class described earlier!