在普通 JS 的画布游戏中移动三角船 [英] Moving a triangular ship in a canvas game in plain JS

查看:43
本文介绍了在普通 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.

一些建议:

  • 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 of setInterval 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 for Math functions isn't strictly necessary. We can use angle 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆