Search code examples
javascriptcanvasgame-engineviewport

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


I have a canvas for the game world and a canvas for the display screen. I also have a polygon with nodes V(x,y) to serve as a viewport that follows the player and his rotation. I would like to know how to clip from the game world along the polygon, rotate and draw to the smaller canvas.enter image description here`

//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();

` I'm using libraries SAT.js and nipplejs.js currently.


Solution

  • Changing the render function to the following will solve the problem, just rushing myself and didn't think things through very well. `

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

    What this is doing is resetting any transformations on the context, clearing it for a new redrawing, creating the world canvas, translating to display center, rotating by the proper amount for reference point, translating to reference center point on negative axis to move game canvas proper amount so that drawing at 0,0 is in the correct location. Thank you for the reference material!