如何在画布中创建一个跟随玩家旋转和旋转的摄像机视图?

Jon*_*ger 5 javascript html5 canvas game-physics html5-canvas

我正在尝试使用javascript在画布上创建一个游戏,你可以在其中控制一艘宇宙飞船,并使其具有平移和旋转,使其看起来就像宇宙飞船保持静止而不是旋转.

任何帮助将不胜感激.

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()
Run Code Online (Sandbox Code Playgroud)

小智 1

起源

当您需要旋转画布中的某些内容时,如果您喜欢 x 轴和 y 轴交叉的位置,它将始终围绕原点或网格中心旋转。

您可能会发现我的回答也很有用

默认情况下,原点位于位图中的左上角 (0, 0)。

因此,为了围绕 (x,y) 点旋转内容,必须首先将原点平移到该点,然后旋转,最后(通常)平移回来。现在可以按正常顺序绘制事物,并且它们都将相对于该旋转点旋转绘制:

ctx.translate(rotateCenterX, rotateCenterY);
ctx.rotate(angleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Run Code Online (Sandbox Code Playgroud)

绝对角度和位置

有时,使用绝对角度比使用随时间累积的角度更容易跟踪。

translate()transform()rotate()等为累计法;它们添加到之前的转换中。我们可以使用设置绝对变换setTransform()(最后两个参数用于翻译):

ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);  // absolute
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Run Code Online (Sandbox Code Playgroud)

rotateCenterX/Y 将表示绘制时未变换的船舶的位置。此外,绝对变换可能是更好的选择,因为您可以使用绝对角度进行旋转、绘制背景、重置变换,然后在旋转中心 X/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);
Run Code Online (Sandbox Code Playgroud)

(根据事物的顺序,您可以将此处的第一行替换为translate()稍后重置转换的内容,例如请参阅演示)。

这使您可以移动船舶而不必担心当前的变换,当需要旋转时,可以使用船舶的当前位置作为平移和旋转的中心。

最后一点:您用于旋转的角度当然是应该表示的反角(即ctx.rotate(-angle);)。

空间演示(“随机”移动和旋转)

红色“流星”朝一个方向(从顶部)落下,但当飞船“航行”时,它们会相对于我们的俯视角度改变方向。摄像机将固定在船的位置。

(忽略凌乱的部分 - 这只是为了演示设置,我讨厌滚动条......专注于中心部分:))

ctx.translate(rotateCenterX, rotateCenterY);
ctx.rotate(angleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Run Code Online (Sandbox Code Playgroud)
ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);  // absolute
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)