Как эмулировать гравитацию на холсте с прямоугольником или Ar c? - PullRequest
2 голосов
/ 06 февраля 2020

Как мне эмулировать гравитацию с холстом Объекты, имеющие только несколько переменных для работы.

Я создал базовый холст, игру, время, счет, все, даже gameStates, но я застрял на часть, где вы «добавляете скорость и коэффициент гравитации в переменные Player Y».

Я попытался умножить коэффициент гравитации на указанное значение, а затем добавить его к yVel, а затем добавить его к фактическому Y значение, но я не могу перевести правильное позиционирование.

Я думаю, если бы я понял, как "создать гравитацию", создавая прыжки, движение вправо и движение влево, не было бы слишком сложно.

Вот Основной код, который я использую для поиска платформ:

map.plates представляет массив, полный массивов, каждое из которых содержит 4 значения для пластины (plate plate). e - map.plates.Arrays. playY - это, в основном, высота игрока, ровно Y Height, все отрисовано в 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
  }
 });
}

Я действительно не знаю, стоит ли мне что-то сюда включать, но если вы хотите весь проект, см. фрагмент ниже .

Если с этим вопросом что-то не так, скажите, пожалуйста, я не был здесь почти полгода.

Полный код, если кодовая ручка умирает (предлагается в комментарии):

"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();
* {
 box-sizing: border-box;
 overflow: hidden;
}

.block {
 border: 2px solid black;
}

@keyframes flash {
 0%, 100% {
  border: 2px solid black;
 }
 50% {
  border: 2px solid red;
 }
}
<canvas class="block"></canvas>

1 Ответ

3 голосов
/ 06 февраля 2020

Гравитация на основе простого шага.

Гравитация

Гравитация проявляется как изменение скорости во времени (ускорение). Он имеет направление и величину (вектор)

Мы определяем вектор гравитации, спускаясь вниз по холсту

const gravity = {x: 0, y: 1};

Обычно мы применяем гравитацию в единицах времени, равных секундам. Это не удобный блок для анимации. В этом случае мы можем определить его как пиксели на кадр. Кадр составляет 1/60 секунды. Таким образом, сила тяжести, определенная выше, имеет величину 1 пиксель на квадрат в тике. Таким образом, за одну секунду объект будет двигаться со скоростью 60 пикселей на тик или 3600 пикселей в секунду.

Это немного слишком быстро для большинства анимаций, поэтому мы можем немного его замедлить

const gravity = {x: 0, y: 0.1};

Объект

У объекта есть позиция (координата) и скорость (вектор), имеющие направление и величину.

const object = {
    pos: {x: 0, y: 0}, // position
    vel: {x, 0, y: 0}, // velocity
}

Чтобы смоделировать гравитацию на этом объекте, мы можем добавить поведение в форме функции. В этом случае мы можем назвать это update. В функции update мы ускоряем объект, добавляя вектор gravity к вектору скорости (object.vel). Затем мы обновляем положение, добавляя вектор скорости object.vel к координате положения object.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;
    }
}

Мир

Сам по себе этот объект упадет навсегда, поэтому нам нужно иметь это взаимодействует с миром. Мы можем определить линию земли. В основном это c линия в позиции y на холсте.

const ground = ctx.canvas.height;  // ground at bottom of canvas.

Для взаимодействия нам нужно добавить к объектам 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.
        }
    }
}

Тогда все, что нужно, это вызвать обновление каждого кадра и нарисовать объект в правильном положении.

Демонстрация

Применение на практике.

Простая коробка под названием object падает с верхней части холста и падает на землю (нижнюю часть холста), отскакивая от немного и остановись. (Нажмите для сброса)

Обновление: Я забыл проверить, находится ли объект в покое.

Математика заставит коробку вибрировать и никогда не перестанет двигаться, если мы не добавим немного дополнительного кода к update.

Теперь коробка будет полностью останавливаться, когда ее отскок меньше гравитации. Смотрите комментарий // check for rest.

const ctx = canvas.getContext("2d");
canvas.width = innerWidth-4;
canvas.height = innerHeight-4;
requestAnimationFrame(mainLoop); // starts the animation

const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height;  // ground at bottom of canvas.
const bounce = 0.9; // very bouncy
const object = {
    pos: {x: ctx.canvas.width / 2, y: 0}, // position halfway on canvas
    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;  
            if (this.vel.y >= -gravity.y) {  // check for rest.
                this.vel.y = 0;
                this.pos.y = g - gravity.y;
            }
        }
    },
    draw() { ctx.fillRect(this.pos.x, this.pos.y, this.size.w, this.size.h) },
    reset() { this.pos.y = this.vel.y = this.vel.x = 0 },
}
function mainLoop() {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    object.update(); // move object
    object.draw();
    requestAnimationFrame(mainLoop);
}
canvas.addEventListener("click", object.reset.bind(object));
body {
  margin: 0px;
  padding: 0px;
}

canvas {
  position: absolute;
  top: 0px;
  left: 0px;
  border: 1px solid black;
}
<canvas id="canvas"></canvas>
...