flash on 2015-7-5

by insocium
雲海のテスト

クリックでカメラを切り替えられます(操作はできません)

BitmapData.draw()を毎フレーム何十回もやってるので結構重いです

雲の数を200~300にするといい感じに雲海になるんですが重すぎて断念

(追加修正)

・カメラを少し調整しました

・初期化時の雲画像生成が重いのでプログレスバーをつけました

・ごく稀に穴だらけの雲が出来るバグを修正

・低画質で遠景画像を傾けた時のぶれを修正

・穴だらけのバグが直ってなかったのでさらに修正(検証→http://wonderfl.net/c/d01w
♥0 | Line 383 | Modified 2015-07-05 01:14:24 | MIT License
play

ActionScript3 source code

/**
 * Copyright insocium ( http://wonderfl.net/user/insocium )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/4YZ2
 */

// forked from hacker_16h9fw4j's forked from: Sea of Clouds

// forked from tencho's Sea of Clouds

/**

 * 雲海のテスト

 * クリックでカメラを切り替えられます(操作はできません)

 * BitmapData.draw()を毎フレーム何十回もやってるので結構重いです

 * 雲の数を200~300にするといい感じに雲海になるんですが重すぎて断念

 * 

 * (追加修正)

 * ・カメラを少し調整しました

 * ・初期化時の雲画像生成が重いのでプログレスバーをつけました

 * ・ごく稀に穴だらけの雲が出来るバグを修正

 * ・低画質で遠景画像を傾けた時のぶれを修正

 * ・穴だらけのバグが直ってなかったのでさらに修正(検証→http://wonderfl.net/c/d01w)

 */

package  {

    import flash.display.Bitmap;

    import flash.display.BitmapData;

    import flash.display.Sprite;

    import flash.events.Event;

    import flash.events.MouseEvent;

    import flash.filters.GlowFilter;

    import flash.geom.ColorTransform;

    import flash.geom.Matrix;

    import flash.geom.Point;

    import flash.utils.getTimer;

    

    import org.papervision3d.core.geom.renderables.Vertex3D;

    import org.papervision3d.core.math.Number3D;

    import org.papervision3d.materials.ColorMaterial;

    import org.papervision3d.materials.utils.MaterialsList;

    import org.papervision3d.objects.DisplayObject3D;

    import org.papervision3d.objects.primitives.Cube;

    import org.papervision3d.objects.primitives.Plane;

    import org.papervision3d.Papervision3D;

    import org.papervision3d.view.BasicView;

    [SWF(width = "1200", height = "465", frameRate = "30", backgroundColor = "#000000")]

    public class Cloud extends Sprite {

        private var PI:Number;

        private var time:int = 0;

        //雲の数

        private var cloudNum:int = 100;

        private var loading:LoadingScene;

        private var loadCount:int = 0;

        private var bv:BasicView;

        private var cameraTarget:DisplayObject3D;

        private var horizonTarget:DisplayObject3D;

        private var plane:DisplayObject3D;

        private var smokes:Vector.<Smoke>;

        private var cameraRotation:Number = 0;

        private var cameraAngle:Number = 100;

        private var cameraRoll:Number = 0;

        private var cameraDist:Number = 500;

        private var campus1:BitmapData;

        private var campus2:BitmapData;

        private var farCloud:Sprite;

        private var bg:BackGround;

        private var sea:Sprite;

        private var cameraMode:int = 0;

        private var planeMaterial:ColorMaterial;

        private var wingMaterial:ColorMaterial;

        private var startTime:int;

        //コンストラクタ

        public function Cloud() {

            Wonderfl.capture_delay(7);

            Param.init();

            PI = Math.PI / 180;

            stage.frameRate = 60;

            stage.quality = "low";

            Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger);

            bv = new BasicView(Param.display.width, Param.display.height, false, false, "Free");

            planeMaterial = new ColorMaterial(Param.planeColor, 1);

            wingMaterial = new ColorMaterial(Param.planeColor, 1);

            wingMaterial.doubleSided = true;

            cameraTarget = bv.scene.addChild(new DisplayObject3D());

            horizonTarget = bv.scene.addChild(new DisplayObject3D());

            horizonTarget.autoCalcScreenCoords = true;

            addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [Param.skyColor1], [1]));

            sea = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [Param.seaColor], [1])) as Sprite;

            bg = addChild(new BackGround()) as BackGround;

            bg.x = Param.display.width / 2;

            bg.mask = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [0x000000], [1]));

            campus1 = new BitmapData(Param.display.width, Param.display.height, true, 0x00FFFFFF);

            campus2 = new BitmapData(Param.display.width, Param.display.height, true, 0x00FFFFFF);

            addChild(new Bitmap(campus1));

            addChild(bv.viewport);

            addChild(new Bitmap(campus2));

            

            //雲生成

            smokes = new Vector.<Smoke>();

            for (var i:int = 0; i < cloudNum; i++) {

                var smoke:Smoke = new Smoke(bv.scene);

                smokes.push(smoke);

                smoke.setPosition(Math.random() * 3500 - 1750, Math.random() * -300 + 10000, Math.random() * 4000 - 2000);

            }

            plane = bv.scene.addChild(createAirPlain());

            plane.rotationY = -90;

            plane.y = 10000;

            plane.scale = 0.5;

            plane.autoCalcScreenCoords = true;

           // bv.viewport.filters = [new GlowFilter(Param.outlineColor, 1, 3, 3, 6, 1)];

            //

            loading = addChild(new LoadingScene()) as LoadingScene;

            addEventListener(Event.ENTER_FRAME, onLoading);

        }

        private function onClick(e:MouseEvent):void {

            cameraMode = (cameraMode + 1) % 5;

        }

        //画像生成中

        private function onLoading(...arg):void {

            for (var i:int = 0; i < 3; i++) {

                if(loadCount >= smokes.length){

                    removeEventListener(Event.ENTER_FRAME, onLoading);

                    start()

                    break;

                }

                var smoke:Smoke = smokes[loadCount];

                smoke.changeImage();

                loadCount++;

                loading.setPercentage(loadCount / smokes.length);

            }

        }

        //レンダリング開始

        private function start():void {

            stage.addEventListener(MouseEvent.CLICK, onClick);

            startTime = getTimer();

            removeChild(loading);

            addEventListener(Event.ENTER_FRAME, onEnter);

            onEnter();

        }

        //毎フレーム処理

        private function onEnter(...arg):void {

            time = getTimer() - startTime + 7000;

            plane.rotationZ = Math.sin(PI * time / 150) * 30;

            var area:int = 2000;

            //雲を動かす

            for each(var sm:Smoke in smokes) {

                sm.do3d.z = ((sm.startPosition.z + time * 0.42) + area) % (area * 2) - area;

                var loop:int = int(((sm.startPosition.z + time * 0.42) + area) / (area * 2));

                if (sm.loop != loop) {

                    sm.loop = loop;

                    sm.do3d.x = Math.random() * 3500 - 1750;

                    sm.do3d.y = Math.random() * -300 + 10000;

                }

            }

            //カメラ移動

            switch(cameraMode) {

                case 0:

                    cameraDist = Math.sin(PI * time / 80) * 200 + 600;

                    cameraRotation = time / 1000 * 60 * 0.2;

                    cameraRoll = Math.sin(PI * time / 50) * 20;

                    cameraAngle = Math.sin(PI * time / 100) * 100 + 50;

                    cameraTarget.position = new Number3D(0, 10000, 0);

                    break;

                case 1:

                    cameraDist = 280;

                    cameraRotation = Math.sin(PI * time / 110) * 20 + 90;

                    cameraRoll = -plane.rotationZ;

                    cameraAngle = Math.sin(PI * time / 90) * 50 + 30;

                    cameraTarget.position = new Number3D(0, 10000, -10000);

                    break;

                case 2:

                    cameraDist = 500;

                    cameraRotation = Math.sin(PI * time / 110) * 10;

                    cameraRoll = 0;

                    cameraAngle = Math.sin(PI * -time / 70) * 200;

                    cameraTarget.position = new Number3D(0, 10000, 0);

                    break;

                case 3:

                    cameraDist = 800;

                    cameraRotation = -time / 200;

                    cameraRoll = 0;

                    cameraAngle = -400;

                    cameraTarget.position = new Number3D(0, 10000, 0);

                    break;

                case 4:

                    cameraDist = 50;

                    cameraRotation = 90;

                    cameraRoll = -plane.rotationZ/2;

                    cameraAngle = 30 + plane.rotationZ;

                    cameraTarget.position = new Number3D(1000, 10000, -700);

                    break;

            }

            bv.camera.position = new Number3D(Math.cos(PI*cameraRotation)*cameraDist, cameraAngle+10000, Math.sin(PI*cameraRotation)*cameraDist);

            bv.camera.lookAt(cameraTarget);

            bv.camera.roll(cameraRoll);

            var crot:Number = Math.atan2(cameraTarget.z - bv.camera.z, cameraTarget.x - bv.camera.x);

            //地平線用ダミーモデルをカメラの前方に配置

            horizonTarget.position = new Number3D(Math.cos(crot) * 500000, 0, Math.sin(crot) * 500000);

            //PV3Dレンダリング

            bv.renderer.renderScene(bv.scene, bv.camera, bv.viewport);

            //背景画像を地平線の高さに移動

            bg.y = int(horizonTarget.screen.y + Param.center.y);

            bg.rotation = cameraRoll;

            bg.setPositionPer(crot / PI / 360);

            sea.visible = horizonTarget.screen.y + Param.center.y < Param.display.height;

            //雲画像処理

            campus1.fillRect(Param.display, 0x00FFFFFF);

            campus2.fillRect(Param.display, 0x00FFFFFF);

            smokes.sort(function(a:Smoke, b:Smoke):Number { return int(a.scale > b.scale) - int(a.scale < b.scale); } );

            for each(var smoke:Smoke in smokes) {

                smoke.refresh();

                if (smoke.do3d.screen.z > 10 && smoke.alpha) {

                    var target:BitmapData = (plane.screen.z < smoke.do3d.screen.z)? campus1 : campus2;

                    var ct:ColorTransform = new ColorTransform(1, 1, 1, smoke.alpha, 0, 0, 0, 0);

                    var mat:Matrix = new Matrix();

                    mat.scale(smoke.scale, smoke.scale);

                    mat.rotate(PI * bv.camera.localRotationZ);

                    var offsetX:Number = smoke.scale * smoke.imageSize.width / 2;

                    var offsetY:Number = smoke.scale * smoke.imageSize.height / 2;

                    var xp:Number = smoke.do3d.screen.x + Param.center.x - (Math.cos(PI * cameraRoll) * offsetX - Math.sin(PI * cameraRoll) * offsetY);

                    var yp:Number = smoke.do3d.screen.y + Param.center.y - (Math.sin(PI * cameraRoll) * offsetX + Math.cos(PI * cameraRoll) * offsetY);

                    mat.translate(xp, yp);

                    target.draw(smoke.image, mat, ct);

                }

            }

        }

        //航空機モデルを生成

        private function createAirPlain():DisplayObject3D {

            var i:int;

            var plane:DisplayObject3D = new DisplayObject3D();

            var d:DisplayObject3D = plane.addChild(new DisplayObject3D());

            var body:Plane = new Plane(planeMaterial, 446, 46, 3, 2);//23

            for (i = 0; i <= 2; i++) body.geometry.vertices[i].y = [-12, -6, 0][i];

            for (i = 3; i <= 5; i++) body.geometry.vertices[i].x = -178;

            for (i = 6; i <= 8; i++) body.geometry.vertices[i].x = 77;

            for (i = 9; i <= 11; i++) body.geometry.vertices[i].y = [7, 15, 23][i-9];

            for (i = 4; i <= 7; i += 3) body.geometry.vertices[i].z = -22;

            var wing:Plane = new Plane(wingMaterial, 190, 96, 2, 1);

            for (i = 0; i <= 5; i++) wing.geometry.vertices[i].x = [0, 64, 190][int(i / 2)];

            for (i = 0; i <= 5; i++) wing.geometry.vertices[i].y = [0, 96, 53, 117, 165, 190][i];

            for (i = 2; i <= 5; i++) wing.geometry.vertices[i].z = [ -5, -12][int((i - 2) / 2)];

            wing.position = new Number3D( -86, -10, -10);

            wing.rotationZ = -90;

            wing.rotationY = 90;

            var wing2:Plane = new Plane(wingMaterial, 66, 72, 1, 1);

            for (i = 0; i <= 3; i++) {

                wing2.geometry.vertices[i].x = [0, 0, 72, 72][i];

                wing2.geometry.vertices[i].y = [0, 66, 72, 100][i];

            }

            wing2.rotationZ = -90;

            wing2.rotationY = 90;

            wing2.position = new Number3D(148,10,0);

            var wing3:Plane = new Plane(wingMaterial, 82, 64, 1, 1);

            for (i = 0; i <= 3; i++) {

                wing3.geometry.vertices[i].x = [0, 82, 82, 110][i];

                wing3.geometry.vertices[i].y = [0, 0, 64, 64][i];

            }

            wing3.position = new Number3D(130, 21, 0);

            d.addChild(body);

            d.addChild(wing);

            d.addChild(wing2);

            var engine1:Cube = d.addChild(new Cube(new MaterialsList( { all:planeMaterial } ), 48, 16, 16, 1, 1, 1)) as Cube;

            var engine2:Cube = d.addChild(new Cube(new MaterialsList( { all:planeMaterial } ), 48, 16, 16, 1, 1, 1)) as Cube;

            engine1.position = new Number3D(-20, -15, 64);

            engine2.position = new Number3D(40, -15, 137);

            engine1.rotationX = engine2.rotationX = 45;

            plane.addChild(wing3);

            var frip:DisplayObject3D = plane.addChild(d.clone());

            frip.scaleZ *= -1;

            for each(var model:DisplayObject3D in frip.children) model.geometry.flipFaces();

            return plane;

        }

    }

}

import flash.display.Bitmap;

import flash.display.BitmapData;

import flash.display.Sprite;

import flash.display.Stage;

import flash.events.Event;

import flash.filters.BevelFilter;

import flash.filters.BlurFilter;

import flash.filters.ColorMatrixFilter;

import flash.filters.GlowFilter;

import flash.geom.Matrix;

import flash.geom.Point;

import flash.geom.Rectangle;

import org.papervision3d.core.math.Number3D;

import org.papervision3d.objects.DisplayObject3D;

import org.papervision3d.scenes.Scene3D;

//各種パラメータ

class Param {

    static public var center:Point;

    static public var display:Rectangle;

    //航空機の色

    static public var planeColor:uint = 0xeeeeff;

    //航空機のアウトライン

    

    //雲の色

    static public var cloudColor:uint = 0xFFD7DDE5;

    //雲のハイライト色

    static public var lightColor:uint = 0xFFFFFF;

    //雲の影の色

    static public var shadowColor:uint = 0x294A59;

    //空の色(上)

    static public var skyColor1:uint = 0x1F437F;

    //空の色(下)

    static public var skyColor2:uint = 0x6290C1;

    //地平線付近の色

    static public var horizonColor:uint = 0xA0BCD1;

    //地上の色

    static public var seaColor:uint = 0x7D8C98;

    public function Param() { }

    static public function init():void {

            display = new Rectangle(0, 0, 1200, 465);

            center = new Point(display.width / 2, display.height / 2);

    }

}

class Painter {

    public function Painter() { }

    static public var errorSeed:Array = [346, 514, 1155, 1519, 1690, 1977, 2327, 2337, 2399, 2860, 2999, 3099, 4777, 4952, 5673, 6265, 7185, 7259, 7371, 7383, 7717, 7847, 8032, 8350, 8676, 8963, 8997, 9080, 9403, 9615, 9685];

    //雲画像生成

    static public function createCloud(width:int, height:int, type:Boolean = true):BitmapData {

        var gradiation:Sprite = new Sprite();

        var drawMatrix:Matrix = new Matrix();

        drawMatrix.createGradientBox(width, height);

        gradiation.graphics.beginGradientFill("radial", [0x000000, 0x000000], [0, 1], [0, 255], drawMatrix);

        gradiation.graphics.drawRect(0, 0, width, height);

        gradiation.graphics.endFill();

        var alphaBmp:BitmapData = new BitmapData(width, height);

        var seed:int = int(Math.random() * 10000);

        //perlinNoise画像の1つのチャンネルにブロック状の穴が発生するランダムシード値があるので危ない値は避ける

        if (errorSeed.indexOf(seed) != -1) seed++;

        alphaBmp.perlinNoise(width / 3, height / 2.5, 6, seed, false, true, 1|2|4, false);

        var zoom:Number = (type)? 1.8 : 1.1;

        var ct:Number = (type)? 1.6 : 0.2;

        var ctMatrix:Array = [ct + 1, 0, 0, 0, -(128 * ct), 0, ct + 1, 0, 0, -(128 * ct), 0, 0, ct + 1, 0, -(128 * ct), 0, 0, 0, 1, 0];

        alphaBmp.draw(gradiation, new Matrix(zoom, 0, 0, zoom, -(zoom - 1) / 2 * width, -(zoom - 1) / 2 * height));

        alphaBmp.applyFilter(alphaBmp, alphaBmp.rect, new Point(), new ColorMatrixFilter(ctMatrix));

        var image:BitmapData = new BitmapData(width, height, true, Param.cloudColor);

        image.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 4, 8);

        image.applyFilter(image, image.rect, new Point(), new GlowFilter(Param.lightColor, 1, 4, 4, 1, 3, true));

        var bsize:Number = Math.min(width, height) / 30;

        image.applyFilter(image, image.rect, new Point(), new BevelFilter(bsize, 45, Param.lightColor, 1, Param.shadowColor, 1, bsize/5, bsize/5, 1, 3));

        alphaBmp.dispose();

        return image;

    }

    //グラデーションスプライト生成

    static public function createGradientRect(w:Number, h:Number, colors:Array, alphas:Array, ratios:Array = null, r:Number = 0):Sprite {

        var rts:Array = new Array();

        if(ratios == null){

            for (var n:int = 0; n < colors.length; n++) rts.push(int(255 * n / (colors.length - 1)));

        } else {

            for (var m:int = 0; m < ratios.length; m++) rts[m] = Math.round(ratios[m] * 255);

        }

        var sp:Sprite = new Sprite();

        var mat:Matrix = new Matrix();

        mat.createGradientBox(w, h, Math.PI / 180 * r, 0, 0);

        if (colors.length == 1 && alphas.length == 1) sp.graphics.beginFill(colors[0], alphas[0]);

        else sp.graphics.beginGradientFill("linear", colors, alphas, rts, mat);

        sp.graphics.drawRect(0, 0, w, h);

        sp.graphics.endFill();

        return sp;

    }

}

//遠景画像

class BackGround extends Sprite {

    private var farWidth:int = 2000;

    private var farHeight:int = 20;

    private var viewWidth:int = 600;

    private var viewImage:BitmapData;

    private var farImage:BitmapData;

    public function BackGround() {

        var sky:Sprite = addChild(Painter.createGradientRect(Param.display.width*2, Param.display.height, [Param.skyColor1, Param.skyColor2, Param.horizonColor], [1, 1, 1], [0, 0.9, 1], 90)) as Sprite;

        sky.x = -Param.display.width;

        sky.y = -Param.display.height;

        var ground:Sprite = addChild(Painter.createGradientRect(Param.display.width*2, Param.display.height, [Param.horizonColor, Param.seaColor], [1, 1], null, 90)) as Sprite;

        ground.y = -1;

        ground.x = -Param.display.width;

        //遠景画像作成

        farImage = new BitmapData(farWidth + viewWidth, farHeight, true, 0x00FFFFFF);

        for (var n:int = 0; n < 40; n++) {

            var cloud:BitmapData = Painter.createCloud(80, 20, true);

            farImage.copyPixels(cloud, cloud.rect, new Point(Math.random() * 1960, farHeight - 20), null, null, true);

        }

        farImage.applyFilter(farImage, farImage.rect, new Point(), new BlurFilter(2, 2, 1));

        farImage.copyPixels(farImage, new Rectangle(0, 0, viewWidth, farHeight), new Point(farWidth, 0));

        viewImage = new BitmapData(viewWidth, farHeight, true, 0x88FFFFFF);

        var img:Bitmap = addChild(new Bitmap(viewImage)) as Bitmap;

        img.x = -viewWidth / 2;

        img.y = -farHeight;

    }

    public function setPositionPer(per:Number):void {

        per = ((per % 1) +1) % 1;

        var px:int = int(farWidth * (1 - per));

        viewImage.copyPixels(farImage, new Rectangle(px, 0, viewWidth, farHeight), new Point(0, 0));

    }

}

//画像生成中画面

class LoadingScene extends Sprite {

    private var lineWidth:Number = 200;

    private var loadedLine:Sprite;

    public function LoadingScene() {

        var bg:Sprite = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [0x000000], [1])) as Sprite;

        var baseLine:Sprite = addChild(Painter.createGradientRect(lineWidth, 2, [0x444444], [1])) as Sprite;

        loadedLine = addChild(Painter.createGradientRect(lineWidth, 2, [0x7DA3C8], [1])) as Sprite;

        baseLine.x = loadedLine.x = int((Param.display.width - lineWidth) / 2);

        baseLine.y = loadedLine.y = int((Param.display.height - baseLine.height) / 2);

        setPercentage(0);

    }

    public function setPercentage(per:Number):void {

        loadedLine.width = lineWidth * per;

    }

}

//雲

class Smoke {

    public var do3d:DisplayObject3D;

    public var image:BitmapData;

    public var imageSize:Rectangle;

    public var alpha:Number;

    public var scale:Number;

    public var loop:int;

    public var startPosition:Number3D;

    public function Smoke(scene:Scene3D) {

        do3d = scene.addChild(new DisplayObject3D());

        do3d.autoCalcScreenCoords = true;

        do3d.position = new Number3D();

        imageSize  = new Rectangle(0, 0, 400, 200);

    }

    public function changeImage():void {

        image = Painter.createCloud(imageSize.width, imageSize.height, Math.random() > 0.3);

    }

    //雲の初期位置を設定

    public function setPosition(x:Number, y:Number, z:Number):void {

        startPosition = new Number3D(x, y, z);

        do3d.position = startPosition.clone();

    }

    //スケールと透明度を更新

    public function refresh():void {

        scale = 700 / do3d.screen.z;

        var per1:Number = Math.max(0, Math.min(1, do3d.screen.z / 100));

        var per2:Number = Math.max(0, Math.min(1, 1 - (do3d.screen.z - 1500) / 800));

        alpha = per1 * per2 * 2;

    }

}