Created
April 29, 2014 18:56
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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