Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Game Multi-Threading


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
11 replies to this topic

#1 Mr Purple   Members   -  Reputation: 126

Like
0Likes
Like

Posted 26 December 2012 - 10:10 AM

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);
    }
}

 

 



Sponsor:

#2 Morphex   Members   -  Reputation: 298

Like
1Likes
Like

Posted 26 December 2012 - 10:15 AM

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 


#3 Mr Purple   Members   -  Reputation: 126

Like
0Likes
Like

Posted 26 December 2012 - 10:33 AM

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.


Edited by Mr Purple, 26 December 2012 - 10:34 AM.


#4 Morphex   Members   -  Reputation: 298

Like
0Likes
Like

Posted 26 December 2012 - 10:40 AM

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 


#5 Mr Purple   Members   -  Reputation: 126

Like
0Likes
Like

Posted 26 December 2012 - 10:49 AM

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.


Edited by Mr Purple, 26 December 2012 - 10:51 AM.


#6 Sollum   GDNet+   -  Reputation: 759

Like
1Likes
Like

Posted 26 December 2012 - 10:49 AM

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



#7 Mr Purple   Members   -  Reputation: 126

Like
0Likes
Like

Posted 26 December 2012 - 11:10 AM

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);
            }
        });

    }
});

Edited by Mr Purple, 26 December 2012 - 11:11 AM.


#8 Satharis   Members   -  Reputation: 1249

Like
1Likes
Like

Posted 26 December 2012 - 01:58 PM

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.



#9 Mr Purple   Members   -  Reputation: 126

Like
0Likes
Like

Posted 26 December 2012 - 02:18 PM

Thanks!

 

I have managed to get this working within one thread!



#10 kubera   Members   -  Reputation: 963

Like
0Likes
Like

Posted 27 December 2012 - 01:52 AM

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.



#11 nife87   Members   -  Reputation: 516

Like
0Likes
Like

Posted 27 December 2012 - 03:57 PM

Well, what other types and how many threads should a game have?
Before thinking about multi-threading, you should reason about your memory-accessing patterns and if they are optimized enough. One thread with optimized/proper cache usage can often outweigh many threads using sloppy/improper cache usage.

Currently, I can think of no less than 4 distinct purposes/methods for multi-threading in games, and I am sure others can think of more.

1) Real-time processing. For faster response to input, Win32 messages, and the like, this processing often takes place in another thread. These threads usually require normal/high priority for fast response, although they are not active very long at a time (they sleep a lot due to inactivity).
2) Background processing. Asynchronous file loading, for instance, requires little processing power since this is an I/O-bound process. Asynchronous resource loading, on the other hand, may require a great deal of processing power when there are resources to load (and loading is not I/O-bound). But none of these tasks have to be carried out right away, so they usually have low/normal priority and will be processed when there are time to spare.
3) Scalable parallel processing. This can be rendering, physics simulation, logic processing, or any other task that can be divided into multiple smaller independent tasks, typically using task schedulers/pools (for queuing and delegation of tasks to worker threads), dependencies (tasks depending on other tasks or frame dependencies) and priorities for choosing the right order of processing. There is typically one global task scheduler and N+C worker threads, where N is the number of available hardware threads and C is some number for balancing the workload of worker threads (I have seen both -2, -1, 0 and 1, depending on the amount of processing the other threads have to perform). For reference, see http://threadingbuildingblocks.org/
4) Another popular solution for systems with few hardware threads (2-4) is to dedicate each thread to one/more subsystems and is often good enough when developing for a platform with a specific amount of threads (consoles, some mobiles, for instance). So for a 2-thread system you might have input, rendering and networking on one thread and physics and logic on another, but often one or more threads will have finished processing the current frame before the others and will have to wait (maybe not at first because it can process the next frame, but it cannot continue beyond X frames). This method can therefore lead to wasted cycles in waiting threads and is not scalable in itself.
Task schedulers, as explained in 3), address this problem by balancing the load amongst the available worker threads in order to avoid stalling any threads = no/less wasted cycles. This requires the work to be split into more-or-less equal-sized and independent tasks for optimal balancing and therefore optimum performance, though, and is therefore easiest to accomplish with something like physics, rendering and (depending on the management) logic.

I did not dive into the impact of different synchronization strategies, as others have already mentioned this.

Answer: A mix of all 4 methods is probably not a bad idea, but the answer should always be "it depends". You should consider the reason for each thread, its responsibilities, its impact on OS thread scheduling, and if the extra synchronization required, if any, is outweighed by the performance gains, also if any.

Edited by nife87, 27 December 2012 - 03:58 PM.


#12 shadowomf   Members   -  Reputation: 323

Like
0Likes
Like

Posted 27 December 2012 - 04:29 PM

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

 

I wouldn't put one subsystem into one thread. And I wouldn't use one thread for just one thing (except where neccessary, e.g. to interact with libraries that are not completly thread-safe).

 

How about a thread pool and a job queue or tasks. Create as many threads as you find reasonable, e.g. number of threads = number of processor cores or whatever.

Add tasks too your queue. Whenever a thread is finished with one task a new task is fetched and executed.

 

Microsoft had once an interesting article/presentation about task level parallelism, but I couldn't find it at the moment.

 

 

I would certainly recommend you to use threads and learn how to use them, sure there is software that is single-threaded, but mostly for historic reasons or because the developer couldn't do it with threads. Adding threads at the end of development is probably of little use.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS