10

I have a 2D array that is 30 across, 20 down. However, the viewport only paints 15 across and 10 down. I had a game like that originally and I've been trying to achieve something like this:

enter image description here

Here's my fiddle: http://jsfiddle.net/sTr7q/

test
  • 17,706
  • 64
  • 171
  • 244
  • Found this to be promising.. Not sure how I'd use it. http://kaioa.com/node/103 – test Jul 13 '12 at 05:23
  • Why doesn't your canvas have width and height attributes? Those define the workable area, while the css does not. – Daedalus Jul 13 '12 at 06:17
  • 1
    Does this answer your question? [HTML5 Canvas camera/viewport - how to actually do it?](https://stackoverflow.com/questions/16919601/html5-canvas-camera-viewport-how-to-actually-do-it) – ggorlen Dec 26 '20 at 18:40

1 Answers1

9

Here I've made a rudimentary viewport for you:

http://jsfiddle.net/kmHZt/

You have to keep track of the viewport X and Y (vX, vY) that represent where you are in the world at large. I also kept the viewport width and height as vars instead of hard coding them in.

You can use the arrow keys to change the viewport X and Y which will scroll the map, drawing only the correct tiles.

Hope that helps! Let me know if you have any questions!

EDIT: example of viewport moving with player: http://jsfiddle.net/kmHZt/10/

var canvas, context, board, imageObj, tiles, board, display;
var NUM_OF_TILES = 2;

// viewport
var vX = 0,
    vY = 0,
    vWidth = 15,
    vHeight = 10;

var playerX = 0,
    playerY = 0;

var worldWidth = 29,
    worldHeight = 19;

function loadMap(map) {
    if (map == 1) {
        return [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0], [0, 1, 1, 2, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 0], [0, 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0], [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 0], [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
    }
}

$(document).ready(function() {


    canvas = document.getElementById("canvas");
    context = canvas.getContext("2d");

    canvas.tabIndex = 0;
    canvas.focus();
    canvas.addEventListener('keydown', function(e) {
        console.log(e);
        var key = null;
        switch (e.which) {
        case 37:
            // Left
            if (playerX > 0) playerX--;
            break;
        case 38:
            // Up
            if (playerY > 0) playerY--;
            break;
        case 39:
            // Right
            if (playerX < worldWidth) playerX++;
            break;
        case 40:
            // Down
            if (playerY < worldHeight) playerY++;
            break;
        }
        // Okay! The player is done moving, now we have to determine the "best" viewport.
        // Ideally the viewport centers the player,
        // but if its too close to an edge we'll have to deal with that edge

        vX = playerX - Math.floor(0.5 * vWidth);
        if (vX < 0) vX = 0;
        if (vX+vWidth > worldWidth) vX = worldWidth - vWidth;


        vY = playerY - Math.floor(0.5 * vHeight);
        if (vY < 0) vY = 0;
        if (vY+vHeight > worldHeight) vY = worldHeight - vHeight;


        draw();
    }, false);

    var board = [];

    canvas.width = 512;
    canvas.height = 352;

    board = loadMap(1);
    imageObj = new Image();
    tiles = [];

    var loadedImagesCount = 0;
    for (x = 0; x <= NUM_OF_TILES; x++) {
        var imageObj = new Image(); // new instance for each image
        imageObj.src = "http://mystikrpg.com/canvas/img/tiles/t" + x + ".png";

        imageObj.onload = function() {
            // console.log("Added tile ... "+loadedImagesCount);
            loadedImagesCount++;
            if (loadedImagesCount == NUM_OF_TILES) {
                // Onces all tiles are loaded ...
                // We paint the map
                draw();
            }
        };
        tiles.push(imageObj);
    }


    function draw() {
        context.clearRect(0,0,canvas.width, canvas.height);
        for (y = 0; y <= vHeight; y++) {
            for (x = 0; x <= vWidth; x++) {
                theX = x * 32;
                theY = y * 32;
                context.drawImage(tiles[board[y+vY][x+vX]], theX, theY, 32, 32);
            }
        }
        context.fillStyle = 'red';
        context.fillRect((playerX-vX)*32, (playerY-vY)*32, 32, 32);
    }
});
Spoeken
  • 2,549
  • 2
  • 28
  • 40
Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • 1
    This is good, very good. But how would I adapt this to a player moving? Like above in my question... a player adapting to the wall so the camera won't move? – test Jul 14 '12 at 06:17
  • Well that depends on how you want the player to move. Do you want the player "ideally" always in the center of the screen, unless he's against the edge of the world? Or does that not matter? – Simon Sarris Jul 14 '12 at 20:18
  • `always in the center of the screen, unless he's against the edge of the world` That seems pretty efficient to me. – test Jul 15 '12 at 04:15
  • 1
    Well, however the player moves you let him move in the world (or not) and then you calculate the viewport X and Y based on this, while always trying to keep it as centered as possible. Here is an example: http://jsfiddle.net/kmHZt/10/ – Simon Sarris Jul 15 '12 at 20:53