简体   繁体   中英

HTML5 canvas viewport for rpg game map not generating

I'm working my way through some online examples and trying to get my head around how a HTML5 game may be put together.

I've got my head around the basics, such as the game loop, and separating the update and render logic.

I'm at the stage where I can generate a map from a tileset, and render a character who can walk around and have its sprite animate as it moves.

What I've struggling with is the way that the viewport follows the character as he moves. It's mostly working, the camera will follow - however, it has a very strange effect on the map that is generated.

The code so far is pretty long, but I've created a JSFiddle of the current way it works, and it illustrates the problems I'm having. The character can be moved with the arrow keys.

jsFiddle of the current issue

jsFiddle of the code

Incidentally, I've noticed if the character walks into the top left corner, it seems to be OK.

The code that is generating the map is:

tileSize = game.currentMap.tileset.tileWidth;

if (player.x >= (game.viewport.x / 2)) r = Math.floor(player.x - ((map.width * map.tileset.tileWidth) / 2))

for (r = Math.floor(game.viewport.x / map.tileset.tileWidth); r < Math.floor(game.viewport.x / map.tileset.tileWidth) + canvas.width / map.tileset.tileWidth + 1; r++) {
    for (c = Math.floor(game.viewport.y / map.tileset.tileHeight); c < Math.floor(game.viewport.y / map.tileset.tileHeight) + canvas.height / map.tileset.tileHeight + 1; c++) {
        var tile = ground[r][c];
        var tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0; // Bitwise OR operation
        var tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
        ctx.drawImage(
        game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
        tileSize,
        tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
        tileSize,
        tileSize);

        tile = layer1[r][c];
        tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0;
        tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
        ctx.drawImage(
        game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
        tileSize,
        tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
        tileSize,
        tileSize);


    }
}

However, as I'm studying the code from other resources - I'm a bit stuck as to whether the issue is being caused by this function or the function which generates and updates the viewport.

Any suggestions?

Update

The code that adjusts the viewport so that if the character is near the edge of the map, it no longer remains in the center of the viewport:

   if (game.viewport.x <= 0) dX = player.x;
else if (game.viewport.x >= game.currentMap.tileset.tileWidth * game.currentMap.width - canvas.width) dX = player.x - game.viewport.x;
else dX = Math.round(canvas.width / 2 - player.width / 2);
if (game.viewport.y <= 0) dY = player.y;
else if (game.viewport.y >= game.currentMap.tileset.tileHeight * game.currentMap.height - canvas.height) dY = player.y - game.viewport.y;
else dY = Math.round(canvas.height / 2 - player.height / 2);

And the code that updates the viewport when the character is moved:

if (player.x + player.width / 2 < canvas.width / 2) {
    game.viewport.x = 0;
} else if (player.x + canvas.width / 2 + player.width / 2 >= map.tileset.tileWidth * map.width) {
    game.viewport.x = map.tileset.tileWidth * map.width - canvas.width;
} else {
    game.viewport.x = Math.floor(player.x - (canvas.width / 2 - player.width / 2));

}


if (player.y + player.height / 2 < canvas.height / 2) {
    game.viewport.y = 0;
} else if (player.y + canvas.height / 2 + player.height / 2 >= map.tileset.tileHeight * map.height) {
    game.viewport.y = map.tileset.tileHeight * map.height - canvas.height;
} else {
    game.viewport.y = Math.floor(player.y - (canvas.height / 2 - player.height / 2));

}

The viewport is initially set using the following:

var game = {
images: 0,
imagesLoaded: 0,
backgroundColor: '#000',
viewport: {
    x: Math.floor(player.x - (canvas.width / 2 - playerSpriteSize / 2)),
    y: Math.floor(player.y - (canvas.height / 2 - playerSpriteSize / 2))
},
currentMap: map,
fps: 0,
lastfps: 0,
fpsTimer: 0

}

Well, after playing with the code for a few days and many cups of coffee, I've managed to solve my own problem which I thought I would share for anyone else having similar issues.

The code to update the viewport was fine, the problem was the initial values assigned to the viewport and the way that the map x and y tiles were being iterated over.

In the game variable, I changed the viewport to be set to:

viewport: {
    x: Math.floor(player.x - (canvas.width / 2)),
    y: Math.floor(player.y - (canvas.height / 2))
},

And then in the drawMap function:

for (r = 0; r < map.rowTileCount; r++) {
for (c = 0; c < map.colTileCount; c++) {

This way, we are always generating the full map (which may be unnecessarily using extra resources, but we can always come back and look at this again later) and then we draw just a clipped part of this to the canvas which is exactly what I wanted.

http://jsfiddle.net/zdMSx/6/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM