ShadowCast
面白そうだったので
ただ高速化は意識してないので重い
/**
* Copyright okoi ( http://wonderfl.net/user/okoi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/1dNB
*/
/**
* ShadowCast
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
[SWF(width = "465", height = "465")]
/**
* ...
* @author
*/
public class Main extends Sprite
{
private var _canvas:BitmapData;
private var _lightCanvas:BitmapData;
private var _lightX:Number;
private var _lightY:Number;
private static const LIGHTLENGTH:int = 150;
private static const BLOCKCOLOR:uint = 0xFF000000;
private var _step:int;
private var _walls:/*Wall*/Array;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
graphics.beginFill(0);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill();
_canvas = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0 );
addChild( new Bitmap(_canvas) );
_lightCanvas = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
addChild( new Bitmap(_lightCanvas) );
_lightX = stage.stageWidth / 2;
_lightY = stage.stageHeight / 2;
_step = 0;
_walls = new Array();
addEventListener(Event.ENTER_FRAME, Update);
}
private function Update(e:Event):void
{
var i:int;
_lightX = stage.mouseX;
_lightY = stage.mouseY;
// DrawWall
if ( _step % 10 == 0 ) GenerationWall();
for ( i = _walls.length - 1; i >= 0; i-- )
{
_walls[i].x -= 5;
if ( _walls[i].x < -_walls[i].width ) _walls.splice( i, 1 );
}
_canvas.fillRect(_canvas.rect, 0x0 );
for ( i = 0; i < _walls.length; i++ )
{
_canvas.fillRect( new Rectangle( _walls[i].x, _walls[i].y, _walls[i].width, _walls[i].height ), 0xFF000000 );
}
// ShadowCut
_lightCanvas.fillRect(_lightCanvas.rect, 0x0);
RecursiveCutOutNortheastNorth( 0, 1, 0 );
RecursiveCutOutNortheastEast(0, 1, 0 );
RecursiveCutOutSoutheastEast(0, 1, 0 );
RecursiveCutOutSoutheastSouth(0, 1, 0);
RecursiveCutOutSouthwestSouth(0, 1, 0);
RecursiveCutOutSouthwestWest(0, 1, 0);
RecursiveCutOutNorthwestWest(0, 1, 0);
RecursiveCutOutNorthwestNorth(0, 1, 0 );
_step++;
}
private function GenerationWall() : void
{
var wall:Wall = new Wall();
wall.width = Math.random() * 20 + 10;
wall.height = Math.random() * 20 + 10;
wall.x = stage.stageWidth + wall.width;
wall.y = Math.random() * stage.stageHeight;
_walls[_walls.length] = wall;
}
private function calcSlope( x1:Number, y1:Number, x2:Number, y2:Number ) : Number
{
return (x2 - x1) / (y2 - y1);
}
private function calcInverseSlope( x1:Number, y1:Number, x2:Number, y2:Number ) : Number
{
return 1 / ((x2 - x1) / (y2 - y1));
}
private function RecursiveCutOutNortheastNorth( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sX:int = _lightX + Math.ceil(sSlope * depth);
var eX:int = _lightX + Math.ceil(eSlope * depth);
var currentY:int = _lightY - depth;
if (currentY < 0) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(sX, currentY) == BLOCKCOLOR;
for (var currentX:int = Math.min(sX, _canvas.width); currentX >= eX; --currentX)
{
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = -calcSlope(_lightX, _lightY, (currentX + 0.5), (currentY + 0.5));
RecursiveCutOutNortheastNorth(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = -calcSlope(_lightX, _lightY, (currentX - 0.5), (currentY - 0.5));
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutNortheastNorth(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutNortheastEast( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sY:int = _lightY - Math.ceil(sSlope * depth);
var eY:int = _lightY - Math.ceil(eSlope * depth);
var currentX:int = _lightX + depth;
if (currentX > 465) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(currentX, sY) == BLOCKCOLOR;
for (var currentY:int = Math.max(sY, 0); currentY <= eY; ++currentY) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = -calcInverseSlope((currentX - 0.5), (currentY - 0.5), _lightX, _lightY);
RecursiveCutOutNortheastEast(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = -calcInverseSlope((currentX + 0.5), (currentY + 0.5), _lightX, _lightY);
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutNortheastEast(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutSoutheastEast( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sY:int = _lightY + Math.ceil(sSlope * depth);
var eY:int = _lightY + Math.ceil(eSlope * depth);
var currentX:int = _lightX + depth;
if (currentX > _canvas.width) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(currentX, sY) == BLOCKCOLOR;
for (var currentY:int = Math.min(sY, _canvas.height); currentY >= eY; --currentY) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = calcInverseSlope((currentX - 0.5), (currentY + 0.5), _lightX, _lightY);
RecursiveCutOutSoutheastEast(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = calcInverseSlope((currentX + 0.5), (currentY - 0.5), _lightX, _lightY);
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutSoutheastEast(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutSoutheastSouth( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sX:int = _lightX + Math.ceil(sSlope * depth);
var eX:int = _lightX + Math.ceil(eSlope * depth);
var currentY:int = _lightY + depth;
if (currentY > _canvas.height) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(sX, currentY) == BLOCKCOLOR;
for (var currentX:int = Math.min(sX, _canvas.width); currentX >= eX; --currentX) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = calcSlope(_lightX, _lightY, (currentX + 0.5), (currentY - 0.5));
RecursiveCutOutSoutheastSouth(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = calcSlope(_lightX, _lightY, (currentX - 0.5), (currentY + 0.5));
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutSoutheastSouth(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutSouthwestSouth( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sX:int = _lightX - Math.ceil(sSlope * depth);
var eX:int = _lightX - Math.ceil(eSlope * depth);
var currentY:int = _lightY + depth;
if (currentY > _canvas.height) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(sX, currentY) == BLOCKCOLOR;
for (var currentX:int = Math.max(sX, 0); currentX <= eX; ++currentX) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = -calcSlope(_lightX, _lightY, (currentX - 0.5), (currentY - 0.5));
RecursiveCutOutSouthwestSouth(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = -calcSlope(_lightX, _lightY, (currentX + 0.5), (currentY + 0.5));
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutSouthwestSouth(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutSouthwestWest( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sY:int = _lightY + Math.ceil(sSlope * depth);
var eY:int = _lightY + Math.ceil(eSlope * depth);
var currentX:int = _lightX - depth;
if (currentX < 0) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(currentX, sY) == BLOCKCOLOR;
for (var currentY:int = Math.min(sY, _canvas.height); currentY >= eY; --currentY) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = -calcInverseSlope((currentX + 0.5), (currentY + 0.5), _lightX, _lightY);
RecursiveCutOutSouthwestWest(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = -calcInverseSlope((currentX - 0.5), (currentY - 0.5), _lightX, _lightY);
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutSouthwestWest(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutNorthwestWest( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sY:int = _lightY - Math.ceil(sSlope * depth);
var eY:int = _lightY - Math.ceil(eSlope * depth);
var currentX:int = _lightX - depth;
if (currentX < 0) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(currentX, sY) == BLOCKCOLOR;
for (var currentY:int = Math.max(sY, 0); currentY <= eY; ++currentY) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = calcInverseSlope((currentX + 0.5), (currentY - 0.5), _lightX, _lightY);
RecursiveCutOutNorthwestWest(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = calcInverseSlope((currentX - 0.5), (currentY + 0.5), _lightX, _lightY);
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutNorthwestWest(depth, sSlope, eSlope);
}
}
private function RecursiveCutOutNorthwestNorth( depth:int, sSlope:Number, eSlope:Number ) : void
{
var sX:int = _lightX - Math.ceil(sSlope * depth);
var eX:int = _lightX - Math.ceil(eSlope * depth);
var currentY:int = _lightY - depth;
if (currentY < 0) return;
var isLastCellBlocked:Boolean = _canvas.getPixel32(sX, currentY) == BLOCKCOLOR;
for (var currentX:int = Math.max(sX, 0); currentX <= eX; ++currentX) {
if ( (currentX - _lightX) * (currentX - _lightX) + (currentY - _lightY) * (currentY - _lightY) > LIGHTLENGTH * LIGHTLENGTH ) continue;
if (_canvas.getPixel32(currentX, currentY) == BLOCKCOLOR) {
if (!isLastCellBlocked) {
var newESlope:Number = calcSlope(_lightX, _lightY, (currentX - 0.5), (currentY + 0.5));
// start recursion for the next depth
RecursiveCutOutNorthwestNorth(depth + 1, sSlope, newESlope);
}
isLastCellBlocked = true;
} else {
if (isLastCellBlocked) {
sSlope = calcSlope(_lightX, _lightY, (currentX + 0.5), (currentY - 0.5));
}
_lightCanvas.setPixel32(currentX, currentY, 0xFFFFFFFF);
isLastCellBlocked = false;
}
}
if (!isLastCellBlocked) {
depth++;
RecursiveCutOutNorthwestNorth(depth, sSlope, eSlope);
}
}
}
}
class Wall {
public var x:Number;
public var y:Number;
public var width:Number;
public var height:Number;
public function Wall() {
}
}