Skip to content

Particle System

hika님의 블로그에 파티클에 관련된 글이 하나 올라 왔습니다. 학습과제와 함께… 원래는 눈팅만 하고 도망가려 하였으나 hika님의 도발에 넘어가서 잠깐 시간을 내어 제 나름대로 재구성 해봤습니다. 짧은 시간에 기능만 구현하려다 보니 손발이 오그라드는 코드가 나와버렸습니다. 특히 중력 계산하는 부분은 그냥 산수 입니다;;;;

원 소스에 있는 선택 값과 구간 값 표현하는 표현식 정말 아이디어 인거 같습니다. 저런 표현식을 그냥 블로깅 하면서 만들어 내시다니 차마 따라 갈 수 없는 내공입니다. orz

그럼 먼저 소스 부터 보겠습니다.

package{

    import com.bit101.components.Label;
    import com.bit101.components.Slider;
    import com.greensock.TweenMax;
    import flash.events.Event;

    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    [SWF(backgroundColor="#EEEEEE", frameRate="110", width="500", height="400")]

    public class Main extends Sprite {

        public static var GRAVITY:Number = 100;
        public static var WIND:Number = 0;
        public static var POWER:Number = 0;

        public var duration:Number;
        public var rate:Number;
        public var particle:Class;
        public var lifeTime:Number;
        public var repeat:int;
        public var emitter:Point = new Point;
        public var power:Point = new Point;

        public function Main(){
            initComps();

            duration = 1;
            rate = 30;
            particle = Drop;
            lifeTime = 5;

            stage.addEventListener( MouseEvent.CLICK , run );
        }

        public static function randFloat( $start:Number, $end:Number ):Number{
            return Math.random()*$end + $start;
        }

        private function run( $e:* ):void{
            if ( repeat === 0 && stage.mouseY > 60 ) {
                repeat = rate*duration;
                emitter.x = stage.mouseX;
                emitter.y = stage.mouseY;
                make();
            }
        }

        private function make():void {
            var life:Number = randFloat(lifeTime-1.5, lifeTime);
            var power:Point = new Point(randFloat(-(POWER/2), POWER), randFloat(POWER, POWER));
            var instance:* = new particle(life, power);
            instance.x = emitter.x;
            instance.y = emitter.y;
            addChild(instance);

            if( --repeat ){
                TweenMax.to( {x:0}, duration/rate, {x:1, onComplete:make} );
            }

        }

        private function initComps():void
        {
            _gravitySlider = new Slider(Slider.HORIZONTAL, this, 30, 30, handleGravity);
            _gravitySlider.minimum = 0;
            _gravitySlider.maximum = 200;
            _gravitySlider.value = 100;
            _gravitySlider.mouseEnabled = false;

            _gravityLabel = new Label(this, 28, 13, "Gravity : "+_gravitySlider.value);
            new Label(this, 27, 36, "0");
            new Label(this, 118, 36, "200");

            _windSlider = new Slider(Slider.HORIZONTAL, this, 150, 30, handleWind);
            _windSlider.minimum = -100;
            _windSlider.maximum = 100;
            _windSlider.value = 0;

            _windLabel = new Label(this, 148, 13, "Wind : "+_windSlider.value);
            new Label(this, 140, 36, "-100");
            new Label(this, 243, 36, "100");

            _powerSlider = new Slider(Slider.HORIZONTAL, this, 270, 30, handlePower);
            _powerSlider.minimum = 0;
            _powerSlider.maximum = 200;
            _powerSlider.value = 100;

            _powerLabel = new Label(this, 268, 13, "Power : "+_powerSlider.value);
            new Label(this, 266, 36, "0");
            new Label(this, 360, 36, "200");

            GRAVITY = _gravitySlider.value;
            WIND = _windSlider.value;
            POWER = _powerSlider.value;
        }

        private function handlePower(e:Event):void
        {
            _powerLabel.text = "Power : " + _powerSlider.value;
            POWER = _powerSlider.value;
        }

        private function handleGravity(e:Event):void
        {
            _gravityLabel.text = "Gravity : " + _gravitySlider.value;
            GRAVITY = _gravitySlider.value;
        }

        private function handleWind(e:Event):void
        {
            _windLabel.text = "Wind : " + _windSlider.value;
            WIND = _windSlider.value;
        }

        private var _gravitySlider:Slider;
        private var _gravityLabel:Label;
        private var _windSlider:Slider;
        private var _windLabel:Label;
        private var _powerSlider:Slider;
        private var _powerLabel:Label;
    }
}

import flash.display.DisplayObject;
import flash.display.Shape;
import flash.events.Event;
import flash.geom.Point;

class Drop extends Shape {

    public function Drop($life:Number, $power:Point)
    {
        super();

        if(!_this) _this

        _life = $life;
        _power = $power;
        _elapsed = 0;

        graphics.beginFill( 0x4f9cf2, Main.randFloat(.5, 1) );
        graphics.drawCircle( 0, 0, 3 );

        addEventListener(Event.ADDED_TO_STAGE, handleAddStage);
    }

    private function handleAddStage(e:Event):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, handleAddStage);

        _rate = 1 / stage.frameRate;
        addEventListener(Event.ENTER_FRAME, hanleThis);
    }

    private function hanleThis(e:Event):void
    {
        x += (-_power.x + (Main.WIND * _elapsed)) * _rate;
        y += (-_power.y + (Main.GRAVITY * _elapsed)) * _rate;

        _elapsed += _rate;

        if (_elapsed/_life>0.9) {
            alpha = 1 + (.9 - _elapsed / _life)*10;
        }

        // Destroy
        if (_elapsed > _life) {
            var _this:DisplayObject = e.currentTarget as DisplayObject;
            if (parent.contains(_this))
                _this.removeEventListener(Event.ENTER_FRAME, hanleThis);
                parent.removeChild(_this);
                _this = null;
        }
    }

    private var _this:Drop;
    private var _life:Number;
    private var _elapsed:Number;
    private var _power:Point;
    private var _rate:Number;
}

원 소스에서는 TweenMax 를 이용해서 구현했는데 저는 조금 다르게 걍 enterFrame 으로 구현했습니다. 저렇게 생성되는 instance 에서 enterFramer 을 도는 건 아무래도 성능상의 문제가 좀 있을 것 같기는 합니다. 그래도 객체 스스로 살고 죽고 하니까 만들어만 주면 따로 신경 쓰지 않아도 되서 즐겨 쓰고 있는 방법 입니다. 중력과 바람은 월드에 단 하나만 있으면 되니까 걍 static 으로 고정 했습니다. 결과는 다음 과 같습니다.

중력계산 하는 걸 제대로 수정하고 싶지만 모르니까 패스~ 일단 TweenMax 를 사용 하지 않고 간단한 중력계를 넣었다는 거에 만족 합니다. ㅎㅎㅎ 다만 누군가 파티클을 만들 고 싶으시다면 말성임 없이 StardustParticle 을 사용하라고 추천해 드리고 싶습니다.

2 thoughts on “Particle System”

    1. ㅎㅎ 원래는 중력을 어떻게 적용 해야 할까 연구해보려고 시작했는데 결국 중력 값은 제대로 적용 시키지 못했네요. 수학과 물리는 참 어려운듯… 지금 저 파티클 들이 중력이 있음에도 무중력 처럼 느껴지는 이유는 파티클의 질량이 계산에 포함 되지 않아서 입니다.

Comments are closed.