연기 입자 시뮬레이팅 시스템

By Josh Marinacci, September 22, 2008

이 예제는 연기의 CPU 집약적인 입자 시뮬레이션을 보여줍니다. 응용프로그램은 원하는 이미지를 생성하기 위해서 어떻게 CustomNode를 사용하는지 그리고 감상을 위해서 그것을 JavaFx The stage에 놓아두는지 설명합니다.

코드 이해하기

Particle

이것은 연기 입자의 위치와 크기를 정의하고 게다가 타이머가 시작할때 어떻게 그것 움직이는지를 도와주는 속성을 가진 클래스 Particle를 생성합니다. 바깥의 파일로 부터 이미지가 로드되는 동안을 주목하세요, 이 클래스는 속성에 불투명도를 바인딩하는 방법으로 이미지를 조종합니다. 아래의 코드는 Particle 클래스를 선언합니다.

Source Code
public class Particle extends CustomNode {
    attribute x : Number;
    attribute y : Number;
    attribute vx : Number;
    attribute vy : Number;
    attribute timer : Number;
    attribute acc : Number;
    
    function create(): Node {
        return ImageView {
            transform: [ 
            Translate{ x : bind x, y : bind y } ]
            image : 
                Image { url: "{__DIR__}/../resources/texture.png" }
            opacity: bind timer / 100
        };
    }
 
    function update(): Void {
        timer -= 2.5;
        x += vx;
        y += vy;
        vx += acc;
    }
    
    function isdead(): Boolean {
        return timer <= 0;
    }    
}

Figure 1: Particle.fx Class

CustomCanvas

CustomCanvas 클래스는 맨 먼저 Particle 클래스의 인스턴스를 생성하고 속성으로 유지되는 배열안에서 그것들을 채우는 애니메이션의 바디를 포함하고 있습니다. CustomCanvas 클래스 안에는 두개의 함수가 있습니다. create 함수는 기하학과 마우스 함수로 어플리케이션이 실행되는 동안 변경되지 않은 채로 남아 있는 모든 그래픽의 부분들을 백그라운드 그림을 채웁니다. update 함수는 입자들을 생성합니다. 그것은 프레임에서 연기 입자의 환영을 만들어 내는 프레임까지 입자를 다르게 만드는 값들인 vxvy값들을 초기화 합니다.

The following code declares the CustomCanvas class.

아래 코드는 CustomCanvas클래스를 선언합니다.

Source Code
public class CustomCanvas extends CustomNode {

    private attribute acc : Number;
    private attribute timeline : Timeline;
    private attribute parts : Particle[];
    private attribute random : Random;;    

    function update() : Void {
        insert 
        Particle {
            x : 84
            y : 164
            vx : 0.3 * random.nextGaussian()
            vy : 0.3 * random.nextGaussian() - 1
            timer : 100
            acc : bind acc
        } into parts;
        var i = sizeof parts - 1;
        while( i >= 0 ) {
                parts
            [i.intValue()].update();
            if( parts
            [i.intValue()].isdead()) {
                delete parts[i.intValue()];
            }
            i--;
        }
    }

    public function create(): Node {
        random = new Random();
        timeline = Timeline {
            repeatCount: java.lang.Double.POSITIVE_INFINITY // HACK
            keyFrames : 
                KeyFrame {
                    time : 16.6ms
                    action: 
                        function() {
                            update();
                        }                
                }
        };
        timeline.start();


        return Group {
            content : bind [
                Rectangle {
                    width : 200, height : 200
                    fill : Color.BLACK
                    blocksMouse : true

                    onMouseMoved : 
                        function( 
                        e : MouseEvent ): Void {
                            acc = ( e.getX() - 100 ) / 1000;
                        }
                },
                Line {
                    startX : bind 100 + ( 500 * acc )
                    startY : 50
                    endX : 100
                    endY : 50
                    stroke : Color.WHITE
                },
                Line {
                    startX : bind 100 + ( 500 * acc )
                    startY : 50
                    endX : bind 100 + ( 500 * acc ) - 4 * acc / Math.abs( acc )
                    endY : 48
                    stroke : Color.WHITE
                },
                Line {
                    startX : bind 100 + ( 500 * acc )
                    startY : 50
                    endX : bind 100 + ( 500 * acc ) - 4 * acc / Math.abs( acc )
                    endY : 52
                    stroke : Color.WHITE
                },
                parts
            ]
        };
    }
}

Figure 2: CustomCanvas Class

Frame

마지막 부분은 stage를 CustomCanvas(create 함수를 호출하는)의 인스턴스로 채웁니다. 이제 여러분은 우리가 어플리케이션을 실행했을때 팝업하는 프레임에서 연기 입자를 볼수 있습니다. 아래 코드는 프레임을 추가합니다.

Source Code
Frame {     
    stage : 
        Stage {
            fill : Color.BLACK
            content : 
                CustomCanvas {}
        }

    visible : true
    title : "Smoke Particle System"
    width : 200
    height : 232
    closeAction : 
        function() { java.lang.System.exit( 0 ); 
        }
}

Figure 3: Creating the Frame