Jon Burger
Jon Burger

Reputation: 51

How to create a camera view in canvas that will follow a players rotation and rotation?

I'm trying to create a game in canvas with javascript where you control a spaceship and have it so that the canvas will translate and rotate to make it appear like the spaceship is staying stationary and not rotating.

Any help would be greatly appreciated.

window.addEventListener("load",eventWindowLoaded, false);

function eventWindowLoaded() {
canvasApp();    
}

function canvasSupport() {
return Modernizr.canvas;    
}

function canvasApp() {
if (!canvasSupport()) {
    return;
}

var theCanvas = document.getElementById("myCanvas");
var height = theCanvas.height; //get the heigth of the canvas
var width = theCanvas.width;  //get the width of the canvas
var context = theCanvas.getContext("2d");  //get the context

var then = Date.now();

var bgImage = new Image();

var stars = new Array;

bgImage.onload = function() {
    context.translate(width/2,height/2);
    main();
}



var rocket = {
    xLoc: 0,
    yLoc: 0,
    score : 0,
    damage : 0,
    speed : 20,
    angle : 0,
    rotSpeed : 1,
    rotChange: 0,
    pointX: 0,
    pointY: 0,

    setScore : function(newScore){
        this.score = newScore;
    }
}

function Star(){
    var dLoc = 100;
    this.xLoc = rocket.pointX+ dLoc - Math.random()*2*dLoc;
    this.yLoc = rocket.pointY + dLoc - Math.random()*2*dLoc;
    //console.log(rocket.xLoc+" "+rocket.yLoc);
    this.draw = function(){
        drawStar(this.xLoc,this.yLoc,20,5,.5);
    }
}

//var stars = new Array;

var drawStars = function(){
    context.fillStyle = "yellow";
    if (typeof stars !== 'undefined'){
        //console.log("working");
        for(var i=0;i< stars.length ;i++){
            stars[i].draw();
        }
    }


}

var getDistance = function(x1,y1,x2,y2){
    var distance = Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2));
    return distance;
}

var updateStars = function(){
    var numStars = 10;
    while(stars.length<numStars){
        stars[stars.length] = new Star();
    }
    for(var i=0; i<stars.length; i++){
        var tempDist = getDistance(rocket.pointX,rocket.pointY,stars[i].xLoc,stars[i].yLoc);
        if(i == 0){
            //console.log(tempDist);
        }
        if(tempDist > 100){
            stars[i] = new Star();
        }
    }
}

function drawRocket(xLoc,yLoc, rWidth, rHeight){
    var angle = rocket.angle;

    var xVals = [xLoc,xLoc+(rWidth/2),xLoc+(rWidth/2),xLoc-(rWidth/2),xLoc-(rWidth/2),xLoc];
    var yVals = [yLoc,yLoc+(rHeight/3),yLoc+rHeight,yLoc+rHeight,yLoc+(rHeight/3),yLoc];


    for(var i = 0; i < xVals.length; i++){
        xVals[i] -= xLoc;
        yVals[i] -= yLoc+rHeight;
        if(i == 0){
            console.log(yVals[i]);
        }
        var tempXVal = xVals[i]*Math.cos(angle) - yVals[i]*Math.sin(angle);
        var tempYVal = xVals[i]*Math.sin(angle) + yVals[i]*Math.cos(angle);
        xVals[i] = tempXVal + xLoc;
        yVals[i] = tempYVal+(yLoc+rHeight);
    }

    rocket.pointX = xVals[0];
    rocket.pointY = yVals[0];
    //rocket.yLoc = yVals[0];
    //next rotate

    context.beginPath();
    context.moveTo(xVals[0],yVals[0])
    for(var i = 1; i < xVals.length; i++){
        context.lineTo(xVals[i],yVals[i]);
    }
    context.closePath();
    context.lineWidth = 5;
    context.strokeStyle = 'blue';
    context.stroke();

}

var world = {
    //pixels per second
    startTime: Date.now(),
    speed: 50,
    startX:width/2,
    startY:height/2,
    originX: 0,
    originY: 0,
    xDist: 0,
    yDist: 0,
    rotationSpeed: 20,
    angle: 0,
    distance: 0,
    calcOrigins : function(){
        world.originX = -world.distance*Math.sin(world.angle*Math.PI/180);
        world.originY = -world.distance*Math.cos(world.angle*Math.PI/180);
    }
};

var keysDown = {};

addEventListener("keydown", function (e) {
   keysDown[e.keyCode] = true;
}, false);

addEventListener("keyup", function (e) {
   delete keysDown[e.keyCode];
}, false);

var update = function(modifier) {
    if (37 in keysDown) { // Player holding left
        rocket.angle -= rocket.rotSpeed* modifier;
        rocket.rotChange = - rocket.rotSpeed* modifier;
        //console.log("left");
    }
    if (39 in keysDown) { // Player holding right
        rocket.angle += rocket.rotSpeed* modifier;
        rocket.rotChange = rocket.rotSpeed* modifier;
        //console.log("right");


    }


};



var render = function (modifier) {
    context.clearRect(-width*10,-height*10,width*20,height*20);

    var dX = (rocket.speed*modifier)*Math.sin(rocket.angle);
    var dY = (rocket.speed*modifier)*Math.cos(rocket.angle);

    rocket.xLoc += dX;
    rocket.yLoc -= dY;

    updateStars();
    drawStars();
    context.translate(-dX,dY);
    context.save();
    context.translate(-rocket.pointX,-rocket.pointY);




    context.translate(rocket.pointX,rocket.pointY);
    drawRocket(rocket.xLoc,rocket.yLoc,50,200);
    context.fillStyle = "red";
    context.fillRect(rocket.pointX,rocket.pointY,15,5);

    //context.restore(); // restores the coordinate system back to (0,0)

    context.fillStyle = "green";
    context.fillRect(0,0,10,10);
    context.rotate(rocket.angle);
    context.restore();











};

function drawStar(x, y, r, p, m)
{
    context.save();
    context.beginPath();
    context.translate(x, y);
    context.moveTo(0,0-r);
    for (var i = 0; i < p; i++)
    {
        context.rotate(Math.PI / p);
        context.lineTo(0, 0 - (r*m));
        context.rotate(Math.PI / p);
        context.lineTo(0, 0 - r);
    }
    context.fill();
    context.restore();
}



// the game loop

function main(){
    requestAnimationFrame(main);


    var now = Date.now();
    var delta = now - then;

   update(delta / 1000);
    //now = Date.now();
    //delta = now - then;
   render(delta / 1000);

   then = now;

// Request to do this again ASAP


}

var w = window;
var requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||    w.mozRequestAnimationFrame;
//start the game loop
//gameLoop();

//event listenters
bgImage.src = "images/background.jpg";



} //canvasApp()

Upvotes: 5

Views: 1526

Answers (1)

user1693593
user1693593

Reputation:

Origin

When you need to rotate something in canvas it will always rotate around origin, or center for the grid if you like where the x and y axis crosses.

You may find my answer here useful as well

By default the origin is in the top left corner at (0, 0) in the bitmap.

So in order to rotate content around a (x,y) point the origin must first be translated to that point, then rotated and finally (and usually) translated back. Now things can be drawn in the normal order and they will all be drawn rotated relative to that rotation point:

ctx.translate(rotateCenterX, rotateCenterY);
ctx.rotate(angleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);

Absolute angles and positions

Sometimes it's easier to keep track if an absolute angle is used rather than using an angle that you accumulate over time.

translate(), transform(), rotate() etc. are accumulative methods; they add to the previous transform. We can set absolute transforms using setTransform() (the last two arguments are for translation):

ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);  // absolute
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);

The rotateCenterX/Y will represent the position of the ship which is drawn untransformed. Also here absolute transforms can be a better choice as you can do the rotation using absolute angles, draw background, reset transformations and then draw in the ship at rotateCenterX/Y:

ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);

// update scene/background etc.

ctx.setTransform(1, 0, 0, 1, 0, 0);   // reset transforms
ctx.drawImage(ship, rotateCenterX, rotateCenterY);

(Depending on orders of things you could replace the first line here with just translate() as the transforms are reset later, see demo for example).

This allows you to move the ship around without worrying about current transforms, when a rotation is needed use the ship's current position as center for translation and rotation.

And a final note: the angle you would use for rotation would of course be the counter-angle that should be represented (ie. ctx.rotate(-angle);).

Space demo ("random" movements and rotations)

The red "meteors" are dropping in one direction (from top), but as the ship "navigates" around they will change direction relative to our top view angle. Camera will be fixed on the ship's position.

(ignore the messy part - it's just for the demo setup, and I hate scrollbars... focus on the center part :) )

var img = new Image();
img.onload = function() {
  var ctx = document.querySelector("canvas").getContext("2d"),
      w = 600, h = 400, meteors = [], count = 35, i = 0, x = w * 0.5, y, a = 0, a2 = 0;
  ctx.canvas.width = w; ctx.canvas.height = h; ctx.fillStyle = "#555";
  while(i++ < count) meteors.push(new Meteor());
  (function loop() {
    ctx.clearRect(0, 0, w, h);
    
    y = h * 0.5 + 30 + Math.sin((a+=0.01) % Math.PI*2) * 60; // ship's y and origin's y

    // translate to center of ship, rotate, translate back, render bg, reset, draw ship
    ctx.translate(x, y);                 // translate to origin
    ctx.rotate(Math.sin((a2+=0.005) % Math.PI) - Math.PI*0.25);  // rotate some angle
    ctx.translate(-x, -y);               // translate back
   
    ctx.beginPath();  // render some moving meteors for the demo
    for(var i = 0; i < count; i++) meteors[i].update(ctx); ctx.fill();
    
    ctx.setTransform(1, 0, 0, 1, 0, 0);  // reset transforms
    ctx.drawImage(img, x - 32, y);       // draw ship as normal
    
    requestAnimationFrame(loop);         // loop animation
  })();
};

function Meteor() {    // just some moving object..
  var size = 5 + 35 * Math.random(), x = Math.random() * 600, y = -200;
  this.update = function(ctx) {
    ctx.moveTo(x + size, y);  ctx.arc(x, y, size, 0, 6.28);
    y += size * 0.5; if (y > 600) y = -200;
  };
}
img.src = "http://i.imgur.com/67KQykW.png?1";
body {background:#333} canvas {background:#000}
<canvas></canvas>

Upvotes: 1

Related Questions