Scrolling of Tiledmap

Asked

Viewed 164 times

3

I’m trying to build a camera system for a game that I intend to do in pure javascript, but I can’t produce the effect correctly. If anyone can see and fix it or guide me to read some tutorial would appreciate it very much!

Images used:

Code:

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");

var grass = new Image();
var sand = new Image();
grass.src = "http://i42.tinypic.com/2ljm6w9.png";
sand.src = "http://www.tibiawiki.com.br/images/7/7a/Sand_Tile.gif";

var tsize = 32; //tile size
var mapArray = [[0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,0,0,0,0],
                [1,1,1,1,1,1,1,1,1,1,1,1,1]];

//Cam configs

var camX = 0;
var camY = 0;
var camWidth = 320;
var camHeight = 160;

//others
var offsetX = 0;
var offsetY = 0;
function draw() {
    requestAnimationFrame(draw);
    var startCol = Math.floor(camX / tsize);
    var endCol = startCol + (camWidth / tsize);
    var startRow = Math.floor(camY / tsize);
    var endRow = startRow + (camHeight /tsize);
    offsetX = -camX + startCol * tsize;
    offsetY = -camY + startRow * tsize;
    console.log(startCol + "," + endCol + "," + startRow + "," + endRow);
    
    for(var r = startRow; r < endRow; r++){
        for(var c = startCol; c < endCol; c++){ 
            var x = (c - startCol) * tsize + offsetX;
            var y = (r - startCol) * tsize + offsetY;
            if(mapArray[r][c] == 0) {
                ctx.drawImage(grass, x, y);
            }
            if(mapArray[r][c] == 1) {
                ctx.drawImage(sand, x, y);
            }
        }
    }
}

document.addEventListener("keydown", function(e){
    if(e.keyCode == 39) {
        camX += 1;
    }
    
    if(e.keyCode == 37) {
        camX -= 1;
    }
    
});

draw();
<html>
    <head>
        <title>Scrolling Tiled Map</title>
    </head>
    
    <body>
        <canvas id="canvas1" width="320" height="160" style = "border: 1px solid black;"></canvas>
        <script src="javascript.js"></script>
    </body>
</html>

I’m sorry for the lack of organization with OO, I’m new in programming and I’m also trying to do something simple and functional.

Note: I did not do the proper treatment of the movement with the arrows.

1 answer

6


I tidied up your code and it was like this:

var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");

var grass = new Image();
var sand = new Image();
grass.src = "https://i.stack.imgur.com/PTwXZ.png";
sand.src = "https://i.stack.imgur.com/TtWhX.gif";
var images = [grass, sand];

var tsize = 32; // Tile size.
var mapArray = [[0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,1,0,0,0],
                [0,0,0,0,0,0,0,0,0,0,0,0,0],
                [1,1,1,1,1,1,1,1,1,1,1,1,1]];

var fieldWidth = mapArray[0].length;
var fieldHeight = mapArray.length;

// Cam configs.

var camX = 0;
var camY = 0;
var camWidth = 320;
var camHeight = 160;

// Others.
var offsetX = 0;
var offsetY = 0;
function draw() {
    requestAnimationFrame(draw);
    var startCol = Math.floor(camX / tsize);
    var endCol = startCol + (camWidth /tsize);
    var startRow = Math.floor(camY / tsize);
    var endRow = startRow + (camHeight / tsize);
    var offsetX = -camX + startCol * tsize;
    var offsetY = -camY + startRow * tsize;
    console.log(startCol + "," + endCol + "," + startRow + "," + endRow);
    
    ctx.fillStyle = "#0000FF";
    ctx.fillRect(0, 0, camWidth, camHeight);
    for (var r = Math.max(0, startRow); r <= endRow && r < fieldHeight; r++) {
        for (var c = Math.max(0, startCol); c <= endCol && c < fieldWidth; c++) {
            var x = (c - startCol) * tsize + offsetX;
            var y = (r - startRow) * tsize + offsetY;
            ctx.drawImage(images[mapArray[r][c]], x, y);
        }
    }
}

document.addEventListener("keydown", function(e) {

    if (e.keyCode == 40) {
        camY += 1;
        e.preventDefault();
    }

    if (e.keyCode == 39) {
        camX += 1;
        e.preventDefault();
    }

    if (e.keyCode == 38) {
        camY -= 1;
        e.preventDefault();
    }

    if (e.keyCode == 37) {
        camX -= 1;
        e.preventDefault();
    }
});

draw();
<html>
    <head>
        <title>Scrolling Tiled Map</title>
    </head>
    
    <body>
        <canvas id="canvas1" width="320" height="160" style = "border: 1px solid black;"></canvas>
        <script src="javascript.js"></script>
    </body>
</html>

The changes I’ve made are:

  1. I implemented the scroll for the keys above and below. According to Bacco’s suggestion, I added a e.preventDefault() on each key so that the page scroll does not occur and will mess up the game experience.

  2. I put the images in an array, so it is easier to access them by the indexes in mapArray. This eliminates the need to use if(mapArray[r][c] == 0) and also makes it very easy to add new figures.

  3. I put a standard blue background. This background will be visible if your viewport (camX, camY, camX + camWidth, camY + camHeight) visualize areas that may be outside the matrix, in addition to its edges. Without this, the result is that the old pixels were dropped backwards, resulting in dirt and artifacts in the drawing for areas outside the matrix.

  4. Here you used startCol instead of startRow:

    var y = (r - startCol) * tsize + offsetY;
    
  5. I entered the variables fieldWidth and fieldHeight so I can have the size of the matrix easily.

  6. I added the keyword var in the variables offsetX and offsetY.

  7. I changed the initialization on the loops for to the following:

    var r = Math.max(0, startRow);
    var c = Math.max(0, startCol);
    

    The reason to do this is to never access mapArray[r][c] when r or c are negative (i.e., exceeding the left and/or upper matrix limits).

  8. I changed the status of the ties shutdown for to the following:

    r <= endRow && r < fieldHeight;
    c <= endCol && c < fieldWidth;
    

    The reason for the expressions after the && is for him not to access mapArray[r][c] when r or c exceed the right and/or lower matrix limits.

    Already the reason why I use <= instead of < is that the viewport may not align exactly to the Tiles grid. Thus, although the height of the viewport is equal to 5 Tiles, there can be 6 lines of visible Tiles, being 4 complete lines, half of the first visible line at the top and half of the last visible line at the bottom. The same principle applies also to columns.

And if you want to stop the movement from overtaking the grid of Tiles, the easiest way is to add this:

    camX = Math.max(0, Math.min(camX, fieldWidth * tsize - camWidth));
    camY = Math.max(0, Math.min(camY, fieldHeight * tsize - camHeight));

The place where this will be added depends on how the camX and the camY will be manipulated in the game, but the two most likely places are:

  • The end of the function addEventListener, especially if they are not changed anywhere other than this function.

  • The beginning of the function draw, if you want to correct these values just before drawing and let them be changed at will outside this function.

And in this case, by adding these two lines, you can remove the drawing from the blue background, which corresponds to these other two lines:

    ctx.fillStyle = "#0000FF";
    ctx.fillRect(0, 0, camWidth, camHeight);
  • Thank you Victor! But now I have another question, what would be the most efficient way to prevent the movement to exceed the grid of Tiles?

  • @Matheushenrique Reply edited. :)

  • I’m still stuck in the code trying to understand everything hehe. I don’t quite understand why you switched the "<" by "<=" inside the FOR.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.