Skip to content

Instantly share code, notes, and snippets.

@jah2488
Created April 29, 2014 18:56
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jah2488/11408848 to your computer and use it in GitHub Desktop.
Save jah2488/11408848 to your computer and use it in GitHub Desktop.
A simple raycasting proof of concept I did a few months back in haxe/openfl. Think of the original doom. Its kinda like that.
package;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.Lib;
import flash.ui.Keyboard;
import flash.Vector;
import flash.text.Font;
import flash.text.TextField;
class Point {
public var x:Float;
public var y:Float;
public function new(_x,_y) {
x = _x;
y = _y;
}
}
class Main extends Sprite {
var Destination:Sprite;
var gX = 0;
var pos:Point;
var dir:Point;
var plane:Point;
var posX = 6.0;
var posY = 6.0;
var dirX = -1.0;
var dirY = 0.0;
var planeX = 0.0;
var planeY = 0.66;//0.02;
var worldMap:Array<Array<Int>>;
var maxRenderDistance = 1000;
var viewSprite:Sprite;
var mapWidth:Int;
var mapHeight:Int;
var time:Float;
var oldTime:Float;
var textField:TextField;
var movingUp = false;
var movingDown = false;
var movingLeft = false;
var movingRight = false;
var fired = false;
var jumped = false;
var raisePlaneY = false;
var lowerPlaneY = false;
public function new () {
super();
// Lib.current.stage.color = 0x000000;
pos = new Point( 6.0, 6.0);
dir = new Point(-1.0, 0.0);
plane = new Point( 0.0, 0.66);//FOV
mapWidth = 13;
mapHeight = 13;
worldMap = [[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,5,5,0,0,2,2,0,0,0,1],
[1,0,0,5,5,0,0,2,2,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,4,4,0,0,3,3,0,0,0,1],
[1,0,0,4,4,0,0,3,3,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]];
Destination = new Sprite();
Destination.x = 0;
Destination.y = 0;
Destination.width = stage.stageWidth;
Destination.height = stage.stageHeight;
addChild(Destination);
// Font.registerFont(DefaultFont);
textField = new TextField();
textField.embedFonts = true;
textField.selectable = false;
textField.x = 5;
textField.y = 5;
textField.width = 200;
textField.text = "fps";
addChild(textField);
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
private function onKeyDown(?keyboardEvent:KeyboardEvent):Void {
switch (keyboardEvent.keyCode) {
case Keyboard.SHIFT: fired = true;
case Keyboard.SPACE: jumped = true;
case Keyboard.DOWN: movingDown = true;
case Keyboard.UP: movingUp = true;
case Keyboard.LEFT: movingLeft = true;
case Keyboard.RIGHT: movingRight = true;
case Keyboard.V: lowerPlaneY = true;
case Keyboard.C: raisePlaneY = true;
}
}
private function onKeyUp(?keyboardEvent:KeyboardEvent):Void {
switch (keyboardEvent.keyCode) {
case Keyboard.SHIFT: fired = false;
case Keyboard.SPACE: jumped = false;
case Keyboard.DOWN: movingDown = false;
case Keyboard.UP: movingUp = false;
case Keyboard.LEFT: movingLeft = false;
case Keyboard.RIGHT: movingRight = false;
case Keyboard.V: lowerPlaneY = false;
case Keyboard.C: raisePlaneY = false;
}
}
private function onEnterFrame(?flashEvent:Event):Void {
var delta = Lib.getTimer() - oldTime;
draw();
update(delta);
oldTime = Lib.getTimer();
}
private function draw():Void {
Destination.graphics.clear();
raycast();
}
private function update(delta:Float):Void {
var frameTime = delta / 1000;
var speed = frameTime * 4;
var rotateSpeed = frameTime * 2;
var oldDirX;
var oldPlaneX;
textField.text = 'fps: ${frameTime}';
#if !web
Sys.print('planeX: ${planeX} planeY: ${planeY} X: ${pos.x} dirX: ${dirX} dirY: ${dirY} speed: ${speed} rotate: ${rotateSpeed} LEFT:${movingLeft} RIGHT:${movingRight} UP:${movingUp} DOWN:${movingDown}\r');
#end
if(movingUp) {
if(worldMap[Std.int(pos.x + (dirX * speed) * 2)][Std.int(pos.y)] == 0) { pos.x += dirX * speed; }
if(worldMap[Std.int(pos.x)][Std.int(pos.y + (dirY * speed) * 2)] == 0) { pos.y += dirY * speed; }
}
if(movingDown) {
if(worldMap[Std.int(pos.x - (dirX * speed) * 2)][Std.int(pos.y)] == 0) { pos.x -= dirX * speed; }
if(worldMap[Std.int(pos.x)][Std.int(pos.y - (dirY * speed) * 2 )] == 0) { pos.y -= dirY * speed; }
}
if(fired) {
fired = false;
var tileInFrontOfCamera = worldMap[Std.int(pos.x + dirX * speed)][Std.int(pos.y + dirY * speed)];
if(tileInFrontOfCamera != 1) { worldMap[Std.int(pos.x + dirX * speed)][Std.int(pos.y + dirY * speed)] = 0; }
//create new sprite; Set sprite going in direction.-- Apply 'velocity'
}
if(jumped) {
jumped = false;
//player "height" goes up. shift drawTop/Height down
}
if(movingRight){
oldDirX = dirX;
oldPlaneX = planeX;
dirX = dirX * Math.cos(-rotateSpeed) - dirY * Math.sin(-rotateSpeed);
dirY = oldDirX * Math.sin(-rotateSpeed) + dirY * Math.cos(-rotateSpeed);
planeX = planeX * Math.cos(-rotateSpeed) - planeY * Math.sin(-rotateSpeed);
planeY = oldPlaneX * Math.sin(-rotateSpeed) + planeY * Math.cos(-rotateSpeed);
}
if(movingLeft){
oldDirX = dirX;
oldPlaneX = planeX;
dirX = dirX * Math.cos(rotateSpeed) - dirY * Math.sin(rotateSpeed);
dirY = oldDirX * Math.sin(rotateSpeed) + dirY * Math.cos(rotateSpeed);
planeX = planeX * Math.cos(rotateSpeed) - planeY * Math.sin(rotateSpeed);
planeY = oldPlaneX * Math.sin(rotateSpeed) + planeY * Math.cos(rotateSpeed);
}
if(raisePlaneY && planeY < 2.0) { planeY += 0.001; }
if(lowerPlaneY && planeY > -2.0) { planeY -= 0.001; }
}
private function raycast():Void {
for(x in 0...(stage.stageWidth + 1)) {
var distance = 0.0;
var camX = 2 * x / (stage.stageWidth * 1.0) - 1;
var rayPosX = pos.x;
var rayPosY = pos.y;
var rayDirX = dirX + planeX * camX;
var rayDirY = dirY + planeY * camX;
var mapX = Std.int(rayPosX);
var mapY = Std.int(rayPosY);
var sideDistX:Float = 0.0;
var sideDistY:Float = 0.0;
var deltaDistX = Math.sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
var deltaDistY = Math.sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
var perpWallDist:Float;
// trace('camX: ${camX}\nrayDir x: ${rayDirX} y: ${rayDirY}\nmap x: ${mapX} y: ${mapY}\ndeltaDist x: ${deltaDistX} y: ${deltaDistY}');
var stepX:Int = 0;
var stepY:Int = 0;
var hit = 0;
var side:Int = 0;
if(rayDirX < 0) {
stepX = -1;
sideDistX = (rayPosX - mapX) * deltaDistX;
} else {
stepX = 1;
sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX;
}
if(rayDirY < 0) {
stepY = -1;
sideDistY = (rayPosY - mapY) * deltaDistY;
} else {
stepY = 1;
sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY;
}
while(hit == 0 && distance < maxRenderDistance) {
if(sideDistX < sideDistY) {
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
} else {
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
if(worldMap[mapX][mapY] > 0) hit = 1;
var distX = pos.x - mapX;
var distY = pos.y - mapY;
distance = Math.sqrt(distX * distX + distY * distY);
// Sys.print('#(hit: ${hit})=> Distance: ${StringTools.rpad(Std.string(Math.fround(distance)), "0", 2)} [sideDist: x ${StringTools.rpad(Std.string(Math.fround(sideDistX)), "0", 3)} y ${StringTools.rpad(Std.string(Math.fround(sideDistY)), "0", 3)}]{ mapX: ${StringTools.rpad(Std.string(mapX), " ", 2)}, mapY ${StringTools.rpad(Std.string(mapY), " ", 2)} }[stepX: ${stepX}][stepY: ${stepY}][pos.x ${pos.x}][pos.y ${pos.y}]\r');
}
if(side == 0) {
perpWallDist = Math.abs((mapX - rayPosX + (1 - stepX) / 2) / rayDirX);//calcPerpWallDist(mapX, rayPosX, stepX, rayDirX);
} else {
perpWallDist = Math.abs((mapY - rayPosY + (1 - stepY) / 2) / rayDirY);//calcPerpWallDist(mapY, rayPosY, stepY, rayDirY);
}
var lineHeight = Std.int(Math.abs(stage.stageHeight / perpWallDist));
var drawStart = -lineHeight / 2 + stage.stageHeight / 2;
var drawEnd = lineHeight / 2 + stage.stageHeight / 2;
if(drawStart < 0) drawStart = 0;
if(drawEnd >= stage.stageHeight) drawEnd = stage.stageHeight - 1;
var color = 0x333333;
switch (worldMap[mapX][mapY]) {
case 1: color = 0x333333;
case 2: color = 0xFF0000;
case 3: color = 0x00FF00;
case 4: color = 0x0000FF;
case 5: color = 0xFFFF00;
}
if(side == 1) {
color = (color & 0xfefefe) >> 1;
}
//Draw Walls
Destination.graphics.lineStyle(1, color);
Destination.graphics.moveTo(x, drawStart);
Destination.graphics.lineTo(x, drawEnd);
//Draw Ceiling
Destination.graphics.lineStyle(1, 0xDDDDDD);
Destination.graphics.moveTo(x, 0);
Destination.graphics.lineTo(x, drawStart);
//Draw Floor
Destination.graphics.lineStyle(1, 0xAAAAAA);
Destination.graphics.moveTo(x, drawEnd);
Destination.graphics.lineTo(x, stage.stageHeight);
//Draw Direction Indecator
Destination.graphics.lineStyle(3, 0xFF00FF);
Destination.graphics.moveTo(posX + dirX * 4, posY + dirY * 4);
Destination.graphics.lineTo(posX + dirX * 4, posY + dirY);
}
}
private inline function calculateStepAndSideDistance(rayDir:Float, step:Float, sideDist:Float, rayPos:Float, map:Float, deltaDist:Float):Void {
if(rayDir < 0) {
step = -1;
sideDist = (rayPos - map) * deltaDist;
} else {
step = 1;
sideDist = (map + 1.0 - rayPos) * deltaDist;
}
}
private inline function digitalDifferentialAnalysisAdjustment(sideDist:Float, deltaDist:Float, map:Float, step:Float, side:Int, sideMod:Int):Void {
sideDist += deltaDist;
map += step;
side = sideMod;
}
private inline function calcPerpWallDist(map:Float, rayPos:Float, step:Float, rayDir:Float):Float {
return Math.abs((map - rayPos + (1 - step) / 2) / rayDir);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment