[Solved] Keeping the player in view

Started by
2 comments, last by ElGeeko7 9 years, 6 months ago

I'm creating a little 2-D platform game, and so far everything works okay, but I'm not sure what to do when the player goes outside of the screen. In Game Maker (now YoYoGameMakerStudio or whatever) they used to have "views" that would follow the player object. This was great, but I'm not sure how that would work in a "real" language like C# (which is what I'm using now) or Java. I could do a simple check like this:


if (player.x > GAME_WIDTH + 32)
    GoToNextRoom();
if (player.x < 0)
    GoToPreviousRoom();

But even the simplest platformers don't seem to do it that way. Even the original Super Mario Bros. seems to have the whole view thing down. So what do you guys do? Thanks.

Advertisement

I was somewhat getting into the same subject over in this thread. This might be a good time to differentiate screen space from world space, and implement a simple camera that is capable of mapping between the two.

In your current code, I'm guessing that player.x is a number that simply represents pixels on the screen. Your world space and screen space are identical, which makes it impossible to view different parts of your world within the same screen area. What you want to achieve instead is to get player.x to refer to some number that is specific to world space, in whatever units you prefer. Meters are common, but any unit of distance will work, even the completely fabricated unit "geeko". Whatever is easiest for you to conceptualize when thinking about distances within your game's world.

So if you know where your player is in the world, how do you determine where they should be drawn on the screen? You use the concept of a camera. The simplest 2D camera will store the position of the camera, preferably in world space. Additionally, it's best to also include a zoom factor, even if it never changes. This relates your world's unit of distance to the screen's unit of distance. Pixels per meter, for example.

So let's say your player's horizontal position in your level is 20 meters. If you also know that your camera's center is looking at the part of your level 18 meters in, then we know that the player is 2 meters to the right of the camera. If we further know that the camera is zoomed in such that there are 64 pixels per meter, then we can determine that in screen space, the player will be 128 pixels to the right of the camera's center. Assuming we have a fixed-size viewport, and the camera's center corresponds to the exact center of that viewport, we can then determine that the player should be drawn 128 pixels to the right of the viewport center.

In code, you might have this function (assuming Point2D, Camera, and Viewport are appropriately defined structs/classes with the necessary members):


Point2D ConvertWorldToScreen(Point2D world, Camera camera, Viewport viewport)
{
    return new Point2D(
        (world.x - camera.x) * camera.screenUnitsPerWorldUnit + viewport.width / 2,
        (world.y - camera.y) * camera.screenUnitsPerWorldUnit + viewport.height / 2);
}

This can naturally work for any object in your world, not just your player.

Sometimes you'll need to also take a screen space position and convert it to world coordinates. If, for example, you handle mouse clicks. Alternatively if you need to know the world space position corresponding to one of the corners of your viewport. This function will handle that:


Point2D ConvertScreenToWorld(Point2D screen, Camera camera, Viewport viewport)
{
    return new Point2D(
        (screen.x - viewport.width / 2) / camera.screenUnitsPerWorldUnit + camera.x,
        (screen.y - viewport.height / 2) / camera.screenUnitsPerWorldUnit + camera.y);
}

Once you have infrastructure like this, and all your objects are represented in world space, all you need to do now is figure out where your camera should be and redraw. The simplest method is to use a camera locked to your player. camera.x = player.x and camera.y = player.y. Your player will now always be in the exact middle of your viewport, no matter where in the level they are. This has some subjective disadvantages, though, so you might want to look into some more advanced systems of managing the camera. This page has a good overview of options and concerns to consider.

"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke

You've got it a bit backwards.

Don't try to "go" to a new place in the world, that has no meaning, but bring the world to you and render just the part of it you want on the screen.

Like Andy said, there's screen space, and there's world space; you need an effective algorithm to convert between the two to know what tile from the world you need to draw where on the screen, and it will usually be based somewhat on the character position in world space, which stays somewhat generally in the center of your screen.

You can think of it physically like, you have a really small table. On the right side, you assemble tiles and slide them on the table, and on the left side they fall off back into your box 'o stuff. You build the world on your screen as you move, but the screen never moves.

Wow, thanks for the quick response!

You were right about the x variable in my example above; it was just a pixel-based coordinate. My player object currently moves around the screen using the arrow keys. Of course the world size is bigger than the screen size (like I said even in old-school Mario lol), so I get what you're saying about "world size"; but it sounds like what you're saying is that instead of moving the player around the world, move the world around the player (or the camera if that's a separate object).

That actually makes a lot of sense. So if I for example hold down the right arrow key, and my player object is at a certain point (in screen size), the world has to shift to the left to make room for new "tiles" on the right, creating the effect of the player moving through the world. Does that sound about right, or am I way off in left field somewhere? lol

PS - that "geeko" crack was awesome. smile.png

EDIT: This was exactly what I was looking for! I tried it in one of my other projects, and it came out great! I'm going to mark this thread as solved. But before I do, here's how I got it working in case anyone else is looking for the answer to this question:


/*
This is the code from a different game I'm working on, a fantasy adventure game similar to Zelda.
In the main window's key press event, I've set it up so the player can move up/down/left/right
*/
private void KeyPressed(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Left)
            {
                if (player.x >= 96)
                    player.MoveLeft();
                else
                {
                    for (int i = 0; i < GameObjects.Count; i++)    // "GameObjects" is a List containing everything except the player
                        GameObjects[i].MoveRight();
                }
            }
            else if (e.KeyCode == Keys.Right)
            {
                if (player.x <= 640)
                    player.MoveRight();
                else
                {
                    for (int i = 0; i < GameObjects.Count; i++)
                        GameObjects[i].MoveLeft();
                }
            }
            else if (e.KeyCode == Keys.Up)
            {
                if (player.y >= 96)
                    player.MoveUp();
                else
                {
                    for (int i = 0; i < GameObjects.Count; i++)
                        GameObjects[i].MoveDown();
                }
            }
            else if (e.KeyCode == Keys.Down)
            {
                if (player.y <= 512)
                    player.MoveDown();
                else
                {
                    for (int i = 0; i < GameObjects.Count; i++)
                        GameObjects[i].MoveUp();
                }
            }
        }

The numbers still need a little tweaking, but the objects in the GameObjects list appear not to move, even though the code above shows otherwise. And when I want the player to reach the edge of the world, I can just put up an object that blocks the player from going past the point where the other objects start moving. Very cool! :)

This topic is closed to new replies.

Advertisement