簡體   English   中英

如何裁剪繪制的畫布,旋轉並繪制到另一個畫布?

[英]How can I clip a drawn canvas, rotate and draw to another canvas?

我有一個用於游戲世界的畫布和一個用於顯示屏的畫布。 我還有一個多邊形,其節點為V(x,y),可以用作跟隨玩家及其旋轉的視口。 我想知道如何從游戲世界中沿着多邊形進行裁剪,旋轉並繪制到較小的畫布上。 在此處輸入圖片說明 `

//main looping function
var requestAnimFrame = (function(){
    return window.requestAnimationFrame       ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame    ||
        window.oRequestAnimationFrame      ||
        window.msRequestAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        };
})();

//joystick setup
var leftManager = null;
var rightManager = null;

//precalculated math
var twoPi = Math.PI*2;
var halfPi = Math.PI/2;
var thirdOfCircleInRadians = twoPi/3;


//game canvas setup
var gameCvs = document.getElementById('gameCanvas');
gameCvs.width = 480;
gameCvs.height = 320;
//gameCvs.width - 960;
//gameCvs.height = 640;
var gameCtx = gameCvs.getContext("2d");

//game loop
var lastTime = 0;
function main() {
    var now = Date.now();
    var dt = lastTime==0? 0.016 : (now - lastTime) / 1000.0;
    update(dt);
    render(dt);
    lastTime = now;
    requestAnimFrame(main);
}

//collision class shorthand
var V = SAT.Vector;
var C = SAT.Circle;
var P = SAT.Polygon;
var R = new SAT.Response();

P.prototype.draw = function (ctx,type) {

    ctx.save();
    switch(type){
        case 'van': ctx.fillStyle = "rgba(66, 66, 66, 0.5)"; break;
        case 'col': ctx.fillStyle = "rgba(0, 0, 0, 1.0)"; break;
        default: ctx.fillStyle = "rgba(0, 0, 0, 1.0)"; break;
    }
    ctx.translate(this.pos.x, this.pos.y);
    ctx.beginPath();
    var points = this.calcPoints;
    ctx.moveTo(points[0].x, points[0].y);
    var i = points.length;
    while (i--) ctx.lineTo(points[i].x, points[i].y);
    ctx.closePath();
    //stroke to see through camera, when camera is not drawn use fill
    ctx.stroke();
    //ctx.fill();
    ctx.restore();
};

//first for collisions, second for vanity. first is black, second is grey
var O = function(colPolygon,vanPolygon){
    this.colPolygon = colPolygon;
    this.vanPolygon = vanPolygon;
    this.visible = false;
};


var objectVendor = function(type,position){
    switch(type){
        case 'tree': 
        return new O(new P(position,[
            new V(10.5,19.5),
            new V(20.5,9.5),
            new V(23,-4),
            new V(15,-16.5),
            new V(-4,-19.5),
            new V(-18,-14.5),
            new V(-23,-0.5),
            new V(-18.5,14.5),
            new V(-8,20)
            ]),new P(position,[
            new V(21,39),
            new V(41,19),
            new V(46,-8),
            new V(30,-33),
            new V(-8,-39),
            new V(-36,-29),
            new V(-46,-1),
            new V(-37,29),
            new V(-16,40)]));
        break;
        default: return false; break;
    }
    return false;
}

//Camera and Player Polygons

var cameraPoly = new P(new V(0,0),[
        new V(-240,-160),
        new V(240,-160),
        new V(240,160),
        new V(-240,160)
    ]);


var player = new P(new V(0,0),[
        new V(5,2.5),
        new V(7.5,2),
        new V(7.5,-2),
        new V(5,-2.5),
        new V(-5,-2.5),
        new V(-7.5,-2),
        new V(-7.5,2),
        new V(-5,2.5)
    ]);
//players start position on the screen, and starting angle, init velocity
player.pos = new V(240,160);
player.setAngle(1);
//players velocity for movement
player.vel = new V(0,0);

var world = {
    objects: [],
    visibleObjects: [],
    worldCvs: null,
    worldCtx: null,
    init: function(){
        //set up world canvas
        this.worldCvs = document.createElement('canvas');
        this.worldCvs.width = 480;
        this.worldCvs.height = 480;
        this.worldCtx = this.worldCvs.getContext("2d");
        //populate world with stuff
        this.objects.push(objectVendor('tree',new V(100,100)));
        this.objects.push(objectVendor('tree',new V(150,200)));
        this.objects.push(objectVendor('tree',new V(75,300)));
    },
    update: function(dt){
            this.visibleObjects = [];

            cameraPoly.setAngle(player.angle);
            //cameraPoly.pos = player.pos;
            cameraPoly.pos = new V(player.pos.x+(110*Math.cos(player.angle+halfPi)),player.pos.y+(110*Math.sin(player.angle+halfPi)));
            //update objects to mark if they are in view
            var i = this.objects.length;
            while(i--){
                if(SAT.testPolygonPolygon(this.objects[i].vanPolygon, cameraPoly, R)){
                    this.visibleObjects.push(this.objects[i]);
                }
            }
        //}
    },
    draw: function(dt){

        this.worldCtx.setTransform(1,0,0,1,0,0);
        this.worldCtx.clearRect(0,0,this.worldCvs.width,this.worldCvs.height);
        player.draw(this.worldCtx);
        var i = this.visibleObjects.length;
        while(i--){
            this.visibleObjects[i].colPolygon.draw(this.worldCtx,'col');
            this.visibleObjects[i].vanPolygon.draw(this.worldCtx,'van');
        }
        //for testing
        cameraPoly.draw(this.worldCtx);
        /*
        this.worldCtx.save();
        this.worldCtx.beginPath();
        var i = cameraPoly.calcPoints.length;
        this.worldCtx.moveTo(cameraPoly.calcPoints[0].x,cameraPoly.calcPoints[0].y);
        while(i--){
            this.worldCtx.lineTo(cameraPoly.calcPoints[i].x,cameraPoly.calcPoints[i].y);
        }
        this.worldCtx.clip();
        this.worldCtx.restore();
        */
    }

}

function render(dt){
    gameCtx.setTransform(1,0,0,1,0,0);
    gameCtx.clearRect(0,0,gameCvs.width,gameCvs.height);

    world.draw();
    //gameCtx.save();
    //gameCtx.translate(cameraPoly.pos.x,cameraPoly.pos.y);
    //gameCtx.translate(gameCtx.width/2,gameCtx.height/2);
    //gameCtx.rotate(-player.angle+halfPi);
    //gameCtx.translate(-world.worldCvs.width/2,-world.worldCvs.height/2);

    gameCtx.drawImage(world.worldCvs,0,0);
    //gameCtx.restore();
}

function update(dt){
    world.update();
}

function init(){

    //joystick setup
    leftManager = nipplejs.create({
        zone:document.getElementById("leftJoystick"),
        color:"black",
        size:75,
        threshold:1.0,
        position:{
            top:"50%",
            left:"50%"
        },
        mode:"static",
        restOpacity:0.75,
    });
    rightManager = nipplejs.create({
        zone:document.getElementById("rightJoystick"),
        color:"black",
        size:75,
        threshold:1.0,
        position:{
            top:"50%",
            right:"50%"
        },
        mode:"static",
        restOpacity:0.75,
    });

    //joystick event setup
    leftManager.get().on('move end', function(evt,data){
        //console.log(evt);
        //console.log(data);
    });
    rightManager.get().on('move end', function(evt,data){
        //console.log(evt);
        //console.log(data);
    });
    world.init();
    main();
}
init();

`我目前正在使用SAT.js和nipplejs.js庫。

通常,此操作與您所想的有些不同。 與其考慮世界上某個地方存在的視口,不如考慮視口是固定的並且世界在其背后發生了變化。 您不將世界的一部分復制到視口,而是將世界偏移並旋轉了一定量,僅繪制了視口內的部分。 矩陣是表示此轉換的一種簡單且常見的方法。 您可能想在這里閱讀更多有關它們的信息

實際上,這僅相當於在每個並條機開始處更改對worldCtx.setTransform()現有調用。 該鏈接包含有關如何計算良好的變換矩陣的信息,並且由於它是標准的數學運算,因此您可以在各處找到類似的資源。

特別是,您需要將旋轉和平移矩陣相乘。 僅當使用比坐標空間高階的矩陣時,才可以使用轉換矩陣。 對於2D,則是3x3矩陣;對於3D,則是4x4矩陣。 相反,您可以選擇在繪制坐標時向其坐標添加一些偏移量,但是worldCtx.setTransform已經采用了帶有第3列的矩陣,用於放置平面偏移量。

將render函數更改為以下內容將解決問題,只是匆匆忙忙,並沒有很好地思考問題。 `

function render(dt){
    gameCtx.setTransform(1,0,0,1,0,0);
    gameCtx.clearRect(0,0,gameCvs.width,gameCvs.height);
    world.draw();
    gameCtx.translate(gameCvs.width/2,gameCvs.height/2);
    gameCtx.rotate(-player.angle+Math.PI);
    gameCtx.translate(-cameraPoly.pos.x,-cameraPoly.pos.y);
    gameCtx.drawImage(world.worldCvs,0,0);
}`

這是在重置上下文中的所有轉換,將其清除以進行新的重畫,創建世界畫布,平移到顯示中心,旋轉適當的參考點數量,平移到負軸上的參考中心點以移動游戲畫布適當的數量,以便0,0處的圖紙位於正確的位置。 感謝您的參考資料!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM