[Starling-10] 터치 이벤트와 Touch 클래스

Programming/Framework 2012. 1. 10. 14:39
Starling은 손가락의 조작이나 마우스 조작 모두 동일한 이벤트( TouchEvent )로 취급한다. Flash Player의 마우스 이벤트와는 달리 이벤트 종류는 TouchEvent.TOUCH 하나 뿐이다. 따라서 클릭 및 이동 등의 조작 상태는 이벤트에 포함되어 있는 Touch 객체 속성에서 판단하게 되어 있다.

Starling의 모든 표시 객체(Sprite, Stage, Quad, …)는 TouchEvent을 처리할 수 있다. 단, Button 클래스는 내부적으로 터치 이벤트를 triggered 이벤트로 변환하고 있기 때문에 자신의 리스너를 추가하는 것은 주의가 필요하다. 또한 표시 객체 자신이나 부모 개체 touchable 속성이 false의 경우는 TouchEvent를 받을 수 없다.

아무튼 Starling에서 터치 작업을 처리하려면 우선 표시 객체에 TouchEvent.TOUCH 이벤트 리스너를 추가해야 한다. 아래는 샘플이다.

import starling.events.TouchEvent;
myImage.addEventListener(TouchEvent.TOUCH,onTouch);

private function onTouch(e:TouchEvent):void {
trace(e.target);
}

Starling의 이벤트는 표시 목록에 버블링하기 위해 아래의 계층 객체에서 이벤트도 검색할 수 있다. (참고:사양에서는 계층의 위에서 아래로 전파하지만 지금의 구현에서는 Stage에서 이벤트를 받을 수도 있는 것 같다.)

샘플에서 사용하는 target 속​​성과 currentTarget 속​​성 사용법은 Flash Player 표준 이벤트와 동일하다. 기타 TouchEvent에는 다음과 같은 속성도 있다. 모든 읽기 전용이다.

ctrlKey : 이벤트가 발생할 때 Ctrl 키가 눌리고 있었는지 나타내는
shiftKey : 이벤트 발생시 Shift 키가 눌리고 있었는지 나타내는
timestamp : 터치가 시작된 이후의 시간(초)
touches : 사용 가능한 Touch 객체의 벡터

마지막 touches은 이벤트와 관련된 하나 이상의 Touch 객체의 벡터다. Touch 객체는 이벤트 발생시킨 손가락이나 마우스 조작 정보를 보유하고 있다.


Touch 객체 가져오기
TouchEvent에는 Touch 객체를 취득하기 위한 메소드가 포함되어 있다. 멀티 터치를 사용하지 않는 경우 getTouch()메소드에서 Touch 객체를 얻을 수 있다. (멀티 터치의 경우는 후술)

private function onTouch(e:TouchEvent):void {
var touch:Touch = e.getTouch(stage);
if (touch) {
  trace(e.target, touch.target);
}
}

getTouch() 메소드의 첫 번째 인수는 검색할 Touch 객체를 좁히려는 조건이다. 작업의 대상이 된 표시 객체가 인수로 지정된 오브젝트와 일치하는 또는 인수 계층 아래에​​ 포함된 경우, getTouch()는 값을 반환한다. 실제 작업의 대상은 Touch 객체 취득 후에 target 속​​성에서 볼 수 있다. (작업의 대상과 이벤트의 대상은 반드시 일치하지 않는다는 것에 유의)

Touch객체는 target 이외에도 작업을 식별하는 특성이 몇 가지가 더 있다.

target : 터치 조작의 대상이 된 표시 객체
id : 터치를 식별하는 숫자
timestamp : 터치 조작 시간 (터치 시작으로부터 초)


TouchPhase
위에서 이야기한 것처럼, Touch 객체는 작업 상태를 나타내는 속성이 있다. 속성 이름은 phase다.

phase : 현재 터치 상황

phase 속성에 설정 가능한 값은 TouchPhases 클래스에 정의되어 있다. 자주 사용되는 값은 다음 세 가지다.

BEGAN : 손가락이 화면에 접한 또는 마우스 버튼이 눌러진
MOVED : 손가락이 화면을 이동하거나 마우스 버튼이 눌러진 상태 이동
ENDED : 손가락이 화면에서 떨어진 또는 마우스 버튼에서 떨어진

BEGAN이 한 번, MOVED가 0 이상 반복되고, ENDED가 한 번 발생하는 것이 기본적인 흐름이다.다음과 같은 상태도 있다.

STATIONARY : 손가락 또는 마우스가 이전 프레임에서 이동

일반적으로 이 상태에서는 이벤트가 발생되지 않아야하지만, 멀티 터치를 취급하는 경우에는 STATIONARY가 포함될 수 있다.마지막으로 마우스 이용 때만 나타나는 상태다.

HOVER : 마우스 버튼이 눌러지지 않은 상태에서 표시 객체에 포인터가 있을 때

손가락으로 (지금까지) 접하지 않고 포인터를 이동시킬 수 없기 때문에 OVER는 있어도 HOVER는 없다는 것이다.


포인터 위치 가져오기
Touch 객체는 스테이지의 현재 위치와 이전 위치 속성을 가지고 있다. 다음 네 가지다. 형태는 모두 Number.

globalX : 스테이지 좌표를 기준으로 한 현재의 손가락 (포인터)의 x 좌표
globalY : 스테이지 좌표를 기준으로 한 현재의 손가락 (포인터)의 y 좌표
previousGlobalX : 스테이지 좌표를 기준으로 이전의 손가락 (포인터)의 x 좌표
previousGlobalY : 스테이지 좌표를 기준으로 이전의 손가락 (포인터)의 y 좌표

그러나 대부분의 경우 다른 좌표계에서의 위치를​​ 계산하게 될 것이다. 그래서, Touch 클래스에는 위의 속성 값을 특정 객체를 기준으로 한 좌표로 변환하는 메소드를 제공 한다.

public function getLocation(space:DisplayObject):Point
public function getPreviousLocation(space:DisplayObject):Point

이 두 메소드는 인수 객체의 좌표를 기준으로 한 현재의 위치 또는 마지막 위치를 반환한다.아래는 Touch 객체의 위치 정보를 사용하는 샘플이다.

var touch:Touch = event.getTouch(this, TouchPhase.MOVED);
if (touch) {
// 부모 개체를 기준으로 이동 거리를 계산하는
var currentPos:Point = touch.getLocation(parent);
var previousPos:Point = touch.getPreviousLocation(parent);
var delta:Point = currentPos.subtract(previousPos);

// 객체의 위치를​​ 변경
x += delta.x;
y += delta.y;
}

getTouch() 메소드의 두 번째 인수는 원하는 상태를 지정할 수 있다. 위의 예제에서는 손가락 이동 정보를 취급하기 위하여 TouchPhase.MOVED를 지정하고 있다.


더블 클릭의 사용 방법
Touch 객체를 통해 더블 클릭을 감지할 수 있도록 tapCount 속성이 있다. 이 속성을 사용하여 더블 클릭에 해당하는 코드를 작성한 것이 아래의 예제다.

touch = event.getTouch(this, TouchPhase.ENDED);
if (touch & & touch.tapCount == 2) parent.addChild (this);

작동 상태가 ENDED의 Touch 객체가 존재하며 탭 횟수가 2 회인 경우를 검색하고 있다.멀티 터치Starling은 멀티 터치도 지원한다. 멀티 터치를 처리하려면 Starling 객체에 다음과 같이 설정한다.

mStarling.simulateMultitouch = true;

이 상태에서 실제로 멀티 터치를 하면 터치 때마다 Touch 객체가 만들어진다. 그리고 TouchEvent에 전달된다. TouchEvent에서 여러 Touch 개체를 가져오려면 getTouches() 메소드를 사용한다. 아래는 그 예제다.

var touches:Vector.<Touch>=touchEvent.getTouches(this); 


반환 값은 Touch 벡터다. getTouch()뿐만 아니라 두 번째 인수로 원하는 상태를 지정할 수 있다.참고로 Ctrl 키를 사용하여 데스크탑 환경에서도 멀티 터치를 시뮬레이션할 수 있게 되어 있다. 물론 실제 터치 디바이스에서는 손가락으로 조작하면 된다.

원본 : http://cuaoar.jp/2012/01/starling-touch.html



package {
	import starling.events.Event;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.display.DisplayObject;
	import starling.animation.Tween;
	import starling.animation.Transitions;
	import starling.utils.deg2rad;
	import flash.display.Bitmap;
	import flash.geom.Point;


	public class Demo extends Sprite {

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

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


		private function onAddedToStage(e:Event):void {
			var myBitmap:Bitmap = new MyBitmap() as Bitmap;
			var myTexture:Texture = Texture.fromBitmap(myBitmap);
			
			myButton = new Image(myTexture);
			myButton.addEventListener(TouchEvent.TOUCH, onTouchHandler);
			
			myButton.pivotX = myButton.width >> 1;
			myButton.pivotY = myButton.height >> 1;
			addChild(myButton);
			setAlign(myButton);

		}
		
		private function onTouchHandler(e:TouchEvent):void{
			var touch:Touch = e.getTouch(myButton);
			if(touch){
				switch(touch.phase){
					case TouchPhase.BEGAN : myButton.setVertexColor(0, 0xFF6600);
											myButton.setVertexColor(1, 0xFF6600);
											myButton.setVertexColor(2, 0xFF6600);
											myButton.setVertexColor(3, 0xFF6600);
					break;
					case TouchPhase.MOVED : 
					break;
					case TouchPhase.ENDED : myButton.setVertexColor(0, 0xFFFFFF);
											myButton.setVertexColor(1, 0xFFFFFF);
											myButton.setVertexColor(2, 0xFFFFFF);
											myButton.setVertexColor(3, 0xFFFFFF);
				}
			}
			
			
			/*
			touch = e.getTouch(myButton, TouchPhase.MOVED);   
			if (touch) {        
					// 부모 개체를 기준으로 이동 거리를 계산하는      
					 var currentPos:Point = touch.getLocation(parent);      
					 var previousPos:Point = touch.getPreviousLocation(parent);     
					 var delta:Point = currentPos.subtract(previousPos);        
			 
					// 객체의 위치를​​ 변경     
					 myButton.x +=  delta.x;     
					 myButton.y +=  delta.y; 
			}
			*/
			
			touch = e.getTouch(this, TouchPhase.ENDED); 
			if (touch && touch.tapCount == 2){
				rotationToTween(0.6, 90, Transitions.EASE_OUT_BOUNCE);
			}
			
			var touches:Vector.<Touch> = e.getTouches(stage); 
			if(touches.length == 2){
				var firstPos:Point = touches[0].getLocation(parent);      
				var secondPos:Point = touches[1].getLocation(parent);  
				var distance:Number = Point.distance(firstPos, secondPos);

				scaleToTween(0.2, distance*0.01, Transitions.EASE_OUT);
			}else{
				touch = e.getTouch(this, TouchPhase.MOVED); 
				if (touch){
					moveToTween(0.14, touch.globalX, touch.globalY, Transitions.EASE_OUT);
				}
			}
			
		}
		
		private function rotationToTween(inTime:Number, inRot:int, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.animate("rotation", myButton.rotation+deg2rad(inRot));
			Starling.juggler.add(tween);
		}
		
		private function moveToTween(inTime:Number, inX:int, inY:int, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.moveTo(inX, inY);
			Starling.juggler.add(tween);
		}
		
		private function scaleToTween(inTime:Number, inScale:Number, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.scaleTo(inScale);
			Starling.juggler.add(tween);
		}

		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();
		}
	}
}

    

설정

트랙백

댓글

[CS4] Vector의 이해

FlashCS4(FlashPlayer10) 버전에서 지원하고 있는 Vector 객체에 대해서 정확히 무엇이며 어떤 역할을 하는지에 관한 자료를 정리하는 차원에서 이야기 해 볼까 한다.

일단 아래는 FlashPlayer10 레퍼런스 문서에 있는 Vector에 관한 기본 내용이다.











----------------------------------------------------------------
Vector 클래스를 사용하면 벡터에 액세스하고 이를 조작할 수 있습니다. 벡터는 요소의 데이터 유형이 모두 같은 배열입니다. 요소의 데이터 유형을 Vector의 기본 유형이라고 합니다. 기본 유형은 내장 클래스 및 사용자 정의 클래스를 포함한 모든 클래스일 수 있습니다. 기본 유형은 Vector 변수를 선언할 때 및 클래스 생성자를 호출하여 인스턴스를 만들 때 지정됩니다.

Array와 마찬가지로 배열 액세스([]) 연산자를 사용하여 Vector 요소의 값을 설정하거나 검색할 수 있습니다. 또한 몇 가지 Vector 메서드를 통해 요소 값을 설정 및 검색하는 메커니즘이 제공됩니다. 이러한 메서드로는 push(), pop(), shift(), unshift() 등이 있습니다. Vector 객체의 속성 및 메서드는 Array의 속성 및 메서드와 비슷하며 대부분의 경우 동일합니다. 사용하는 Array의 모든 요소가 같은 데이터 유형인 경우 항상 Vector 인스턴스를 사용하는 것이 좋습니다.

Vector의 기본 유형은 접미사 유형 매개 변수 구문을 사용하여 지정됩니다. 유형 매개 변수 구문은 다음 예제와 같이 마침표(.), 여는 각괄호(<), 클래스 이름, 닫는 각괄호(>) 순서로 구성됩니다.

var v:Vector.<String>;
 v = new Vector.<String>();

예제의 첫 번째 행에서는 변수 v를 Vector.<String> 인스턴스로 선언합니다. 즉, 이 변수는 String 인스턴스만 포함할 수 있으며 String 인스턴스만 검색할 수 있는 Vector(배열)를 나타냅니다. 두 번째 행에서는 동일한 Vector 유형(요소가 모두 String 객체인 Vector)의 인스턴스를 생성하여 v에 할당합니다.

Vector.<T> 데이터 유형으로 선언된 변수에는 같은 기본 유형 T로 생성된 Vector 인스턴스만 저장할 수 있습니다. 예를 들어 new Vector.<String>()을 호출하여 생성된 Vector를 Vector.<int> 데이터 유형으로 선언된 변수에 할당할 수는 없습니다. 기본 유형은 정확히 일치해야 합니다. 예를 들어 다음 코드에서는 객체의 기본 유형이 변수의 선언된 기본 유형과 다르므로 코드가 컴파일되지 않습니다. Sprite가 DisplayObject의 하위 클래스이지만 결과는 마찬가지입니다.

// This code doesn't compile even though Sprite is a DisplayObject subclass
 var v:Vector.<DisplayObject> = new Vector.<Sprite>();

기본 유형이 T인 Vector를 T의 수퍼 클래스의 Vector로 변환하려면 Vector() 전역 함수를 사용합니다.

Vector 클래스에는 데이터 유형 제한뿐만 아니라 Array 클래스의 경우와 다른 몇 가지 제한이 더 있습니다.

    * Vector는 밀착형 배열입니다. 1에서 6 사이의 위치에 값이 없어도 인덱스 0과 7에 값이 있을 수 있는 Array와 달리 Vector의 경우에는 모든 인덱스에 값 또는 null이 있어야 합니다.
    * Vector는 고정 길이일 수도 있습니다. 즉, 포함된 요소 수를 변경할 수 없을 수 있습니다.
    * Vector의 요소에 액세스할 때는 경계가 검사됩니다. 마지막 요소(length - 1)보다 큰 인덱스에서는 값을 읽을 수 없습니다. 현재 마지막 인덱스를 벗어난 인덱스에는 값을 설정할 수 없습니다. 즉, 기존 인덱스나 [length] 인덱스에만 값을 설정할 수 있습니다.

이러한 제한 사항으로 인해 Vector에는 모든 요소가 단일 클래스의 인스턴스인 Array 인스턴스보다 두 가지 우수한 점이 있습니다.

    * 성능: Vector 인스턴스를 사용하면 Array를 사용할 때보다 훨씬 빠르게 배열 요소에 액세스하고 반복할 수 있습니다.
    * 유형 안전: 엄격 모드를 사용하면 Vector에 잘못된 데이터 유형의 값을 할당하거나 Vector에서 값을 읽을 때 잘못된 데이터 유형을 사용하는 등의 데이터 유형 오류를 컴파일러에서 식별할 수 있습니다. 그러나 push() 메서드나 unshift() 메서드를 사용하여 Vector에 값을 추가할 때는 인수의 데이터 유형이 컴파일 타임이 아닌 런타임에 검사됩니다.
----------------------------------------------------------------

Array와 Vector
Vector는 ECMAScript4에서 도입된 기능으로 이번 FlashPlayer10에서도 적용이 되었다. 모든 요소가 동일한 객체를 포함해야 한다는 제약이 따르지만 위 레퍼런스의 내용과 같이 Array 배경보다 몇 가지 장점을 가지고 있다. 속도와 엄격한 유형검사가 그것이다.

Vector는 Array와 같이 Object를 확장하며 dynamic 클래스 이지만 Array와 같이 정의되지 않은 프로퍼티에 값을 할당할 수는 없다. 예를 들어

var arrStr:Array = new Array();
arrStr.name = "jasu";
trace(arrStr.name);

이런 형태로 실행을 하면 arrStr에 있는 name 속성을 런타임에서 생성하여 값을 참조할 수 있다. 하지만 Vector의 경우 컴파일에서 에러를 throw 한다.

var vecStr:Vector.<String> = new Vector.<String>();
vecStr.name = "jasu"; // error 1119: Access of possibly undefined property name through a reference with static type __AS3__.vec:Vector.<String>.
trace(vecStr.name);


Vector의 연속된 배열과 고정 길이
var arrStr:Array = new Array();
arrStr[0] = "100";
arrStr[1] = "200";
trace(arrStr[1]); // 정상적으로 컴파일 됨.

var vecStr:Vector.<String> = new Vector.<String>(2, true);
vecStr [0] = "100";
vecStr [1] = "200";
vecStr [2] = "300"; // vecStr의 고정된 길이(2) 값을 벗어난 인덱스 참조로 에러 발생
trace(vecStr[2]);

var vecStr:Vector.<String> = new Vector.<String>(2, false);
vecStr [0] = "100";
vecStr [1] = "200";
vecStr [2] = "300";
trace(vecStr[2]); // 정상적으로 컴파일 됨.

Vector의 constructor 첫번째(length), 두번째(fixed) default 값은 0과 false이다.


Vector에서 push 메소드의 암묵적 형 변환
var vetStr:Vector.<int> = new Vector.<int>();
vetStr[0] = true; // TypeError 발생
trace(vetStr[0]);

var vetStr:Vector.<int> = new Vector.<int>();
vetStr.push(true);
trace(vetStr[0]); // output 1 (push 메소드 실행시 암묵적으로 할당된 값의 형을 강제 변환함.

Vector의 push 시에도 Type 체크를 하여 TypeError를 throw 해주는 것이 좋을 것 같으나 현재 Vector의 경우 push로 값을 넣을 경우 암묵적으로 강제 형 변환을 하고 있다.

정리
Vector의 경우 기존에 사용하는 문법과 약간은 다른 형태를 취하고 있기 때문에 처음 접할 때는 생소하다. 하지만 기존에 사용하던 방식 “:객체명”에서 :객체명.<단일형> 으로 추가하되, 그 Vector에는 포함하고자 하는 형태만 사용한다는 것을 머리 속에서 명확하게 인지한다면 기존에 Array를 사용하여 생기던 속도 문제와 에러를 예방할 수 있다는 차원에서 좀더 명확하고 빠른 알고리즘을 작성할 수 있지 않을까 싶다.



    

설정

트랙백

댓글