class Component { constructor(radius, color, x, y, direction) { this.perceptionDistance = 20; this.turnStepAmount = 5; this.collisionTurnStepAmount = 20; this.stepAmount = 1; this.radius = radius; this.x = x; this.y = y; this.direction = direction; this.color = color; } move(context) { this.direction += Math.random() * (2 * this.turnStepAmount) - this.turnStepAmount var localDirection = this.direction; // first we need to work out if we detect any walls var perceptionVector = this.detectionPoint(this.x, this.y, this.perceptionDistance, localDirection); while (perceptionVector.x < 0 || perceptionVector.y < 0 || perceptionVector.x > context.canvas.width || perceptionVector.y > context.canvas.height) { localDirection += Math.random() * (2 * this.collisionTurnStepAmount) - this.collisionTurnStepAmount perceptionVector = this.detectionPoint(this.x, this.y, this.perceptionDistance, localDirection); } // Here we should now have a new vector that's not clipping this.direction = localDirection; var vector = this.detectionPoint(this.x, this.y, this.stepAmount, this.direction); this.x = vector.x; this.y = vector.y; this.update(context); } update(context) { context.beginPath(); context.fillStyle = "blue"; context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); context.stroke(); context.restore(); context.beginPath(); this.lineToAngle(context, this.x, this.y, this.perceptionDistance, this.direction); context.lineWidth = 1; // context.stroke(); context.restore(); } lineToAngle(context, x1, y1, length, angle) { angle *= Math.PI / 180; var x2 = x1 + length * Math.cos(angle), y2 = y1 + length * Math.sin(angle); context.moveTo(x1, y1); context.lineTo(x2, y2); return { x: x2, y: y2 }; } detectionPoint(x1, y1, length, angle) { angle *= Math.PI / 180; var x2 = x1 + length * Math.cos(angle), y2 = y1 + length * Math.sin(angle); return { x: x2, y: y2 }; } } class GameArea { constructor(canvas_width, canvas_height) { this.canvas = document.createElement("canvas"); this.canvas_width = canvas_width; this.canvas_height = canvas_height; } init() { this.canvas.width = this.canvas_width; this.canvas.height = this.canvas_height; this.context = this.canvas.getContext("2d"); document.getElementById('container').appendChild(this.canvas); } clear() { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); } } class Scene { constructor(component_size, no_of_components) { this.component_size = component_size; this.no_of_components = no_of_components; this.components = new Array; this.gameArea = {}; this.initGameArea(); this.initComponents(); this.started = false; } initComponents() { for (let i = 0; i < this.no_of_components; i++) { let new_component = new Component( this.component_size, "black", Math.random() * this.gameArea.canvas.width, Math.random() * this.gameArea.canvas.height, Math.random() * 360 ); this.components.push(new_component); } } initGameArea() { this.gameArea = new GameArea(600, 600); this.gameArea.init() } start() { if (this.started) { return; } // Kinda annoying, setInterval is a piece of shite and is always run from global scope // Therefore "this.update" will be undefined unless we bind it const updateMe = this.update.bind(this); this.interval = setInterval(updateMe, 5); this.started = true; } update() { this.gameArea.clear(); for (let i = 0; i < this.no_of_components; i++) { this.components[i].move(this.gameArea.context); } } stop() { if (!this.started) { return; } clearInterval(this.interval); this.interval = null; this.started = false; } reset() { if (this.started) { this.stop(); } this.components = new Array; this.initComponents(); this.gameArea.clear(); this.started = false; } } let scene = new Scene(2, 500); function stop() { scene.stop(); } function start() { scene.start(); } function reset() { scene.reset(); }