Archive for the ‘Tip’ Category.

particle system #2

이전 포스트에서 파티클 시스템의 기초를 다뤘습니다. 이제 보다 본질적으로 파티클 시스템을 파고 들어갈 수 있는 기초가 잡힌 셈이죠. 따라서 이번 포스트에서는 보다 깊은 곳까지 파티클 시스템 구조를 생각해 보겠습니다.

particle과 emitter

파티클 시스템에 등장하는 중요 등장인물은 당연히 파티클 객체와 이 파티클을 통제할 emitter입니다. 파티클 시스템은 수학적으로 봤을 때 무한히 복잡한 함수로 표현해야 하는 모델은 작고 간단한 함수의 집합결과로 대체하는 시스템입니다(뭔소리냐 ^^)

예를 들어 파도가 출렁출렁 거리는 화면을 표현하려면 유체역학을 통해 매우 복잡한 함수를 구성해야 합니다. 하지만 개별 파티클의 단순한 움직임의 조합으로도 그러한 표현을 할 수 있다는 거죠. 이 때 전체 파도를 시뮬레이션 하는 함수보다는 개별 파티클의 움직임을 통제하는 함수가 훨씬 간단합니다.

emitter의 역할은 개별 파티클이 행동해야 할 방식, 즉 파티클용 함수를 지정하는데 있습니다.

등장하는 9가지 함수

약간 상대적이긴 한데 emitter는 오직 파티클의 x,y,rotation에만 관여하고 파티클 스스로가 alpha, scaleX, scaleY를 처리한다고 해보죠. 그럼 각 파티클이 생성시점에 초기화하기 위해서는 emitter가 위치와 회전을 초기화하는 함수를 갖고 있어야 할 테고 파티클도 크기와 알파를 조정할 초기화 함수가 필요합니다. 이를 각각 표현하면 아래와 같습니다.

function particleInit():Object{
	return {alpha:1, scaleX:rand(1,3), scaleY:rand(1,3)}
}
function emitterInit():Object{
	return {x:stage.mouseX, y:stage.mouseY, rotation:rand(0,360)}
}

생성된 파티클이 어떤 목표점을 향해서 갈 것이냐를 정하려면 타겟을 정해주는 함수가 마찬가지로 파티클과 emitter에 필요합니다. 보통 타겟을 정하는 경우에는 init에서 정해진 값과 어떤 파티클이냐를 참조하는 경우가 많습니다. 따라서 이를 인자로 보낼 필요가 있습니다.

function particleTarget( $init:Object, $particle:Class ):Object{
	return {alpha:0, scaleX:$init.scaleX/3, scaleY:$init.scaleY/3};
}
function emitterTarget( $init:Object, $particle:Class ):Object{
	var tx:Number;
	//초기값 중앙점으로부터 거리
	tx = stage.stageWidth*2 - $init.x;
	return {
		//거리의 비율로 멀리 퍼지게
		x:tx*3,
		//중점을 기준으로 화면세로70%지점에 2차방정식 곡선에 수렴하도록
		y:stage.stageHeight*.7 - 0.01*tx*3*tx*3,
		rotation:$init.rotation+rand(0,360)
	}
}

위 타겟함수는 이전 포스트의 댓글에 달았던 컵모양에 수렴하게 만드는 효과를 갖습니다.

초기화와 타겟을 정했다고 해도 그 사이에 어떻게 변화해가면서 타겟에 수렴할 것인지는 처리단계의 함수를 따로 요구합니다. 예를들어 0,0에서 500,500까지 전진을 할 때도 직선으로 이징 없이 할 것이냐 회오리를 그리면서 elastic으로 수렴할 것이냐는 전혀 다릅니다. 이는 보통 시간에 대한 함수로 주어지며 초기값, 타겟값, 파티클인스턴스 전부를 요구합니다. 아래의 함수에서 $rate의 의미는 ‘진행된시간/전체시간’ 의 비율입니다. 여기서는 단순하게 선형보간법만 이용하도록 하겠습니다.

function particleProcess( $init:Object, $target:Object, $particle:*, $rate:Number ):void{
	var key:*;
	for( key in $init ){
		$particle[key] = ( $target[key] - $init[key] )*$rate + $init[key];
	}
}
function emitterProcess( $init:Object, $target:Object, $particle:*, $rate:Number ):void{
	var key:*;
	for( key in $init ){
		$particle[key] = ( $target[key] - $init[key] )*$rate + $init[key];
	}
}

이러한 파티클의 여행이 끝나면 종료이벤트에 맞춰 실행될 함수도 제공되어야 합니다. 대부분 뒷정리를 하기 위한 목적이지만 파티클의 여행이 끝난 후 다른 움직임을 보이거나 원래 자리로 돌아가는 등의 추가적인 시나리오를 제공할 수도 있습니다.

function particleComplete( $init:Object, $target:Object, $particle:* ):void{
	//아무것도 안함
}
function emitterComplete( $init:Object, $target:Object, $particle:* ):void{
	$particle.parent.removeChild( $particle ); //화면에서 제거
}

마지막으로 파티클군에서 파티클을 선택하는 함수가 필요합니다. 예를 들어 파티클군으로 삼각형, 사각형, 오각형….원 까지를 제공했을 때 생성되는 위치에 따라 점점 원에 수렴하는 형태의 파티클을 생성하고 싶다면 다음과 같은 함수를 생각해볼 수 있습니다.

var particles = [Cpoly3, Cpoly4, Cpoly6, Cpoly8, Cpoly12, Cpoly16, Ccircle];
function getParticle():Class{
	return particles[( particles.length - 1 ) * stage.mouseX / stage.stageWidth];
}

위에서 언급한 9가지 함수 중 particle에 정의한 init, target, process, complete의 경우엔 파티클 클래스에 정의해도 무방합니다. 함수와 분리는 것보다 훨씬 안정적이지만 대신 손쉽게 파티클을 생성하기 어렵게 됩니다. 또한 getParticle의 경우도 클래스 내부에서 factory를 통해 제공해도 무방합니다.

시간함수가 아닌 경우의 emitter

위의 9가지 함수의 예를 들 때 잠정적으로 가정하고 있는 모델이 있었는데 시간기반의 함수라는 점입니다. 대표적으로 트위닝이죠. 하지만 결과를 알 수 없고 진행상황의 누적으로 결과가 도출되는 파티클 시스템도 많습니다. 대표적으로 물리모델링을 하는 경우인데 사실 이 물리모델을 시간함수로 변경할 수 있다면 다시 위의 모델에 수렴하게 됩니다. 유일한 문제점은 누적치의 결과를 미리 확정하고 함수를 시간의 함수로 변경하면 영향을 주는 거꾸로 역산해야 하는데 이 때 적분구간을 등비수열의 합으로 바꾼 뒤 결과를 유도해야 함으로 로그함수에 수렴하게 되는 복잡한 과정을 거친다는 점입니다. 수학적인 모델로 봤을 때 훨씬 설계하기 어려워집니다. 따라서 시간함수가 아닌 진행 함수만 존재하는 경우가 있을 수 있습니다(대표적으로 물리량을 쓰는 경우죠)

하지만 이 경우에도 위의 9가지 함수 모델은 유지한체 target이 설정되지 않고 process만 작동하는 방식이 될 것입니다.

결론

이제 매우 복잡한 파티클 시스템을 만드실 수 있는 프레임웍을 만드실 수 있습니다. 이 구조는 확장을 지원하므로 물리계파티클이든, 펄린노이즈모델을 쓰시든, 플루이드 모델을 쓰시든 다양한 형태로 확장할 수 있습니다.

particle system #1

파티클 시스템을 제작하려면 emitter의 개념이 필요합니다. emitter란 일종의 factory메서드를 제공하는 클래스 입니다. 하지만 factory는 보통 하나의 객체를 직접 반환하는데 비해 emitter는 일정한 조건이 충족되는 동안 계속 객체를 생성합니다.

문제는 이 일정한 조건을 나타내는 부분인데 정적 요소로 수량을 조건으로 사용할 뿐만 아니라 동적 요소로 시간을 사용합니다. 따라서 시간에 따른 생성관리를 하기 위해서는 트위너와 결합하게 되는 것이 일반적입니다.

만약 파티클 시스템을 바닥부터 만들려면 트위너부터 만들어야 하기 때문에 상당한 큰 작업입니다만, 트윈맥스 등의 기저층을 활용할 생각이라면 매우 간단히 제작할 수 있습니다.

Continue reading ‘particle system #1’ »

수 많은 디스플레이 객체를 관리하기 2

이전 포스팅(수 많은 디스플레이 객체를 관리하기 1)에서 생각해 낸 인스턴스에게 태그를 붙이는 아이디어를 보다 구체적이고 깊이 있게 생각해 보겠습니다.
일단 인스턴스와 태그와의 관계를 정립했으니 태그 자체에 대한 고민으로 옮겨갈 필요가 있습니다.

Continue reading ‘수 많은 디스플레이 객체를 관리하기 2’ »

수 많은 디스플레이 객체를 관리하기 1

조직화할 그래픽 객체가 대규모로 존재하는 경우엔 적당한 식별할 자료구조물이 없다는 생각을 자주 합니다. 객체란 행위중심으로 설계하기 때문에 상태는 보조적인 역할만 수행합니다. 따라서 데이터 덩어리를 효과적으로 정리하고 접근성을 향상시키는 구조물을 만드는 경우에 OOP패러다임은 큰 쓸모가 없습니다.
오히려 힌트를 받은 만한 곳은 데이터베이스로 개별 데이터가 아닌 집합을 지원하는 점을 이용할 수 있습니다. 이러한 아이디어를 차용하여 대규모로 등장하는 그래픽스 데이터에 대한 조직화에 대해서 간단히 얘기해보겠습니다.

Continue reading ‘수 많은 디스플레이 객체를 관리하기 1’ »

파이프라인의 구축

파이프라인이란 매우 간단한 개념입니다. 어떤 일이 처리될 때 독립된 단계별로 처리한다는 의미인데 이 내용에 대해 가장 많이 등장하는 예는 세차장입니다.

만약 손세차장을 운영한다고 해보죠. 땅이 충분히 넓고 인원이 많다면 길이로 길게 세차장을 만들어서 공정을 분업화하는 방법을 사용할 수 있습니다.

  1. 따뜻한 물을 끼엊는 파트
  2. 비누칠을 하는 파트
  3. 솔질하는 파트
  4. 헹구는 파트
  5. 마른 수건으로 닦는 파트

위의 파이프라인을 구축한다는 것은 단순히 분업했다는 것이 아니라 여러 가지 의미를 갖게 됩니다.

Continue reading ‘파이프라인의 구축’ »