CREATE.JS BASIC
animate CC란?
HTML5 CANVAS + CREATEJS !
animate CC 저장형식
.fla (compressed) = one single file .xfl (uncompressed) = 하나가 아닌 여러 폴더로 구성되어 있음.
action 패널
자바스크립트 삽입부
addchild
케릭터나 배경같은 오브젝트를 stage(cavnas 영역)에 나타낼 때 사용하는 메서드 addchild(symbol명)으로 나타낸다.
이미지를 animateCC에 업로드 한 후 Linkage에 symbol명을 저장해둔다.
function init() { c = createjs; w = stage.canvas.width; h = stage.canvas.height; buildBackground(); game = new c.Container(); stage.addChild(game); buildShip(); buildPickups(); buildObstacles(); buildHUD(); buildTitleScreen(); c.Ticker.on("tick", gameLoop); stage.on("stagemousedown", handleMouseClick); window.addEventListener("keydown", handleKeyPress); }
위 파일에는 init()으로 뺴놓았지만 되도록 즉시실행함수 (function(){})을 통해 선언하도록 합니다.
init 분석
c = createjs;
createjs는 animatecc에서 설정하는 예약어 입니다.
w = stage.canvas.width; h = stage.canvas.height;
stage란 createjs에서 cavans 영역을 담당하는 최상위 영역입니다. 하위 container들로 stage를 구성합니다. 각 canvas의 width와 height값을 담습니다.
buildBackground(); game = new c.Container(); stage.addChild(game); buildShip(); buildPickups(); buildObstacles(); buildHUD(); buildTitleScreen();
오브젝트들을 stage 영역에 띄우는 메서드들입니다. 아래쪽에서 사물을 생성하는 방법에 대해 자세히 기술하겠습니다.
c.Ticker.on("tick", gameLoop);
tick 역시 바로 아래쪽에서 기술하겠습니다. 레퍼런스
stage.on("stagemousedown", handleMouseClick); window.addEventListener("keydown", handleKeyPress);
키보드 입력 관련이므로 추후에 정리하도록 하겠습니다.
Tick 분석
createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { // Actions carried out each tick (aka frame) if (!event.paused) { // Actions carried out when the Ticker is not paused. } }
tick의 기본 문법으로 1초에 fps 값이 셋팅된 수만큼 handleTick이 실행이 됩니다. 미리 animateCC 문서를 만들때도 설정할 수 있습니다.
function handleTick(event) { if (!event.paused) { //hero.x의 처음 위치는 0입니다. hero.x += 1; } }
위와 같은 함수가 있을경우 fps값을 10으로 잡으면 hero는 1초에 10픽셀을 움직이지만 60으로 잡혀있으면 1초에 60픽셀 이동하게 됩니다.
사물띄우기
function buildShip() { ship = new lib.Ship(); ship.x = w / 2; ship.y = h - ship.nominalBounds.height - 50; var shipRect = ship.frameBounds[0]; ship.setBounds(shipRect.x, shipRect.y, shipRect.width, shipRect.height); game.addChild(ship); }
비행기를 만드는 방법입니다.
ship = new lib.ship();
ship으로 linkage를 걸어 new lib.ship()을 통해 ship이라는 변수에 담습니다.
ship.x = w / 2; ship.y = h - ship.nominalBounds.height - 50;
위에서 w를 전체 canvas 값으로 설정되었기에 w/2 로 설정할경우 중앙에 나타나게됩니다.
game.addChild(ship);
이렇게 설정한 ship은 game에 container에 넣어줍니다. 최종적으로 game 컨테이너역시 stage.addChild(game) 메서드를 통해 stage에 넣어주게 됩니다.
nominalBounds와 frameBounds
nominalBounds는 전체 크기를 반환한다 예를들어 비행기가 평소 크기는 50x50 픽셀이다가 터지는 모션일때는 60x60 픽셀로 늘어나는 애니메이션이 있을경우 nominalBounds 는 60픽셀이 된다 하지만 frameBounds는 현재 모습 그대로의 크기를 반환하여준다.
setbounds
ship.setBounds(shipRect.x, shipRect.y, shipRect.width, shipRect.height);
setBounds는 해당 obj의 영역을 지정하여준다.
gameLoop
function gameLoop(e) { if (isPlaying) { if(shields == 0) { setTimeout(endGame, 1000); ship.gotoAndPlay("explode"); c.Sound.play("Death"); } currentTime = (new Date()).getTime(); var time = Math.floor((currentTime - startTime) / 1000); timeDisplayString = convertTime(time); var seconds = time % 60 + ""; if (seconds == "20" || seconds == "40" || seconds == "60") { if (increaseDifficulty) { increaseDifficulty = false; d += 0.35; ship.gotoAndPlay("speed"); } } else { increaseDifficulty = true; } pointsDisplay.text = currentPoints; var actualSpeed = s * (d + 1); performShipMovements(); performPickupMovements(actualSpeed); performObstacleMovements(actualSpeed); performBackgroundMovements(actualSpeed); } }
tick에 의해 계속 실행되는 gameloop function입니다. isPlaying을 통해 현재 게임이 진행중인지 확인하며 shileds를 통해 계속 목숨이 남아있는지 확인을 합니다.
if(shields == 0) { setTimeout(endGame, 1000); ship.gotoAndPlay("explode"); c.Sound.play("Death"); }
쉴드가 없을경우 endGame을 통해 게임을 종료하며 gotoAndPlay 메서드를 이용하게 됩니다. gotoAndPlay는 케릭터의 다양한 모습중 explode된 모습이 출력되게 하는것으로 비행기만해도 공격중인 애니메이션 이동중인 애니메이션 폭팔하는 애니메이션등 다양한 애니메이션이 있는데 그 중 explode 상태의 애니메이션이 출력되게 설정해주는 것입니다. 자세한 내용은 animateCC 파트에서 다루겠습니다.
function boostLeft() { var newPosition = ship.x - 120; if (newPosition <= 0) { newPosition = 0; } else { c.Tween.get(backGround, { override: true }).to({ x: backGround.x + 20 }, 1000, c.Ease.linear); } c.Tween.get(ship, { override: true }).to({ x: newPosition }, 1200, c.Ease.quintOut).call(boostComplete); ship.gotoAndPlay("boostleft"); c.Sound.play("Booster", 0, 0, 200); isBoost = true; }
케릭터가 왼쪽으로 이동하는 함수입니다.
var newPosition = ship.x - 120;
위 메서드를 통해 이동할 포지션을 정하여줍니다.
newPosition <= 0
현재 케릭터가 화면밖으로 나가고 있는지를 확인합니다.
c.Tween.get(backGround, { override: true }).to({ x: backGround.x + 20 }, 1000, c.Ease.linear);
그 후 위 내용이 실행되게 되는데
c.Tween.get(backGround
c(container) 속 담겨있는 backGround를 가져옵니다. 우리가 갤로퍼를 떠올리면 알겠지만 케릭터는 항상 중앙에 있고 배경만 움직이기 떄문에 배경을 가져오게 됩니다.
to({ x: backGround.x + 20 }, 1000, c.Ease.linear);
to를 통해 이동할 위치값을 세로 잡아줍니다. x와 y는 설정해주지 않아도 자동으로 backGround.x backGorund.y 값을 가리키게 됩니다. 1000은 이동하는 속도이며 c.Ease.liner는 이동 그래프입니다.
override:false를 줄경우 이동할때마다 케릭터가 겹쳐서 나오게 되고 최종적 화면에서 사라지게됩니다.
function performPickupMovements(a) { if((nextPickup -= a) < 0) { addPickup(); } for(var i = pickups.numChildren - 1; i >= 0; i--) { var shield = pickups.getChildAt(i); shield.y += a / 2; if(shield.y > h + 100) { pickups.removeChild(shield); } var shieldGTL = shield.globalToLocal(0, 0); var shipGTL = ship.globalToLocal(0, 0); if (shieldGTL.x < shipGTL.x + ship.nominalBounds.width && shieldGTL.x + shield.nominalBounds.width > shipGTL.x && shieldGTL.y < shipGTL.y + ship.nominalBounds.height && shield.nominalBounds.height + shieldGTL.y > shipGTL.y) { currentPoints += 5; shieldsPickup(); pickups.removeChild(shield); } } }
performPickupMovements 메서드는 목숨(하트 container)을 먹었을 때 메서드입니다. 인자로받는 a는 현재 이동속도입니다.
var shield = pickups.getChildAt(i); shield.y += a / 2; if(shield.y > h + 100) { pickups.removeChild(shield); }
pickups.removeChild(shield);는 현재 맵에 나타난 하트가 맵에서 사라질 때 해당 하트를 삭제하는 메서드입니다.
var shieldGTL = shield.globalToLocal(0, 0); var shipGTL = ship.globalToLocal(0, 0); if (shieldGTL.x < shipGTL.x + ship.nominalBounds.width && shieldGTL.x + shield.nominalBounds.width > shipGTL.x && shieldGTL.y < shipGTL.y + ship.nominalBounds.height && shield.nominalBounds.height + shieldGTL.y > shipGTL.y) { currentPoints += 5; shieldsPickup(); pickups.removeChild(shield); }
var shieldGTL = shield.globalToLocal(0, 0); var shipGTL = ship.globalToLocal(0, 0); 두 메서드를 통해 각 비행기와 목숨의 위치값을 가져오게 됩니다.
위에서 기술한 nomailBounds나 frameBounds를 쓰지 않는 이유는 케릭터가 정사각형이 아니기 때문입니다. nomailBounds와 frameBounds를 는 정사각형으로 된 값을 반환하지만 globalToLocal는 해당 케릭터값을 벡터값(곡선값)으로 반환하게 됩니다. 0.0으로 초기화 하는 이유는 기본값을 설정하기 위함인데
위처럼 중심값을 0,0을 기준으로 할경우 정확하게 맞고 1,1로 할경우 케릭터 전체 값에서 1픽씩 밀린 값을 기준으로 하게 됩니다.
shieldGTL.x < shipGTL.x + ship.nominalBounds.width && shieldGTL.x + shield.nominalBounds.width > shipGTL.x && shieldGTL.y < shipGTL.y + ship.nominalBounds.height && shield.nominalBounds.height + shieldGTL.y > shipGTL.y
각 x,y값이 서로 닿는지 안닿는지 체크하는 내용입니다.
currentPoints += 5; shieldsPickup(); pickups.removeChild(shield);
닿았을경우 currentPoint를 올리고 shiledsPickup을 통해 목숨카운트를 늘리며 방금먹은 shiled를 remove 합니다.