Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (542)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (604)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Slow 2D Tile Engine  (Read 4677 times)
0 Members and 1 Guest are viewing this topic.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Posted 2011-10-10 23:27:34 »

Hey, guys! I'm new to this forum and I'd like to say first that I was amused by the account activation.

I decided to join here because it seems like the most likely place I can finally find the solution to a problem I've been having for the last week... I've tried help from GameDev and from the Oracle forums over the last few days, but I haven't received the solution to it yet.

Basically, what I have is a tile engine that I've constructed for a side-scrolling game I'm developing. It reads in a .png image that gets converted to an array representing the level's tiles, which then is used to calculate the view that is drawn to the screen. The user can move the little character in the middle of the screen (which right now there is no collision detection, so he/she can move the player about in any direction regardless of what tile).

The problem is that the thing is super slow! Originally, I found that I was redrawing the view every time rather than shifting it, which was thought to have been the bottleneck, but I converted it to draw the view only when the player has moved to where a new view has to be calculated and rerendered, and the slowness is still there. My computer is a beast gaming machine, so I'm not thinking it's computer performance related at all, but I must have messed up somewhere.

I'll post the code for the classes that compose the tile engine below. I would greatly appreciate any and all help and/or criticism and/or advice pertaining to the tile engine that anyone is willing to offer. :] I apologize that this post will be so lengthy, but since there are several components to the engine rather than just one class, I don't want to leave a stone unturned for anybody. Thanks again! :]

This class represents the Player and manages his current state of animation:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
/* This class's purpose will be to store the data of the player character for movement and appearance. It will also
 * take input. The player image will be created as a sheet of animation frames and split by the class. */

 
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.awt.*;
 
public class Player
{
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // image to store the player's frames of animation as a sheet/loaded first
    private BufferedImage playerSheet;
   
    // image array to store the individual frames of animation/split by program
    private BufferedImage playerFrames[];
   
    // index used to represent the current frame to draw on the screen of the player
    private int currentIndex = 0;
   
    // enum representing the player's state of movement for animation
    private enum PlayerState {STILL_LEFT, STILL_RIGHT, MOVE_LEFT, MOVE_RIGHT, JUMP}
   
    // variable used to represent the player's current animation state
    private PlayerState currentState;
   
    // variable to keep track of last direction the character was facing when going back to still
    private PlayerState lastState;
   
    // long used to represent the system time, used as a timer
    private long movementTimer;
   
    // long used to represent the previously used time as a timer
    private long stillTimer;
   
    // CONSTRUCTOR
    public Player()
    {
        playerSheet = makeColorTransparent("playersheet1.png", 0xFFFF65F6);
        playerFrames = splitImage(playerSheet, 4, 2);
        movementTimer = System.nanoTime();
        stillTimer = movementTimer;
        currentState = PlayerState.STILL_LEFT;
        lastState = PlayerState.STILL_LEFT;
    }
   
    // this method will draw to whatever graphics context is passed to the method (game window)
    public void drawPlayer(Graphics gr, int x, int y)
    {
        gr.drawImage(playerFrames[currentIndex], x, y, null);
    }
   
    // method to simply load an image from a path
    private static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // method to create a tile array for tile sets
    private BufferedImage[] splitImage(BufferedImage img, int cols, int rows)
    {
        int w = img.getWidth() / cols;
        int h = img.getHeight() / rows;
        int num = 0;
       
        BufferedImage imgs[] = new BufferedImage[w * h];
       
        for (int y = 0; y < rows; y++)
        {
            for (int x = 0; x < cols; x++)
            {
                imgs[num] = new BufferedImage(w, h, img.getType());
               
                Graphics2D g = imgs[num].createGraphics();
                g.drawImage(img, 0, 0, w, h, w * x, h * y, w * x + w, h * y + h, null);
                g.dispose();
                num++;
            }
        }
       
        return imgs;
    }
   
    // image-loading method that will also alpha the color key for each tile
    public static BufferedImage makeColorTransparent(String ref, int color)
    {
        BufferedImage image = loadImage(ref);
        BufferedImage dimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = dimg.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.drawImage(image, null, 0, 0);
        g.dispose();
       
        for (int i = 0; i < dimg.getHeight(); i++)
        {
            for (int j = 0; j < dimg.getWidth(); j++)
            {
                if (dimg.getRGB(j, i) == color)
                {
                    dimg.setRGB(j, i, 0x8F1C1C);
                }
            }
        }
       
        return dimg;
    }
   
    // method to update the player based on user input
    public void updatePlayer(int input)
    {
        // update the still timer to manage when the last key press was
        stillTimer = System.nanoTime();
       
        switch (input)
        {
            // up
            case 0:
                break;
            // left
            case 1:
                if (currentState != PlayerState.MOVE_LEFT)
                {
                    movementTimer = System.nanoTime();
                }
                currentState = PlayerState.MOVE_LEFT;
                lastState = PlayerState.MOVE_LEFT;
                break;
            // right
            case 2:
                if (currentState != PlayerState.MOVE_RIGHT)
                {
                    movementTimer = System.nanoTime();
                }
                currentState = PlayerState.MOVE_RIGHT;
                lastState = PlayerState.MOVE_RIGHT;
                break;
            // down
            case 3:
                break;
            // jump
            case 4:
                break;
           
            // still left
            case 5:
                currentState = PlayerState.STILL_LEFT;
                lastState = PlayerState.STILL_LEFT;
                break;
           
            // still right
            case 6:
                currentState = PlayerState.STILL_RIGHT;
                lastState = PlayerState.STILL_RIGHT;
                break;
        }
    }
   
    // method to manage the player's animation
    public void animatePlayer()
    {  
        switch (currentState)
        {
            case STILL_LEFT:
                currentIndex = 0;
                break;
            case STILL_RIGHT:
                currentIndex = 4;
                break;
            case MOVE_LEFT:
                // if set to a still frame, set it to start running left
                if (currentIndex == 0 || currentIndex == 4)
                {
                    currentIndex = 1;
                }
                // if a 300 nanosecond gap has passed, allow the next frame
                if (System.nanoTime() - movementTimer > 100000000)
                {
                    if (currentIndex == 1)
                    {
                        currentIndex = 2;
                    }
                    else
                    {
                        currentIndex = 1;
                    }
                   
                    movementTimer = System.nanoTime();
                }
                break;
            case MOVE_RIGHT:
                // if set to a still frame, set it to start running right
                if (currentIndex == 0 || currentIndex == 4)
                {
                    currentIndex = 5;
                }
                // if a 300 nanosecond gap has passed, allow the next frame
                if (System.nanoTime() - movementTimer > 100000000)
                {
                    if (currentIndex == 5)
                    {
                        currentIndex = 6;
                    }
                    else
                    {
                        currentIndex = 5;
                    }
                   
                    movementTimer = System.nanoTime();
                }
                break;
            case JUMP:
           
                break;
        }
    }
}


This class represents the Level object that stores the .png images that will compose the drawn level and the array that represents the tiles in a more memory-efficient manner. It also manages updating the level's view and rendering it, as well as drawing it to the screen:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
/* The purpose of this class will be to load and manage a level, including its camera. */

import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.awt.*;

public class Level
{
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // stores the pixel image of the current level
    private BufferedImage levelImage;
   
    // stores the width and height of the level
    private int width, height;
   
    // stores the name of the level
    private String levelName;
   
    // stores collision map for level
    private LevelCollisions myCollisions;
   
    // stores the tile types in an array as assigned by colors
    private int levelTiles[][];
   
    // image used as the sheet for the level's tiles
    private BufferedImage tileSheet;
   
    // image array used to store the different tiles
    private BufferedImage[] tiles;
   
    // the image which represents the current view of the level
    private BufferedImage cameraImage;
   
    // images to represent buffers for the sides, left, right, top, bottom
    private BufferedImage sideBufferL, sideBufferR, sideBufferT, sideBufferB;
   
    // Graphics context of the camera image
    private Graphics cameraG;
   
    // variables to represent the level's offset from the top left corner while moving
    private int offsetX, offsetY;
   
    // variables to represent the level's pixel map coordinate
    private int coordX, coordY;
   
    //
    // STATIC COLOR VARIABLES
    //
   
    private static final int SPACE_COLOR = 0xFF000000;
    private static final int WALL_COLOR = 0xFFFFFFFF;
   
    //
    // END OF STATIC COLOR VARIABLES
    //
   
    //
    // CONSTRUCTOR
    //
    public Level(String level)
    {
        // load level image and collision map
        levelName = level;
        levelImage = loadImage(level + ".png");
        myCollisions = new LevelCollisions(level + "Collision");
        levelTiles = loadLevel();  
       
        // create blank camera canvas
        cameraImage = new BufferedImage(1280, 768, BufferedImage.TYPE_INT_ARGB);
        cameraImage.createGraphics();
        cameraG = cameraImage.getGraphics();
       
        // offsets start at 0
        offsetX = offsetY = 0;
       
        // coordinate starts at bottom right
        coordX = 600;
        coordY = 383;
       
        // fill tile images
        tileSheet = loadImage("obstacletiles.png");
        tiles = splitImage(tileSheet, 2, 1);
       
        this.renderLevel();
    }
   
    // method to load the color values into an array
    public int[][] loadLevel()
    {
        height = levelImage.getHeight();
        width = levelImage.getWidth();
       
        int levelValues[][] = new int[width][height];
       
        // fill array with color values layer by layer
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                levelValues[x][y] = levelImage.getRGB(x, y);
            }  
        }
       
        return levelValues;
    }
   
    // method to get the tile color from a given tile
    public int getTile(int x, int y)
    {
        return levelTiles[x][y];
    }
   
    // method to draw the current camera view of the level on the screen
    public void drawLevel(Graphics gr, int x, int y)
    {
        gr.drawImage(cameraImage, x + offsetX, y + offsetY, null);
    }
   
    // method to render the actual image before drawing it
    public void renderLevel()
    {
        // keeps track of graphics coordinate
        int x, y;
       
        // keeps track of tile to draw
        int tileX, tileY;
       
        tileY = coordY;
       
        // draw all the tiles based on offsets, layer by layer
        for (y = 0; y < SCREEN_HEIGHT; y += TILE_SIZE)
        {
            tileX = coordX;
            for (x = 0; x < SCREEN_WIDTH; x += TILE_SIZE)
            {
                // determine which tile to draw based on tile color in array
                switch (this.getTile(tileX, tileY))
                {
                    case SPACE_COLOR:
                        cameraG.drawImage(tiles[0], x, y, null);
                        break;
                       
                    case WALL_COLOR:
                        cameraG.drawImage(tiles[1], x, y, null);
                        break;
                }
               
                tileX++;
            }
           
            tileY++;
        }
       
        // steps to take in case of an offset
        if (offsetX > 0)
        {
       
        }
       
        if (offsetX < 0)
        {
       
        }
       
        if (offsetY < 0)
        {
       
        }
       
        if (offsetY > 0)
        {
       
        }
    }
   
    // method to update the level's current position for the camera
    public void updateLevel(int input)
    {
        switch (input)
        {
            // up
            case 0:
                // update offset up if not too far up
                if (coordY > 30)
                {
                    offsetY += 2;
                }  
               
                // if a tile length has been moved, then offset becomes 0 and coordY is decreased
                if (offsetY >= TILE_SIZE)
                {
                    offsetY = 0;
                    coordY--;
                    this.renderLevel();
                }
                break;
            // left
            case 1:
                // update offset to the left if not too far left
                if (coordX > 30)
                {
                    offsetX += 2;
                }
               
                // if a tile length has been moved, then offset becomes 0 and coordX is decreased
                if (offsetX >= TILE_SIZE)
                {
                    offsetX = 0;
                    coordX--;
                    this.renderLevel();
                }
                break;
            // right
            case 2:
                // update offset to the right if not too far right
                if (coordX < width - 30)
                {
                    offsetX -= 2;
                }
               
                // if a tile length has been moved, then offset becomes 0 and coordX is increased
                if (offsetX <= -TILE_SIZE)
                {
                    offsetX = 0;
                    coordX++;
                    this.renderLevel();
                }
                break;
            // down
            case 3:
                // update offset down if not too far down
                if (coordY < height - 30)
                {
                    offsetY -= 2;
                }
               
                // if a tile legnth has been moved, then offset becomes 0 and coordY is increased
                if (offsetY <= -TILE_SIZE)
                {
                    offsetY = 0;
                    coordY++;
                    this.renderLevel();
                }
                break;
            // jump
            case 4:
                break;
        }
    }
   
    // method to simply load an image from a path
    public static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // method to create a tile array for tile sets
    public static BufferedImage[] splitImage(BufferedImage img, int cols, int rows)
    {
        int w = img.getWidth() / cols;
        int h = img.getHeight() / rows;
        int num = 0;
       
        BufferedImage imgs[] = new BufferedImage[w * h];
       
        for (int y = 0; y < rows; y++)
        {
            for (int x = 0; x < cols; x++)
            {
                imgs[num] = new BufferedImage(w, h, img.getType());
               
                Graphics2D g = imgs[num].createGraphics();
                g.drawImage(img, 0, 0, w, h, w * x, h * y, w * x + w, h * y + h, null);
                g.dispose();
                num++;
            }
        }
       
        return imgs;
    }
   
    // image-loading method that will also alpha the color key for each tile
    public static BufferedImage makeColorTransparent(String ref, int color)
    {
        BufferedImage image = loadImage(ref);
        BufferedImage dimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = dimg.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.drawImage(image, null, 0, 0);
        g.dispose();
       
        for (int i = 0; i < dimg.getHeight(); i++)
        {
            for (int j = 0; j < dimg.getWidth(); j++)
            {
                if (dimg.getRGB(j, i) == color)
                {
                    dimg.setRGB(j, i, 0x8F1C1C);
                }
            }
        }
       
        return dimg;
    }
}


This is the code for the renderer itself, the class I called LevelRenderer. It calls all of the methods that perform the drawing and updating:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
/* This class's job is to manage a Player and Level object and call their render and update routines. */
 
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.JFrame;
import javax.swing.*;
import java.util.Random;
import java.awt.Color;

public class LevelRenderer extends JFrame
{  
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // will be used as a buffer before everything is drawn to the screen
    private BufferedImage buffer2;
   
    // back buffer
    private BufferStrategy buffer;
   
    // character object
    private Player myPlayer;
   
    // level object
    private Level myLevel;
   
    // screen object
    private Screen s;
   
    // graphics object of the buffer
    private Graphics gr;
   
    // Graphics object for the buffer strategy
    private Graphics graphics;
   
    // boolean to determine when to end the game
    private boolean endGame;
   
    // CONSTRUCTOR
    public LevelRenderer()
    {
        setPreferredSize(new Dimension(1280, 768));
        setIgnoreRepaint( true );
        setUndecorated( true );

        setFocusable(true);
        requestFocus();
       
        setResizable(false);
       
        addKeyListener( new KeyAdapter()
        {
            public void keyPressed(KeyEvent e)
            {
                processKey(e);  
            }
           
            public void keyReleased(KeyEvent e)
            {
                processRelease(e);
            }
        });
       
        buffer2 = new BufferedImage(1280, 768, BufferedImage.TYPE_INT_ARGB);
       
        buffer2.createGraphics();
        gr = buffer2.getGraphics();
       
        myPlayer = new Player();
        myLevel = new Level("obstaclemap");
       
        endGame = false;
    }
   
    // method to simply load an image from a path
    public static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // Run method for class
    public void run(DisplayMode dm)
   {  
        setBackground(Color.WHITE);
      s = new Screen();
        s.setFullScreen(dm, this);
       
        this.createBufferStrategy( 2 );
        buffer = this.getBufferStrategy();

        while (!endGame)
        {
            try
            {  
                // edit player and level
                myPlayer.animatePlayer();
                myLevel.drawLevel(gr, 0, 0);
                myPlayer.drawPlayer(gr, (SCREEN_WIDTH / 2) - TILE_SIZE / 2, (SCREEN_HEIGHT / 2) - TILE_SIZE);
               
                graphics = buffer.getDrawGraphics();
                graphics.drawImage(buffer2, 0, 0, null);
               
                if( !buffer.contentsLost() )
                {
                    buffer.show();
                }
            }
            catch (Exception ex)
            {
                System.err.println("Game Update Error: " + ex);
            }
           
            try
            {
                Thread.sleep(10);
            }
            catch (Exception ex)
            {
                System.out.println("Can't sleep!");
            }
        }
       
      s.restoreScreen();
   }
   
    // method to handle inputs and adjust the player accordingly
    public void processKey(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // termination key
        if (keyCode == KeyEvent.VK_ESCAPE)
        {
            endGame = true;
        }
       
        // 0 - up
        // 1 - left
        // 2 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                myLevel.updateLevel(0);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(1);
                myLevel.updateLevel(1);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(2);
                myLevel.updateLevel(2);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                myLevel.updateLevel(3);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                myLevel.updateLevel(4);
                break;
        }
    }
   
    // method to handle inputs and adjust the player accordingly
    public void processRelease(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // 0 - up
        // 5 - left
        // 6 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(5);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(6);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                break;
        }
    }
}


Lastly, this is the file that just has the main loop in it to call the other classes:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
/* This class is what controls and runs the main game loop. */

import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;

public class GameTest
{  
    public static void main(String args[])
    {
        // initialize the level, player, renderer, and display mode
        LevelRenderer myRenderer = new LevelRenderer();
        DisplayMode dm = new DisplayMode(1280, 768, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
       
        myRenderer.run(dm);
    }
}


Once again, I apologize for posting so much, especially as a first post. I really hope you guys will be able to assist me in locating the problem! :] Thanks so much!

Edit: Also, I put the application through the Profiler in Netbeans and it seems that the drawLevel() method is incurring a lot of overhead, as is the run method. However, I'm still not sure as to how to go about fixing this.

Best regards,
Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #1 - Posted 2011-10-11 03:39:55 »

The few improvements you can make:
- You don't need to draw into a buffer and then draw that buffer to the screen. Since you are using BufferStrategy, it draws it into a back buffer for you until you call show().
- Follow BufferStrategy's javadoc's instructions on how to correctly put the drawing code in while loops.
- You are drawing a 1280x768 image in drawLevel()!! That might be a big cause of the slowdown.

EDIT: Aha, I found out why the code looks familiar: been watching Bucky lately? Wink

Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #2 - Posted 2011-10-11 03:43:44 »

If you post the complete source as a .rar uploaded to somewhere I can try it out on my computer. It's kind of time consuming to catch the flow of a program by just looking at the code. But do listen to Ra4king's advice first and see if it solves the problem. I'm no Java2D expert.

Myomyomyo.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #3 - Posted 2011-10-11 04:19:55 »

Thanks for the responses, guys!

ra4king: I made the changes you suggested. However, it did not make the program any faster. Is there a way I can restructure or modify the code so that it will process any faster than this? It seems like the bottleneck may be the design of the method and the data's structure. Else, how could scrollers achieve the speed that they get? Appreciate the assistance!

theagentd: I do not have a .rar compressor; however, I uploaded it as a .zip to the following URL: http://www.mediafire.com/?md5faqehgkub4z2
I would of course appreciate if you could take a look and see if there is anything that you think could be done to speed it up :] It doesn't seem to me like a side scroller should be a troublesome program to make, but I think I just have it designed improperly.

Thank you guys very much! Looking forward to see if there is any possible solution in sight.

Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #4 - Posted 2011-10-11 04:25:30 »

Have you tried using a debugger? Also try putting println()s that time how long method calls take.

Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #5 - Posted 2011-10-11 04:49:35 »

ra4king,

Btw, I forgot to say, yes, I had watched the Bucky videos. :p He actually derived much of what he used in that video from a book called Developing Games in Java, which I realized once I gave it a read; that, or the two are just uncannily similar. I like his manner and way of explaining things; he does a good job.

Anywho, I used the Profiler in Netbeans and found that the drawLevel method was taking up 21% of the program's CPU usage, whereas nearly every other method didn't even scratch the surface. When it has to redraw the whole level view, it usually takes up 26 ms, whereas otherwise it takes up about 0-1 to just shift the view.

Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #6 - Posted 2011-10-11 06:08:16 »

Well since you are drawing a 1280x768 BufferedImage to another Image, that sounds about right.

Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #7 - Posted 2011-10-11 06:20:37 »

ra4king,

Have you seen or played the game Terraria?

Straight flippin.
Offline dishmoth
« Reply #8 - Posted 2011-10-11 09:50:06 »

Some comments.  Don't know if they'll help.

- +1 for what ra4king said about the correct use of BufferStrategy.

- Create 'compatible' images rather than instantiating BufferedImages directly.  That is, use GraphicsConfiguration.createCompatibleImage(width,height,transparency).  Search the forums for more details.  I can't promise it will help, but in theory it will mean that the BufferedImage can use hardware acceleration.

- Don't call nanoSeconds() all over the place.  Calculate the time that has passed since the last update in the game loop, and pass that 'delta time' to the update methods.  If each update method calculates its own delta time, there's a danger of different game objects getting out of sync.

- As I recall, the Java documentation recommends creating then disposing of Graphics objects whenever you change a BufferedImage, rather than hanging on to a reference to the same one indefinitely.  I don't know how much this affects things in practice.

- You can cast your Graphics references to Graphics2D if you like.  This will give you more functions to play with.

- Your KeyListener methods should just record which keys are pressed or released.  They shouldn't do any real work.  Currently you appear to be using the methods to redraw the entire level.  Calls to the update functions should happen in the game loop itself, not in the key listeners.  The problem is that the key events are processed in a different thread to the game loop, and you're going to run into some nasty thread-safety glitches if you're not careful.

- Try chopping your code down so that it's just a game loop that redraws the level tiles each frame.  That'll be easier to debug (for you and, more importantly, for us!).

Simon

Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #9 - Posted 2011-10-11 09:55:48 »

I tried to run the code and got constant 60 FPS. My laptop owned your gaming beast... >_>

Myomyomyo.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #10 - Posted 2011-10-11 10:15:54 »

I tried to run the code and got constant 60 FPS. My laptop owned your gaming beast... >_>

I get 60 fps on my computer as well. It's not the FPS that's the issue, it's how fast the scrolling is on the level that's bugging me. But thank you for taking a look at the code, in any case, theagentd.

Simon,

Thank you for all those tips... I'm going to go over them tomorrow and refactor my code to reflect them. (Gotta hit the sack soon.) All of those make sense, and since this is my first really big game project, I want to get as many things right as I can, for now and for future games. Once I've made the changes, I'll post back to inform everyone of how those tips helped the code. I'm hoping there's some way I can get this to all work without having to resort to learning OpenGL. XD

Colton

Straight flippin.
Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #11 - Posted 2011-10-11 11:27:09 »

...
How can it be slow and have constant 60 FPS?! Your problem has absolutely nothing to do with rendering. It's your input handling that's the problem. Just move the input handling to the game loop and do things properly and your "slowness" will magically disappear. You should have described your problem better...

Myomyomyo.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #12 - Posted 2011-10-11 22:34:37 »

theagentd,

Forgive me for not explaining it better. I will work on fixing that soon and post regarding the performance increase. Question, though: Must I place the KeyListener in main instead of in the LevelRenderer's run method, or is there an alternate means of accomplishing this? I appreciate your help.

Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #13 - Posted 2011-10-11 23:40:45 »

Yes I have heard of Terraria. But what does that have to do with this topic?

Where KeyListener is and the way you have things set up is fine for now. If you want to get rid of the pause that comes between the first key press and all further key presses when holding down a key, use your listeners to turn boolean variables that hold the state of certain keys into true if the key is pressed and false when released.

If your game runs at 60FPS, then there is nothing wrong with rendering. What exactly is being slow?

Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #14 - Posted 2011-10-12 00:51:21 »

Just keep a boolean for each key (boolean leftKey, rightKey, etc;) and in keyPressed() set the right boolean to true. In keyReleased() set it to false. Then in your gameloop (your run() method) do a check for each of the key booleans and update the player's movement according to which keys are pressed.

You're slowness is as I've said not related to rendering or computer performance at all. It's how the logic is done.
Quote
- Don't call nanoSeconds() all over the place.  Calculate the time that has passed since the last update in the game loop, and pass that 'delta time' to the update methods.  If each update method calculates its own delta time, there's a danger of different game objects getting out of sync.
Definitely do this. When you've done that, move all the logic out of the key listener functions.

If your game runs at 60FPS, then there is nothing wrong with rendering. What exactly is being slow?
The player movement.

Myomyomyo.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #15 - Posted 2011-10-12 02:29:26 »

ra4king and theagentd,

I was just asking because that kind of smooth scrolling is the kind I'd like to see with my game... and the same sort of tile size. But yes, I will update the nanoSeconds() functions so that they are passed in so everything is universal. I appreciate the advice. Also, I've changed the logic of the whole program so that the update methods switch booleans on and off, and as a result, I've noticed a lot of improvement and it looks much smoother. However, the character movement is still on the slow side, much more so that what I'm going for. I'll post the updated code below.

LevelRenderer:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
/* This class's job is to manage a Player and Level object and call their render and update routines. */
 
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.JFrame;
import javax.swing.*;
import java.util.Random;
import java.awt.Color;

public class LevelRenderer extends JFrame
{  
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // back buffer
    private BufferStrategy buffer;
   
    // character object
    private Player myPlayer;
   
    // level object
    private Level myLevel;
   
    // screen object
    private Screen s;
   
    // Graphics object for the buffer strategy
    private Graphics graphics;
   
    // boolean to determine when to end the game
    private boolean endGame;
   
    // CONSTRUCTOR
    public LevelRenderer()
    {
        setPreferredSize(new Dimension(1280, 768));
        setIgnoreRepaint( true );
        setUndecorated( true );

        setFocusable(true);
        requestFocus();
       
        setResizable(false);
       
        addKeyListener( new KeyAdapter()
        {
            public void keyPressed(KeyEvent e)
            {
                processKey(e);  
            }
           
            public void keyReleased(KeyEvent e)
            {
                processRelease(e);
            }
        });
       
        myPlayer = new Player();
        myLevel = new Level("obstaclemap");
       
        endGame = false;
    }
   
    // method to simply load an image from a path
    public static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // Run method for class
    public void run(DisplayMode dm)
   {  
        setBackground(Color.WHITE);
      s = new Screen();
        s.setFullScreen(dm, this);
       
        this.createBufferStrategy( 2 );
        buffer = this.getBufferStrategy();
       
        // Variables for counting frames per second
        int fps = 0;
        int frames = 0;
        long totalTime = 0;
        long curTime = System.currentTimeMillis();
        long lastTime = curTime;

        while (!endGame)
        {
            do
            {
                do
                {
                    try
                    {  
                        // count Frames per second...
                        lastTime = curTime;
                        curTime = System.currentTimeMillis();
                        totalTime += curTime - lastTime;
                        if( totalTime > 1000 )
                        {
                            totalTime -= 1000;
                            fps = frames;
                            frames = 0;
                        }
                        ++frames;
                       
                        // get new draw graphics
                        graphics = buffer.getDrawGraphics();
                       
                        // edit/draw player and level
                        myPlayer.animatePlayer();
                        myLevel.updateOffsets();
                        myLevel.drawLevel(graphics, 0, 0);
                        myPlayer.drawPlayer(graphics, (SCREEN_WIDTH / 2) - TILE_SIZE / 2, (SCREEN_HEIGHT / 2) - TILE_SIZE);
                       
                        // display frames per second...
                        graphics.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
                        graphics.setColor( Color.GREEN );
                        graphics.drawString( String.format( "FPS: %s", fps ), 20, 20 );
                       
                        graphics.dispose();
                    }
                    catch (Exception ex)
                    {
                        System.err.println("Game Update Error: " + ex);
                    }
                   
                    try
                    {
                        Thread.sleep(2);
                    }
                    catch (Exception ex)
                    {
                        System.out.println("Can't sleep!");
                    }
                } while (buffer.contentsRestored());
               
                buffer.show();
               
            } while (buffer.contentsLost());
        }

      s.restoreScreen();
   }
   
    // method to handle inputs and adjust the player accordingly
    public void processKey(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // termination key
        if (keyCode == KeyEvent.VK_ESCAPE)
        {
            endGame = true;
        }
       
        // 0 - up
        // 1 - left
        // 2 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                myLevel.updateLevel(0);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(1);
                myLevel.updateLevel(1);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(2);
                myLevel.updateLevel(2);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                myLevel.updateLevel(3);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                break;
        }
    }
   
    // method to handle inputs and adjust the player accordingly
    public void processRelease(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // 0 - up
        // 5 - left
        // 6 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                myLevel.updateLevel(5);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(5);
                myLevel.updateLevel(6);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(6);
                myLevel.updateLevel(7);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                myLevel.updateLevel(8);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                break;
        }
    }
}


Level:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
329  
330  
331  
332  
333  
334  
335  
336  
337  
338  
339  
340  
341  
342  
343  
344  
345  
346  
347  
348  
349  
350  
351  
352  
353  
354  
355  
356  
357  
358  
359  
360  
361  
362  
363  
364  
365  
366  
367  
368  
369  
370  
371  
372  
373  
374  
375  
/* The purpose of this class will be to load and manage a level, including its camera. */

import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.awt.*;

public class Level
{
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // stores the pixel image of the current level
    private BufferedImage levelImage;
   
    // stores the width and height of the level
    private int width, height;
   
    // stores the name of the level
    private String levelName;
   
    // stores collision map for level
    private LevelCollisions myCollisions;
   
    // stores the tile types in an array as assigned by colors
    private int levelTiles[][];
   
    // image used as the sheet for the level's tiles
    private BufferedImage tileSheet;
   
    // image array used to store the different tiles
    private BufferedImage[] tiles;
   
    // the image which represents the current view of the level
    private BufferedImage cameraImage;
   
    // images to represent buffers for the sides, left, right, top, bottom
    private BufferedImage sideBufferL, sideBufferR, sideBufferT, sideBufferB;
   
    // Graphics context of the camera image
    private Graphics cameraG;
   
    // variables to represent the level's offset from the top left corner while moving
    private int offsetX, offsetY;
   
    // variables to represent the level's pixel map coordinate
    private int coordX, coordY;
   
    // variables that represent where the level is moving
    private boolean isLeft, isUp, isDown, isRight;
   
    //
    // STATIC COLOR VARIABLES
    //
   
    private static final int SPACE_COLOR = 0xFF000000;
    private static final int WALL_COLOR = 0xFFFFFFFF;
   
    //
    // END OF STATIC COLOR VARIABLES
    //
   
    //
    // CONSTRUCTOR
    //
    public Level(String level)
    {
        // load level image and collision map
        levelName = level;
        levelImage = loadImage(level + ".png");
        myCollisions = new LevelCollisions(level + "Collision");
        levelTiles = loadLevel();  
       
        // create blank camera canvas
        cameraImage = new BufferedImage(1280, 768, BufferedImage.TYPE_INT_ARGB);
        cameraImage.createGraphics();
        cameraG = cameraImage.getGraphics();
       
        // offsets start at 0
        offsetX = offsetY = 0;
       
        // make sure the level is set not to move
        isLeft = isRight = isUp = isDown = false;
       
        // coordinate starts at bottom right
        coordX = 600;
        coordY = 383;
       
        // fill tile images
        tileSheet = loadImage("obstacletiles.png");
        tiles = splitImage(tileSheet, 2, 1);
       
        this.redrawCamera();
    }
   
    // method to load the color values into an array
    public int[][] loadLevel()
    {
        height = levelImage.getHeight();
        width = levelImage.getWidth();
       
        int levelValues[][] = new int[width][height];
       
        // fill array with color values layer by layer
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                levelValues[x][y] = levelImage.getRGB(x, y);
            }  
        }
       
        return levelValues;
    }
   
    // method to get the tile color from a given tile
    public int getTile(int x, int y)
    {
        return levelTiles[x][y];
    }
   
    // method to draw the current camera view of the level on the screen
    public void drawLevel(Graphics gr, int x, int y)
    {
        gr.drawImage(cameraImage, x + offsetX, y + offsetY, null);
    }
   
    // method to redraw camera, needed every time the view is offset by 32
    public void redrawCamera()
    {
        // keeps track of graphics coordinate
        int x, y;
       
        // keeps track of tile to draw
        int tileX, tileY;
       
        tileY = coordY;
       
        // draw all the tiles based on offsets, layer by layer
        for (y = 0; y < SCREEN_HEIGHT; y += TILE_SIZE)
        {
            tileX = coordX;
            for (x = 0; x < SCREEN_WIDTH; x += TILE_SIZE)
            {
                // determine which tile to draw based on tile color in array
                switch (this.getTile(tileX, tileY))
                {
                    case SPACE_COLOR:
                        cameraG.drawImage(tiles[0], x, y, null);
                        break;
                       
                    case WALL_COLOR:
                        cameraG.drawImage(tiles[1], x, y, null);
                        break;
                }
               
                tileX++;
            }
           
            tileY++;
        }
       
        // steps to take in case of an offset
        if (offsetX > 0)
        {
       
        }
       
        if (offsetX < 0)
        {
       
        }
       
        if (offsetY < 0)
        {
       
        }
       
        if (offsetY > 0)
        {
       
        }
    }
   
    // method to render the actual image before drawing it
    public void updateOffsets()
    {
        // up
        if (isUp)
        {
            // update offset up if not too far up
            if (coordY > 30)
            {
                offsetY += 2;
            }  
           
            // if a tile length has been moved, then offset becomes 0 and coordY is decreased
            if (offsetY >= TILE_SIZE)
            {
                offsetY = 0;
                coordY--;
                this.redrawCamera();
            }
        }
        // left
        if (isLeft)
        {
            // update offset to the left if not too far left
            if (coordX > 30)
            {
                offsetX += 2;
            }
           
            // if a tile length has been moved, then offset becomes 0 and coordX is decreased
            if (offsetX >= TILE_SIZE)
            {
                offsetX = 0;
                coordX--;
                this.redrawCamera();
            }
        }
        // right
        if (isRight)
        {
            // update offset to the right if not too far right
            if (coordX < width - 30)
            {
                offsetX -= 2;
            }
           
            // if a tile length has been moved, then offset becomes 0 and coordX is increased
            if (offsetX <= -TILE_SIZE)
            {
                offsetX = 0;
                coordX++;
                this.redrawCamera();
            }
        }
        // down
        if (isDown)
        {
            // update offset down if not too far down
            if (coordY < height - 30)
            {
                offsetY -= 2;
            }
           
            // if a tile legnth has been moved, then offset becomes 0 and coordY is increased
            if (offsetY <= -TILE_SIZE)
            {
                offsetY = 0;
                coordY++;
                this.redrawCamera();
            }
        }
    }
   
    // method to update the level's current position for the camera
    public void updateLevel(int input)
    {
        switch (input)
        {
            // up on
            case 0:
                isUp = true;
                break;
            // left on
            case 1:
                isLeft = true;
                break;
            // right on
            case 2:
                isRight = true;
                break;
            // down on
            case 3:
                isDown = true;
                break;
            // up off
            case 5:
                isUp = false;
                break;
            // left off
            case 6:
                isLeft = false;
                break;
            // right off
            case 7:
                isRight = false;
                break;
            // down off
            case 8:
                isDown = false;
                break;
            // jump off
            case 4:
                break;
        }
    }
   
    // method to simply load an image from a path
    public static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // method to create a tile array for tile sets
    public static BufferedImage[] splitImage(BufferedImage img, int cols, int rows)
    {
        int w = img.getWidth() / cols;
        int h = img.getHeight() / rows;
        int num = 0;
       
        BufferedImage imgs[] = new BufferedImage[w * h];
       
        for (int y = 0; y < rows; y++)
        {
            for (int x = 0; x < cols; x++)
            {
                imgs[num] = new BufferedImage(w, h, img.getType());
               
                Graphics2D g = imgs[num].createGraphics();
                g.drawImage(img, 0, 0, w, h, w * x, h * y, w * x + w, h * y + h, null);
                g.dispose();
                num++;
            }
        }
       
        return imgs;
    }
   
    // image-loading method that will also alpha the color key for each tile
    public static BufferedImage makeColorTransparent(String ref, int color)
    {
        BufferedImage image = loadImage(ref);
        BufferedImage dimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = dimg.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.drawImage(image, null, 0, 0);
        g.dispose();
       
        for (int i = 0; i < dimg.getHeight(); i++)
        {
            for (int j = 0; j < dimg.getWidth(); j++)
            {
                if (dimg.getRGB(j, i) == color)
                {
                    dimg.setRGB(j, i, 0x8F1C1C);
                }
            }
        }
       
        return dimg;
    }
}


I hope I've got things now in the right place, or at least closer to how they should be. Thank you guys very much for your help! Not that it probably matters too much, but I gave both of you appreciation. ;]

Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #16 - Posted 2011-10-12 02:36:25 »

Why are you sleeping for 2 milliseconds in your loop?

Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #17 - Posted 2011-10-12 03:33:11 »

ra4king,

I read in a few places that it's good to sleep in situations like these to let the other threads process more efficiently. Is this unnecessary?

Colton

Straight flippin.
Offline ra4king

JGO Kernel


Medals: 356
Projects: 3
Exp: 5 years


I'm the King!


« Reply #18 - Posted 2011-10-12 03:52:56 »

Your game loop should be following a simple flow: update -> render -> sleep.
However the sleep should be the amount you want to sleep to achieve the target FPS minus the amount of time it took to update and render, like this example.

Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #19 - Posted 2011-10-12 04:13:58 »

ra4king,

Thank you very much for all the advice! I'm learning a lot. I did what you told me and created a couple bits that calculate the sleep amount using the example you showed. I also made a render() and update() function that are now called within the game loop instead, which I think is much more readable, as well. Here's the updated code for LevelRenderer:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
/* This class's job is to manage a Player and Level object and call their render and update routines. */
 
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.JFrame;
import javax.swing.*;
import java.util.Random;
import java.awt.Color;

public class LevelRenderer extends JFrame
{  
    //
    // CONSTANTS
    //
   
    private final int TILE_SIZE = 32;
    private final int SCREEN_WIDTH = 1280;
    private final int SCREEN_HEIGHT = 768;
   
    //
    // END OF CONSTANTS
    //
   
    // back buffer
    private BufferStrategy buffer;
   
    // character object
    private Player myPlayer;
   
    // level object
    private Level myLevel;
   
    // screen object
    private Screen s;
   
    // Graphics object for the buffer strategy
    private Graphics graphics;
   
    // boolean to determine when to end the game
    private boolean endGame;
   
    // Variables for counting frames per second
    private int fps = 0;
    private int frames = 0;
    private long totalTime = 0;
    private long curTime = System.currentTimeMillis();
    private long lastTime = curTime;
   
    // thread sleep count variables
    private long now = 0;
    private long rest = now;
   
    // CONSTRUCTOR
    public LevelRenderer()
    {
        setPreferredSize(new Dimension(1280, 768));
        setIgnoreRepaint( true );
        setUndecorated( true );

        setFocusable(true);
        requestFocus();
       
        setResizable(false);
       
        addKeyListener( new KeyAdapter()
        {
            public void keyPressed(KeyEvent e)
            {
                processKey(e);  
            }
           
            public void keyReleased(KeyEvent e)
            {
                processRelease(e);
            }
        });
       
        myPlayer = new Player();
        myLevel = new Level("obstaclemap");
       
        endGame = false;
    }
   
    // method to simply load an image from a path
    public static BufferedImage loadImage(String ref)
    {
        BufferedImage bimg = null;
           
        try
        {
           bimg = ImageIO.read(new File(ref));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
       
        return bimg;
    }
   
    // Run method for class
    public void run(DisplayMode dm)
   {  
        setBackground(Color.WHITE);
      s = new Screen();
        s.setFullScreen(dm, this);
       
        this.createBufferStrategy( 2 );
        buffer = this.getBufferStrategy();

        while (!endGame)
        {
            do
            {
                do
                {
                    try
                    {  
                        update();
                        render();
                    }
                    catch (Exception ex)
                    {
                        System.err.println("Game Update Error: " + ex);
                    }
                   
                    try
                    {
                        Thread.sleep(rest);
                    }
                    catch (Exception ex)
                    {
                        System.out.println("Can't sleep!");
                    }
                } while (buffer.contentsRestored());
               
                buffer.show();
               
            } while (buffer.contentsLost());
        }

      s.restoreScreen();
   }
   
    // method to update player and level
    private void update()
    {
        // count Frames per second...
        lastTime = curTime;
        curTime = System.currentTimeMillis();
        totalTime += curTime - lastTime;
       
        if( totalTime > 1000 )
        {
            totalTime -= 1000;
            fps = frames;
            frames = 0;
        }
        ++frames;
       
        // get first reference to calculate sleep time
        now = System.currentTimeMillis();
       
        // get new draw graphics
        graphics = buffer.getDrawGraphics();
       
        // edit/draw player and level
        myPlayer.animatePlayer();
        myLevel.updateOffsets();
    }  
   
    // method to render player, level, and FPS
    private void render()
    {
        // draw level and player
        myLevel.drawLevel(graphics, 0, 0);
        myPlayer.drawPlayer(graphics, (SCREEN_WIDTH / 2) - TILE_SIZE / 2, (SCREEN_HEIGHT / 2) - TILE_SIZE);
       
        // display frames per second...
        graphics.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
        graphics.setColor( Color.GREEN );
        graphics.drawString( String.format( "FPS: %s", fps ), 20, 20 );
       
        // calculate sleep time
        rest = 1000 / 60 - (System.currentTimeMillis() - now);
                       
        if (rest < 0)
        {
            rest = 0;
        }
       
        // dispose graphics
        graphics.dispose();
    }
   
    // method to handle inputs and adjust the player accordingly
    public void processKey(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // termination key
        if (keyCode == KeyEvent.VK_ESCAPE)
        {
            endGame = true;
        }
       
        // 0 - up
        // 1 - left
        // 2 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                myLevel.updateLevel(0);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(1);
                myLevel.updateLevel(1);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(2);
                myLevel.updateLevel(2);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                myLevel.updateLevel(3);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                break;
        }
    }
   
    // method to handle inputs and adjust the player accordingly
    public void processRelease(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        boolean moved = false;
        int xDisplace, yDisplace;
       
        // 0 - up
        // 5 - left
        // 6 - right
        // 3 - down
        // 4 - jump
       
        switch (keyCode)
        {
            case KeyEvent.VK_UP:
                myPlayer.updatePlayer(0);
                myLevel.updateLevel(5);
                break;
            case KeyEvent.VK_LEFT:
                myPlayer.updatePlayer(5);
                myLevel.updateLevel(6);
                break;
            case KeyEvent.VK_RIGHT:
                myPlayer.updatePlayer(6);
                myLevel.updateLevel(7);
                break;
            case KeyEvent.VK_DOWN:
                myPlayer.updatePlayer(3);
                myLevel.updateLevel(8);
                break;
            case KeyEvent.VK_SPACE:
                myPlayer.updatePlayer(4);
                break;
        }
    }
}


Any thoughts left on what I could do make the level scroll faster? Appreciate all the assistance.

Colton

Straight flippin.
Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #20 - Posted 2011-10-12 09:48:14 »

If the game is running at 60 FPS you are displaying as many frames as the screen can show. Increasing the FPS of the game further is out of the question, as it will increase the speed of everything in the game PLUS making the game require better hardware to run. What you want to do to achieve faster movement is just to increase the movement speed of the player, right? I haven't been able to figure out how you actually move the player (e.g. update it's coordinates based on input, TL;DR it all you know...) so I can't tell you exactly how to do it in your code, but you should do movement like this:
1  
2  
3  
4  
if(upKeyDown){
    player.y -= delta * moveSpeed;
}
//Very similar code for all the other directions

 - player.y is the current position of the player (you'll probably not write it exactly like this (public access), but whatever)
 - moveSpeed is a variable that decides the speed of movement (DR. OBVIOUS TO THE RESCUE).
 - delta is the time since the last update, which should be calculated universally for all objects in the game in the game loop.

I think you should add delta calculation to your game loop:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
        long time = System.currentTimeMillis();
        while (!endGame)
        {
            do
            {
                do
                {
                    try
                    {  
                        long currentTime = System.currentTimeMillis();
                        /*Delta = time passed since last update. Also, cast to int, as I doubt that delta will ever exceed Integer.MAX_VALUE... xD*/
                        int delta = (int)(currentTime - time);
                        time = currentTime;
                        update(delta); //Update the game based on how long time has passed.
                        render();
                    }
                    catch (Exception ex)
                    {
                        System.err.println("Game Update Error: " + ex);
                    }
                     
                    try
                    {
                        Thread.sleep(rest);
                    }
                    catch (Exception ex)
                    {
                        System.out.println("Can't sleep!");
                    }
                } while (buffer.contentsRestored());
                 
                buffer.show();
                 
            } while (buffer.contentsLost());
        }

All your update methods should take an int delta parameter, and use it to determine how far to move things each update. Doing it like this also makes sure that the game runs at the same speed, regardless of how fast the computer running the game is. Even though the game only runs at 30FPS for example, the objects would make double as long "jumps" each update so they will move the same distance in the same time.
I hope things have cleared up a bit. xD

Myomyomyo.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #21 - Posted 2011-10-13 08:48:55 »

theagentd,

Thank you very much... that's been the problem all along, and I was unable to pinpoint it. It now makes sense... the player's been moving at the slowest possible speed this whole time. The delta method also makes perfect sense now with that illustrated example. I'll get to work updating everything and trying to tie it all together. I appreciate your help and your examples; you've been very helpful.

Kind regards,
Colton

Straight flippin.
Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #22 - Posted 2011-10-13 09:03:46 »

No problem. Those who ask questions are the ones that learn the most.  Roll Eyes

Myomyomyo.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #23 - Posted 2011-10-13 09:25:34 »

Lol yes, indeed. I know I have a crap ton to learn. Before this, I was just doing highly simple, static-screen based games like Pong. But I implemented a rough version of what you just explained to me, not everything yet but the most important parts, and it works wonderfully, just like I wanted it to. Thanks so much! I'm sure you'll have plenty of questions to answer from me on the forums in the future (;]) and I know I can come here for help from now on without a problem.

Colton

Straight flippin.
Offline theagentd

« JGO Bitwise Duke »


Medals: 366
Projects: 2
Exp: 8 years



« Reply #24 - Posted 2011-10-13 09:27:51 »

Yeah, well, I'm not going anywhere. xp

Myomyomyo.
Offline coltonoscopy

Junior Devvie


Medals: 2



« Reply #25 - Posted 2011-10-13 09:29:27 »

Thank goodness.... you're the man!  Grin

Straight flippin.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Elsealabs (15 views)
2014-12-28 10:39:27

CopyableCougar4 (18 views)
2014-12-28 02:10:29

BurntPizza (22 views)
2014-12-27 22:38:51

Mr.CodeIt (14 views)
2014-12-27 04:03:04

TheDudeFromCI (19 views)
2014-12-27 02:14:49

Mr.CodeIt (26 views)
2014-12-23 03:34:11

rwatson462 (58 views)
2014-12-15 09:26:44

Mr.CodeIt (47 views)
2014-12-14 19:50:38

BurntPizza (96 views)
2014-12-09 22:41:13

BurntPizza (115 views)
2014-12-08 04:46:31
How do I start Java Game Development?
by gouessej
2014-12-27 19:41:21

Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!