0

ボール (画像付き) を 2D シーンに投げて、ある程度の距離に到達したときに衝突をチェックしたいと思います。しかし、正しく「飛ばす」ことはできません。これは何百万回も尋ねられているようですが、見つければ見つけるほど混乱します..今、私はこの答えに従いましたが、ボールの動作が予想とは大きく異なるようです. 実際、キャンバスの左上に移動し、速度が速すぎて小さすぎます.vzを0.01などに設定することでこれを調整できますが、ボールがまったく表示されません...

これは私の目的です (簡略化) / 興味のある完全なソースへのリンク. 重要な部分は update() と render() です

var ball = function(x,y) {

  this.x        = x;
  this.y        = y;
  this.z        = 0;
  this.r        = 0;
  this.src      = 'img/ball.png';
  this.gravity  = -0.097;

  this.scaleX   = 1;
  this.scaleY   = 1;

  this.vx       = 0;
  this.vy       = 3.0;
  this.vz       = 5.0;

  this.isLoaded = false;

  // update is called inside window.requestAnimationFrame game loop
  this.update = function() {
    if(this.isLoaded) {
      // ball should fly 'into' the scene
      this.x += this.vx;
      this.y += this.vy;
      this.z += this.vz;

      // do more stuff like removing it when hit the ground or check for collision
      //this.r += ?

      this.vz += this.gravity;
    }
  };

  // render is called inside window.requestAnimationFrame game loop after this.update()
  this.render = function() {
    if(this.isLoaded) {

      var x       = this.x / this.z;
      var y       = this.y / this.z;

      this.scaleX = this.scaleX / this.z;
      this.scaleY = this.scaleY / this.z;

      var width   = this.img.width * this.scaleX;
      var height  = this.img.height * this.scaleY;

      canvasContext.drawImage(this.img, x, y, width, height);

    }
  };

  // load image
  var self      = this;
  this.img      = new Image();
  this.img.onLoad = function() {
    self.isLoaded = true;
    // update offset to spawn the ball in the middle of the click
    self.x        = this.width/2;
    self.y        = this.height/2;
    // set radius for collision detection because the ball is round
    self.r        = this.x;
  };
  this.img.src = this.src;

} 

また、「自然な」飛行アニメーションを実現するために、requestAnimationFrameを使用して〜60fpsでキャンバスをレンダリングするときに、速度のどのパラメータが適切であるべきか疑問に思っています

誰かが私を正しい方向に向けることができれば、とても感謝しています(もちろん、論理を説明する疑似コードも)。

ありがとう

4

2 に答える 2

1

最初にメートル法で状況をシミュレートするのが最善の方法だと思います。

speed = 30; // 30 meters per second or 108 km/hour -- quite fast ...
angle = 30 * pi/180;  // 30 degree angle, moved to radians.

speed_x = speed * cos(angle);
speed_y = speed * sin(angle);  // now you have initial direction vector

x_coord = 0;
y_coord = 0;  // assuming quadrant 1 of traditional cartesian coordinate system

time_step = 1.0/60.0;    // every frame...

// at most 100 meters and while not below ground
while (y_coord > 0 && x_coord < 100) {

   x_coord += speed_x * time_step;
   y_coord += speed_y * time_step;

   speed_y -= 9.81 * time_step;   // in one second the speed has changed 9.81m/s

   // Final stage: ball shape, mass and viscosity of air causes a counter force
   // that is proportional to the speed of the object. This is a funny part:
   // just multiply each speed component separately by a factor (< 1.0)
   // (You can calculate the actual factor by noticing that there is a limit for speed
   //  speed == (speed - 9.81 * time_step)*0.99, called _terminal velocity_
   // if you know or guesstimate that, you don't need to remember _rho_,
   // projected Area or any other terms for the counter force.

   speed_x *= 0.99; speed_y *=0.99;
}

これで、0,0 から始まる時間/位置系列が得られます (これは Excel または OpenOffice Calc で計算できます)。

speed_x        speed_y       position_x     position_y    time 
25,9807687475  14,9999885096 0              0             0 
25,72096106    14,6881236245 0,4286826843   0,2448020604  1 / 60
25,4637514494  14,3793773883 0,8530785418   0,4844583502  2 / 60
25,2091139349  14,0737186144 1,2732304407   0,7190203271
...
5,9296028059   -9,0687933774 33,0844238036  0,0565651137  147 / 60
5,8703067779   -9,1399704437 33,1822622499 -0,0957677271  148 / 60

そのシートから、ボールが地面に当たった距離と時間を最初に見積もることができます。それらは 33,08 メートルと 2.45 秒 (または 148 フレーム) です。エクセルでシミュレーションを続けると、終末速度が約 58 km/h になることもわかりますが、これはそれほど大きくありません。

終末速度 60 m/s または 216 km/h が適切であると判断すると、正しい減衰係数は 0.9972824054451614 になります。

残りの作業は、画面の長さ (メートル単位) を決定し、pos_x、pos_y に正しい倍率を掛けることだけです。1024 ピクセルの画面が 32 メートルの場合、各ピクセルは 3.125 センチメートルに相当します。アプリケーションによっては、現実を「改善」してボールを大きくしたい場合があります。

編集:もう1つは、これを3Dに投影する方法です。以前のアルゴリズム (または Excel) によって生成されたパスを、回転および移動できる可視オブジェクト (線分で構成される) として作成することをお勧めします。

于 2012-11-17T14:45:30.613 に答える
1

あなたが見ている悪い動作の原因は、(0,0) を中心とした、より一般的には見栄えがよくない単純すぎる投影です。
中心、スケールなどを備えたより完全な投影が必要です...
私はそれを使用して少し3Dを追加します:

     projectOnScreen : function(wx,wy,wz) {
            var screenX =   ... real X size of your canvas here ... ;
            var screenY =  ... real Y size of your canvas here ... ;
            var scale   = ... the scale you use between world / screen coordinates ...;
            var ZOffset=3000; // the bigger, the less z has effet
            var k =ZOffset;  // coeficient to have projected point = point for z=0
            var zScale =2.0; // the bigger, the more a change in Z will have effect

            var worldCenterX=screenX/(2*scale);
            var worldCenterY=screenY/(2*scale);


            var sizeAt = ig.system.scale*k/(ZOffset+zScale*wz);
            return {
                     x: screenX/2  +  sizeAt * (wx-worldCenterX) ,
                     y: screenY/2  +  sizeAt * (wy-worldCenterY) ,
                     sizeAt : sizeAt
                  }
        }

明らかに、ゲームに応じて最適化できます。たとえば、解像度とスケールが変わらない場合、その関数からいくつかのパラメーターを一度計算できます。
sizeAt は、画像に適用する必要があるズーム倍率 (canvas.scale) です。

編集 : 更新/レンダリング コードについては、Aki Suihkonen の投稿で指摘されているように、2 つの更新の間の時間である「dt」を使用する必要があります。したがって、1 秒あたりのフレーム数 (fps) を後で変更した場合、またはゲームが一時的に遅くなった場合は、dt を変更してもすべて同じように動作します。
式は x+=vx*dt / ... / vx+=gravity*dt; になります。
画面サイズに関係なく同じ動作をさせるには、画面の高さを基準にして速度と重力を計算する必要があります。
また、最初に負の z を使用します。最初に大きなボールを持ってください。また、私は懸念を分けます:
- 画像のロードを個別に処理します。必要なすべてのアセットがロードされた後、ゲームが開始するはずです。無料の小さなフレームワークの中には、多くのことを行うことができるものがあります。ほんの一例:crafty.jsですが、良いものはたくさんあります。
- クリック位置と画像サイズに関連する調整はレンダリングで行う必要があり、x、y は単なるマウス座標です。

var currWidth = this.width *scaleAt, currHeight= this.height*scaleAt;
canvasContext.drawImage(this.img, x-currWidth/2, y-currHeight/2, currWidth, currHeight);

または、キャンバスを使用してスケーリングを行うこともできます。ボーナスは、この方法で簡単に回転できることです:

 ctx.save();
 ctx.translate(x,y);
 ctx.scale(scaleAt, scaleAt);  // or scaleAt * worldToScreenScale if you have 
                               // a scaling factor
 // ctx.rotate(someAngle);   // if you want...
 ctx.drawImage(this.img, x-this.width/2, x-this.height/2);
 ctx.restore();
于 2012-11-17T15:06:35.773 に答える