[Starling-07] MovieClip 클래스를 이용한 애니메이션

Project/Programming 2012. 1. 9. 00:49
Flash에서 애니메이션이라고 하면 역시 MovieClip이지만 Starling에도 MovieClip 클래스가 있다. 물론 애니메이션을 위한 클래스지만 Starling의 MovieClip은 화면에 표시할 수 있는 것이 비트맵 이미지 또는 색상을 지정한 사각형 뿐이다. 따라서 MovieClip라는 이름은 같지만 Starling의 MovieClip은 프레임마다 할당된 비트맵을 표시하는 클래스인 것이다.

또한 Starling MovieClip의 자식 객체를 관리하거나 프레임에 스크립트가 들어가는 기능은 없다. 이 점도 Flash Player의 MovieClip과는 크게 다르다. (Starling은 표시 목록의 관리는 Sprite의 책임이다. 한편, MovieClip은 Image 클래스에 타임 라인 기능을 더한 Image의 서브 클래스다. MovieClip이 Sprite의 하위 클래스인 Flash Player와는 달리, 양쪽의 역할은 분리되어 있다.)


스프라이트 시트와 TextureAtlas
MovieClip의 각 프레임을 볼 때 GPU는 표시 프레임에 할당된 텍스처, 셰이더에서 사용할 수 있도록 준비가 이루어진다. 이 때 텍스처가 프레임마다 물리적으로 다른 비트맵 파일이면 프레임을 진행할 때마다 텍스처 전환이 발생한다.

텍스처 전환 처리에는 당연히 시간이 걸린다. 또한 텍스처의 GPU에 업로드가 필요할 때 추가 오버헤드가 증가한다. 일반적으로 이 문제를 해결하기 위해 각 프레임의 텍스처를 하나의 파일에 정의한다. 이 것을 스프 라이트 시트라고  한다.

아래 그림은 Starling 샘플에 포함된 스프라이트 시트의 일부를 자른 것이다.




여러 프레임에 해당하는 텍스처가 1개의 파일로 되어 있으면 GPU를 이용해서 사용 위치를 바꾸는 것만으로 텍스처 자체를 전환하지 않고 렌더링 처리가 가능하다. 따라서 고속 프레임을 업데이트할 수 있다.

또한 Stage3D에서 취급할 수 있는 텍스처의 폭은 2의 계승이라는 규칙 때문에, 그 이외의 너비 텍스처는 Starling 내부에 여분의 사전 처리가 발생한다. 하지만 스프라이트 시트의 경우 표시되는 텍스처가 더 큰 텍스처(스프라이트시트)가 아니기 때문에 개별 텍스처 너비 사이즈를 걱정할 필요가 없다.

이 것을 최적화한 1단계가 TextureAtlas다. TextureAtlas는 여러 스프라이트시트를 하나의 파일에 정리한 것이다. 동시에 여러 애니메이션을 재생하는 경우 특히 유용하다. TextureAtlas를 만들기 위해, 차기 버전의 Flash Professional CS6에서 지원되는 것 같다. 벡터 애니메이션 스프라이트시트로 내보내기 기능을 기다리거나 Texture Packer 등의 도구를 이용하면 된다.


TextureAtlas 로드
TextureAtlas를 사용하여 프레임마다 각각 TextureAtlas의 어떤 영역을 표시할지 정보가 필요하다. 따라서 다음과 같은 형식으로 각 프레임의 텍스처 정보를 지정하게 되어 있다. 도구를 사용하여 TextureAtlas를 생성하는 경우 이러한 XML 파일도 동시에 생성된다.

  
  
  
  


TextureAtlas 태그 imagePath 속성은 TextureAtlas의 파일 이름이다. SubTexutre 태그의 name 속성은 애니메이션의 고유 이름 뒤에 프레임 번호를 추가한 것이다. 나머지 x, y, height, width에서 텍스처로 사용 영역을 지정한다. (실제로 SubTexture는 텍스처의 일부를 처리하는 클래스가 있다) TextureAtlas가 준비되면 다음과 같은 프로그램에 포함한다.
[Embed (source = "atlas.png")]
private static const MyBitmap:Class;
 
[Embed (source = "atlas.xml"mimeType = "application / octet - stream")]
private static const MyXml:Class;

이러한 정보는 TextureAtlas라는 클래스에서 함께 관리한다. TextureAtlas(PNG 파일)은 일단 Texture 클래스 로딩하면서 XML 파일과 함께 TextureAtras 생성자에 전달한다. TextureAtlas 객체에서 고유 이름을 지정하여 Texture(실제로는 SubTexture의) 벡터를 얻을 수 있다. 이 때 사용하는 메소드가 getTextures()다.
import starling.textures.TextureAtlas;

	private function onAddedToStage(e:Event):void {
		// 텍스처 아틀라스로드
		var myTexture:Texture = Texture.fromBitmap(new MyBitmap);
		// 텍스처 아틀라스의 영역 정보 가져오기
		var myXml:XML = XML(new MyXml);

		// TextureAtlas 인스턴스 생성
		var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture,myXml);
		// "walk_"를 식별 이름으로 가진 Texture 벡터 생성
		var frames:Vector. = myTextureAtlas.getTextures("walk_");

		// ... 앞으로는 아래에 계속
	}

벡터의 첫 번째 텍스처의 크기가 애니메이션의 영역을 결정한다. 애니메이션 여기까지 왔으면 나머지는 MovieClip의 인스턴스를 생성하면 된다. MovieClip 생성자의 인수에서 얻은 텍스처 벡터를 지정한다.
import starling.display.MovieClip;

	private var myMovieClip:MovieClip;

	private function onAddedToStage(e:Event):void {
		//... 위에서 계속

		// MovieClip 객체의 생성
		myMovieClip = new MovieClip(frames);

		// 애니메이션 재생 시작
		Starling.juggler.add(myMovieClip);
		addChild(myMovieClip);
	}

알겠지만, Tween뿐만 아니라 MovieClip의 재생도 Juggler을 사용한다. MovieClip도 IAnimatable의 서브클래스다. Juggler에 MovieClip을 추가하면 재생이 시작된다. 재생을 제어하는​​ 세 가지 메소드가 포함되어 있다.
myMovieClip.play();
myMovieClip.pause();
myMovieClip.stop();

pause()를 실행한 다음에 play()를 실행하면 현재 상태부터 재생되지만 stop()을 실행한 다음에 play()를 호출하면 처음 부터 다시 재생된다. 애니메이션은 default로 반복 재생된다. 이 설정은 loop 속성을 설정하여 바꿀 수 있다. 재생 여부 isPlaying 속성을 확인한다. 애니메이션이 끝까지 재생되면 movieComplete 이벤트가 발생한다. 재생이 완​​료되면 다음 작업으로 이동하고자하는 경우에 사용할 수 있을 것이다.
myMovieClip.addEventListener(Event.MOVIE_COMPLETED, onMovieComplete);

애니메이션이 반복되는 경우에는 재생이 완​​료될 때마다 movieComplete 이벤트가 발생한다. MovieClip의 부모 Sprite가 Stage에서 제거되면 MovieClip 재생을 중지해야한다. 그래서 다음과 같은 코드를 추가한다.
addEventListener(Event.REMOVED_FROM_STAGE,onRemovedFromStage);

	private function onRemovedFromStage(event:Event):void {
		Starling.juggler.remove(myMovieClip);
	}

Juggler의 remove() 메소드는 MovieClip뿐만 아니라 Tween도 삭제할 수 있다. 또 다른 MovieClip 사용 MovieClip의 생성자의 두 번째 파라미터에 임의의 프레임 속도를 지정할 수 있다.
// 40 fps를 지정
myMovieClip = new MovieClip (frames, 40);

MovieClip마다 다른 프레임 속도를 지정할 수 있다. 재생중인 프레임 속도는 fps 속성에서 가져올 수 있다. 또한 fps 속성 값을 설정하여 프레임 속도를 변경할 수도 있다. 그리고 모든 프레임에 대해 별도의 재생 시간을 지정할 수 있다. 아래는 5 번째 프레임을 2 초 동안 재생하도록 설정하는 예다.
myMovieClip.setFrameDuration(5, 2);

나중에 프레임을 추가하거나 삭제할 수도 있다. 이를 위해서 addFrameAt() removeFrameAt() 메서드를 사용한다. 아래는 5 프레임 애니메이션을 10번 째 프레임에 추가하거나 5 프레임을 제거하는 것이다. 
myMovieClip.addFrameAt(5, frames [10]);
myMovieClip.removeFrameAt(5);

프레임의 텍스처를 바꿀 수도 있다. setFrameTexture() 메소드를 사용하면 된다.
myMovieClip.setFrameTexture(5, frames [10]);

프레임에 소리를 연결할 수도 있다. setFrameSound() 메서드를 사용한다.
[Embed (source = "step.mp3")]

private static const StepSound:Class;
 
myMovieClip.setFrameSound (5, new StepSound() as Sound);

이제 5번 째 프레임이 표시되는 타이밍에 지정한 소리가 재생된다. 프레임에 직접 스크립트를 연결할 수는 없다. 그 대신 Juggler.delayCall() 메서드를 사용할 수 있다. 이상에서 Starling 애니메이션의 기본은 끝이다. Starling 표현의 유연성은 CPU 렌더링보다 미약하다. 그러나 빠른 렌더링을 안정적으로 해야할 경우에 선택하면 적당할 것이다.


원본 : http://cuaoar.jp/2011/12/movieclip-starling.html


 


package {
	import starling.events.Event;
	import starling.display.Sprite;
	import starling.display.DisplayObject;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.textures.RenderTexture;
	import starling.textures.TextureAtlas;
	import starling.display.MovieClip;
	import starling.core.Starling;

	public class Demo extends Sprite {

		[Embed(source = "patch.png")]
		private var MyBitmap:Class;

		[Embed(source = "patch.xml",mimeType = "application/octet-stream")]
		private var MyXml:Class;
		
		private var _arrMovieClips:Array;


		public function Demo() {
			// addedToStage 이벤트에 대한 리스너 추가
			addEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
		}
		private function onAddedToStage(e:Event):void {

			// 텍스처 아틀라스로드
			var myTexture:Texture = Texture.fromBitmap(new MyBitmap());

			// 텍스처 아틀라스의 영역 정보 가져오기
			var myXml:XML = XML(new MyXml());

			// TextureAtlas 인스턴스 생성
			var myTextureAtlas:TextureAtlas = new TextureAtlas(myTexture,myXml);

			// "walk_"를 식별 이름으로 가진 Texture 벡터 생성
			var frames:Vector.<Texture>=myTextureAtlas.getTextures("patch_");

			// MovieClip 객체의 생성
			
			_arrMovieClips = [];
			var count:int = 20;
			for(var i:int=0;i<count;i++){
				var myMovieClip = new MovieClip(frames, 60);
				myMovieClip.x = 20+((stage.stageWidth-90)/count)*i;
				myMovieClip.y = stage.stageHeight - myMovieClip.height >> 1;
				Starling.juggler.add(myMovieClip);
				_arrMovieClips.push(myMovieClip);
				addChild(myMovieClip);
				
			}
			
		//	myMovieClip.setFrameDuration(5, 2);

		//	myMovieClip.addFrameAt(5,frames[10]);
		//	myMovieClip.removeFrameAt(5);
		}

		private function setAlign(inTarget:DisplayObject):void{
			inTarget.x = stage.stageWidth >> 1;
			inTarget.y = stage.stageHeight >> 1;
		}

		public override function dispose():void {
			removeEventListener(Event.ADDED_TO_STAGE,onAddedToStage);
			super.dispose();
		}
	}
}

    

설정

트랙백

댓글

[Starling-06] 비트맵을 표시하는 방법

Project/Programming 2012. 1. 8. 04:49
이번에는 비트맵(텍스처)을 표시하는 방법이다. Starling에서는 비트맵 또는 이전 포스트와 같이 사각형을 사용하여 화면을 구성한다. 기사의 뒷 부분에서 동적으로 텍스처 업데이트하는 방법도 소개한다. (참고 : 앞으로 내용에서 "텍스처 = 비트맵"으로 이해)


Image와 Texture 클래스
Starling 프레임워크는 비트맵을 표시하기 위하여 Image를 사용한다. Image는 Quad의 서브클래스다. 따라서 Quad에서 비트맵 처리 기능을 추가한 것이 Image 클래스가 되는 것이다. Flash Player 표시 객체는 Bitmap에 BitmapData가 있지만 Starling에서는 Image에 Texture가 있다. Texture에 로드할 수 있는 이미지 포멧은 PNG, JPEG, JPEG-XR, ATF 이렇게 4 종류이다. 각 형식마다 전용 읽기 함수가 포함되어 있다. 예를 들어, Bitmap 객체에서 Texture 객체를 만드는 경우 다음과 같다.
var myTexture:Texture = Texture.fromBitmap(myBitmap);

다음은 생성된 Texture에서 Image 객체를 생성하고 표시 목록에 추가하는 것만으로 화면에 비트맵이 표시된다. 아래는 myBitmap.png라는 파일을 표시하는 예제다. 포함된 비트맵 처리 부분을 제외하면 사각형을 표시하는 샘플 코드와 거의 동일하다.

package { import starling.events.Event; import starling.display.Sprite; import starling.display.Image; import starling.textures.Texture; import flash.display.Bitmap; public class Demo extends Sprite { // PNG 파일을 포함 [Embed(source = "icon128.png")] private var MyBitmap:Class; private var myImage:Image; public function Demo() { // addedToStage 이벤트에 대한 리스너 추가 addEventListener( Event.ADDED_TO_STAGE , onAddedToStage); } private function onAddedToStage(e:Event):void { var myBitmap:Bitmap = new MyBitmap() as Bitmap; // Bitmap에서 Texture 오브젝트를 생성 var myTexture:Texture = Texture.fromBitmap(myBitmap); // Image 객체를 생성 myImage = new Image(myTexture); // 표시 목록에 추가 addChild(myImage); } public override function dispose():void { removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage); super.dispose(); } } }

Starling의 Texture는 가로와 세로 픽셀 수가 2층(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048)을 전제로하고 있는 것 같다. 다른 크기의 비트맵에서 Texture를 생성하면 Starling은 자동으로 가장 가까운 크기에 맞게 Texture를 다시 만든다.


벡터 이미지를 텍스처로보기
Flash Player 표시 객체는 벡터 데이터를 비트맵화 하는 기능을 가지고 있다. 이것을 이용하면 AS3 및 Flash Professional 기능을 사용하여 그린 벡터 이미지에서 Image 개체를 생성할 수 있다. 처리 효율을 생각하면 일부러 런타임에 비트맵을 생성하는 것보다는 사전에 비트맵화해 두는 것이 좋을 것이다. 따라서 이 방법은 동적으로 텍스처를 생성해야하는 경우에 필요하다. 구체적인 예를 소개한다. 아래는 Sprite의 graphic 특성에 그린 원을 텍스처로 변환하여 Image 객체로 화면에 표시하는 예제다.

private function onAddedToStage(e:Event):void{ var radius:uint = 100; // Flash Player의 Sprite를 생성 var shape : flash.display.Sprite = new flash.display.Sprite(); // 임의의 색상을 결정 graphics 속성 원을 그린다 var color:uint = Math.random() * 0xFFFFFF; shape.graphics.beginFill(color); shape.graphics.drawCircle(radius, radius, radius); shape.graphics.endFill(); // Sprite를 BitmapData로 변환; var buffer : BitmapData = new BitmapData (radius * 2, radius * 2, true, color); buffer.draw(shape); // BitmapData에서 Texture를 생성 var myTexture:Texture = Texture.fromBitmapData(buffer); // Texture에서 Image를 만듭니다 myImage = new Image(myTexture); // 표시 목록에 추가 addChild(myImage); }

이 경우 코드에서 Flash Player 표시 객체를 사용하게되므로, Starling 표시 객체와 혼동하지 않도록 주의가 필요하다.


동적 텍스처의 업데이트
위의 방법을 사용하면 동적으로 생성한 비트맵을 볼 수 있다. 그러나 표시 비트맵을 업데이트하려면 벡터 이미지 만들기부터 다시 한다. 즉, CPU 그리기 및 GPU 렌더링이 모두 행해지는 것은 그다지 효율적 수단은 아닌 것 같다.(항상 피해야한다는 의미는 아니다.)

이럴 때는 RenderTexture 클래스가 유용하다. RenderTexture은 Texture의 서브 클래스에서, "Starling 표시 객체"를 기존의 Image 객체에 그리는 기능을 제공한다. 이는 GPU만을 사용하기 때문에 효율적이다. RenderTexture은 "Flash Player 표시 객체"를 취급하지 않기 때문에 이미 준비되어 있는 Image 객체를 비트맵으로 합성하는 방법이 기본이다. 특히 미리보기 그래픽을 알고 있는 경우에 유효하다. RenderTexture 생성자는 그리기 영역의 크기만을 지정한다. 이 시점에서 RenderTexture 아직 새하얀(투명)상태다.다음으로 RenderTextire 인스턴스를 사용하여 Image의 인스턴스를 생성하는 단계로 위 코드에서 보았던 방식 그대로다.

import starling.textures.RenderTexture; private var myRenderTexture:RenderTexture; private function onAddedToStage(e : Event):void { // RenderTexture 객체를 생성 myRenderTexture = new RenderTexture(320,480); // RenderTexture에서 Image를 만든다 var canvas:Image = new Image(myRenderTexture); // 표시 목록에 추가 addChild(canvas); }

이제 그리기 영역 지정이 완료되었다.
다음은 RenderTexture를 업데이트하면 화면 표시도 자동으로 업데이트된다. RenderTexuture에 Starling의 표시 객체를 그리려면, draw() 메서드를 사용한다. 그릴 표시 객체는 위치, 비율, 각도, 알파를 지정할 수 있다. 아래 예제에서는 RenderTexture의 x 좌표와 y 좌표를 지정하고 있다.

myImage.x = 10; myImage.y = 20; myRenderTexture.draw(myImage);

많은 표시 객체를 그리고 싶은 경우를 위해, drawBundled() 메소드도 포함되어 있다. 이것은 여러 표시 객체 그리기를 일괄적으로 실행하는 것으로, 처리 효율의 향상을 노린 것이다. GPU를 효율적으로 사용하기 위해서 GPU에 대한 그리기 요청 횟수를 줄이는 것은 중요하다. drawBundled()의 인수는 함수를 지정한다. 함수에서 draw()은 함께 처리된다. 아래는 이미지를 조금씩 회전시키는 예제다.
myRenderTexture.drawBundled(rot);
	private function rot():void {
		var count:int = 30;
		var diff:Number = 2 * Math.PI / count;

		for (var i:int = 0; i < count; ++i) {
			myImage.rotation = diff * i;
			myRenderTexture.draw(myImage);
		}
	}

행하면 아래와 같이된다.



이미지는 Starling 샘플에 포함된 것을 사용했다. RenderTexture에 그려진 내용을 모두 삭제하려면 clear() 메소드를 사용한다.


자원의 해제
불필요하게 된 텍스처가 있는 경우에는 dispose()가 리소스를 해제한다. 특히 RenderTexture은 일시적인 이용이 많다고 생각되므로 잊지 말아야한다.
public override function dispose():void {
		myRenderTexture.dispose();
		super.dispose();
	}

원본 : http://cuaoar.jp/2011/12/starling-flash-1.html


 
package {
	import starling.events.Event;
	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import starling.textures.RenderTexture;

	public class Demo extends Sprite {

		[Embed(source = "icon128.png")]
		private var MyBitmap:Class;

		private var myRenderTexture:RenderTexture;
		private var myImage:Image;

		public function Demo() {
			// addedToStage 이벤트에 대한 리스너 추가
			addEventListener( Event.ADDED_TO_STAGE , onAddedToStage);
		}
		private function onAddedToStage(e:Event):void {

			// RenderTexture 객체를 생성
			myRenderTexture = new RenderTexture(330,330);

			// RenderTexture에서 Image를 만든다
			var canvas:Image = new Image(myRenderTexture);

			canvas.x = stage.stageWidth- canvas.width >> 1;
			canvas.y = stage.stageHeight- canvas.height >> 1;
			// 표시 목록에 추가
			addChild(canvas);


			var myBitmap:Bitmap = new MyBitmap() as Bitmap;

			// Bitmap에서 Texture 오브젝트를 생성
			var myTexture:Texture = Texture.fromBitmap(myBitmap);

			// Image 객체를 생성
			myImage = new Image(myTexture);
			myImage.x = 165;
			myImage.y = 165;
			
			addEventListener(Event.ENTER_FRAME, run);

		}
		
		private function run(e:Event):void{
			myRenderTexture.drawBundled(rot);
		}

		private function rot():void {
			var count:int = 8;
			var diff:Number = (Math.random()*2) * Math.PI / count;

			for (var i:int = 0; i < count; ++i) {
				myImage.rotation += diff * i;
				myRenderTexture.draw(myImage);
			}
		}

		public override function dispose():void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			removeEventListener(Event.ENTER_FRAME, run);
			myRenderTexture.dispose();
			myImage.dispose();
			super.dispose();
		}
	}
}
 
    

설정

트랙백

댓글