export class Ship { constructor(canvas, x, y, color = "crimson") { this.canvas = canvas; this.position = { x: x, y: y }; this.color = color; this.velocity = { x: 0.1, y: -0.1 }; this.acceleration = { x: 0, y: 0 }; this.target = { x: canvas.width / 2, y: canvas.height / 2 }; this.futurePoint = { x: x, y: y }; this.angle = 0; this.debug = false; this.wrapDelay = 0; this.width = 10; this.height = 15; } update(delta) { this.velocity.x += this.acceleration.x; this.position.x += this.velocity.x; this.velocity.y += this.acceleration.y; this.position.y += this.velocity.y; this.velocity.x = Math.sign(this.velocity.x) * Math.min(5, Math.abs(this.velocity.x)); this.velocity.y = Math.sign(this.velocity.y) * Math.min(5, Math.abs(this.velocity.y)); this.acceleration = this.computeAcceleration(); let wrappedPosition = this.screenWrap(this.position); if (wrappedPosition.x != this.position.x || wrappedPosition.y != this.position.y) { this.wrapDelay = 30; } this.position = wrappedPosition; } screenWrap(position) { let wrappedPosition = { x: position.x, y: position.y }; if (position.x < 0 - 10) { wrappedPosition.x = this.canvas.width + 10; } if (position.x > this.canvas.width + 10) { wrappedPosition.x = 0 - 10; } if (position.y < 0 - 10) { wrappedPosition.y = this.canvas.height + 10; } if (position.y > this.canvas.height + 10) { wrappedPosition.y = 0 - 10; } return wrappedPosition; } computeAcceleration() { // let angle = Math.atan2(this.target.y - this.position.y, this.target.x - this.position.x); // let distanceToTarget = Math.hypot(this.target.y - this.position.y, this.target.x - this.position.x); // if (this.position.x != this.target.x) { // acceleration.x = 0.1 * Math.cos(angle); // } // if (this.position.y != this.target.y) { // acceleration.y = 0.1 * Math.sin(angle); // } if (this.wrapDelay > 0) { this.wrapDelay--; return this.acceleration; } let acceleration = { x: 0, y: 0 }; // //predictive slowing this.futurePoint = { x: this.position.x, y: this.position.y }; let futureVelocity = { x: this.velocity.x, y: this.velocity.y }; let isCloseEnough = false; for (let i = 0; i < 10; i++) { this.futurePoint.x += futureVelocity.x; this.futurePoint.y += futureVelocity.y; if (this.closeEnough(this.target.x, this.futurePoint.x, this.canvas.width / 8) && this.closeEnough(this.target.y, this.futurePoint.y, this.canvas.height / 8)) { isCloseEnough = true; break; } } this.angle = Math.atan2(this.target.y - this.position.y, this.target.x - this.position.x); let distanceToTarget = Math.hypot(this.target.y - this.position.y, this.target.x - this.position.x); if (!isCloseEnough) { acceleration.x = 0.1 * Math.cos(this.angle); acceleration.y = 0.1 * Math.sin(this.angle); } return acceleration; } closeEnough(n1, n2, amount = 0.1) { return Math.abs(n1 - n2) <= amount; } draw(context) { context.save(); context.translate(this.position.x, this.position.y); context.rotate(this.angle - (Math.PI / 2)); context.fillStyle = this.color; context.beginPath(); this.shipShape(context); context.fill(); if (this.acceleration.x != 0 || this.acceleration.y != 0) { context.fillStyle = "cyan"; context.beginPath(); this.shipThrustShape(context); context.fill(); } context.restore(); if (this.debug) { context.strokeStyle = "yellow"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.lineTo(this.target.x, this.target.y); context.stroke(); context.strokeStyle = "red"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.lineTo(this.canvas.width, this.position.y); context.stroke(); context.strokeStyle = "red"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.lineTo(0, this.position.y); context.stroke(); context.strokeStyle = "cyan"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.lineTo(this.position.x, this.canvas.height); context.stroke(); context.strokeStyle = "cyan"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.lineTo(this.position.x, 0); context.stroke(); context.fillStyle = "yellow"; context.beginPath(); context.moveTo(this.position.x, this.position.y); context.arc(this.futurePoint.x, this.futurePoint.y, 2, 0, 2 * Math.PI); context.fill(); } } shipShape(context) { context.moveTo(this.width / 2, -this.height / 2); context.lineTo(0, this.height / 2); context.lineTo(-this.width / 2, -this.height / 2); context.lineTo(this.width / 2, -this.height / 2); } shipThrustShape(context) { context.moveTo(-this.width / 4, -this.height / 2); context.lineTo(this.width / 4, -this.height / 2); context.lineTo(0, -1.2 * this.height); context.lineTo(-this.width / 4, -this.height / 2); } setTarget(x, y) { this.target.x = x; this.target.y = y; } }