在普通 JS 的画布游戏中移动三角船 [英] Moving a triangular ship in a canvas game in plain JS
问题描述
我试图沿三角形旋转的方向移动三角形.当我按下键移动它时,它不会移动,但是当我旋转它时,它的旋转中心会因为我之前移动它按下的键而移动.
I am trying to move a triangle in the direction which the triangle is rotated by. When I press the key to move it, it doesn't move, but when I rotate it, its center of rotation shifts because of the key I pressed to move it previously.
我尝试检查公式以确定移动三角形的方向,但这些似乎是正确的,并且旋转它的平移点没有根据这些公式移动.
I tried checking the formulas to determine the direction to move the triangle, but those seemed correct, and the translation point to rotate it is not moving based on those formulas.
预期结果:点击向上箭头键,三角形沿旋转角度方向移动.
Expected results: On click of the up arrow key, triangle moves in rotation angle direction.
实际结果:点击向上箭头键,三角形不向方向移动,但如果我点击向上箭头键,然后向左或向右箭头键旋转,三角形远离旋转中心旋转.
Actual results: On click of up arrow key, triangle doesn't move in the direction, but if I click up arrow key, then left or right arrow key to rotate, triangle rotates away from center of rotation.
这是我的代码:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let ship_width = 20;
let ship_height = 20;
let angle = 0;
let ship_velocity_change = {
x: 0,
y: 0
}
//initial center coordinates of triangle
let ship_center = {
x: 450,
y: 300
};
let ship_points = [
//coordinates for vertices of triangle
{
x: ship_center.x - ship_width / 2,
y: ship_center.y +
ship_height / 2
},
{
x: ship_center.x + ship_width / 2,
y: ship_center.y +
ship_height / 2
},
{
x: ship_center.x,
y: ship_center.y - ship_height / 2
}
];
function drawRect(x, y, width, height, color) {
ctx.rect(x, y, width, height);
ctx.fillStyle = color;
ctx.fill();
}
//vertices for triangle as parameters
function drawTriangle(bottom_left, bottom_right, top, color) {
ctx.beginPath();
ctx.moveTo(top.x, top.y);
ctx.lineTo(bottom_left.x, bottom_left.y);
ctx.lineTo(bottom_right.x, bottom_right.y);
ctx.lineTo(top.x, top.y);
ctx.strokeStyle = color;
ctx.stroke();
}
//rotate triangle by an angle in degrees
function rotate(angle) {
ctx.translate(ship_center.x, ship_center.y);
ctx.rotate(Math.PI / 180 * angle);
ctx.translate(-ship_center.x, -ship_center.y);
drawTriangle(ship_points[2], ship_points[1], ship_points[0],
"white");
}
document.addEventListener('keydown', function(event) {
//rate of degree change per 10 milliseconds
if (event.keyCode === 37) {
angle = -2;
} else if (event.keyCode === 38) {
//move triangle by direction of angle
ship_center.x += Math.cos(Math.PI / 180 * angle) * 5;
ship_center.y += Math.sin(Math.PI / 180 * angle) * 5;
} else if (event.keyCode === 39) {
angle = 2;
}
});
document.addEventListener('keyup', function(event) {
if (event.keyCode === 37 || event.keyCode === 39) {
angle = 0;
}
});
function game() {
drawRect(0, 0, 900, 600, "black");
rotate(angle);
}
let gameLoop = setInterval(game, 10);
<canvas id="canvas" width="900" height="600"></canvas>
推荐答案
不错的尝试!这是需要一些实验才能正确处理的事情之一.
Nice attempt! This is one of those things that takes some experimentation to get right.
一些建议:
- 将船舶的所有相关数据和功能封装在一个对象中.这使事情井井有条且易于理解,并将简化您的逻辑并显着减少错误.这使得实体状态和呈现功能更易于管理.
- 避免直接从按键事件回调中调用船舶移动函数.这样做会导致键盘重新触发时出现不稳定、不一致的行为.这个回调应该简单地翻转按键标志,然后让事件循环根据这些标志来调用相关函数.
- 使用
requestAnimationFrame
对于大多数动画,a> 而不是setInterval
. - 像
angle = 2;
这样的东西太死板了(这可能是暂时的).我们需要在这里使用+=
并引入一个速度变量(理想情况下还有加速度)以实现真实的旋转. Math.PI/180 * angle
用于Math
函数并不是绝对必要的.我们可以直接在弧度中使用angle
.如果您的游戏依赖于设置角度和度数,那么您可以添加转换功能.- 如果您希望运动感觉逼真,请使用加速度和速度变量.在下面的草图中,我添加了其中的一些,但这不是确定的方法,旨在根据口味进行调整.
- Encapsulate all relevant data and functions for the ship in one object. This keeps things organized and easy to understand and will simplify your logic and reduce bugs significantly. This makes entity state and rendering functions much easier to manage.
- Avoid calling ship movement functions directly from the key event callback. Doing so can result in jerky, inconsistent behavior when the keyboard re-triggers. This callback should simply flip key flags, then let the event loop take care of calling the relevant functions based on those flags.
- Use
requestAnimationFrame
instead ofsetInterval
for most animations. - Something like
angle = 2;
is too rigid (this may have been temporary). We need to use+=
here and introduce a velocity variable (and, ideally, acceleration too) for realistic rotation. Math.PI / 180 * angle
forMath
functions isn't strictly necessary. We can useangle
directly in radians. If your game depends on setting angles with degrees, then you can add a conversion function.- Use variables for acceleration and velocity if you want the movement to feel realistic. In the sketch below, I've added a few of these, but it's not a definitive approach and is intended to be tweaked to taste.
以下是所有这些操作的简单示例.有很大的改进空间,但它应该是一个不错的入门.
Here's a simple example of all of this in action. There is much room for improvement, but it should be a decent starter.
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 200;
const kbd = {
ArrowLeft: false,
ArrowUp: false,
ArrowRight: false
};
const ship = {
angle: 0,
color: "white",
x: canvas.width / 2,
y: canvas.height / 2,
width: 10,
height: 15,
drag: 0.9,
accSpeed: 0.04,
rotSpeed: 0.012,
rotv: 0,
ax: 0,
ay: 0,
vx: 0,
vy: 0,
rotateLeft() {
this.rotv -= this.rotSpeed;
},
rotateRight() {
this.rotv += this.rotSpeed;
},
accelerate() {
this.ax += this.accSpeed;
this.ay += this.accSpeed;
},
move() {
this.angle += this.rotv;
this.rotv *= this.drag;
this.vx += this.ax;
this.vy += this.ay;
this.ax *= this.drag;
this.ay *= this.drag;
this.vx *= this.drag;
this.vy *= this.drag;
this.x += Math.cos(this.angle) * this.vx;
this.y += Math.sin(this.angle) * this.vy;
},
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.beginPath();
ctx.moveTo(this.height, 0);
ctx.lineTo(-this.height, this.width);
ctx.lineTo(-this.height, -this.width);
ctx.closePath();
ctx.strokeStyle = this.color;
ctx.stroke();
ctx.restore();
}
};
document.addEventListener("keydown", event => {
if (event.code in kbd) {
event.preventDefault();
kbd[event.code] = true;
}
});
document.addEventListener("keyup", event => {
if (event.code in kbd) {
event.preventDefault();
kbd[event.code] = false;
}
});
(function update() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const shipActions = {
ArrowLeft: "rotateLeft",
ArrowUp: "accelerate",
ArrowRight: "rotateRight",
};
for (const key in shipActions) {
if (kbd[key]) {
ship[shipActions[key]]();
}
}
ship.move();
ship.draw(ctx);
requestAnimationFrame(update);
})();
这篇关于在普通 JS 的画布游戏中移动三角船的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!