Skip to content

Instantly share code, notes, and snippets.

@comfuture
Created December 24, 2010 05:02
Show Gist options
  • Save comfuture/753913 to your computer and use it in GitHub Desktop.
Save comfuture/753913 to your computer and use it in GitHub Desktop.
as3 coding convention and example
/**
* 객체들끼리 서로 밀어내며 자리를 찾아 지속적으로 동일한 간격을 유지하게 하는 프로그램 예제
* @author 거친마루 <comfuture@_GMAIL_COM_>
*/
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
[SWF(width=640, height=480, frameRate=30)]
public class Main extends Sprite
{
private var director:Director;
public function Main()
{
super();
director = new Director();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
// 스테이지 그리기
graphics.lineStyle(0x0);
graphics.drawRect(0, 0, 640, 200);
// 왼쪽 벽
var left:Wall = new Wall();
left.x = 0;
addChild(left);
// 오른쪽 벽
var right:Wall = new Wall();
right.x = 640;
addChild(right);
// 공 10개 추가
for (var i:int = 0; i < 10; i++) {
addBall();
}
// 공 추가 버튼
var button:Sprite = new Sprite();
with (button) {
y = 220;
graphics.beginFill(0x0000FF);
graphics.drawRect(0, 0, 60, 25);
graphics.endFill();
addEventListener(MouseEvent.CLICK, addBall);
}
addChild(button);
}
private function addBall(event:MouseEvent=null):void
{
var ball:Ball = new Ball();
ball.x = Math.random() * 640;
ball.y = 100;
ball.addEventListener(MouseEvent.CLICK, removeBall);
addChild(ball);
}
private function removeBall(event:MouseEvent):void
{
var ball:Ball = event.target as Ball;
removeChild(ball);
}
override public function addChild(child:DisplayObject):DisplayObject
{
// 추가하려는 차일드가 Actor면 Director가 관리
if (child is Actor)
director.addActor(child as Actor);
return super.addChild(child);
}
override public function removeChild(child:DisplayObject):DisplayObject
{
// 제거하려는 차일드가 Actor면 Director로부터도 제거
if (child is Actor)
director.removeActor(child as Actor);
return super.removeChild(child);
}
private function onEnterFrame(event:Event):void
{
var a:IAlignable;
var b:IAlignable;
// NOTE: 차일드의 갯수만큼 반복하여 해당 차일드보다 인덱스가 큰 나머지 차일드와
// 배척 테스트를 실시한다.
for (var i:int = 0; i < director.numActors - 1; i++) {
// XXX: 이 프로그램에선 액터는 IAlignable 밖에 없다고 가정.
// 실전에선 이런 가정이 버그를 만든다.
a = director.getActorAt(i) as IAlignable;
for (var j:int = i + 1; j < director.numActors; j++) {
b = director.getActorAt(j) as IAlignable;
a.repulse(b);
}
}
// NOTE: 매 프레임마다 tick을 발생한다. 플래시 플레이어는 머신 성능에 맞추어
// 프레임레이트를 조절해주므로
// 복잡한 연산이 필요하지 않은 대부분의 경우 enterframe을
// tick 대용으로 쓰기에 문제가 없다.
director.tick();
}
}
}
import flash.display.Sprite;
import flash.text.TextField;
/**
* 엑터들을 관리하는 감독(매니져) 클래스
* 틱을 관리하고 틱 마다 액터들에게 onTick 을 실행시키는 역할을 한다.
*/
internal class Director
{
protected var actors:Vector.<Actor>;
public function Director()
{
actors = new Vector.<Actor>();
}
/**
* 관리될 actor를 추가한다.
* 추가된 actor는 다음 tick 부터 onTick 메소드를 호출당한다.
* @param actor 액터
*/
public function addActor(actor:Actor):void
{
actors.push(actor);
}
/**
* actors로부터 actor를 제거
* @param actor 제거할 actor
*/
public function removeActor(actor:Actor):void
{
var found:int = actors.indexOf(actor);
if (found > -1)
actors.splice(found, 1);
}
public function get numActors():int { return actors.length; }
/**
* index 번째 액터를 반환한다.
* @param index 순번
* @return index번째 액터
*/
public function getActorAt(index:int):Actor
{
if (index >= 0 && index < numActors) {
return actors[index];
}
throw Error("Index out of range");
}
/**
* 관리당하는 Actors 의 동작을 각각 1턴씩 실행시키는 tick 을 발생한다.
*/
public function tick():void
{
for each (var actor:Actor in actors) {
actor.onTick();
}
}
}
/**
* onTick 함수를 가지고 있는 DisplayObject 원형
* Director::addActor 메소드로 추가될 수 있다.
*/
internal class Actor extends Sprite
{
public function Actor()
{
super();
}
/**
* Actor는 onTick 퍼블릭 메소드를 가지며
* 매니져클래스가 매 tick마다 이 메소드를 호출해줌으로써
* 머신의 성능과 관계 없이 동일한 속도의 애니메이션을 구현할 수 있도록 한다.
* 이 메소드는 반드시 재 구현해야 하므로 override 된 메소드에서
* super.onTick() 으로 호출하지 않도록 주의한다.
*/
/* abstract */ public function onTick():void
{
throw new Error("Must implement this");
}
}
/**
* 정렬 가능한 객체의 인터페이스
*/
internal interface IAlignable
{
/**
* x 좌표를 알 수 있어야 한다.
*/
function get x():Number;
/**
* IAlignable 을 배척할 수 있어야 한다.
* @param opponent 배척할 상대 정렬가능 객체
*/
function repulse(opponent:IAlignable):void;
/**
* 가속도를 얻을 수 있어야 한다.
* @param value 얻을 가속도
*/
function addAccel(value:Number):void;
}
/**
* 공 클래스.
* 주위 객체들과 가로 방향으로 서로 밀어내며 위치를 찾는다.
*/
internal class Ball extends Actor implements IAlignable
{
private var accel:Number;
private var factor:Number;
private var dropMode:Boolean = false;
public function Ball()
{
super();
accel = 0;
factor = 10;
graphics.beginFill(0xFF0000);
graphics.drawCircle(0, 0, 10);
graphics.endFill();
//tf = new TextField();
//addChild(tf);
}
override public function onTick():void
{
x += accel;
accel *= 0.95; // 가속력 5% 씩 자연 감소
// 벽에 부딛치면 속력 90% 잃고 반대방향으로
if (x < 0) {
accel = 0;
x = 1;
}
if (x > 640) {
accel = 0;
x = 639;
}
}
public function repulse(opponent:IAlignable):void
{
var distance:Number = x - opponent.x;
if (Math.abs(distance) > 320) return;
var amp:Number = (1 / distance) * factor;
// 보정
if (amp > 10) amp = 10;
if (amp < -10) amp = -10;
addAccel(amp);
opponent.addAccel(-amp);
}
public function addAccel(value:Number):void
{
accel += value;
}
}
/**
* 벽 클래스.
* 공과 마찬가지로 근처 객체들을 배쳑하지만 자기 자신은 밀려나지 않고 자신을 배척하는 객체에
* 반대방향으로 2배 강한 가속력을 반영한다.
*/
internal class Wall extends Actor implements IAlignable
{
private var factor:Number;
public function Wall()
{
super();
factor = 20;
}
override public function onTick():void {}
public function repulse(opponent:IAlignable):void
{
var distance:Number = x - opponent.x;
if (Math.abs(distance) > 160) return;
var amp:Number = (1 / distance) * factor ;
// 보정
if (amp > 20) amp = 20;
if (amp < -20) amp = -20;
opponent.addAccel(-amp);
}
public function addAccel(value:Number):void
{
// NOTE: 벽은 가속력을 얻지 않음
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment