2
\$\begingroup\$

I'm working on a college project using the educational Java framework Greenfoot, and as stated above we're making an r-type clone.

I've been looking at using an array of strings to populate the levels. The array contains 60 strings, filling the Y axis, each element in the array represents a y co-ordinate += 10. I use "0" to represent empty space, "1" to represent a block of terrain.

Then I use a for loop and charAt() method to create a new array representing each column along the X axis, and populate the screen this way.

I thought I was being clever by making a "block" class that checked to see if it was connected to any other blocks and adjust it's image accordingly to make the terrain look as if it's a single block/shape. However, this is now slowing down the game to a crawl as there are so many objects in it.

I've tried to add the columns as the game is scrolling and removing them once they leave the screen, but I end up with gaps in the terrain.

How are levels in these types of game normally implemented? Would I be better creating a few big objects to make the terrain and build the whole level from start to finish?

Is there some method I've completely missed?

Here is a screen shot of how it looks at the minute, each block is 22 x 10 pixels. Game Screenshot - too many block objects

\$\endgroup\$
3
  • 1
    \$\begingroup\$ If the checks are slowing down, why not do it ones and save the result? \$\endgroup\$
    – Bobby
    Dec 7, 2011 at 15:54
  • \$\begingroup\$ Forgive my ignorance, but how might I do that? \$\endgroup\$
    – mal
    Dec 7, 2011 at 16:26
  • \$\begingroup\$ Extend your block class with f.e. an enum which contains where to draw a line: TOP = 1, RIGHT = 2, BOTTOM = 4, LEFT = 8. Add a checkBorders method which will check against the surrounding blocks and set that bit field (only once, right after loading the level). And then let the render method check against that enum to see where it needs to draw lines. \$\endgroup\$
    – Bobby
    Dec 7, 2011 at 16:31

5 Answers 5

2
\$\begingroup\$

Looking at your picture, is that Greenfoot? If it is, then most tips for how it's usually done in other games won't really apply very well.

I've actually created a little sidescrolling platformer (think Mario) on Greenfoot before (to help a friend) and I must say that Greenfoot made everything a lot more complicated than it would be in XNA for instance. Not to mention slower - even as simple as it was, it didn't take too much for the framerate to take a hit.

And I only had one or two dozen platforms and a few enemies and bullets.

Now in your case, where every single tile is an object added to the game world... yikes! Maybe if you implement some intelligent culling for pieces that are outside of view, but still, I just don't think Greenfoot was created with this type of game in mind. It's much more oriented toward smaller, board-sized, experiments.

So my recommendation would be to indeed try to create the levels out of larger pieces, as big as they possible can be, in order to reduce the number of objects to a minimum. That's what I did on the platformer - I probably had between 20-30 pieces in the entire level, and performance was tolerable.

\$\endgroup\$
2
  • \$\begingroup\$ Yes it is greenfoot, and it's killing me using it. Moving on from this next semester though. \$\endgroup\$
    – mal
    Dec 8, 2011 at 7:13
  • \$\begingroup\$ I've awarded you the correct answer basex on this : meta.gamedev.stackexchange.com/questions/680/… \$\endgroup\$
    – mal
    Dec 9, 2011 at 23:31
5
\$\begingroup\$

In my mind, there are 2 things related to terrian that you should concern:

  • Object-terrain collision detection
  • Terrain rendering

In your data representation, object-terrain collision should be done in constant time. You can start from the coordinate of object (player), and check the blocks it overlaps. Please check if you have the right algorithm. It should not cause any performance issue.

For rendring part, I didn't fully understand your description:

I then use a for loop and charAt() method to create a new array representing each column along the X axis, and populate the screen this way. I thought I was being clever by making a "block" class that checked to see if it was connected to any other blocks and adjust it's image accordingly to make the terrain look as if it's a single block/shape.

I have several questions:

  • Did you render each block every frame? (I thought the answer is yes)
  • Did you check the string with charAt() every frame?
  • Did you check the connectivity every frame?

The good answer will be: No, no, no.

Rendering a image to buffer is slow, especially on software renderer (Java doesn't use hardware renderer on a lot of platforms). Anyway, we can do some optimization to minimize the number of draw call.

For example, assuming your window size is 660x480. Your block size is 22x10, so you will have about 30*48=1440 blocks on your screen.

  • You can precalculate a "column" of terrian image. For example, when a column appears, you can calculate the connectivity of the column, draw the image of the column (the size should be 22x480). You will only need to render 48 images each frame.
  • Moreover, you can apply a "sliding window" technique. Create a buffer 1 column wider than your window (686*480). Render all visible columns on the buffer that might be visible. Whenever a column disappears, "slide" the buffer by 22 pixels. You will only need render 1 big image per frame (in amortized).

Last suggestion: Do profiling before and optimization. You can start from some simple test, such as "don't render terrains" and see if FPS is really improved.

\$\endgroup\$
2
  • \$\begingroup\$ I think the answers to your questions are yes, no, no. I think Greenfoot renders each block every frame automatically. Must check if I can control this. \$\endgroup\$
    – mal
    Dec 8, 2011 at 7:22
  • \$\begingroup\$ I had coded to render a column at a time as you suggest, but I end up with gaps, in the terrain, it appears the terrain scrolls faster than greenfoot can draw the columns (even though this doesn't make sense, that's what it looks like). \$\endgroup\$
    – mal
    Dec 8, 2011 at 7:25
1
\$\begingroup\$

I know nothing of this "greenfoot", but with many tile-based games, if you have a way to render to a different buffer (that isn't limited by any specific size), you can get away with rendering your entire world only ONE time to the separate buffer. (Because the world in tile-based games usually never changes, only where you're looking at it from)

You could then just grab your current screen from this larger buffer during your rendering. This can often be a HUGE optimization. Especially for something like yours where you need to calculate where lines are drawn.

Alternatively, if you can't render to a different buffer, you could store the information of which blocks are drawing which lines. For example if you had a character array representing the tiles in your map, you could have another array of the same size storing which lines they would draw, and store the information bitwise.

#define TOP_LINE     0x0001
#define BOTTOM_LINE  0x0010
#define RIGHT_LINE   0x0100
#define LEFT_LINE    0x1000

// Then for each cell 
// (assuming CellData[height][width] and CellLineData[height][width])
if( NeedsToDrawTopLine(CellData[i][j]) )    CellLineData[i][j] |= TOP_LINE;
if( NeedsToDrawBottomLine(CellData[i][j]) ) CellLineData[i][j] |= BOTTOM_LINE;
if( NeedsToDrawRightLine(CellData[i][j]) )  CellLineData[i][j] |= RIGHT_LINE;
if( NeedsToDrawLeftLine(CellData[i][j]) )   CellLineData[i][j] |= LEFT_LINE;

With just these four bits, there are only 16 possible types of cell/lines combos, so you could store them all, and then your render loop could just draw these exact shapes based on the values within CellLineData. That way you can avoid saying something like "Does this cell have a left and right line?" and just instead say "Draw shape 14", etc.

And then for physics, assuming you're going for simple tilemap physics, you only need to store a big binary array, and in your physics checks, you'll (hopefully) only have to check in the exact cells around your character's position.

Also, if you're looking for a simple tool for basic tilemap level design, I recommend checking out Tiled Map Editor. It is really simple to use, and can export a CSV file (comma seperated values) which you can parse EXTREMELY easily to load in your levels.

Pardon if any of my math/code is wrong, it's pretty late. Just trying to help. Best of luck

\$\endgroup\$
0
\$\begingroup\$

I recommend that you simply add data to your Block class that decides where to render it out, you should only EVER have to calculate the relationship between blocks being connected to other blocks one time. If more, it's only because it has been modified, and at that point you should decide where it is, so that you only have to test that block, and not all of them.

I would have a method that will setup which blocks have a border in enum, or 4 boolean values in some sort of structure, that show which do. Populate the data only once, right after loading the map.

\$\endgroup\$
0
\$\begingroup\$

If you only use '1' to represent terrain and '0' to represent empty space why not use boolean arrays instead of strings/string arrays this will probably help a bit and might be a quick fix. Although I don't think this is really the way to do it, authoring a level will become really hard like this.

I suggest you just use a 2 color bitmap. This would make for easy level creation and finding out if a piece of space is empty or not would be just checking the color at that specific coordinate.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .