如何在JavaScript中解决轴对齐的矩形与矩形边界框的碰撞?

huangapple go评论76阅读模式
英文:

How to Resolve Axis Aligned Rectangle to Rectangle Bounding Box Collision in Javascript?

问题

以下是您提供的代码的翻译部分:

// 鼠标类
class Mouse {
    constructor() {
        throw new Error(`new Mouse() is not allowed.\nTry using the Mouse.init() method instead.`);
    }

    static x = 0;
    static y = 0;

    static movement = {
        x: 0,
        y: 0
    }

    static pressed = false;

    static setMousePosition(e) {
        this.x = e.pageX;
        this.y = e.pageY;
        this.movement.x = e.movementX;
        this.movement.y = e.movementY;
    }

    static init() {
        window.addEventListener("mousedown", (e) => { this.setMousePosition(e); this.pressed = true; });
        window.addEventListener("mouseup", (e) => { this.pressed = false; });
        window.addEventListener("mousemove", (e) => { this.setMousePosition(e); });
    }
}

/**
 * @description 一组辅助函数,用于在2D画布上绘制更容易。
 */
class Draw {
    /**
     * @param context 用于绘制的画布上下文。
     */
    constructor(context) {
        this.ctx = context;
    }

    /**
     * @description 清除指定的矩形区域,使其完全透明。
     */
    clear(x, y, width, height) {
        this.ctx.clearRect(x, y, width, height);
    }

    // 其他绘图函数...
}

// 其他类和函数...

如果您需要对代码的其他部分进行翻译,请告诉我需要翻译的具体部分。

英文:

I've been working on a game recently where the game has walls that the player and other entities can collide with, but the only problem I have is that I don't know how to resolve Rectangle to Rectangle collision.

Here is the code:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class Mouse {
constructor() {
throw new Error(`new Mouse() is not allowed.\nTry using the Mouse.init() method instead.`);
}
static x = 0;
static y = 0;
static movement = {
x: 0,
y: 0
}
static pressed = false;
static setMousePosition(e) {
this.x = e.pageX;
this.y = e.pageY;
this.movement.x = e.movementX;
this.movement.y = e.movementY;
}
static init() {
window.addEventListener(&quot;mousedown&quot;, (e) =&gt; { this.setMousePosition(e); this.pressed = true; });
window.addEventListener(&quot;mouseup&quot;, (e) =&gt; { this.pressed = false; });
window.addEventListener(&quot;mousemove&quot;, (e) =&gt; { this.setMousePosition(e); });
}
}
/**
* @description A set of helper functions to make drawing on a 2d canvas easier.
*/
class Draw {
/**
* @param context The canvas context to use for drawing.
*/
constructor(context) {
this.ctx = context;
}
/**
* @description Clears the specified rectangular area, making it fully transparent.
*/
clear(x, y, width, height) {
this.ctx.clearRect(x, y, width, height);
}
rectangle(x, y, width, height, roundness = 0, fill = true, stroke = false, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
this.ctx.roundRect(x, y, width, height, roundness);
if (fill) this.ctx.fill();
if (stroke) this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
arc(x, y, radius, a1, a2 = Math.PI * 2, fill = true, stroke = false, options = {}, counterClockwise = false) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
this.ctx.arc(x, y, radius, a1, a2, counterClockwise);
if (fill) this.ctx.fill();
if (stroke) this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
text(text, x, y, fill = true, stroke = false, options = {}, maxWidth = undefined) {
this.ctx.save();
Object.assign(this.ctx, options);
if (fill) this.ctx.fillText(text, x, y, maxWidth);
if (stroke) this.ctx.strokeText(text, x, y, maxWidth);
this.ctx.restore();
}
path(path, fill = false, stroke = true, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
if (fill) this.ctx.fill(path);
if (stroke) this.ctx.stroke(path);
this.ctx.closePath();
this.ctx.restore();
}
grid(x, y, width, height, cellSize, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
for (var cx = x; cx &lt;= x + width; cx += cellSize) {
this.ctx.moveTo(cx, y);
this.ctx.lineTo(cx, y + height);
}
for (var cy = y; cy &lt;= y + height; cy += cellSize) {
this.ctx.moveTo(x, cy);
this.ctx.lineTo(x + width, cy);
}
this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
text(text, x, y, fill = true, stroke = false, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
if (fill) this.ctx.fillText(text, x, y);
if (fill) this.ctx.strokeText(text, x, y);
this.ctx.restore();
}
}
function random(min, max) {
return Math.random() * (max - min) + min;
}
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
var tankClass = {
basic: function (tank) {
return [
new Gun(0, -tank.height * 0.15, tank.width * 0.9, tank.height * 0.3, tank, 0)
];
},
doubleShot: function (tank) {
return [
new Gun(0, -tank.height * 0.12, tank.width * 0.9, tank.height * 0.24, tank, -4),
new Gun(0, -tank.height * 0.12, tank.width * 0.9, tank.height * 0.24, tank, 4),
new Gun(0, -tank.height * 0.15, tank.width * 0.9, tank.height * 0.3, tank, 0)
];
}
}
class Player {
constructor(x, y, width, height, color, bc, startingWeapons = &quot;basic&quot;) {
this.initX = x;
this.initY = y;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.bc = bc;
this.velX = 0;
this.velY = 0;
this.acceleration = 0.3;
this.gunAngle = 0;
this.weapons = this.setWeapons(startingWeapons);
this.bullets = [];
this.currentReloadTime = 0;
this.reloadTime = 60;
this.recoilX = 0;
this.safeZone = {
x: x - 100,
y: y - 100,
width: width + 100,
height: height + 100
}
}
setWeapons(weaponString) {
var newWeapons = tankClass[weaponString];
return newWeapons(this);
}
reset() {
this.x = this.initX;
this.y = this.initY;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
}
setSafeZone(x, y, width, height) {
this.safeZone.x = x;
this.safeZone.y = y;
this.safeZone.width = width;
this.safeZone.height = height;
}
}
class Enemy {
constructor(x, y, width, height, color, bc, startingWeapons = &quot;basic&quot;) {
this.initX = x;
this.initY = y;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.bc = bc;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
this.weapons = this.setWeapons(startingWeapons);
this.bullets = [];
this.currentReloadTime = 0;
this.reloadTime = 60;
this.recoilX = 0;
}
setWeapons(weaponString) {
var newWeapons = tankClass[weaponString];
return newWeapons(this);
}
reset() {
this.x = this.initX;
this.y = this.initY;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
}
}
class Gun {
constructor(x, y, width, height, parent, rotation = 0) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.parent = parent;
this.color = &quot;#606060&quot;;
this.bc = &quot;#404040&quot;;
this.rotation = degreesToRadians(rotation);
}
}
class Bullet {
constructor(x, y, radius, color, bc, parent, velX, velY) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.bc = bc;
this.parent = parent;
this.velX = velX;
this.velY = velY;
}
}
var levels = {
level0: {
map: [
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0]
]
}
}
class Info_Level {
constructor(gridSize = 64) {
this.GRID_SIZE = gridSize;
this.map = {
walls: []
};
}
load(map) {
var tileOffsetX = 0;
var tileOffsetY = 0;
for (var i = 0; i &lt; map.length; i++) {
for (var j = 0; j &lt; map[i].length; j++) {
if (map[i][j] === 1) {
this.createWall(tileOffsetX, tileOffsetY, this.GRID_SIZE, this.GRID_SIZE, &quot;#000000&quot;);
}
if (map[i][j] === 2) {
this.createWall(tileOffsetX, tileOffsetY, this.GRID_SIZE, this.GRID_SIZE, &quot;#000000&quot;);
}
tileOffsetX += this.GRID_SIZE;
}
tileOffsetX = 0;
tileOffsetY += this.GRID_SIZE;
}
tileOffsetY = 0;
tileOffsetX = 0;
}
createWall(x, y, width, height, color, id = &quot;&quot;) {
this.map.walls.push({
x: x,
y: y,
width: width,
height: height,
color: color,
id: id
});
}
}
var infoLevel = new Info_Level(64);
infoLevel.load(levels.level0.map);
var friction = 0.85;
var canvas = document.getElementById(&quot;canvas&quot;);
var ctx = canvas.getContext(&quot;2d&quot;);
var vWidth = window.innerWidth;
var vHeight = window.innerHeight;
var player = new Player(vWidth / 2 - 33, vHeight / 2 - 27.5, 75, 60, &quot;#608060&quot;, &quot;#204020&quot;, &quot;basic&quot;);
var keysDown = [];
var enemies = [];
// enemies.push(new Enemy(0, 0, 75, 60, &quot;#806060&quot;, &quot;#402020&quot;, &quot;basic&quot;));
var draw = new Draw(ctx);
var fps = 60;
function resizeCanvas(canvasElement, width, height) {
vWidth = width;
vHeight = height;
canvasElement.width = vWidth;
canvasElement.height = vHeight;
}
resizeCanvas(canvas, window.innerWidth, window.innerHeight);
function updateTank(tank, isEnemy = false) {
if (tank.currentReloadTime &gt;= 0) {
tank.currentReloadTime--;
}
if (tank.recoilX &lt; 0) {
tank.recoilX += 0.5;
}
if (tank.x &lt; 0) {
tank.x = 0;
}
if (tank.y &lt; 0) {
tank.y = 0;
}
if (tank.x + tank.width &gt; vWidth) {
tank.x = vWidth - tank.width;
}
if (tank.y + tank.height &gt; vHeight) {
tank.y = vHeight - tank.height;
}
tank.velX *= friction;
tank.velY *= friction;
tank.x += tank.velX;
tank.y += tank.velY;
/* This is where the collision detection of the player and a wall happen. */
for (var i = 0; i &lt; infoLevel.map.walls.length; i++) {
var wall = infoLevel.map.walls[i];
if (rectangleToRectangleCollision(tank, wall)) {
tank.velX = 0;
tank.velY = 0;
}
}
if (isEnemy == true) {
var enemyAV = Math.atan2((player.y + player.height / 2) - (tank.y + tank.height / 2), (player.x + player.width / 2) - (tank.x + tank.width / 2));
tank.gunAngle = enemyAV;
if (rectangleToRectangleCollision(player, tank) == false) {
tank.velX += Math.cos(enemyAV) * 0.3;
tank.velY += Math.sin(enemyAV) * 0.3;
}
if (tank.currentReloadTime &lt;= 0) {
for (var i = 0; i &lt; tank.weapons.length; i++) {
var gun = tank.weapons[i];
shootBullet(gun, tank);
}
tank.currentReloadTime = tank.reloadTime;
}
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(Math.atan2(tank.velY / 2, tank.velX / 2));
draw.rectangle(-tank.width / 2, -tank.height / 2, tank.width, tank.height, 2, true, true, { fillStyle: tank.color, strokeStyle: tank.bc, lineWidth: tank.width / tank.height * 1.75 });
ctx.restore();
for (var i = 0; i &lt; tank.bullets.length; i++) {
var bullet = tank.bullets[i];
bullet.x += bullet.velX;
bullet.y += bullet.velY;
draw.arc(bullet.x, bullet.y, bullet.radius, 0, 2 * Math.PI, true, true, { fillStyle: bullet.color, strokeStyle: bullet.bc, lineWidth: tank.width / tank.height * 1.75 });
}
for (var i = 0; i &lt; tank.weapons.length; i++) {
var gun = tank.weapons[i];
if (gun.x &lt; 0) {
gun.x += gun.width / 240;
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(tank.gunAngle + gun.rotation);
draw.rectangle(gun.x, gun.y, gun.width, gun.height, 2, true, true, { fillStyle: gun.color, strokeStyle: gun.bc, lineWidth: tank.width / tank.height * 1.75 });
ctx.restore();
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(tank.gunAngle);
draw.rectangle(-tank.width / 2 * 0.6 + tank.recoilX, -tank.height / 2 * 0.7, tank.width * 0.6, tank.height * 0.7, 2, true, true, { fillStyle: tank.color, strokeStyle: tank.bc, lineWidth: tank.width / tank.height * 1.75 });
ctx.restore();
}
function shootBullet(gun, tank) {
var shootS = new Audio(&quot;./assets/shoot.wav&quot;);
shootS.play();
gun.x -= gun.width / 12;
tank.recoilX = -tank.width / 16;
var rawVX = Math.cos(tank.gunAngle + gun.rotation);
var rawVY = Math.sin(tank.gunAngle + gun.rotation);
var velX = (rawVX + random(-0.02, 0.02)) * 5;
var velY = (rawVY + random(-0.02, 0.02)) * 5;
tank.bullets.push(new Bullet(tank.x + tank.width / 2 + (rawVX * (tank.width - (tank.height / 2))), tank.y + tank.height / 2 + (rawVY * (tank.width - (tank.height / 2))), gun.height / 2, &quot;#ff0000&quot;, &quot;#800000&quot;, tank, velX, velY));
}
function main() {
if (keysDown[&quot;w&quot;]) {
player.velY -= player.acceleration;
}
if (keysDown[&quot;a&quot;]) {
player.velX -= player.acceleration;
}
if (keysDown[&quot;s&quot;]) {
player.velY += player.acceleration;
}
if (keysDown[&quot;d&quot;]) {
player.velX += player.acceleration;
}
if (Mouse.pressed) {
if (player.currentReloadTime &lt;= 0) {
for (var i = 0; i &lt; player.weapons.length; i++) {
var gun = player.weapons[i];
shootBullet(gun, player);
}
player.currentReloadTime = player.reloadTime;
}
}
player.setSafeZone(player.x + player.width / 2 - 100, player.y + player.height / 2 - 100, 200, 200);
ctx.save();
draw.clear(0, 0, vWidth, vHeight);
updateTank(player, false);
for (var i = 0; i &lt; enemies.length; i++) {
updateTank(enemies[i], true);
}
for (var i = 0; i &lt; infoLevel.map.walls.length; i++) {
var wall = infoLevel.map.walls[i];
draw.rectangle(wall.x, wall.y, wall.width, wall.height, 0, true, false, { fillStyle: wall.color });
}
draw.text(&quot;Add Collision Resolution To These Black Boxes&quot;, 0, 10, true, true, { textBaseline: &quot;top&quot;, textAlign: &quot;left&quot;, fillStyle: &quot;#ffffff&quot;, strokeStyle: &quot;#000000&quot;, font: &quot;Bold 30px Arial&quot; });
ctx.restore();
}
window.onload = function () {
Mouse.init();
setInterval(main, 1000 / fps);
}
function rectangleToRectangleCollision(obj1, obj2) {
if (obj1.x + obj1.width &gt; obj2.x &amp;&amp; obj1.y + obj1.height &gt; obj2.y
&amp;&amp; obj1.x &lt; obj2.x + obj2.width &amp;&amp; obj1.y &lt; obj2.y + obj2.height) {
return true;
}
return false;
}
window.onresize = function () {
resizeCanvas(canvas, window.innerWidth, window.innerHeight);
}
document.addEventListener(&quot;keydown&quot;, (e) =&gt; {
keysDown[e.key] = true;
});
document.addEventListener(&quot;keyup&quot;, (e) =&gt; {
keysDown[e.key] = false;
});
document.addEventListener(&quot;mousemove&quot;, () =&gt; {
player.gunAngle = Math.atan2(Mouse.y - (player.y + player.height / 2), Mouse.x - (player.x + player.width / 2));
});

<!-- language: lang-css -->

*, *:before, *:after {
font-family: roboto, Arial, Helvetica, sans-serif, system-ui;
padding: 0px 0px;
margin: 0px 0px;
box-sizing: border-box;
}

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;CD&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->
WASD to move, click to shoot.

Note: I am not using any JavaScript libraries or plugins, as I like to make things from scratch.

答案1

得分: 0

以下是翻译好的部分:

这原来并不像我想的那么难,所以我自己解决了。

以下是代码部分:

function rectangleToRectangleCollisionResolution(obj1, obj2) {
  var vx = (obj1.x + obj1.width / 2) - (obj2.x + obj2.width / 2);
  var vy = (obj1.y + obj1.height / 2) - (obj2.y + obj2.height / 2);

  if (Math.abs(vx / obj2.width) > Math.abs(vy / obj2.height)) {
    if (vx < 0) {
      obj1.x = obj2.x - obj1.width;
    } else {
      obj1.x = obj2.x + obj2.width;
    }
  } else {
    if (vy < 0) {
      obj1.y = obj2.y - obj1.height;
    } else {
      obj1.y = obj2.y + obj2.height;
    }
  }
}

class Mouse {
  constructor() {
    throw new Error(`new Mouse() is not allowed.\nTry using the Mouse.init() method instead.`);
  }

  static x = 0;
  static y = 0;

  static movement = {
    x: 0,
    y: 0
  }

  static pressed = false;

  static setMousePosition(e) {
    this.x = e.pageX;
    this.y = e.pageY;
    this.movement.x = e.movementX;
    this.movement.y = e.movementY;
  }

  static init() {
    window.addEventListener("mousedown", (e) => {
      this.setMousePosition(e);
      this.pressed = true;
    });
    window.addEventListener("mouseup", (e) => {
      this.pressed = false;
    });
    window.addEventListener("mousemove", (e) => {
      this.setMousePosition(e);
    });
  }
}

/**
 * @description 用于在2D画布上绘制的一组辅助函数。
 */
class Draw {
  /**
   * @param context 用于绘制的画布上下文。
   */
  constructor(context) {
    this.ctx = context;
  }

  /**
   * @description 清除指定的矩形区域,使其完全透明。
   */
  clear(x, y, width, height) {
    this.ctx.clearRect(x, y, width, height);
  }

  rectangle(x, y, width, height, roundness = 0, fill = true, stroke = false, options = {}) {
    this.ctx.save();
    Object.assign(this.ctx, options);
    this.ctx.beginPath();
    this.ctx.roundRect(x, y, width, height, roundness);
    if (fill) this.ctx.fill();
    if (stroke) this.ctx.stroke();
    this.ctx.closePath();
    this.ctx.restore();
  }

  // 其他绘制函数...

  // 注意:此处省略了其他绘制函数的实现,可以根据需要在代码中添加。
}

// 其他代码...

请注意,由于文字量较大,我只提供了部分代码的翻译。如果您需要更多部分的翻译,请告诉我需要翻译的具体部分。

英文:

Turns out this wasn't as hard as I thought this was, so I figured it out on my own.

Here is the code:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function rectangleToRectangleCollisionResolution(obj1, obj2) {
var vx = (obj1.x + obj1.width / 2) - (obj2.x + obj2.width / 2);
var vy = (obj1.y + obj1.height / 2) - (obj2.y + obj2.height / 2);
if (Math.abs(vx / obj2.width) &gt; Math.abs(vy / obj2.height)) {
if (vx &lt; 0) {
obj1.x = obj2.x - obj1.width;
} else {
obj1.x = obj2.x + obj2.width;
}
} else {
if (vy &lt; 0) {
obj1.y = obj2.y - obj1.height;
} else {
obj1.y = obj2.y + obj2.height;
}
}
}
class Mouse {
constructor() {
throw new Error(`new Mouse() is not allowed.\nTry using the Mouse.init() method instead.`);
}
static x = 0;
static y = 0;
static movement = {
x: 0,
y: 0
}
static pressed = false;
static setMousePosition(e) {
this.x = e.pageX;
this.y = e.pageY;
this.movement.x = e.movementX;
this.movement.y = e.movementY;
}
static init() {
window.addEventListener(&quot;mousedown&quot;, (e) =&gt; {
this.setMousePosition(e);
this.pressed = true;
});
window.addEventListener(&quot;mouseup&quot;, (e) =&gt; {
this.pressed = false;
});
window.addEventListener(&quot;mousemove&quot;, (e) =&gt; {
this.setMousePosition(e);
});
}
}
/**
* @description A set of helper functions to make drawing on a 2d canvas easier.
*/
class Draw {
/**
* @param context The canvas context to use for drawing.
*/
constructor(context) {
this.ctx = context;
}
/**
* @description Clears the specified rectangular area, making it fully transparent.
*/
clear(x, y, width, height) {
this.ctx.clearRect(x, y, width, height);
}
rectangle(x, y, width, height, roundness = 0, fill = true, stroke = false, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
this.ctx.roundRect(x, y, width, height, roundness);
if (fill) this.ctx.fill();
if (stroke) this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
arc(x, y, radius, a1, a2 = Math.PI * 2, fill = true, stroke = false, options = {}, counterClockwise = false) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
this.ctx.arc(x, y, radius, a1, a2, counterClockwise);
if (fill) this.ctx.fill();
if (stroke) this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
text(text, x, y, fill = true, stroke = false, options = {}, maxWidth = undefined) {
this.ctx.save();
Object.assign(this.ctx, options);
if (fill) this.ctx.fillText(text, x, y, maxWidth);
if (stroke) this.ctx.strokeText(text, x, y, maxWidth);
this.ctx.restore();
}
path(path, fill = false, stroke = true, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
if (fill) this.ctx.fill(path);
if (stroke) this.ctx.stroke(path);
this.ctx.closePath();
this.ctx.restore();
}
grid(x, y, width, height, cellSize, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
this.ctx.beginPath();
for (var cx = x; cx &lt;= x + width; cx += cellSize) {
this.ctx.moveTo(cx, y);
this.ctx.lineTo(cx, y + height);
}
for (var cy = y; cy &lt;= y + height; cy += cellSize) {
this.ctx.moveTo(x, cy);
this.ctx.lineTo(x + width, cy);
}
this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
}
text(text, x, y, fill = true, stroke = false, options = {}) {
this.ctx.save();
Object.assign(this.ctx, options);
if (fill) this.ctx.fillText(text, x, y);
if (fill) this.ctx.strokeText(text, x, y);
this.ctx.restore();
}
}
function random(min, max) {
return Math.random() * (max - min) + min;
}
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
var tankClass = {
basic: function(tank) {
return [
new Gun(0, -tank.height * 0.15, tank.width * 0.9, tank.height * 0.3, tank, 0)
];
},
doubleShot: function(tank) {
return [
new Gun(0, -tank.height * 0.12, tank.width * 0.9, tank.height * 0.24, tank, -4),
new Gun(0, -tank.height * 0.12, tank.width * 0.9, tank.height * 0.24, tank, 4),
new Gun(0, -tank.height * 0.15, tank.width * 0.9, tank.height * 0.3, tank, 0)
];
}
}
class Player {
constructor(x, y, width, height, color, bc, startingWeapons = &quot;basic&quot;) {
this.initX = x;
this.initY = y;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.bc = bc;
this.velX = 0;
this.velY = 0;
this.acceleration = 0.3;
this.gunAngle = 0;
this.weapons = this.setWeapons(startingWeapons);
this.bullets = [];
this.currentReloadTime = 0;
this.reloadTime = 60;
this.recoilX = 0;
this.safeZone = {
x: x - 100,
y: y - 100,
width: width + 100,
height: height + 100
}
}
setWeapons(weaponString) {
var newWeapons = tankClass[weaponString];
return newWeapons(this);
}
reset() {
this.x = this.initX;
this.y = this.initY;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
}
setSafeZone(x, y, width, height) {
this.safeZone.x = x;
this.safeZone.y = y;
this.safeZone.width = width;
this.safeZone.height = height;
}
}
class Enemy {
constructor(x, y, width, height, color, bc, startingWeapons = &quot;basic&quot;) {
this.initX = x;
this.initY = y;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.bc = bc;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
this.weapons = this.setWeapons(startingWeapons);
this.bullets = [];
this.currentReloadTime = 0;
this.reloadTime = 60;
this.recoilX = 0;
}
setWeapons(weaponString) {
var newWeapons = tankClass[weaponString];
return newWeapons(this);
}
reset() {
this.x = this.initX;
this.y = this.initY;
this.velX = 0;
this.velY = 0;
this.gunAngle = 0;
}
}
class Gun {
constructor(x, y, width, height, parent, rotation = 0) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.parent = parent;
this.color = &quot;#606060&quot;;
this.bc = &quot;#404040&quot;;
this.rotation = degreesToRadians(rotation);
}
}
class Bullet {
constructor(x, y, radius, color, bc, parent, velX, velY) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.bc = bc;
this.parent = parent;
this.velX = velX;
this.velY = velY;
}
}
var levels = {
level0: {
map: [
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0]
]
}
}
class Info_Level {
constructor(gridSize = 64) {
this.GRID_SIZE = gridSize;
this.map = {
walls: []
};
}
load(map) {
var tileOffsetX = 0;
var tileOffsetY = 0;
for (var i = 0; i &lt; map.length; i++) {
for (var j = 0; j &lt; map[i].length; j++) {
if (map[i][j] === 1) {
this.createWall(tileOffsetX, tileOffsetY, this.GRID_SIZE, this.GRID_SIZE, &quot;#000000&quot;);
}
if (map[i][j] === 2) {
this.createWall(tileOffsetX, tileOffsetY, this.GRID_SIZE, this.GRID_SIZE, &quot;#000000&quot;);
}
tileOffsetX += this.GRID_SIZE;
}
tileOffsetX = 0;
tileOffsetY += this.GRID_SIZE;
}
tileOffsetY = 0;
tileOffsetX = 0;
}
createWall(x, y, width, height, color, id = &quot;&quot;) {
this.map.walls.push({
x: x,
y: y,
width: width,
height: height,
color: color,
id: id
});
}
}
var infoLevel = new Info_Level(64);
infoLevel.load(levels.level0.map);
var friction = 0.85;
var canvas = document.getElementById(&quot;canvas&quot;);
var ctx = canvas.getContext(&quot;2d&quot;);
var vWidth = window.innerWidth;
var vHeight = window.innerHeight;
var player = new Player(vWidth / 2 - 33, vHeight / 2 - 27.5, 75, 60, &quot;#608060&quot;, &quot;#204020&quot;, &quot;basic&quot;);
var keysDown = [];
var enemies = [];
// enemies.push(new Enemy(0, 0, 75, 60, &quot;#806060&quot;, &quot;#402020&quot;, &quot;basic&quot;));
var draw = new Draw(ctx);
var fps = 60;
function resizeCanvas(canvasElement, width, height) {
vWidth = width;
vHeight = height;
canvasElement.width = vWidth;
canvasElement.height = vHeight;
}
resizeCanvas(canvas, window.innerWidth, window.innerHeight);
function updateTank(tank, isEnemy = false) {
if (tank.currentReloadTime &gt;= 0) {
tank.currentReloadTime--;
}
if (tank.recoilX &lt; 0) {
tank.recoilX += 0.5;
}
if (tank.x &lt; 0) {
tank.x = 0;
}
if (tank.y &lt; 0) {
tank.y = 0;
}
if (tank.x + tank.width &gt; vWidth) {
tank.x = vWidth - tank.width;
}
if (tank.y + tank.height &gt; vHeight) {
tank.y = vHeight - tank.height;
}
tank.velX *= friction;
tank.velY *= friction;
tank.x += tank.velX;
tank.y += tank.velY;
for (var i = 0; i &lt; infoLevel.map.walls.length; i++) {
var wall = infoLevel.map.walls[i];
if (rectangleToRectangleCollision(tank, wall)) {
rectangleToRectangleCollisionResolution(tank, wall);
}
}
if (isEnemy == true) {
var enemyAV = Math.atan2((player.y + player.height / 2) - (tank.y + tank.height / 2), (player.x + player.width / 2) - (tank.x + tank.width / 2));
tank.gunAngle = enemyAV;
if (rectangleToRectangleCollision(player, tank) == false) {
tank.velX += Math.cos(enemyAV) * 0.3;
tank.velY += Math.sin(enemyAV) * 0.3;
}
if (tank.currentReloadTime &lt;= 0) {
for (var i = 0; i &lt; tank.weapons.length; i++) {
var gun = tank.weapons[i];
shootBullet(gun, tank);
}
tank.currentReloadTime = tank.reloadTime;
}
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(Math.atan2(tank.velY / 2, tank.velX / 2));
draw.rectangle(-tank.width / 2, -tank.height / 2, tank.width, tank.height, 2, true, true, {
fillStyle: tank.color,
strokeStyle: tank.bc,
lineWidth: tank.width / tank.height * 1.75
});
ctx.restore();
for (var i = 0; i &lt; tank.bullets.length; i++) {
var bullet = tank.bullets[i];
bullet.x += bullet.velX;
bullet.y += bullet.velY;
draw.arc(bullet.x, bullet.y, bullet.radius, 0, 2 * Math.PI, true, true, {
fillStyle: bullet.color,
strokeStyle: bullet.bc,
lineWidth: tank.width / tank.height * 1.75
});
}
for (var i = 0; i &lt; tank.weapons.length; i++) {
var gun = tank.weapons[i];
if (gun.x &lt; 0) {
gun.x += gun.width / 240;
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(tank.gunAngle + gun.rotation);
draw.rectangle(gun.x, gun.y, gun.width, gun.height, 2, true, true, {
fillStyle: gun.color,
strokeStyle: gun.bc,
lineWidth: tank.width / tank.height * 1.75
});
ctx.restore();
}
ctx.save();
ctx.translate(tank.x + tank.width / 2, tank.y + tank.height / 2);
ctx.rotate(tank.gunAngle);
draw.rectangle(-tank.width / 2 * 0.6 + tank.recoilX, -tank.height / 2 * 0.7, tank.width * 0.6, tank.height * 0.7, 2, true, true, {
fillStyle: tank.color,
strokeStyle: tank.bc,
lineWidth: tank.width / tank.height * 1.75
});
ctx.restore();
}
function shootBullet(gun, tank) {
var shootS = new Audio(&quot;./assets/shoot.wav&quot;);
shootS.play();
gun.x -= gun.width / 12;
tank.recoilX = -tank.width / 16;
var rawVX = Math.cos(tank.gunAngle + gun.rotation);
var rawVY = Math.sin(tank.gunAngle + gun.rotation);
var velX = (rawVX + random(-0.02, 0.02)) * 5;
var velY = (rawVY + random(-0.02, 0.02)) * 5;
tank.bullets.push(new Bullet(tank.x + tank.width / 2 + (rawVX * (tank.width - (tank.height / 2))), tank.y + tank.height / 2 + (rawVY * (tank.width - (tank.height / 2))), gun.height / 2, &quot;#ff0000&quot;, &quot;#800000&quot;, tank, velX, velY));
}
function main() {
if (keysDown[&quot;w&quot;]) {
player.velY -= player.acceleration;
}
if (keysDown[&quot;a&quot;]) {
player.velX -= player.acceleration;
}
if (keysDown[&quot;s&quot;]) {
player.velY += player.acceleration;
}
if (keysDown[&quot;d&quot;]) {
player.velX += player.acceleration;
}
if (Mouse.pressed) {
if (player.currentReloadTime &lt;= 0) {
for (var i = 0; i &lt; player.weapons.length; i++) {
var gun = player.weapons[i];
shootBullet(gun, player);
}
player.currentReloadTime = player.reloadTime;
}
}
player.setSafeZone(player.x + player.width / 2 - 100, player.y + player.height / 2 - 100, 200, 200);
ctx.save();
draw.clear(0, 0, vWidth, vHeight);
updateTank(player, false);
for (var i = 0; i &lt; enemies.length; i++) {
updateTank(enemies[i], true);
}
for (var i = 0; i &lt; infoLevel.map.walls.length; i++) {
var wall = infoLevel.map.walls[i];
draw.rectangle(wall.x, wall.y, wall.width, wall.height, 0, true, false, {
fillStyle: wall.color
});
}
ctx.restore();
}
window.onload = function() {
Mouse.init();
setInterval(main, 1000 / fps);
}
function rectangleToRectangleCollision(obj1, obj2) {
if (obj1.x + obj1.width &gt; obj2.x &amp;&amp; obj1.y + obj1.height &gt; obj2.y &amp;&amp;
obj1.x &lt; obj2.x + obj2.width &amp;&amp; obj1.y &lt; obj2.y + obj2.height) {
return true;
}
return false;
}
window.onresize = function() {
resizeCanvas(canvas, window.innerWidth, window.innerHeight);
}
document.addEventListener(&quot;keydown&quot;, (e) =&gt; {
keysDown[e.key] = true;
});
document.addEventListener(&quot;keyup&quot;, (e) =&gt; {
keysDown[e.key] = false;
});
document.addEventListener(&quot;mousemove&quot;, () =&gt; {
player.gunAngle = Math.atan2(Mouse.y - (player.y + player.height / 2), Mouse.x - (player.x + player.width / 2));
});

<!-- language: lang-css -->

*,
*:before,
*:after {
font-family: roboto, Arial, Helvetica, sans-serif, system-ui;
padding: 0px 0px;
margin: 0px 0px;
box-sizing: border-box;
}

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;CD&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
&lt;/body&gt;
&lt;/html&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月17日 20:45:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76272268.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定