【问题标题】:Simulate car rotation in JavaScript [duplicate]在 JavaScript 中模拟汽车旋转
【发布时间】:2020-07-17 10:36:13
【问题描述】:

我想在我正在设计的游戏中模拟汽车在新方向上的旋转和运动。

根据the following answer,在 HTML5 画布元素中,您不能旋转单个元素。

移动本身发生在这个函数中,我希望车辆移动但整个画布移动(尝试按左和右)。我不知道如何旋转汽车。

Game._rotate = function(dirx, direction) {
  this.ctx.translate(200,200);
}

根据这个tutorial,我只能通过旋转画布来旋转汽车本身。我希望旋转和移动模拟以下内容:https://oseiskar.github.io/js-car/

这里的代码本身:https://plnkr.co/edit/K5X8fMhUlRLhdeki

【问题讨论】:

  • 不要旋转图像,而是使用带有 NSEW 方向的 spritesheet?
  • 你的车应该知道如何渲染自己。您需要将画布传递到汽车的渲染中。
  • 你有什么问题?你似乎明白你不能用一张画布做你所要求的。如果您只是寻求建议,那对于堆栈溢出来说太开放了,因为有多种方法:使用多个画布标签,使用 svg,使用 HTML

标签: javascript


【解决方案1】:

您需要相对于 ITS 位置渲染汽车。此外,您可以在其 RENDER 中进行旋转。

下面的更改将处理汽车的旋转,但您有更大的问题。我会花时间投资学习向量的工作原理。位置、速度和加速度都应该是二维向量,提供像加法和乘法这样的向量数学运算。

另外,为您的汽车提供一个初始角度,以便它最初以正确的方式呈现。您的加速和减速似乎也混淆了。

function Car(map, x, y) {
  this.map = map;
  this.x = x;
  this.y = y;
  this.angle = 0; // IMPORTANT
  this.width = map.tsize;
  this.height = map.tsize;
  this.image = Loader.getImage('car');
}

Car.speed = 0;
Car.acceleration = 0;
Car.friction = 5;
Car.moveAngle = 0;
Car.maxSpeed = 500;
Car.forward = 'FORWARD';
Car.backward = 'BACKWARD';
Car.left = 'LEFT';
Car.right = 'RIGHT';

// Render relative to car...
Car.prototype.render = function(ctx) {
  ctx.save();
  ctx.translate(this.x, this.y);
  ctx.rotate(this.angle);
  ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
  ctx.restore();
};

Game.update = function (delta) {
  var dirx = 0;
  var diry = 0;
  if (Keyboard.isDown(Keyboard.LEFT)) {
    this._rotate(dirx, Car.left)
  }
  else if (Keyboard.isDown(Keyboard.RIGHT)) {
    this._rotate(dirx, Car.right)
  }
  else if (Keyboard.isDown(Keyboard.UP)) {
    this._rotate(dirx, Car.up)
    diry = accelerate(diry, Car.forward);
  }
  else if (Keyboard.isDown(Keyboard.DOWN)) {
    this._rotate(dirx, Car.down)
    diry = accelerate(diry, Car.backward);
  }
  else {
    decelerate();
  }

  this.car.move(delta, dirx, diry);
  this.camera.update();
};

Game._rotate = function(dirx, direction) {
  let angleInDegrees = 0;
  switch (direction) {
    case 'UP':
     angleInDegrees = 0;
     break;
    case 'RIGHT':
     angleInDegrees = 90;
     break;
    case 'DOWN':
     angleInDegrees = 180;
     break;
    case 'LEFT':
     angleInDegrees = 270;
     break;
  }
  this.car.angle = angleInDegrees * (Math.PI / 180);
}

Game.render = function () {
  // draw map background layer
  this._drawLayer(0);

  this.car.render(this.ctx);

  // draw map top layer
  this._drawLayer(1);
};

向量

这是一个使用向量的示例。

loadImage('https://i.stack.imgur.com/JY7ai.png')
  .then(function(img) {
    main(img);
  })

function main(img) {
  let game = new Game({
    canvas: document.querySelector('#viewport')
  });
  let car = new Car({
    img: img,
    imgRadiansOffset : -Math.PI / 2,
    position: new Victor(game.canvas.width, game.canvas.height).divide(new Victor(2, 2)),
    velocity: new Victor(0, -1),
    showBorder: true
  });
  game.addLayer(car);
  game.start();
}

class Game {
  constructor(options) {
    Object.assign(this, Game.defaultOptions, options);
    if (this.canvas != null) {
      Object.assign(this, {
        width: this.canvas.width,
        height: this.canvas.height
      });
      this.addListeners();
    }
  }
  addLayer(layer) {
    this.layers.push(layer);
    layer.parent = this;
  }
  start() {
    this.id = setInterval(() => {
      this.render();
    }, 1000 / this.rate); // frames per second
  }
  render() {
    let ctx = this.canvas.getContext('2d');
    ctx.clearRect(0, 0, this.width, this.height);
    this.layers.forEach(layer => layer.render(ctx));
  }
  addListeners() {
    if (this.canvas != null) {
      window.addEventListener('keydown', (e) => {
        this.handleKeyDown(e.keyCode);
      });
      window.addEventListener('keyup', (e) => {
        this.handleKeyUp(e.keyCode);
      });
    }
  }
  handleKeyDown(keyCode) {
    this.layers.forEach(layer => {
      layer.update(keyCode !== this.lastKeyCode ? keyCode : null);
    });
    this.lastKeyCode = keyCode;
  }
  handleKeyUp(keyCode) {
    this.layers.forEach(layer => {
      layer.update(this.lastKeyCode); // calls reset...
    });
  }
}

Game.defaultOptions = {
  id: null,
  rate: 30,
  layers: [],
  canvas: null,
  width: 0,
  height: 0
};

class Car {
  constructor(options) {
    Object.assign(this, Car.defaultOptions, options);
    if (this.img != null) {
      Object.assign(this, {
        width: this.img.width,
        height: this.img.height
      });
    }
  }
  render(ctx) {
    ctx.save();
    ctx.translate(this.position.x, this.position.y);
    ctx.rotate(this.velocity.angle() - this.imgRadiansOffset);
    ctx.drawImage(this.img, -this.width / 2, -this.height / 2, this.width, this.height);
    if (this.showBorder) {
      ctx.strokeStyle = '#C00';
      ctx.setLineDash([4, 8]);
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.rect(-this.width / 2, -this.height / 2, this.width, this.height);
      ctx.stroke();
    }
    ctx.restore();
  }
  update(keyCode) {
    if (keyCode != null) this.changeDirection(keyCode);
    this.position.add(this.velocity.add(this.acceleration));
    this.detectCollision();
  }
  detectCollision() {
    let xMin = this.width / 2, xMax = this.parent.width - xMin;
    let yMin = this.height / 2, yMax = this.parent.height - yMin;
    if (this.position.x < xMin) this.position.x = xMin;
    if (this.position.x > xMax) this.position.x = xMax;
    if (this.position.y < yMin) this.position.y = yMin;
    if (this.position.y > yMax) this.position.y = yMax;
  }
  changeDirection(keyCode) {
    switch (keyCode) {
      case 37:
        this.reset(new Victor(-1, 0)); // LEFT
        break;
      case 38:
        this.reset(new Victor(0, -1)); // UP
        break;
      case 39:
        this.reset(new Victor(1, 0)); // RIGHT
        break;
      case 40:
        this.reset(new Victor(0, 1)); // DOWN
        break;
    }
  }
  reset(dirVect) {
    this.velocity = new Victor(this.speedFactor, this.speedFactor).multiply(dirVect);
    this.acceleration = new Victor(this.accelFactor, this.accelFactor).multiply(dirVect);
  }
}

Car.defaultOptions = {
  position: new Victor(0, 0),
  width: 0,
  height: 0,
  img: null,
  imgRadiansOffset: 0,
  velocity: new Victor(0, 0),
  acceleration: new Victor(0, 0),
  showBorder: false,
  speedFactor: 3, // Velocity scalar
  accelFactor: 1  // Acceleration scalar
};

function loadImage(url) {
  return new Promise(function(resolve, reject) {
    var img = new Image;
    img.onload = function() {
      resolve(this)
    };
    img.onerror = img.onabort = function() {
      reject("Error loading image")
    };
    img.src = url;
  })
}
#viewport {
  border: thin solid grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/victor/1.1.0/victor.min.js"></script>
<canvas id="viewport" width="400" height="160"></canvas>

【讨论】:

  • 谢谢。非常感激。会放弃这个。您对向量方面的资源有什么建议吗?
  • @methuselah victorjs.org
  • @methuselah 我在上面添加了一个矢量示例。
猜你喜欢
  • 1970-01-01
  • 2012-11-09
  • 1970-01-01
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多