added sensor objects and drawing, multiple extended types, no logic yet
parent
0676c077fd
commit
485aa4557d
13
game.js
13
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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Real-Time Python Execution with Pyodide</h1>
|
||||
<h1>Real-Time Python Execution</h1>
|
||||
<textarea id="python-code" rows="10" cols="50" placeholder="Enter your Python code here..."></textarea>
|
||||
<br><br>
|
||||
<button id="compile-button">Compile and Run</button>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<div id="console"></div>
|
||||
|
||||
<!-- Game canvas -->
|
||||
<h2>2D Tank Simulation</h2>
|
||||
<h2>Robot Simulation</h2>
|
||||
<canvas id="gameCanvas" width="800" height="600"></canvas>
|
||||
|
||||
<script type="module" src="game.js"></script>
|
||||
|
|
|
|||
47
robot.js
47
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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
6
todo.md
6
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.
|
||||
Loading…
Reference in New Issue