为了账号安全,请及时绑定邮箱和手机立即绑定

在 JavaScript 中模拟汽车旋转

在 JavaScript 中模拟汽车旋转

MMTTMM 2022-06-16 10:11:00
我想在我正在设计的游戏中模拟汽车在新方向上的旋转和运动。根据以下答案,在 HTML5 画布元素中,您不能旋转单个元素。运动本身发生在此功能中,我希望车辆移动但整个画布移动(尝试按左和右)。我不知道如何旋转汽车。Game._rotate = function(dirx, direction) {  this.ctx.translate(200,200);}根据本教程,我只能通过旋转画布来旋转汽车本身。我希望旋转和运动来模拟以下内容:https ://oseiskar.github.io/js-car/ 。代码本身在这里:https ://plnkr.co/edit/K5X8fMhUlRLhdeki 。
查看完整描述

1 回答

?
Qyouu

TA贡献1786条经验 获得超11个赞

您需要相对于 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>


查看完整回答
反对 回复 2022-06-16
  • 1 回答
  • 0 关注
  • 297 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号