In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Random Map Test

Get Adobe Flash player
by chimad 26 Apr 2015
    Embed
/**
 * Copyright chimad ( http://wonderfl.net/user/chimad )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/8TuO
 */

package {
    import com.bit101.components.*;
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.events.Event;
    
    public class Main extends Sprite {
        private const WIDTH:int = 64;
        private const HEIGHT:int = 64;
        private const NUM_ROOMS:int = 6;
        
        private var floor:Floor;
        private var monitor:Bitmap;
        private var cmpNumRooms:NumericStepper;
        private var cmpButton:PushButton;
        private var cmpSplittingRate:HRangeSlider;
        
        public function Main() {
            monitor = new Bitmap();
            monitor.scaleX = monitor.scaleY = 6;
            addChild(monitor);
            
            initComponents();
        }
        
        private function initComponents():void {
            var panel:Panel = new Panel(this, 0, 0);
            var height:Number = 0;
            var margin:int = 20;

            cmpNumRooms = new NumericStepper(panel, 0, 0);
            cmpNumRooms.minimum = 0;
            cmpNumRooms.maximum = 16;
            cmpNumRooms.value = 4;
            cmpNumRooms.width = panel.width;
            height += (cmpNumRooms.height + margin);
            
            cmpSplittingRate = new HRangeSlider(panel, 0, height);
            cmpSplittingRate.minimum = 0;
            cmpSplittingRate.lowValue = 0.5;
            cmpSplittingRate.highValue = 0.5;
            cmpSplittingRate.maximum = 1;
            cmpSplittingRate.labelPosition = RangeSlider.BOTTOM;
            cmpSplittingRate.labelPrecision = 1;
            cmpSplittingRate.tick = 0.1;
            cmpSplittingRate.width = panel.width;
            height += (cmpSplittingRate.height + margin);

            cmpButton = new PushButton(panel, 0, height, "Create", onClick);
            height += cmpButton.height;
            
            monitor.x = panel.width;
        }
        
        private function onClick(e:Event):void {
            floor = new Floor(WIDTH, HEIGHT);
            floor.makeRooms(cmpNumRooms.value, new Range(cmpSplittingRate.lowValue, cmpSplittingRate.highValue));
            floor.makeTunnels();
            
            monitor.bitmapData = floor.data;
        }
    }
}

import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;

class Floor {
    public var width:int;
    
    public var height:int;
    
    public var rect:Rectangle;
    
    public var data:BitmapData;
    
    public var rooms:Vector.<Room> = new Vector.<Room>();
    
    // 追加の通路をつなぐ確率
    private var extraTunnelChance:Number = 0.3;
    
    // 空間を分割する際の比率
    private var defaultSplittingRate:Range = new Range(0.5, 0.5);

    // 空間に対する部屋のサイズの割合
    private var roomSizeRate:Range = new Range(0.5, 1.0);
    
    public function Floor(width:int, height:int) {
        this.width = width;
        this.height = height;
        this.rect = new Rectangle(0, 0, width, height);
        this.data = new BitmapData(width, height, false, 0x0);
    }
    
    public function clear():void {
        while (rooms.length > 0)
            rooms.pop();
        data.fillRect(data.rect, 0x0);
    }
    
    public function makeRooms(numRooms:int, splittingRate:Range = null):void {
        if (numRooms < 1)
            return;

        if (splittingRate == null)
            splittingRate = defaultSplittingRate;
        
        //  部屋の元になる矩形を生成する
        var rectangles /*Rectangle*/:Array = splitRectangle(this.rect, numRooms - 1, 0.5, splittingRate, Orientation.AUTO, SplittingMethod.AUTO);
        for each (var rect:Rectangle in rectangles) {
            fixRectangle(rect);
        }
        
        for each (rect in rectangles) {
            var copy:Rectangle = rect.clone();
            
            // 周囲1マスを空ける
            copy.inflate( -1, -1);
            
            // 部屋のサイズと位置を決める
            var width:Number = copy.width * roomSizeRate.randomValue;
            var height:Number = copy.height * roomSizeRate.randomValue;
            var x:Number = rect.x + Math.random() * (copy.width - width);
            var y:Number = rect.y + Math.random() * (copy.height - height);
            
            var roomRect:Rectangle = new Rectangle(x, y, width, height);
            fixRectangle(roomRect);
            
            // 部屋が小さすぎる場合は破棄する(暫定対応)
            if (roomRect.width < 2 || roomRect.height < 2)
                continue;
            
            var room:Room = new Room(roomRect, rect);
            rooms.push(room);
        }
        
        // dataに反映する
        data.fillRect(data.rect, Tile.GAP);
        for each (room in rooms) {
            data.fillRect(room.outerRect, Tile.ROOM_SPACE);
            data.fillRect(room.rect, Tile.ROOM);
        }
    }
    
    public function makeTunnels():void {
        if (rooms.length < 2)
            return;
        
        // 隣接している部屋同士を検出する
        for (var i:int = 0; i < rooms.length; i++) {
            var from:Room = rooms[i];
            for (var j:int = i + 1; j < rooms.length; j++) {
                var to:Room = rooms[j];
                if (from.isAdjacentTo(to)) {
                    from.adjacentRooms.push(to);
                    to.adjacentRooms.push(from);
                }
            }
        }
        
        // メインストリームをつなぐ
        var start:Room = rooms[int(Math.random() * rooms.length)];
        start.isAccessible = true;
        makeTunnelRecursively(start);
        
        // メインストリーム以外の通路をつなぐ
        for each (from in rooms) {
            for each (to in from.adjacentRooms) {
                if (!from.isConnectedTo(to) && Math.random() < extraTunnelChance) {
                    makeTunnel(from, to);
                }
            }
        }
        
        // データに反映する
        while (Ant.track.length > 0) {
            var p:Point = Ant.track.pop();
            data.setPixel(p.x, p.y, Tile.TRACK);
        }
        data.threshold(data, data.rect, new Point(), "==", 0xFF << 24 | Tile.TRACK, 0xFF << 24 | Tile.TUNNEL);
        // data.threshold(data, data.rect, new Point(), "==", 0xFF << 24 | Tile.GAP, 0xFF << 24 | Tile.ROOM_SPACE);
    }
    
    private function makeTunnelRecursively(from:Room):void {
        var nexts:Vector.<Room> = new Vector.<Room>();
        for each (var to:Room in from.adjacentRooms) {
            if (!to.isAccessible) {
                makeTunnel(from, to);
                to.isAccessible = true;
                nexts.push(to);
            }
        }
        for each (var next:Room in nexts) {
            makeTunnelRecursively(next);
        }
    }
    
    private function makeTunnel(from:Room, to:Room):void {
        var direction:int = from.getIntersectingDirection(to);
        
        if (direction == Direction.NONE)
            return;
        
        var pointFrom:Point = getRandomPoint(from.rect);
        var pointTo:Point = getRandomPoint(to.rect);
        var ant:Ant = new Ant(pointFrom, data);
        var tile:uint;
        
        // 空間と空間のすき間まで進む
        do {
            tile = ant.walk(direction, true);
        } while (tile != Tile.GAP);
        
        if (direction == Direction.UP || direction == Direction.DOWN) {
            // X座標を合わせる
            var dir:int = (pointTo.x > pointFrom.x) ? Direction.RIGHT : Direction.LEFT;
            while (ant.x != pointTo.x)
                ant.walk(dir, false);
        }
        
        if (direction == Direction.LEFT || direction == Direction.RIGHT) {
            // Y座標を合わせる
            dir = (pointTo.y > pointFrom.y) ? Direction.DOWN : Direction.UP;
            while (ant.y != pointTo.y)
                ant.walk(dir, false);
        }

        // 部屋に出るまで進む
        do {
            tile = ant.walk(direction, true);
        } while (tile != Tile.ROOM);
        
        from.connectedRooms.push(to);
        to.connectedRooms.push(from);
    }
}

class Ant extends Point {
    private var data:BitmapData;
    
    public static var track:Vector.<Point> = new Vector.<Point>();
    
    public function Ant(point:Point, data:BitmapData) {
        setTo(point.x, point.y);
        this.data = data;
    }
    
    public function walk(direction:int, joiningExistingTrack:Boolean = true):uint {
        if (direction == Direction.NONE)
            return data.getPixel(x,y);
        
        var v:Point = directionToPoint(direction);
        x += v.x;
        y += v.y;
        var tile:uint = data.getPixel(x, y);
        if (tile == Tile.ROOM_SPACE)
            data.setPixel(x, y, Tile.TRACK);
        if (tile == Tile.GAP)
            track.push(new Point(x, y));

        return tile;
    }
    
    private static function directionToPoint(direction:int):Point {
        var r:int = direction % 3;
        var x:int = (r == 1) ? -1 : (r == 2) ? 0 : 1;
        var q:int = (direction - 1) / 3;
        var y:int = (q == 0) ? 1 : (q == 1) ? 0 : -1;
        return new Point(x, y);
    }
}

class Tile {
    public static const ROOM:uint = 0xFFFFFF;
    public static const ROOM_SPACE:uint = 0x7F7F7F;
    public static const TUNNEL:uint = 0xC0C0C0;
    public static const GAP:uint = 0x0000FF;
    public static const TRACK:uint = 0x00FF00;
}

class Room {
    public var rect:Rectangle;
    
    public var outerRect:Rectangle;
    
    private var outerRectInflated:Rectangle;
    
    public var adjacentRooms:Vector.<Room> = new Vector.<Room>();
    
    public var connectedRooms:Vector.<Room> = new Vector.<Room>();
    
    public var isAccessible:Boolean = false;
    
    public function isConnectedTo(room:Room):Boolean {
        return connectedRooms.indexOf(room) > -1;
    }
    
    public function Room(rect:Rectangle, outerRect:Rectangle) {
        this.rect = rect;
        this.outerRect = outerRect;
        
        this.outerRectInflated = outerRect.clone();
        this.outerRectInflated.inflate(1, 1);
    }
    
    public function isAdjacentTo(room:Room):Boolean {
        var intersection:Rectangle = outerRectInflated.intersection(room.outerRectInflated);
        // 面積2未満の交差は交差していると見なさない
        return (intersection.width * intersection.height >= 2);
    }
    
    // 部屋と部屋の重なっている方向を取得する
    public function getIntersectOrientation(room:Room):String {
        var intersection:Rectangle = outerRectInflated.intersection(room.outerRectInflated);
        if (intersection.width > intersection.height) {
            return Orientation.HORIZONTAL;
        } else {
            return Orientation.VERTICAL;
        }
    }
    
    public function getIntersectingDirection(room:Room):int {
        var intersection:Rectangle = outerRectInflated.intersection(room.outerRectInflated);
        if (intersection.width * intersection.height < 2) {
            // 面積2未満の交差は交差していると見なさない
            return Direction.NONE;
        }
        if (intersection.width > intersection.height) {
            // 水平方向(上下)の交差
            if (this.rect.y < intersection.y) {
                return Direction.DOWN;
            } else {
                return Direction.UP;
            }
        } else if (intersection.width == intersection.height) {
            // 斜め方向の交差?
            return Direction.NONE;
        } else {
            // 垂直方向(左右)の交差
            if (this.rect.x < intersection.x) {
                return Direction.RIGHT;
            } else {
                return Direction.LEFT;
            }
        }
    }
}

function splitRectangle(src:Rectangle, time:int = 1, ratio:Number = 0.5, randomRatio:Range = null, initialOrientation:String = Orientation.AUTO, splittingMethod:String = SplittingMethod.AUTO):Array {
    var rect:Rectangle = src.clone();
    var result:Array = [ rect ];
    var orientation:String = initialOrientation;
    var tinyNumber:Number = Math.pow(2, -8);
    
    if (time < 1 || ratio >= 1)
        return result;
    
    for (var i:int = 0; i < time; i++) {
        if (randomRatio != null)
            ratio = randomRatio.randomValue;
        
        if (orientation == Orientation.AUTO)
            orientation = (rect.width >= rect.height) ? Orientation.VERTICAL : Orientation.HORIZONTAL;
        
        var rectB:Rectangle = rect.clone();
        if (orientation == Orientation.VERTICAL) {
            rect.width *= ratio;
            rectB.x += rect.width;
            rectB.width *= (1 - ratio);
            // 1マス空ける用の補正
            if (rect.width % 1 == 0)
                rect.width -= tinyNumber;
        } else if (orientation == Orientation.HORIZONTAL) {
            rect.height *= ratio;
            rectB.y += rect.height;
            rectB.height *= (1 - ratio);
            // 1マス空ける用の補正
            if (rect.height % 1 == 0)
                rect.height -= tinyNumber;
        }
        // 二周目以降は長い方を分割していく
        orientation = Orientation.AUTO;
        
        if (splittingMethod == SplittingMethod.AUTO) {
            // 面積順にソートし、一番大きいものを分割対象とする
            var copy:Array = result.concat();
            copy.sort(compareRectangle, Array.DESCENDING);
            rect = copy[0];
        } else {
            rect = rectB;
        }
        
        result.push(rectB);
    }
    
    return result;
}

function compareRectangle(a:Rectangle, b:Rectangle):int {
    return (a.width * a.height) - (b.width * b.height);
}

class SplittingMethod {
    // 最も大きい矩形を優先的に分割する
    public static const AUTO:String = "auto";
    // 分割した片割れ(右側・下側)を連続的に分割する
    public static const SEQUENTIAL:String = "sequential";
}

function fixRectangle(rect:Rectangle):void {
    var diffX:Number, diffY:Number;
    
    diffX = Math.ceil(rect.x) - rect.x;
    diffY = Math.ceil(rect.y) - rect.y;
    rect.x = Math.ceil(rect.x);
    rect.width = Math.floor(rect.width - diffX);
    rect.y = Math.ceil(rect.y);
    rect.height = Math.floor(rect.height - diffY);
}

function getRandomPoint(rect:Rectangle, flooring:Boolean = true):Point {
    var x:Number = Math.random() * rect.width + rect.x;
    var y:Number = Math.random() * rect.height + rect.y;
    if (flooring) {
        x = Math.floor(x);
        y = Math.floor(y);
    }
    return new Point(x, y);
}

class Orientation {
    public static const AUTO:String = "auto";
    public static const VERTICAL:String = "v";
    public static const HORIZONTAL:String = "h";
}

class Direction {
    public static const DOWN:int = 2;
    public static const LEFT:int = 4;
    public static const NONE:int = 5;
    public static const RIGHT:int = 6;
    public static const UP:int = 8;
}

class Range {
    private var _min:Number = Number.MIN_VALUE;
    
    public function get min():Number {
        return _min;
    }
    
    public function set min(value:Number):void {
        if (value > max)
            value = max;
        _min = value;
    }
    
    private var _max:Number = Number.MAX_VALUE;
    
    public function get max():Number {
        return _max;
    }
    
    public function set max(value:Number):void {
        if (value < min)
            value = min;
        _max = value;
    }
    
    public function get randomValue():Number {
        if (min == max)
            return min;
        else
            return Math.random() * (max - min) + min;
    }
    
    public function Range(min:Number, max:Number) {
        this.min = min;
        this.max = max;
    }
}