javascript - How do I emulate Gravity in a canvas with a Rectangle or Arc? - Stack Overflow

How do I emulate Gravity with canvas Objects having only a few variables to work with.I created the bas

How do I emulate Gravity with canvas Objects having only a few variables to work with.

I created the base canvas, game, time, score, everything, even gameStates, but I'm stuck on the part where you "add the velocity and the gravity factor into the Player Y variables".

I tried multiplying the gravity factor by a specified value, and then adding it to the yVel and then adding that to the actual Y value, but I cant translate the positioning correctly.

I think if I figured out how to "create gravity" creating jumping, moving right, and moving left wouldnt be too difficult.

Heres the main code im using to look for the platforms:

map.plates represents an array full of arrays which each contain 4 values for a plate (platform plate) e is the map.plates.Arrays. playY is basically the player's exactly Y Height,, all rendered into 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
  }
 });
}

I dont really know if I should include anything else here, but if you want the whole project, see the snippet below.

If theres anything wrong with the question, please tell me, I havent been on here in nearly half a year.

Full code incase codepen dies (suggested in ments):

"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>

How do I emulate Gravity with canvas Objects having only a few variables to work with.

I created the base canvas, game, time, score, everything, even gameStates, but I'm stuck on the part where you "add the velocity and the gravity factor into the Player Y variables".

I tried multiplying the gravity factor by a specified value, and then adding it to the yVel and then adding that to the actual Y value, but I cant translate the positioning correctly.

I think if I figured out how to "create gravity" creating jumping, moving right, and moving left wouldnt be too difficult.

Heres the main code im using to look for the platforms:

map.plates represents an array full of arrays which each contain 4 values for a plate (platform plate) e is the map.plates.Arrays. playY is basically the player's exactly Y Height,, all rendered into 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
  }
 });
}

I dont really know if I should include anything else here, but if you want the whole project, see the snippet below.

If theres anything wrong with the question, please tell me, I havent been on here in nearly half a year.

Full code incase codepen dies (suggested in ments):

"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>

Share Improve this question edited Feb 7, 2020 at 15:29 Mister SirCode asked Feb 6, 2020 at 13:35 Mister SirCodeMister SirCode 1,76717 silver badges34 bronze badges 3
  • Please include your code in a code snippet within this question so any answers here remain useful for future visitors when the codepen doesn't exist anymore – Tschallacka Commented Feb 6, 2020 at 13:41
  • Also, have you considered looking into existing physics libraries like cdn.soft8soft./demo/examples/… ? – Tschallacka Commented Feb 6, 2020 at 13:44
  • 1 @Tschallacka Ill include it, also I cant use a library.. Its an assignment :( -- Actually, Ill just make it a code snippet – Mister SirCode Commented Feb 6, 2020 at 14:09
Add a ment  | 

1 Answer 1

Reset to default 6

Simple step based gravity.

Gravity

Gravity manifests as a change in speed over time (acceleration). It has a direction and magnitude (a vector)

We define the gravity vector going down the canvas

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

Normally we apply gravity over time units of seconds. This is not a handy unit for animation. In this case we can define it as pixels per frame. A frame is 1/60th of a second. Thus the gravity defined above having a magnitude of 1 pixel per tick squared. So in one second an object would be traveling at 60 pixels per tick or 3600 pixels per second.

This is a little too fast for most animations so we can slow it down somewhat

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

The object

An object has a position (a coordinate) and a velocity (a vector) having a direction and magnitude.

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

To simulate gravity on this object we can add a behavior in the form of a function. In this case we can call it update. In the update function we accelerate the object, by adding the gravity vector to the velocity vector (object.vel). Then we update the position by adding the velocity vector object.vel to the position coordinate 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;
    }
}

The world

By its self this object will fall forever so we need to have it interact with the world. We can define a ground line. In its most basic a line at a y position on the canvas.

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

To interact we need to add to the objects update function. In this we check the object position against the ground. If the position is below the ground, we move the position up away from the ground the same distance it has moved into the ground, and reverse the velocity (bounced).

We can define the springyness of the ground as a fraction of the velocity.

We also need to give the object a size.

Thus we get.

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.
        }
    }
}

Then all that is needed is to call update every frame and draw the object at the correct position.

Demo

Putting it into practice.

A simple box called object falls from the top of the canvas and hits the ground (bottom of the canvas) bounces a bit and stop. (Click to reset)

Update: I forgot to check if the object is at rest.

The math will have the box vibrate and never really stop moving if we don't add a little extra code to update.

The box will now appear to e to a plete stop when its bounce is less than gravity. See ment // 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>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745342126a4623375.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信