如何使用矩形或弧形在画布中模拟重力?

Tay*_*orS 4 javascript canvas game-physics html5-canvas

如何使用只有几个变量的画布对象来模拟重力。

我创建了基础画布、游戏、时间、得分、一切,甚至游戏状态,但我被困在“将速度和重力因子添加到玩家 Y 变量中”的部分。

我尝试将重力系数乘以指定值,然后将其添加到 yVel,然后将其添加到实际 Y 值,但我无法正确转换定位。

我想如果我弄清楚如何“创造重力”来创造跳跃、向右移动和向左移动就不会太困难了。

这是我用来寻找平台的主要代码:

map.plates 表示一个充满数组的数组,每个数组包含一个车牌(平台车牌)的 4 个值,e 是 map.plates.Arrays。playY 基本上就是玩家的 Y 高度,全部渲染到 fillRect() 中;

function detectGravity() {
 map.plates.forEach(e => {
  if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
  } else {
   playY += 0; // Gravity Calculations here
  }
 });
}
Run Code Online (Sandbox Code Playgroud)

我真的不知道是否应该在此处包含其他内容,但如果您想要整个项目,请参阅下面的代码片段。

如果问题有什么问题请告诉我,我已经快半年没有来过这里了。

完整代码以防 codepen 死亡(在评论中建议):

"esversion: 6";

const can = document.querySelector(".block"),
 ctx = can.getContext("2d"),
 mScore = 100,
 map = {
  plates: [
   [25, 25, 25, 2],
   [75, 25, 25, 2],
   [125, 25, 25, 2],
   [175, 25, 25, 2],
   [225, 25, 25, 2],
   [25, 75, 25, 2],
   [75, 62, 25, 2],
   [125, 50, 25, 2],
   [175, 38, 25, 2],
   [25, 87, 25, 2],
   [75, 100, 25, 2]
  ],
  moneys: [
   [25, 25],
   [125, 25],
   [225, 25],
   [75, 62],
   [75, 100]
  ],
  player: [25, 25, 2, 2],
  badSpt: []
 };

let score = 0,
 time = 60,
 gameOn = 0;

let playX,
 playY,
 velX,
 velY,
 grav = 1.05;

can.addEventListener("click", startGame);

function startGame() {
 if (gameOn != 1) {
  gameOn = 1;
  init();
  gameTime = setInterval(() => {
   if (time != 0) {
    time -= 1;
   }
  }, 1000);
 }
}

function init() {
 can.width = 300;
 can.height = 300;
 drawEnviron();
 drawLevel();
 drawPlayer();
 drawGame();
 drawPixels();
 if (time == 0) {
  clearInterval(gameTime);
  time = 60;
  gameOn = 2;
 }
 window.requestAnimationFrame(init);
}

function drawEnviron() {
 with (ctx) {
  fillStyle = "#000000";
  fillRect(0, 0, can.width, can.height);
  fillStyle = "rgba(255, 255, 255, 0.5)";
  fillRect(0, 0, can.width, can.height);
  fillStyle = "#000000";
  fillRect(0, 0, can.width, can.height / 15);
  fillStyle = "#ffffff";
  font = can.height / 15 + "px Verdana";
  fillText("Score: " + score + "/" + mScore, 1, can.height / 19);
  fillText("Time: " + time, can.width / 1.5 + 6, can.height / 19);
 }
}

function drawLevel() {
 map.plates.forEach(e => {
  ctx.fillStyle = "#ffffff";
  ctx.fillRect(e[0], can.height / 15 + e[1], e[2], e[3]);
 });
 map.moneys.forEach(e => {
  ctx.beginPath();
  ctx.fillStyle = "#fcba03";
  ctx.arc(e[0] + 12.5, e[1] + 12.5, 4, 0, 2 * Math.PI);
  ctx.fill();
 });
}

function detectGravity() {
 map.plates.forEach(e => {
  if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
   
  } else {
   playY += 0;
  }
 });
}

function drawPlayer() {
 const a = map.player;
 if (gameOn == 0 || gameOn == 2) {
  playX = a[0];
  playY = a[1];
  velX = 0;
  velY = 0;
 }
 ctx.beginPath();
 ctx.fillStyle = "#ff0000";
 ctx.arc(playX + 12.5, playY + 12.5, 4, 0, 2 * Math.PI);
 ctx.fill();
}

function drawGame() {
 if (gameOn == 0) {
  can.style.animation = "none";
  with (ctx) {
   fillStyle = "rgba(0, 0, 0, 0.5)";
   fillRect(0, 0, can.width, can.height);
   strokeStyle = "#000000";
   lineWidth = 5;
   fillStyle = "#ffffff";
   textAlign = "center";
   strokeText("Click to Start", 150, can.height / 4);
   fillText("Click to Start", 150, can.height / 4);
  }
 } else if (gameOn == 2) {
  can.style.animation = "0.2s flash infinite";
  with (ctx) {
   fillStyle = "rgba(0, 0, 0, 0.5)";
   fillRect(0, 0, can.width, can.height);
   strokeStyle = "#000000";
   lineWidth = 5;
   fillStyle = "#ff0000";
   textAlign = "center";
   strokeText("-- Game Over --", 150, can.height / 4);
   fillText("-- Game Over --", 150, can.height / 4);
  }
 } else {
  can.style.animation = "none";
 }
}

function drawPixels() {
 var fw = (can.width / 2) | 0,
  fh = (can.height / 2) | 0;
 ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.msImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false;
 ctx.drawImage(can, 0, 0, fw, fh);
 ctx.drawImage(can, 0, 0, fw, fh, 0, 0, can.width, can.height);
}

init();
Run Code Online (Sandbox Code Playgroud)
* {
 box-sizing: border-box;
 overflow: hidden;
}

.block {
 border: 2px solid black;
}

@keyframes flash {
 0%, 100% {
  border: 2px solid black;
 }
 50% {
  border: 2px solid red;
 }
}
Run Code Online (Sandbox Code Playgroud)
<canvas class="block"></canvas>
Run Code Online (Sandbox Code Playgroud)

Bli*_*n67 6

基于重力的简单步骤。

重力

重力表现为速度随时间的变化(加速度)。它有方向和大小(矢量)

我们定义沿着画布向下的重力矢量

const gravity = {x: 0, y: 1};
Run Code Online (Sandbox Code Playgroud)

通常我们在以秒为单位的时间内施加重力。这不是一个方便的动画单元。在这种情况下,我们可以将其定义为每帧的像素。一帧是 1/60 秒。因此,上面定义的重力大小为每平方刻度 1 像素。因此,在一秒钟内,物体将以每刻 60 像素或每秒 3600 像素的速度移动。

对于大多数动画来说这有点太快了,所以我们可以稍微放慢速度

const gravity = {x: 0, y: 0.1};
Run Code Online (Sandbox Code Playgroud)

物体

物体具有位置(坐标)和速度(矢量),速度(矢量)具有方向和大小。

const object = {
    pos: {x: 0, y: 0}, // position
    vel: {x, 0, y: 0}, // velocity
}
Run Code Online (Sandbox Code Playgroud)

为了模拟该对象上的重力,我们可以添加函数形式的行为。在这种情况下我们可以调用它update。在函数中,我们通过将向量添加到速度向量 ( ) 来update加速对象。然后我们通过将速度向量添加到位置坐标来更新位置gravityobject.velobject.velobject.pos

const gravity = {x: 0, y: 0.1};
const object = {
    pos: {x: 0, y: 0}, // position
    vel: {x, 0, y: 0}, // velocity
    update() {
        this.vel.x += gravity.x;
        this.vel.y += gravity.y;
        this.pos.x += this.vel.x;
        this.pos.y += this.vel.y;
    }
}
Run Code Online (Sandbox Code Playgroud)

世界

这个物体本身将会永远坠落,所以我们需要让它与世界互动。我们可以定义一条地线。最基本的是画布上任意位置的一条线。

const ground = ctx.canvas.height;  // ground at bottom of canvas.
Run Code Online (Sandbox Code Playgroud)

为了进行交互,我们需要添加到对象update函数中。在此我们检查物体相对于地面的位置。如果该位置低于地面,我们将该位置向上移动到远离地面的距离,与移动到地面的距离相同,并反转速度(弹跳)。

我们可以将地面的弹性定义为速度的一部分。

我们还需要给对象一个大小。

这样我们就得到了。

const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height;  // ground at bottom of canvas.
const bounce = 0.5;
const object = {
    pos: {x: 0, y: 0}, // position
    vel: {x, 0, y: 0}, // velocity
    size: {w: 10, h: 10},
    update() {
        this.vel.x += gravity.x;
        this.vel.y += gravity.y;
        this.pos.x += this.vel.x;
        this.pos.y += this.vel.y;
        const g = ground - this.size.h; // adjust for size
        if(this.pos.y >= g) {  
            this.pos.y = g - (this.pos.y - g); // 
            this.vel.y = -Math.abs(this.vel.y) * bounce;  // change velocity to moving away.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后所需要做的就是每帧调用 update 并将对象绘制在正确的位置。

演示

将其付诸实践。

一个简单的盒子object从画布顶部落下并撞击地面(画布底部),弹起一点然后停止。(点击重置)

更新:我忘记检查物体是否静止。

如果我们不添加一些额外的代码,数学将使盒子振动并且永远不会真正停止移动update

当盒子的弹跳力小于重力时,盒子现在看起来会完全停止。查看评论// check for rest.

const gravity = {x: 0, y: 1};
Run Code Online (Sandbox Code Playgroud)
const gravity = {x: 0, y: 0.1};
Run Code Online (Sandbox Code Playgroud)
const object = {
    pos: {x: 0, y: 0}, // position
    vel: {x, 0, y: 0}, // velocity
}
Run Code Online (Sandbox Code Playgroud)

  • @Tschallacka我太老了,因为我的眼睛没有注意到,但这是不受欢迎的行为,我会解决它。 (2认同)