├── .gitignore
├── Procfile
├── README.md
├── index.js
├── package-lock.json
├── package.json
└── public
├── index.html
├── libs
└── p5.min.js
└── sketch.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node index.js
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # javascript-ray-casting
2 |
3 | **javascript-ray-casting** is a [Ray Casting] engine built using [p5.js] to render all the graphics. It attempts to simulate a pseudo 3D environment from a 2D map
4 |
5 | This technique was very popular in game development in the 90s, when computer could not simulate a fully 3D environment in real time. Ray casting can go very fast, because only a calculation has to be done for every vertical line of the screen. Some of the most well known games that used this technique, are of course [Wolfenstein 3D] and [DOOM].
6 |
7 | * [Try the app now]
8 |
9 | For a more in depth explanation of the calculations involved, I recommend the following awesome tutorials:
10 |
11 | * [Lode's Computer Graphics Tutorial: Raycasting] by Lode Vandevenne
12 | * [Ray-Casting Tutorial For Game Development And Other Purposes] by F. Permadi
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 | [Ray Casting]:https://en.wikipedia.org/wiki/Ray_casting
30 | [p5.js]:https://p5js.org/
31 | [Wolfenstein 3D]:https://en.wikipedia.org/wiki/Wolfenstein_3D
32 | [DOOM]:https://en.wikipedia.org/wiki/Doom_(series)
33 | [Try the app now]:https://ray-casting-aruvham.herokuapp.com/
34 | [Lode's Computer Graphics Tutorial: Raycasting]:http://lodev.org/cgtutor/raycasting.html
35 | [Ray-Casting Tutorial For Game Development And Other Purposes]:http://permadi.com/1996/05/ray-casting-tutorial-table-of-contents/
36 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | app.use(express.static(__dirname + '/public/'));
5 | app.set('port', (process.env.PORT || 3000));
6 | app.listen(app.get('port'), function() {
7 | console.log('Node app is running on port', app.get('port'));
8 | });
9 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-ray-casting",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "dependencies": {
6 | "accepts": {
7 | "version": "1.3.3",
8 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
9 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo="
10 | },
11 | "array-flatten": {
12 | "version": "1.1.1",
13 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
14 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
15 | },
16 | "content-disposition": {
17 | "version": "0.5.2",
18 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
19 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
20 | },
21 | "content-type": {
22 | "version": "1.0.2",
23 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
24 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0="
25 | },
26 | "cookie": {
27 | "version": "0.3.1",
28 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
29 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
30 | },
31 | "cookie-signature": {
32 | "version": "1.0.6",
33 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
34 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
35 | },
36 | "debug": {
37 | "version": "2.6.7",
38 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz",
39 | "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4="
40 | },
41 | "depd": {
42 | "version": "1.1.0",
43 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
44 | "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM="
45 | },
46 | "destroy": {
47 | "version": "1.0.4",
48 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
49 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
50 | },
51 | "ee-first": {
52 | "version": "1.1.1",
53 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
54 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
55 | },
56 | "encodeurl": {
57 | "version": "1.0.1",
58 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
59 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
60 | },
61 | "escape-html": {
62 | "version": "1.0.3",
63 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
64 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
65 | },
66 | "etag": {
67 | "version": "1.8.0",
68 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
69 | "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE="
70 | },
71 | "express": {
72 | "version": "4.15.3",
73 | "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz",
74 | "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI="
75 | },
76 | "finalhandler": {
77 | "version": "1.0.3",
78 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz",
79 | "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk="
80 | },
81 | "forwarded": {
82 | "version": "0.1.0",
83 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
84 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M="
85 | },
86 | "fresh": {
87 | "version": "0.5.0",
88 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz",
89 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44="
90 | },
91 | "http-errors": {
92 | "version": "1.6.1",
93 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz",
94 | "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc="
95 | },
96 | "inherits": {
97 | "version": "2.0.3",
98 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
99 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
100 | },
101 | "ipaddr.js": {
102 | "version": "1.3.0",
103 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz",
104 | "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew="
105 | },
106 | "media-typer": {
107 | "version": "0.3.0",
108 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
109 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
110 | },
111 | "merge-descriptors": {
112 | "version": "1.0.1",
113 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
114 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
115 | },
116 | "methods": {
117 | "version": "1.1.2",
118 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
119 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
120 | },
121 | "mime": {
122 | "version": "1.3.4",
123 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
124 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM="
125 | },
126 | "mime-db": {
127 | "version": "1.27.0",
128 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
129 | "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE="
130 | },
131 | "mime-types": {
132 | "version": "2.1.15",
133 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz",
134 | "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0="
135 | },
136 | "ms": {
137 | "version": "2.0.0",
138 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
139 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
140 | },
141 | "negotiator": {
142 | "version": "0.6.1",
143 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
144 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
145 | },
146 | "on-finished": {
147 | "version": "2.3.0",
148 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
149 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc="
150 | },
151 | "parseurl": {
152 | "version": "1.3.1",
153 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
154 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
155 | },
156 | "path-to-regexp": {
157 | "version": "0.1.7",
158 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
159 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
160 | },
161 | "proxy-addr": {
162 | "version": "1.1.4",
163 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz",
164 | "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM="
165 | },
166 | "qs": {
167 | "version": "6.4.0",
168 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
169 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
170 | },
171 | "range-parser": {
172 | "version": "1.2.0",
173 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
174 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
175 | },
176 | "send": {
177 | "version": "0.15.3",
178 | "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz",
179 | "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk="
180 | },
181 | "serve-static": {
182 | "version": "1.12.3",
183 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz",
184 | "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI="
185 | },
186 | "setprototypeof": {
187 | "version": "1.0.3",
188 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
189 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
190 | },
191 | "statuses": {
192 | "version": "1.3.1",
193 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
194 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
195 | },
196 | "type-is": {
197 | "version": "1.6.15",
198 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
199 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA="
200 | },
201 | "unpipe": {
202 | "version": "1.0.0",
203 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
204 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
205 | },
206 | "utils-merge": {
207 | "version": "1.0.0",
208 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
209 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
210 | },
211 | "vary": {
212 | "version": "1.1.1",
213 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
214 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc="
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-ray-casting",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/aruvham/javascript-ray-casting.git"
8 | },
9 | "author": "Arturo Ruvalcaba",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.15.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ray Casting
6 |
7 |
8 |
9 |
10 |
20 |
21 |
--------------------------------------------------------------------------------
/public/sketch.js:
--------------------------------------------------------------------------------
1 | // Ray Casting
2 | // aruvham
3 |
4 | var worldMap = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
5 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
6 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
7 | [1,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,1],
8 | [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
9 | [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1],
10 | [1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1],
11 | [1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1],
12 | [1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1],
13 | [1,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1],
14 | [1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,1],
15 | [1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
16 | [1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
17 | [1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
18 | [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
19 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
20 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
21 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
22 | [1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1],
23 | [1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1],
24 | [1,0,0,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1],
25 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
26 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
27 | [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]];
28 |
29 | var mapWidth = worldMap[0].length, // in blocks
30 | mapHeight = worldMap.length; // in blocks
31 |
32 | var screenWidth = 320, // px
33 | screenHeight = 240;// px
34 |
35 | var FOV = 60, // degrees
36 | resolution = 2, // px
37 | numberOfRays = screenWidth / resolution;
38 |
39 | function setup() {
40 | createCanvas(640, 240);
41 | minimap = new Minimap(0, 0, 10);
42 | player = new Player(150, 170);
43 | }
44 |
45 | function draw() {
46 | noStroke();
47 | background(255);
48 | fill(255);
49 | rect(320,0,320,240);
50 | fill(0);
51 | rect(320,0,320,120);
52 | fill(200);
53 | rect(320,120,320,120);
54 |
55 | minimap.draw();
56 | castRays();
57 | player.update();
58 | player.draw();
59 | }
60 |
61 | function castRays() {
62 | var angleStep = FOV / numberOfRays; // 60 / 80 = 0.75
63 | var radianStep = (angleStep / 180) * PI // 0.013
64 | var initialAngle = player.dir - (0.5 * FOV / 180) * PI;
65 | stroke(0);
66 |
67 | for(var i = 0; i < numberOfRays; i++) {
68 |
69 | // make sure angle is between 0 and 2PI rads
70 | var newAngle = initialAngle + (radianStep * i);
71 | if(newAngle < 0) newAngle = (2 * PI) + newAngle;
72 | var distance = castSingleRay(newAngle);
73 | stroke("#FFF1A7");
74 | line(player.x, player.y, player.x + distance[0] * cos(newAngle), player.y + distance[0] * sin(newAngle));
75 |
76 | // render
77 | var z = (distance[0]/10);
78 | var z = z * cos(player.dir - newAngle);
79 | var wallHeight = 240/z;
80 | if(wallHeight > 240) wallHeight = 240;
81 |
82 | fill("#01A1A1");
83 | stroke("#01A1A1");
84 | if(distance[1] == 0 || distance[1] == 1) {
85 | fill("#016666");
86 | stroke("#016666");
87 | }
88 | rect(320 + i * resolution, 120-(0.5 * wallHeight), resolution, wallHeight);
89 | }
90 | }
91 |
92 | function castSingleRay(angle) {
93 | // Moving right/left? up/down? Determined by
94 | // which quadrant the angle is in
95 | var right = angle > (3 * PI / 2) || (angle >= 0 && angle < PI / 2);
96 | var up = angle > PI && angle < 2 * PI;
97 |
98 | // HORIZONTAL WALL COLLISIONS
99 | var hCol = horizontalCollision(up, angle);
100 | var vCol = verticalCollision(right, angle);
101 | return (hCol[0] < vCol[0]) ? hCol : vCol;
102 | }
103 |
104 | function horizontalCollision(up, angle) {
105 | var x = floor(player.gridX);
106 | var y = up ? floor(player.gridY) : floor(player.gridY) + 1;
107 | var aY = y * minimap.scale;
108 | var aX = player.x - (player.y - aY) / tan(angle);
109 | var dX = minimap.scale / tan(angle);
110 | var dY = minimap.scale;
111 | var distance = [1000, 0]; // arbitrary large number
112 | var i = 0;
113 |
114 | // moving up
115 | if(up) {
116 | // while inside the map
117 | while((aY - i * dY) >= minimap.y) {
118 | // cordinate positions
119 | var posX = aX - i * dX;
120 | var posY = aY - i * dY;
121 | // grid positions
122 | var gX = floor(posX / minimap.scale);
123 | var gY = (posY/ minimap.scale) - 1;
124 |
125 | if((gX < mapWidth) && (gY >= 0) && worldMap[gY][gX] != 0) {
126 | distance = [dist(player.x, player.y, posX, posY), 0];
127 | break;
128 | }
129 | i++;
130 | }
131 | // moving down
132 | } else {
133 | while((aY + i * dY) <= minimap.y + mapHeight * minimap.scale) {
134 | var posX = aX + i * dX;
135 | var posY = aY + i * dY;
136 | var gX = floor(posX / minimap.scale);
137 | var gY = (posY)/ minimap.scale;
138 |
139 | if((gX < mapWidth) && (gY >= 0) && worldMap[gY][gX] != 0) {
140 | distance = [dist(player.x, player.y, posX, posY), 1];
141 | break;
142 | }
143 | i++;
144 | }
145 | }
146 | return distance;
147 | }
148 |
149 | function verticalCollision(right, angle) {
150 | var x = right ? floor(player.gridX) + 1: floor(player.gridX);
151 | var y = floor(player.gridY);
152 | var aX = x * minimap.scale;
153 | var aY = player.y + (aX - player.x) * tan(angle);
154 | var dY = minimap.scale * tan(angle);
155 | var dX = minimap.scale;
156 |
157 | var distance = [1000, 0]; // arbitrary large number
158 | var i = 0;
159 | fill(255, 0, 255);
160 |
161 | // moving right
162 | if(right) {
163 | // while inside the map
164 | while((aX + i * dX) <= minimap.x + mapWidth * minimap.scale) {
165 | // cordinate positions
166 | var posX = aX + i * dX;
167 | var posY = aY + i * dY;
168 | // grid positions
169 | var gX = posX / minimap.scale;
170 | var gY = floor(posY/ minimap.scale);
171 |
172 | if((gX < mapWidth) && (gY >= 0) && (gY < mapHeight) && worldMap[gY][gX] != 0) {
173 |
174 | distance = [dist(player.x, player.y, posX, posY), 2];
175 | break;
176 | }
177 | i++;
178 | }
179 | } else {
180 | // while inside the map
181 | while((aX - i * dX) >= minimap.x) {
182 | // cordinate positions
183 | var posX = aX - i * dX;
184 | var posY = aY - i * dY;
185 | // grid positions
186 | var gX = (posX / minimap.scale) - 1;
187 | var gY = floor(posY/ minimap.scale);
188 |
189 | if((gX >= 0) && (gY >= 0) && (gY < mapHeight) && worldMap[gY][gX] != 0) {
190 | distance = [dist(player.x, player.y, posX, posY), 3];
191 | break;
192 | }
193 | i++;
194 | }
195 | }
196 | return distance;
197 | }
198 |
199 | function Player(x, y) {
200 | this.x = x + minimap.x;
201 | this.y = y + minimap.y;
202 | this.gridX = this.x / minimap.scale;
203 | this.gridY = this.y / minimap.scale;
204 | this.dir = 0; // angle
205 | this.rot = 0;
206 | this.speed = 0;
207 | this.moveSpeed = 1;
208 | this.rotSpeed = 2.5 * PI/180;
209 | this.radius = 4;
210 |
211 | this.draw = function() {
212 | var squareSize = (2 * this.radius) * (2 * this.radius);
213 | squareSize /= 2;
214 | squareSize = sqrt(squareSize);
215 | fill(255, 0, 0);
216 | stroke(255, 0, 0);
217 | rectMode(CENTER);
218 | rect(this.x, this.y, squareSize, squareSize);
219 | rectMode(CORNER);
220 | }
221 |
222 | this.update = function() {
223 | // so player angle is always between 0 and 2PI rads
224 | this.dir += (this.rot > 0) ? this.rot * this.rotSpeed : 2 * PI + this.rot * this.rotSpeed;
225 | this.dir %= 2 * PI;
226 | newX = this.x + this.speed * this.moveSpeed * cos(this.dir);
227 | newY = this.y + this.speed * this.moveSpeed * sin(this.dir);
228 |
229 | if(!this.collision(newX, newY)) {
230 | this.x = newX;
231 | this.y = newY;
232 | }
233 |
234 | this.gridX = this.x / minimap.scale;
235 | this.gridY = this.y / minimap.scale;
236 | }
237 |
238 | this.collision = function(newX, newY) {
239 | if(newX - this.radius < minimap.x ||
240 | newX + this.radius > minimap.x + mapWidth * minimap.scale ||
241 | newY - this.radius < minimap.y ||
242 | newY + this.radius > minimap.y + mapHeight * minimap.scale) {
243 | return true;
244 | }
245 | return worldMap[floor((newY - minimap.y) / minimap.scale)][floor((newX - minimap.x) / minimap.scale)] != 0;
246 | }
247 | }
248 |
249 | function Minimap(x, y, scale) {
250 | this.x = x;
251 | this.y = y;
252 | this.scale = scale; // in pixels
253 |
254 | this.draw = function() {
255 | fill(255);
256 | noStroke();
257 | rect(this.x, this.y, mapWidth * this.scale, mapHeight * this.scale);
258 |
259 | fill(200);
260 | for(y = 0; y < mapHeight; y++) {
261 | for(x = 0; x < mapWidth; x++) {
262 | if(worldMap[y][x] != 0) {
263 | rect(this.x + x * this.scale, this.y + y * this.scale, this.scale, this.scale);
264 | }
265 | }
266 | }
267 | }
268 |
269 | this.drawGrid = function() {
270 | stroke(0);
271 | for(x = 0; x <= mapWidth; x++) {
272 | line(this.x + x * this.scale, this.y, this.x + x * this.scale, this.y + mapHeight * this.scale);
273 | }
274 | for(y = 0; y <= mapHeight; y++) {
275 | line(this.x, this.y + y * this.scale, this.x + mapWidth * this.scale, this.y + y * this.scale);
276 | }
277 | }
278 | }
279 |
280 | function keyPressed() {
281 | if(keyCode == UP_ARROW) player.speed = 1;
282 | if(keyCode == DOWN_ARROW) player.speed = -1;
283 | if(keyCode == LEFT_ARROW) player.rot = -1;
284 | if(keyCode == RIGHT_ARROW) player.rot = 1;
285 | }
286 |
287 | function keyReleased() {
288 | if(keyCode == UP_ARROW || keyCode == DOWN_ARROW) player.speed = 0;
289 | if(keyCode == LEFT_ARROW || keyCode == RIGHT_ARROW) player.rot = 0;
290 | }
291 |
292 | window.addEventListener("keydown", function(e) {
293 | if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
294 | e.preventDefault();
295 | }
296 | }, false);
297 |
--------------------------------------------------------------------------------