Skip to content

Commit 1eabde2

Browse files
committed
raycasting implementedgit status
1 parent a53f007 commit 1eabde2

File tree

4 files changed

+203
-160
lines changed

4 files changed

+203
-160
lines changed

src/bundles/robot_minigame/functions.ts

Lines changed: 156 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -5,176 +5,211 @@
55

66
import context from 'js-slang/context';
77

8-
const robotPos: Point = {x: 0, y: 0};
8+
type Point = {x: number, y: number};
9+
type Wall = {p1: Point, p2: Point};
910

10-
const DIRECTIONS = {
11-
UP: 0,
12-
RIGHT: 1,
13-
DOWN: 2,
14-
LEFT: 3
15-
};
11+
type Polygon = Point[];
12+
13+
type StateData = {
14+
isInit: boolean,
15+
width: number,
16+
height: number,
17+
walls: Polygon[],
18+
movePoints: Point[],
19+
message: String,
20+
success: boolean,
21+
messages: String[]
22+
}
1623

17-
let robotRotation = 1;
24+
type Robot = {
25+
x: number; // the top left corner
26+
y: number;
27+
dx: number;
28+
dy: number;
29+
radius: number
30+
}
1831

19-
// default grid width and height is 25
20-
context.moduleContexts.robot_minigame.state = {
32+
let stateData: StateData = {
2133
isInit: false,
22-
width: 25,
23-
height: 25,
34+
width: 500,
35+
height: 500,
2436
walls: [],
2537
movePoints: [],
2638
message: 'moved successfully',
27-
success: true
28-
};
39+
success: true,
40+
messages: []
41+
}
2942

30-
type Point = {x: number, y: number};
31-
type Wall = {p1: Point, p2: Point};
43+
let robot: Robot = {
44+
x: 25, // default start pos, puts it at the top left corner of canvas without colliding with the walls
45+
y: 25,
46+
dx: 1,
47+
dy: 0,
48+
radius: 20 //give the robot a circular hitbox
49+
}
50+
51+
let bounds: Point[] = []
52+
53+
// default grid width and height is 25
54+
context.moduleContexts.robot_minigame.state = stateData;
3255

3356
export function set_pos(x: number, y: number): void {
34-
robotPos.x = x;
35-
robotPos.y = y;
57+
robot.x = x;
58+
robot.y = y;
3659
}
3760

38-
export function set_grid_width(width: number) {
39-
context.moduleContexts.robot_minigame.state.width = width;
61+
export function set_width(width: number) {
62+
stateData.width = width;
4063
}
4164

42-
export function set_grid_height(height: number) {
43-
context.moduleContexts.robot_minigame.state.height = height;
65+
export function set_height(height: number) {
66+
stateData.height = height;
4467
}
4568

46-
export function init(gridWidth: number, gridHeight: number, posX: number, posY: number) {
47-
set_grid_width(gridWidth);
48-
set_grid_height(gridHeight);
69+
export function init(width: number, height: number, posX: number, posY: number) {
70+
set_width(width);
71+
set_height(height);
4972
set_pos(posX, posY);
50-
context.moduleContexts.robot_minigame.state.movePoints.push({x: posX, y: posY});
51-
context.moduleContexts.robot_minigame.state.isInit = true;
73+
stateData.movePoints.push({x: posX, y: posY});
74+
stateData.isInit = true;
75+
76+
bounds = [
77+
{x: 0, y: 0},
78+
{x: width, y: 0},
79+
{x: width, y: height},
80+
{x: 0, y: height}
81+
]
82+
5283
}
5384

5485
export function turn_left() {
5586
if (alrCollided()) return;
5687

57-
robotRotation -= 1;
58-
if (robotRotation < 0) {
59-
robotRotation = 3;
60-
}
88+
let currentAngle = Math.tan(robot.dy / robot.dx);
89+
currentAngle -= Math.PI / 2;
90+
91+
robot.dx = Math.cos(currentAngle);
92+
robot.dy = Math.sin(currentAngle);
6193
}
6294

6395
export function turn_right() {
6496
if (alrCollided()) return;
6597

66-
robotRotation = (robotRotation + 1) % 4;
98+
let currentAngle = Math.tan(robot.dy / robot.dx);
99+
currentAngle += Math.PI / 2;
100+
101+
robot.dx = Math.cos(currentAngle);
102+
robot.dy = Math.sin(currentAngle);
67103
}
68104

69-
// takes the top left and bottom right corners of walls
70-
// in terms of grid boxes
71-
// grid starts from (0, 0) at the top left corner btw
72-
export function set_wall(x1: number, y1: number, x2: number, y2: number) {
73-
const wall: Wall = {p1: {x: x1, y: y1}, p2: {x: x2, y: y2}};
74-
context.moduleContexts.robot_minigame.state.walls.push(wall);
105+
export function set_rect_wall(x: number, y: number, width: number, height: number) {
106+
const polygon: Polygon = [
107+
{x: x, y: y},
108+
{x: x + width, y: y},
109+
{x: x+width, y: y+height},
110+
{x: x, y:y+height}
111+
]
112+
113+
stateData.walls.push(polygon);
75114
}
76115

77-
export function move_forward(dist: number): void {
116+
export function move_forward(): void {
78117
if (alrCollided()) return;
79118

80-
simulate(dist);
119+
const collisionPoint: Point | null = raycast(stateData.walls)
120+
if (collisionPoint !== null) {
121+
let nextPoint: Point = {
122+
x: collisionPoint.x - robot.dx * (robot.radius + 5),
123+
y: collisionPoint.y - robot.dy * (robot.radius + 5)
124+
}
125+
126+
robot.x = nextPoint.x;
127+
robot.y = nextPoint.y;
128+
stateData.movePoints.push(nextPoint);
129+
}
81130
}
82131

83132
export function getX():number {
84-
return robotPos.x;
133+
return robot.x;
85134
}
86135

87136
export function getY():number {
88-
return robotPos.y;
89-
}
90-
91-
function simulate(moveDist: number) {
92-
let dx: number = 0;
93-
let dy: number = 0;
94-
switch (robotRotation) {
95-
case DIRECTIONS.UP:
96-
dy = -1;
97-
break;
98-
case DIRECTIONS.RIGHT:
99-
dx = 1;
100-
break;
101-
case DIRECTIONS.DOWN:
102-
dy = 1;
103-
break;
104-
case DIRECTIONS.LEFT:
105-
dx = -1;
106-
break;
107-
}
137+
return robot.y;
138+
}
108139

109-
// moves robot by one grid box and checks collision
110-
for (let i = 0; i < moveDist; i++) {
111-
robotPos.x += dx;
112-
robotPos.y += dy;
113-
114-
const walls = context.moduleContexts.robot_minigame.state.walls;
115-
for (let j = 0; j < walls.length; j++) {
116-
if (checkWallCollision(walls[j], robotPos)) {
117-
context.moduleContexts.robot_minigame.state.success = false;
118-
context.moduleContexts.robot_minigame.state.message = 'collided';
119-
context.moduleContexts.robot_minigame.state.movePoints.push({x: robotPos.x, y: robotPos.y});
120-
return;
140+
function raycast(polygons: Polygon[]): Point | null {
141+
let nearest: Point | null = null;
142+
let minDist = Infinity;
143+
144+
for (const polygon of polygons) {
145+
stateData.messages.push("checking polygon");
146+
const numVertices = polygon.length;
147+
148+
for (let i = 0; i < numVertices; i++) {
149+
const x1 = polygon[i].x, y1 = polygon[i].y;
150+
const x2 = polygon[(i + 1) % numVertices].x, y2 = polygon[(i + 1) % numVertices].y;
151+
152+
const intersection = getIntersection(robot.x, robot.y, robot.dx + robot.x, robot.dy + robot.y, x1, y1, x2, y2);
153+
154+
if (intersection.collided && intersection.dist < minDist) {
155+
minDist = intersection.dist
156+
nearest = {x: intersection.x, y: intersection.y};
157+
}
121158
}
122-
}
123159
}
124160

125-
context.moduleContexts.robot_minigame.state.movePoints.push({x: robotPos.x, y: robotPos.y});
126-
127-
// OLD CODE
128-
// let destX = robotPos.x + moveDist * Math.cos(robotAngle);
129-
// let destY = robotPos.y + moveDist * Math.sin(robotAngle);
130-
// let destPoint: Point = {x: destX, y: destY}
161+
// if no collisions with obstacles, check the outer bounds of map
162+
if (nearest === null) {
163+
for (let i = 0; i < bounds.length; i++) {
164+
const x1 = bounds[i].x, y1 = bounds[i].y;
165+
const x2 = bounds[(i + 1) % bounds.length].x, y2 = bounds[(i + 1) % bounds.length].y;
131166

132-
// for (let i = 0; i < steps; i++) {
133-
// let distX: number = moveSpeed * Math.cos(robotAngle);
134-
// let distY: number = moveSpeed * Math.sin(robotAngle);
167+
const intersection = getIntersection(robot.x, robot.y, robot.dx + robot.x, robot.dy + robot.y, x1, y1, x2, y2);
135168

136-
// robotPos.x += distX;
137-
// robotPos.y += distY;
138-
139-
// for (let j = 0; j < walls.length; j++) {
140-
// if(checkWallCollision(walls[j], robotPos)) {
141-
// addMessage("Collided with wall!!");
142-
// addMessage(`Position: (${robotPos.x.toFixed(3)}, ${robotPos.y.toFixed(3)}), Rotation: ${robotAngle}\n`);
143-
// return;
144-
// }
145-
// }
146-
147-
// if (distanceBetween(destPoint, robotPos) < robotRadius) {
148-
// robotPos = destPoint;
149-
// addMessage(`Robot moved forward by ${moveDist} at angle ${robotAngle} radians\n`);
150-
// addMessage(`Position: (${robotPos.x.toFixed(3)}, ${robotPos.y.toFixed(3)}), Rotation: ${robotAngle}\n`);
151-
// return;
152-
// }
153-
// }
169+
if (intersection.collided && intersection.dist < minDist) {
170+
minDist = intersection.dist
171+
nearest = {x: intersection.x, y: intersection.y};
172+
}
173+
}
174+
}
154175

176+
return nearest; // Closest intersection point
155177
}
156178

157-
function checkWallCollision(wall: Wall, pos: Point): boolean {
158-
// // Apply the distance formula
159-
// const p1 = wall.p1;
160-
// const p2 = wall.p2;
161-
162-
// const numerator = Math.abs((p2.y - p1.y) * pos.x - (p2.x - p1.x) * pos.y + p2.x * p1.y - p2.y * p1.x);
163-
// const denominator = Math.sqrt((p2.y - p1.y) ** 2 + (p2.x - p1.x) ** 2);
164-
165-
// return numerator / denominator < robotRadius;
166-
167-
const p1 = wall.p1;
168-
const p2 = wall.p2;
169-
170-
const minX = Math.min(p1.x, p2.x);
171-
const maxX = Math.max(p1.x, p2.x);
172-
const minY = Math.min(p1.y, p2.y);
173-
const maxY = Math.max(p1.y, p2.y);
174-
175-
return pos.x >= minX && pos.x <= maxX && pos.y >= minY && pos.y <= maxY;
179+
//Determine if a ray and a line segment intersect, and if so, determine the collision point
180+
function getIntersection(x1, y1, x2, y2, x3, y3, x4, y4){
181+
var denom = ((x2 - x1)*(y4 - y3)-(y2 - y1)*(x4 - x3));
182+
var r;
183+
var s;
184+
var x;
185+
var y;
186+
var b = false;
187+
188+
//If lines not collinear or parallel
189+
if(denom != 0){
190+
//Intersection in ray "local" coordinates
191+
r = (((y1 - y3) * (x4 - x3)) - (x1 - x3) * (y4 - y3)) / denom;
192+
193+
//Intersection in segment "local" coordinates
194+
s = (((y1 - y3) * (x2 - x1)) - (x1 - x3) * (y2 - y1)) / denom;
195+
196+
//The algorithm gives the intersection of two infinite lines, determine if it lies on the side that the ray is defined on
197+
if (r >= 0)
198+
{
199+
//If point along the line segment
200+
if (s >= 0 && s <= 1)
201+
{
202+
b = true;
203+
//Get point coordinates (offset by r local units from start of ray)
204+
x = x1 + r * (x2 - x1);
205+
y = y1 + r * (y2 - y1);
206+
}
207+
}
208+
}
209+
var p = {collided: b, x: x, y: y, dist: r};
210+
return p;
176211
}
177212

178213
function alrCollided() {
179-
return !context.moduleContexts.robot_minigame.state.success;
214+
return !stateData.success;
180215
}

src/bundles/robot_minigame/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
export {
1212
init,
1313
set_pos,
14-
set_wall,
14+
set_rect_wall,
1515
move_forward,
1616
turn_left,
1717
turn_right,

0 commit comments

Comments
 (0)