瓷砖之间平滑过渡

时间:2013-03-02 22:06:24

标签: javascript jquery html5 canvas

正如您在此JSFiddle中所看到的,当您使用箭头键移动红色方块时,瓷砖之间的过渡非常不稳定且看起来不太好。

我想知道是否有办法让瓷砖之间平滑过渡,看起来像一个平滑的动作?

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);
    }
});

1 个答案:

答案 0 :(得分:2)

我玩了一会儿,得到了一些工作:

    case 39:
       // Right
       if (playerX < worldWidth) {
           var start = playerX;
           var end   = Math.round(playerX + 1);
           $({ i : start }).animate({ i: end}, {
                duration: 400,
                step: function(now) {
                   playerX = now;
                   draw();
                }
            });
        }
        break;

我使用jquery animate函数来在值之间进行插值。我意识到绘制函数只被调用一次,为了获得动画,我必须每步调用它。

为了避免重新定心的错误,请使用Math.round()

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


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

当然,你必须做额外的工作,例如起点和终点可能会有所不同。正如所建议的那样,你可能应该使用像素位置来代替瓷砖。但我不知道你的意图。

您可以通过检查

来检查磁贴是否处于“完整”位置,从而避免卡在keydown上
if (playerY < worldHeight && playerY % 1 == 0) {...