jjzjj

javascript - 如何在 Canvas 中创建一个跟随玩家旋转和旋转的相机 View ?

coder 2023-08-12 原文

我正在尝试使用 javascript 在 Canvas 中创建一个游戏,您可以在其中控制宇宙飞船并拥有它,以便 Canvas 平移和旋转,使其看起来像是宇宙飞船保持静止而不旋转。

如有任何帮助,我们将不胜感激。

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

最佳答案

起源

当您需要在 Canvas 中旋转某些东西时,它总是围绕原点旋转,或者如果您喜欢 x 轴和 y 轴的交叉点,则以网格为中心旋转。

您可能会找到我的 answer here 也很有用

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

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

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

绝对 Angular 和位置

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

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

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

rotateCenterX/Y 将代表未转换绘制的船的位置。此外,绝对变换可能是更好的选择,因为您可以使用绝对 Angular 进行旋转、绘制背景、重置变换,然后在 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);

(根据事情的顺序,您可以只用 translate() 替换这里的第一行,因为稍后会重置转换,例如参见演示)。

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

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

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

红色“ meteor ”朝一个方向(从顶部)落下,但随着飞船“航行”,它们会根据我们的俯视角改变方向。相机将固定在船的位置。

(忽略乱七八糟的部分——它只是为了演示设置,我讨厌滚动条……关注中心部分:))

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>

关于javascript - 如何在 Canvas 中创建一个跟随玩家旋转和旋转的相机 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29807957/

有关javascript - 如何在 Canvas 中创建一个跟随玩家旋转和旋转的相机 View ?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  5. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  6. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  7. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  8. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  9. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  10. ruby-on-rails - 如何在 ruby​​ 交互式 shell 中有多行? - 2

    这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式ruby​​shell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f

随机推荐