6

I'm having a little problem with figuring something out (Obviously).

I'm creating a 2D Top-down mmorpg, and in this game I wish the player to move around a tiled map similar to the way the game Pokemon worked, if anyone has ever played it.

If you have not, picture this: I need to load various areas, constructing them from tiles which contain an image and a location (x, y) and objects (players, items) but the player can only see a portion of it at a time, namely a 20 by 15 tile-wide area, which can be 100s of tiles tall/wide. I want the "camera" to follow the player, keeping him in the center, unless the player reaches the edge of the loaded area.

I don't need code necessarily, just a design plan. I have no idea how to go about this kind of thing.

I was thinking of possibly splitting up the entire loaded area into 10x10 tile pieces, called "Blocks" and loading them, but I'm still not sure how to load pieces off screen and only show them when the player is in range.

The picture should describe it: enter image description here

Any ideas?


My solution: The way I solved this problem was through the wonderful world of JScrollPanes and JPanels.

I added a 3x3 block of JPanels inside of a JScrollPane, added a couple scrolling and "goto" methods for centering/moving the JScrollPane around, and voila, I had my camera.

While the answer I chose was a little more generic to people wanting to do 2d camera stuff, the way I did it actually helped me visualize what I was doing a little better since I actually had a physical "Camera" (JScrollPane) to move around my "World" (3x3 Grid of JPanels)

Just thought I would post this here in case anyone was googling for an answer and this came up. :)

Luke Dowell
  • 177
  • 2
  • 15
  • This problem may seem complex to you because you are dealing with 2 dimensions; x & y. You should get some experience doing game development for an easier problem, e.g. a side-scrolling platformer which would only require you to think about the problem with x, ignoring y. When I started game development as a hobby, I made a couple static view games like pong and space invaders, moved to 2D side scrolling with a platformer. My next project will be something like what you are doing with both horizontal and vertical movements and I hope I can wrap my head around 3D after that. Good luck! – Jesse Webb Oct 14 '11 at 20:23
  • One other thing to consider: you say the black is "loaded area". In Pokemon, Zelda, all other games that use this camera style, you cannot move your character to a position that is not already on the screen. This means you do not have "load" the entire area, only the parts that are seen by the player. Making this decision will have consequences, if you only load the area you are drawing, a player could walk away and walk back and maybe some random item that wasn't there before would appear, and then disappear if they once again walked away and returned. Two ways of thinking about the problem.. – Jesse Webb Oct 14 '11 at 20:30
  • I figured out my problem. I decided to have a collection of tiles and piece together each map from a string array passed from the server and the current collection of tiles. The camera stays centered over the player, and it works very well. – Luke Dowell Oct 14 '11 at 20:48

3 Answers3

5

For a 2D game, it's quite easy to figure out which tiles fall within a view rectangle, if the tiles are rectangular. Basically, picture a "viewport" rectangle inside the larger world rectangle. By dividing the view offsets by the tile sizes you can easily determine the starting tile, and then just render the tiles in that fit inside the view.

First off, you're working in three coordinate systems: view, world, and map. The view coordinates are essentially mouse offsets from the upper left corner of the view. World coordinates are pixels distances from the upper left corner of tile 0, 0. I'm assuming your world starts in the upper left corner. And map cooridnates are x, y indices into the map array.

You'll need to convert between these in order to do "fancy" things like scrolling, figuring out which tile is under the mouse, and drawing world objects at the correct coordinates in the view. So, you'll need some functions to convert between these systems:

// I haven't touched Java in years, but JavaScript should be easy enough to convey the point

var TileWidth = 40,
    TileHeight = 40;

function View() {
    this.viewOrigin = [0, 0];   // scroll offset
    this.viewSize = [600, 400];
    this.map = null;
    this.worldSize = [0, 0];
}

View.prototype.viewToWorld = function(v, w) {
    w[0] = v[0] + this.viewOrigin[0];
    w[1] = v[1] + this.viewOrigin[1];
};

View.prototype.worldToMap = function(w, m) {
    m[0] = Math.floor(w[0] / TileWidth);
    m[1] = Math.floor(w[1] / TileHeight);
}

View.prototype.mapToWorld = function(m, w) {
    w[0] = m[0] * TileWidth;
    w[1] = m[1] * TileHeight;
};

View.prototype.worldToView = function(w, v) {
    v[0] = w[0] - this.viewOrigin[0];
    v[1] = w[1] - this.viewOrigin[1];
}

Armed with these functions we can now render the visible portion of the map...

View.prototype.draw = function() {
    var mapStartPos = [0, 0],
    worldStartPos = [0, 0],
    viewStartPos = [0, 0];
    mx, my, // map coordinates of current tile
    vx, vy; // view coordinates of current tile

    this.worldToMap(this.viewOrigin, mapStartPos); // which tile is closest to the view origin?
    this.mapToWorld(mapStartPos, worldStartPos);    // round world position to tile corner...
    this.worldToView(worldStartPos, viewStartPos);  // ... and then convert to view coordinates. this allows per-pixel scrolling

    mx = mapStartPos[0];
    my = mapStartPos[y];

    for (vy = viewStartPos[1]; vy < this.viewSize[1]; vy += TileHeight) {
        for (vx = viewStartPos[0]; vx < this.viewSize[0]; vy += TileWidth) {
            var tile = this.map.get(mx++, my);
            this.drawTile(tile, vx, vy);
        }
        mx = mapStartPos[0];
        my++;
        vy += TileHeight;
    }
};

That should work. I didn't have time to put together a working demo webpage, but I hope you get the idea. By changing viewOrigin you can scroll around. To get the world, and map coordinates under the mouse, use the viewToWorld and worldToMap functions.

If you're planning on an isometric view i.e. Diablo, then things get considerably trickier.

Good luck!

alekop
  • 2,838
  • 2
  • 29
  • 47
1

The way I would do such a thing is to keep a variable called cameraPosition or something. Then, in the draw method of all objects, use cameraPosition to offset the locations of everything.

For example: A rock is at [100,50], while the camera is at [75,75]. This means the rock should be drawn at [25,-25] (the result of [100,50] - [75,75]).

You might have to tweak this a bit to make it work (for example maybe you have to compensate for window size). Note that you should also do a bit of culling - if something wants to be drawn at [2460,-830], you probably don't want to bother drawing it.

Toomai
  • 3,974
  • 1
  • 20
  • 22
  • That's kind of along the lines of what I was thinking, but how would I load this large "image" into memory? – Luke Dowell Oct 14 '11 at 20:09
  • Just writing that last comment, do you think it would be possible to just have a huge store of these "tiles" and only paint each tile as it is seen? In an array, perhaps? I feel like that wouldnt be memory efficient, since I have to do checks on each array the player plans on moving to, to make sure a player or object isnt there, and that its not a blocked tile – Luke Dowell Oct 14 '11 at 20:10
0

One approach is along the lines of double buffering ( Java Double Buffering ) and blitting ( http://download.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html ). There is even a design pattern associated with it ( http://www.javalobby.org/forums/thread.jspa?threadID=16867&tstart=0 ).

Community
  • 1
  • 1
gkuan
  • 921
  • 6
  • 6
  • I'm not sure I understand. Your saying I should "buffer" my image outside the screen's field of view, and then move it in as the player progresses towards it? How would I move it back to the "buffered" stage if the player moves away? – Luke Dowell Oct 14 '11 at 20:07