Game Multi-Threading

Started by
10 comments, last by shadowomf 11 years, 3 months ago

I am currently writing a library, and this library currently has 1 Main game thread, and it gets started by creating an instance of a Room:

Room room1 = new Room1();

Well, what other types and how many threads should a game have?


package SpaceShooters;

import JGame.Game.Game;
import JGame.Room.Room;
import SpaceShooters.Rooms.Room1;
import javax.swing.JFrame;

public class Main extends JFrame{

    public static void main(String[] args){
        Game game = new Game("Test Game");
        game.startWindowed(800, 600);

        // This starts the main game thread by extending "Room"
        // It tests for key pressings mouse clicks, and repaints the screen
        Room room1 = new Room1();
        game.setRoom(room1);
    }
}

Advertisement

You can get along with only one thread, why do you want more?

You could have threads for networking,Audio,Rendering,Physics, ETC. Each subsystem of the engine could be a thread to improve performance. But only when needed, starting to write a multithreaded application is no trivial task. IMO you should create a engine and then update it to multithreaded enviroment.

Check out my new blog: Morphexe

Okay, so I have a method called moveToY() what it does is move an object to a Y point, so if your at (0, 200) and you do

moveToY(myObject, 0, 5, myAction); // Object, Y, Speed, AbstracAction

The object will move to point 0. in my library what I currently have is this:


    public void moveToY(GameObject obj, int y, int amount, AbstractAction complete){
        this.obj = obj;
        this.endY = y;
        this.moveAmount = amount;
        this.complete = complete;
        thread = new Thread(this);
        thread.start();
    }

    public void run(){
        try{
            boolean run = true;
            while(run){
                int objY = obj.getY();
                if(objY > this.endY){
                    obj.setY(obj.getY() - 1);
                }else if(objY < this.endY){
                    obj.setY(obj.getY() + 1);
                }else{
                    run = false;
                    this.actionComplete();
                }
                thread.sleep(moveAmount);
            }
        }catch(Exception e){
        }
    }

I have been told doing that is a bad idea, but I am not really sure how to take that and move it into my main game's thread.

I don't get what is your problem with that, in your game thread you sure have a GameLoop right? So somewhere in there you should update your position.

So you would have something like :

GameLoop:
Update(TimeSinceLastFrame);
EngineStuff // Render, and other things.
EndLoop:


In your update method you would have that code you have, it would work the same and still be inside the same thread. Unless you are trying to do something else, and I am not getting what.

Check out my new blog: Morphexe

The problem is, I need to somehow know which objects need to have an updated position, and how much to move them. Since this is a Game Library/Engine, not all games are built the same so I need to know is moveToY() attached to that object if so move it.... Maybe I am just over thinking this.

I had a similar problem when i was starting out. I didn't comprehend how its possible to do everything in a loop, because frankly, something has to catch those damn KeyPress when your software is drawing, right? So i had keyboard events in one thread, drawing and logic in another, and i was using GDI with Windows API.

Well worry not. Since you're using Java, try libraries like LWJGL ( http://www.lwjgl.org/ ), they have a good set of tutorials that will get you started.

You don't need multithreading for this. Just search for some gameloop explanations.

But the most standart thing is

EnterLoop

- Filter Keyboard/Mouse input

- Do logic, like move units or select unit

- Draw

EndLoop

I additionally like to create one additional class i call "DataSet", where i store all information, that is being used by Graphic, Logic and Input classes

I am hoping this will help you to understand my library. This is where the games heart and soul lies. It goes through the objects and should check what commands were given from other objects, such as a key press mouse click, if one is found run the action. I can seem to grasp how to preform actions that need to run longer than an event. Most events have actions that only need to happen on one iteration of the loop, while others need to happen on multiple iterations of the loop. The multiple iteration thing is where I am kinda stumped.

So, when I loop through each game object, how would I know that moveToY is attached to that game object and it still needs to move to the position?


package JGame.Room;

import JGame.Game.Game;
import JGame.GameObject.GameObject;
import JGame.Util.KeyboardMap;
import JGame.Util.Mapping;
import JGame.Util.MouseMap;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class Room extends JPanel implements Runnable{

    protected Image bg;
    ArrayList<GameObject> gameObjects = new ArrayList<>();
    protected int width = Game.width, height = Game.height;
    public static int CENTERX = 0, CENTERY = 0;
    protected int roomSpeed = 5;

    /*
     * This runs the main thread for the current room.
     *
     * run() tests for key presses and Mouse Events, it then runs its set method
     * if a key press or mouse click is activated.
     */
    @Override
    public void run(){
        try{
            while(true){
                // Check for key press events
                Iterator actions = KeyboardMap.map.entrySet().iterator();
                while(actions.hasNext()){
                    Map.Entry ap = (Map.Entry)actions.next();
                    Mapping mp = (Mapping)ap.getValue();
                    if(mp.pressed){
                        mp.run();
                    }
                }

                // Check for mouse events
                Iterator actions2 = MouseMap.map.entrySet().iterator();
                while(actions2.hasNext()){
                    Map.Entry ap = (Map.Entry)actions2.next();
                    Mapping mp = (Mapping)ap.getValue();
                    if(mp.pressed){
                        mp.run();
                    }
                }

                for(GameObject go : gameObjects){
                    // If An object isn't allowed to leave the screen don't let it leave
                    if(!go.getLeaveScreen()){
                        int goWidth = go.getWidth();
                        int goHeight = go.getHeight();
                        int goX = go.getX();
                        int goY = go.getY();
                        int gameWidth = Game.width;
                        int gameHeight = Game.height;
                        if(goX + goWidth >= gameWidth){
                            go.setX(gameWidth - goWidth);
                        }
                        if(goX <= 0){
                            go.setX(0);
                        }
                        if(goY + goHeight >= gameHeight){
                            go.setY(gameHeight - goHeight);
                        }
                        if(goY <= 0){
                            go.setY(0);
                        }
                    }
                }
                this.repaint();
                Thread.sleep(roomSpeed);
            }
        }catch(Exception e){
        }
    }

    public Room(){
        Thread thread = new Thread(this);
        thread.start();
    }

    /**
     *
     * @param speed
     *
     * Sets the room speed; this tells the thread how long it should sleep for.
     *
     * The higher the number the slower the room becomes.
     */

    public void setRoomSpeed(int speed){
        this.roomSpeed = speed;
    }

    /**
     *
     * @param width
     * @param height
     *
     * Sets the size of a room.
     */

    public void setRoomSize(int width, int height){
        this.width = width;
        this.height = height;

        Room.CENTERX = (int)(this.width / 2.0);
        Room.CENTERY = (int)(this.height / 2.0);
    }

    /**
     *
     * @param color
     *
     * Sets a color to be the background a room
     */

    @Override
    public void setBackground(Color color){
        super.setBackground(color);
    }

    /**
     *
     * @param filename
     *
     * Sets an image to be the background of a room
     */

    public void setBackground(String filename){
        URL loc = this.getClass().getResource(filename);
        ImageIcon imgIcon = new ImageIcon(loc);
        bg = imgIcon.getImage();
    }

    /**
     *
     * @param gameObject
     * @param x
     * @param y
     *
     * Adds a GameObject to the room at a particular location
     */

    public void addGameObjectAt(GameObject gameObject, int x, int y){
        gameObject.setX(x);
        gameObject.setY(y);
        gameObjects.add(gameObject);
    }

    /**
     *
     * @param go
     *
     * Removes a known GameObject from the room
     */

    public void removeGameObject(GameObject go){
        gameObjects.remove(go);
        System.gc();
    }

    /**
     *
     * @param g
     *
     * Draws out all of the GameObjects in the room
     */

    @Override
    public void paintComponent(Graphics g){
        try{
            g.drawImage(bg, 0, 0, this);
            for(int i = 0; i < gameObjects.size(); i++){
                GameObject go = gameObjects.get(i);
                g.drawImage(go.getSprite(), go.getX(), go.getY(), this);
            }
        }catch(Exception e){
        }
    }

    /**
     *
     * @return
     *
     * Gets the room's width
     */

    @Override
    public int getWidth(){
        return this.width;
    }

    /**
     *
     * @return
     *
     * Gets the room's height
     */

    @Override
    public int getHeight(){
        return this.height;
    }
}

Here is then how I attach actions to an object (very basic Moves the ship +X to move it to the right):


keyboard.keyPress("RIGHT", new Runnable(){
    @Override
    public void run(){
        move.moveX(Ship.this, shipSpeed);
    }
});

Something a little more complex, this listens for the space bar, if pressed: create a laser image, and move it vertically to the top of the screen, once it gets to it's designation destroy it:


keyboard.keyPress("SPACE", new Runnable(){
    TimeRegulator tr = new TimeRegulator(200);

    @Override
    public void run(){
        if(!tr.checkTime()){
            return;
        }
        MoveAction move = new MoveAction();
        CollisionEvent colide = new CollisionEvent();
        Sprite sprite = new Sprite("/media/images/laser.png");
        final GameObject laser = create.create(
                sprite,
                (Room)Ship.this.room,
                Ship.this.getX() + (Ship.this.getWidth() / 2) - 3,
                Ship.this.getY());
        move.moveToY(laser, 0 - sprite.getHeight(), 1, new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent evt){
                DestroyAction destroy = new DestroyAction();
                destroy.destroyGameObject((Room)Ship.this.room, laser);
            }
        });

    }
});

Quite frankly you shouldn't even begin to think of using threads for every little thing, they're relatively expensive if you start talking about context changes and waiting on locks to the point where you're going to derive the most performance by using threads for things that do not collide with each other. Usually you only want to use threads for major subsystems like audio or physics or when you're waiting on something to load via streaming(aka having a thread run file read commands and then "feeding" things into memory as they finish.)

You really should -not- be using a new thread for every object that moves, it is not only expensive but it means when you get to things like collision detection between entities, it not only is a lot of calculation but you'll be using a bunch of locks on each object, slowing down the code even more. It is a really inappropriate place to use threading.

Generally a game runs in a game loop and follows a generic system of update and draw, update moves your actual game objects around and does their behavior and draw simply draws them where they are at the time the draw is actually called. You may want to do some research on this because I could try and explain the different types of game loops like fixed vs variable time and different systems of gathering keyboard input like polling vs events, but the general idea is:

you check if the input state changed, like if a keyboard key is held down, and set it appropriately, then you go through each object and move it in relation to what the inputs currently are, finally you draw everything in its updated position, and repeat the process. In each of these processes there are many options and variations how to accomplish them.

Thanks!

I have managed to get this working within one thread!

I am not an expert in Java, but even smartphones have multicore hardware and making a few threads would be a good idea in my opinion,

Creating single core applications was the standard in the '80, but those days INTs was programmed.

This topic is closed to new replies.

Advertisement