javascript - 如何在画布上用矩形或圆弧模拟重力?
问题描述
如何使用只有几个变量的画布对象来模拟重力。
我创建了基础画布、游戏、时间、分数、所有内容,甚至是游戏状态,但我被困在“将速度和重力因素添加到 Player 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
}
});
}
我真的不知道我是否应该在此处包含其他任何内容,但是如果您想要整个项目,请参阅下面的代码段。
如果问题有什么问题,请告诉我,我已经快半年没来这里了。
完整的代码以防 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();
* {
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>
解决方案
简单的基于步骤的重力。
重力
重力表现为速度随时间的变化(加速度)。它有一个方向和大小(一个向量)
我们定义沿着画布向下的重力矢量
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;
}
}
世界
这个物体本身将永远坠落,所以我们需要让它与世界互动。我们可以定义一条地线。最基本的一条线位于画布上的任意位置。
const ground = ctx.canvas.height; // ground at bottom of canvas.
要进行交互,我们需要添加到 objectsupdate
函数。在此,我们检查物体相对于地面的位置。如果位置低于地面,我们将位置从地面向上移动到与地面相同的距离,并反转速度(反弹)。
我们可以将地面的弹性定义为速度的一小部分。
我们还需要给对象一个大小。
这样我们就得到了。
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>
推荐阅读
- javascript - SharePoint 加载项,找不到“样式库”?语言问题
- php - 自定义功能,仅允许提交中的预期数据
- android - 我如何使用 Android XML 制作剪辑的部分图层列表
- keras - plot_model 无法正常工作,因为带有 args 的“点”
- c# - 基于决策的游戏与 2 个按钮统一?
- python - 回归线图
- python - 如何重定向到分配给python中索引号的特定url
- sql - 在 Oracle 中查询以使用子查询进行选择
- android - 当我将移动网络从 wifi 更改为 4G 时,如何在 Android 和 iOS 上继续 WebRTC 流
- uwp - 没有涓流冰的 Webrtc 和 UWP