diff --git a/game.js b/game.js
index c3f61f5..fde345b 100644
--- a/game.js
+++ b/game.js
@@ -1,9 +1,12 @@
import { Robot } from "./robot.js";
+import { GameWorld } from "./gameworld.js";
const consoleElement = document.getElementById("console");
const gameCanvas = document.getElementById("gameCanvas");
const ctx = gameCanvas.getContext("2d");
+const gameWorld = new GameWorld();
+
let pyodideWorker = startPyodideWorker();
let robots = createInitialRobots();
let paused = false;
@@ -41,9 +44,9 @@ function startPyodideWorker() {
// ✅ Function to create initial robots
function createInitialRobots() {
return {
- "player": new Robot("player", 50, 50, "blue"),
- "enemy1": new Robot("enemy1", 200, 150, "red"),
- "enemy2": new Robot("enemy2", 400, 250, "red")
+ "player": new Robot("player", 50, 50, "blue")
+ //"enemy1": new Robot("enemy1", 200, 150, "red"),
+ //"enemy2": new Robot("enemy2", 400, 250, "red")
};
}
@@ -96,8 +99,10 @@ function resetGame() {
function gameLoop() {
if (!paused) {
ctx.clearRect(0, 0, gameCanvas.width, gameCanvas.height);
+ gameWorld.draw(ctx);
+
for (let id in robots) {
- robots[id].update(ctx);
+ robots[id].update(ctx, gameWorld);
}
}
requestAnimationFrame(gameLoop);
diff --git a/gameworld.js b/gameworld.js
new file mode 100644
index 0000000..8c06ccf
--- /dev/null
+++ b/gameworld.js
@@ -0,0 +1,33 @@
+export class GameWorld {
+ constructor() {
+ this.obstacles = [
+ { x: 300, y: 300, width: 50, height: 50 }, // Example obstacle
+ { x: 500, y: 400, width: 70, height: 70 } // Another obstacle
+ ];
+ }
+
+ // Check if a point (x, y) intersects with any obstacles
+ isObstacle(x, y) {
+ return this.obstacles.some(
+ obj => x > obj.x && x < obj.x + obj.width && y > obj.y && y < obj.y + obj.height
+ );
+ }
+
+ // Return the floor color based on (x, y) coordinates
+ getFloorColor(x, y) {
+ return (x + y) % 50 < 25 ? "black" : "white"; // Example pattern
+ }
+
+ // Draw the game world (e.g., obstacles, background)
+ draw(ctx) {
+ // Draw obstacles
+ ctx.fillStyle = "gray"; // Obstacle color
+ this.obstacles.forEach(obstacle => {
+ ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
+ });
+
+ // Optionally, draw the grid or other visual elements (e.g., floor color patterns)
+ ctx.fillStyle = "lightgray"; // Example background
+ ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Draw background
+ }
+}
diff --git a/index.html b/index.html
index 70ac71e..6ae8923 100644
--- a/index.html
+++ b/index.html
@@ -25,7 +25,7 @@
- Real-Time Python Execution with Pyodide
+ Real-Time Python Execution
@@ -37,7 +37,7 @@
- 2D Tank Simulation
+ Robot Simulation
diff --git a/robot.js b/robot.js
index 28c3d20..9b27850 100644
--- a/robot.js
+++ b/robot.js
@@ -1,22 +1,41 @@
+import { RaycastSensor } from "./sensor.js";
+import { FloorColorSensor } from "./sensor.js";
+
export class Robot {
constructor(id, x, y, color = "blue") {
this.id = id;
this.x = x;
this.y = y;
this.velocity = 0;
- this.direction = 0; // Degrees
+ this.angle = 0; // Degrees
this.width = 20;
this.height = 20;
this.color = color;
+ this.sensors = [];
+
+ this.addSensor(new RaycastSensor(this, -40, 13, -45, 60));
+ this.addSensor(new RaycastSensor(this, 40, 13, 45, 60));
+ this.addSensor(new FloorColorSensor(this, 0, 0));
}
- update(ctx){
+ update(ctx, gameWorld){
this.update_position();
+ this.update_sensors(gameWorld);
this.draw(ctx);
}
+ update_sensors(gameWorld) {
+ this.sensors.forEach(sensor => sensor.read(this, gameWorld));
+ }
+
+ addSensor(sensor) {
+ if (this.sensors.length < 8) {
+ this.sensors.push(sensor);
+ }
+ }
+
update_position() {
- const radians = (this.direction * Math.PI) / 180;
+ const radians = (this.angle * Math.PI) / 180;
this.x += Math.cos(radians) * this.velocity;
this.y += Math.sin(radians) * this.velocity;
}
@@ -26,14 +45,14 @@ export class Robot {
}
turn(degrees) {
- this.direction += degrees;
+ this.angle += degrees;
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.save();
ctx.translate(this.x, this.y);
- ctx.rotate((this.direction * Math.PI) / 180);
+ ctx.rotate((this.angle * Math.PI) / 180);
// Draw the rectangle (tank body)
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
@@ -46,7 +65,25 @@ export class Robot {
ctx.closePath();
ctx.fill(); // Fill both the rectangle and the triangle
+
+
ctx.restore();
+
+ this.sensors.forEach(sensor => sensor.draw(ctx));
+ }
+
+ draw_sensors(ctx) {
+ ctx.fillStyle = "yellow";
+
+ this.sensors.forEach(sensor => {
+ const radians = ((this.angle + sensor.angleOffset) * Math.PI) / 180;
+ const sensorX = this.x + Math.cos(radians) * 15;
+ const sensorY = this.y + Math.sin(radians) * 15;
+
+ ctx.beginPath();
+ ctx.arc(sensorX, sensorY, 3, 0, 2 * Math.PI);
+ ctx.fill();
+ });
}
diff --git a/sensor.js b/sensor.js
new file mode 100644
index 0000000..5a5a4c0
--- /dev/null
+++ b/sensor.js
@@ -0,0 +1,106 @@
+export class Sensor {
+ constructor(robot, offsetAngle, offsetDistance) {
+ this.robot = robot
+ this.value = null; // Last recorded value
+ this.offsetAngle = offsetAngle;
+ this.offsetDistance = offsetDistance;
+
+
+ }
+
+ updatePosition() {
+ const robotAngle = this.robot.angle * (Math.PI / 180);
+ const offsetAngleRad = this.offsetAngle * (Math.PI / 180);
+ this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad);
+ this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad);
+ }
+
+ read(robot, gameWorld) {
+ throw new Error("read() must be implemented in subclasses");
+ }
+
+ draw(ctx) {
+ // Default empty draw, can be overridden
+ }
+}
+
+export class RaycastSensor extends Sensor {
+ constructor(robot, offsetAngle, offsetDistance, angle, distance) {
+ super(robot, offsetAngle, offsetDistance); // Call the parent class constructor
+ this.angle = angle;
+ this.distance = distance;
+ this.range = 100;
+ }
+
+ updatePosition() {
+ const robotAngle = this.robot.angle * (Math.PI / 180);
+ const offsetAngleRad = this.offsetAngle * (Math.PI / 180);
+ this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad);
+ this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad);
+ const sensorAngle = robotAngle + (this.angle * (Math.PI / 180));
+ this.endX = this.startX + Math.cos(sensorAngle) * this.range;
+ this.endY = this.startY + Math.sin(sensorAngle) * this.range;
+ }
+
+ read(robot, gameWorld) {
+ //console.log(robot);
+ //console.log(gameWorld);
+ const angleInRadians = (this.robot.direction + this.angle) * Math.PI / 180;
+ const x = this.robot.x + Math.cos(angleInRadians) * this.distance;
+ const y = this.robot.y + Math.sin(angleInRadians) * this.distance;
+
+ // Ensure gameWorld is available and properly passed to the sensor
+ if (gameWorld.isObstacle(x, y)) {
+ //console.log("Obstacle detected!");
+ } else {
+ //console.log("Clear path!");
+ }
+ }
+
+ draw(ctx) {
+ this.updatePosition();
+
+ ctx.strokeStyle = "lime";
+ ctx.fillStyle = "green"
+ ctx.beginPath();
+ ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI);
+ ctx.fillStyle = "red";
+ ctx.fill();
+ ctx.stroke();
+
+
+ ctx.strokeStyle = "yellow";
+ ctx.beginPath();
+ ctx.lineWidth = 2;
+ ctx.moveTo(this.startX, this.startY);
+ ctx.lineTo(this.endX, this.endY);
+ ctx.stroke();
+
+ }
+}
+
+
+export class FloorColorSensor extends Sensor {
+ constructor(robot, offsetAngle, offsetDistance) {
+ super(robot, offsetAngle, offsetDistance); // No angle offset, directly below robot
+ }
+
+ read(robot, gameWorld) {
+ this.value = gameWorld.getFloorColor(robot.x, robot.y);
+ return this.value;
+ }
+
+ draw(ctx) {
+ this.updatePosition();
+
+ ctx.strokeStyle = "purple";
+ ctx.fillStyle = "green"
+ ctx.beginPath();
+ ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI);
+ ctx.fillStyle = "red";
+ ctx.fill();
+ ctx.stroke();
+
+
+ }
+}
diff --git a/todo.md b/todo.md
index cce3a75..f9565cf 100644
--- a/todo.md
+++ b/todo.md
@@ -1,9 +1,11 @@
DO
-
+Add robot.angle and other state variables
+Add robot sensor data connections to detect and change parameters
+Add pan and zoom to canvas
IN PROGRESS
-
DONE
+Add robot sensors that detect things in game world, color sense and distance sense for start
Add Pause Button
Add reset button.
\ No newline at end of file