ActionScript 3
게임 개발 기본 사항
수색…
소개
아이소 메트릭 문자 애니메이션 + 움직임
①이 예에서 사용 된 개념들 :
수업 | 용법 |
---|---|
URLRequest + Loader + Event | 외부 경로에서지도 (스프라이트)를로드 중입니다. |
BitmapData + Sprite + beginBitmapFill + Matrix + stageWidth & stageHeight | 로드 된 리소스를 비트 맵 데이터에 드로잉 바둑판 식으로 된 비트 맵을 사용하여 변형으로 그리기. |
MovieClip + scrollRect + Bitmap + Rectangle | 캐릭터 무비 클립 제작 및 클립핑 비트 맵을 타임 라인으로 사용합니다. |
KeyboardEvent | 사용자 입력 감지 |
Event.EXIT_FRAME | 게임 루프 기능 구현 |
② 자원 : (이 자원을 상업적 목적으로 사용하기위한 허가가 없음)
③ 코드 및 코멘트 :
참고 : FPS 15이 자습서에서 사용되는 권장 코드이지만 필요한 경우 코드 부분을 직접 수정해야합니다.
처음에는 외부 URL에서 리소스를 다운로드해야합니다.
const src_grass_tile_url:String = "https://i.stack.imgur.com/sjJFS.png";
const src_character_atlas_url:String = "https://i.stack.imgur.com/B7ztZ.png";
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setGround);
loader.load(new URLRequest(src_grass_tile_url));
setGround는 src_grass_tile_url src_grass_tile_url
드되고 사용할 준비가되면 src_grass_tile_url
됩니다. setGround를 구현하여 리소스를 얻고 게임 배경으로 그리십시오.
function setGround(e:Event):void {
/* drawing ground */
/* loader is a displayObject, so we can simply draw it into the bitmap data*/
/* create an instance of Bitmapdata with same width and height as our window*/
/* (also set transparent to false because grass image, does not contains any transparent pixel) */
var grass_bmd:BitmapData = new BitmapData(loader.width, loader.height, false, 0x0);
/* time to draw */
grass_bmd.draw(loader); // drawing loader into the bitmapData
/* now we have to draw a tiled version of grass_bmd inside a displayObject Sprite to displaying
BitmapData on stage */
var grass_sprite:Sprite = new Sprite();
// for drawing a bitmap inside sprite, we must use <beginBitmapFill> with graphic property of the sprite
// then draw a full size rectangle with that Fill-Data
// there is a repeat mode argument with true default value so we dont set it true again.
// use a matrix for scalling grass Image during draw to be more cute!
var mx:Matrix = new Matrix();
mx.scale(2, 2);
grass_sprite.graphics.beginBitmapFill(grass_bmd, mx);
grass_sprite.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
// now add sprite to displayobjectcontainer to be displayed
stage.addChild(grass_sprite);
// well done, ground is ready, now we must initialize our character
// first, load its data, i just re-use my loader for loading new image, but with another complete handler (setCharacter)
// so remove existing handler, then add new one
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, setGround);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setCharacter);
loader.load(new URLRequest(src_character_atlas_url));
}
이 코드는 우리가 작업을 마친 후에 성격을 구현하는 데 걸리는 시간입니다. 캐릭터도 같은 방법으로로드해야하는 리소스를 포함합니다. 그래서 우리는 setGround
의 끝에서 또 다른 완전한 콜백 인 setCharacter
로 향하고 있습니다.
function setCharacter(e:Event):void {
// let assuming that what is really character!
// a set of images inside a single image!
// that images are frames of our character (also provides movement for different directions)
// first load this
var character_bmd:BitmapData = new BitmapData(loader.width, loader.height, true, 0x0); // note character is transparent
character_bmd.draw(loader);
// take a look at sprite sheet, how many frames you see?
// 42 frames, so we can get width of a single frame
const frame_width:uint = character_bmd.width / 42; // 41 pixels
// as i show you above, to displaying a BitmapData, we have to draw it using a DisplayObject,
// another way is creating a Bitmap and setting its bitmapdata
var character_bmp:Bitmap = new Bitmap(character_bmd);
// but its not enough yet, a movieClip is necessary to cliping and animating this bitmap (as a child of itself)
var character_mc:MovieClip = new MovieClip();
character_mc.addChild(character_bmp);
character_bmp.name = "sprite_sheet"; // setting a name to character_bmp, for future accessing
character_mc.scrollRect = new Rectangle(0, 0, frame_width, character_bmd.height); // cliping movieclip, to dusplaying only one frame
character_mc.name = "character"; // setting a name to character_mc, for future accessing
stage.addChild(character_mc); // adding it to stage.
// well done, we have a character, but its static yet! 2 steps remaining. 1 controlling 2 animating
// at first setting a control handler for moving character in 8 directions.
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
}
이제 캐릭터가 컨트롤 할 준비가되었습니다. 그 잘 표시되고 제어 준비. 그래서 키보드 이벤트가 붙어 있고 다음과 같은 화살표 키를 듣고 :
// we storing key stats inside <keys> Object
var keys:Object = {u:false, d:false, l:false, r:false};
function keyDown(e:KeyboardEvent):void {
switch (e.keyCode) {
case 38: //up
keys.u = true;
break;
case 40: //down
keys.d = true;
break;
case 37: //left
keys.l = true;
break;
case 39: //right
keys.r = true;
break;
}
}
function keyUp(e:KeyboardEvent):void {
switch (e.keyCode) {
case 38: //up
keys.u = false;
break;
case 40: //down
keys.d = false;
break;
case 37: //left
keys.l = false;
break;
case 39: //right
keys.r = false;
break;
}
}
keys:Object
각 화살표 키마다 4 개의 부울 변수를 저장하는 keys:Object
. 이동 처리는 게임의 업데이트 (루프) 함수 내에서 수행되어야하므로 키보드 통계를 전달해야합니다. 루프 기능을 구현할 수 있습니다.
// initialize game Loop function for updating game objects
addEventListener(Event.EXIT_FRAME, loop);
// speed of character movement
const speed:Number = 5;
// this function will be called on each frame, with same rate as your project fps
function loop(e:Event):void {
if (keys.u) stage.getChildByName("character").y -= speed;
else if (keys.d) stage.getChildByName("character").y += speed;
if (keys.l) stage.getChildByName("character").x -= speed;
else if (keys.r) stage.getChildByName("character").x += speed;
}
속도는 헬퍼 상수이며, 문자의 속도를 정의합니다. 위의 코드는이 우선 순위가 낮은 간단한 8 방향 이동을 나타냅니다 : Up > Down
Left > Right
. 그래서 위쪽 화살표와 아래쪽 화살표가 동시에 눌러지면 캐릭터는 위로 움직입니다 (동결 상태가 아님).
잘 했어!!! 한 단계 만 남았습니다. 애니메이션,이 자습서의 가장 중요한 부분입니다.
정말로 애니메이션이란 무엇입니까? 적어도 하나의 프레임을 포함하는 키 프레임 세트
키 프레임의 이름을 포함하는 Keyframs Object를 만들 수 있습니다.
이 키 프레임의 시작 및 종료 프레임에 대한 일부 데이터
등각 게임에서, 각 키 프레임은 8 방향이 포함되어 있습니다 (반전의 사용과 5로 감소 될 수있다)
var keyframs:Object = {
idle: {up:[0,0], up_right:[1,1], right:[2,2], down_right:[3,3], down:[4,4]}, // [2,2] means start frame is 2 and end frame is 2
run: {up:[5,10], up_right:[11,16], right:[17,22], down_right:[23,28], down:[29,34]}
};
우리는 나머지 프레임을 무시해야합니다.이 예제는 유휴 상태 만 제공하고 애니메이션을 실행합니다.
예를 들어 방향이 오른쪽 인 유휴 애니메이션의 시작 프레임은 다음과 같습니다. <keyframs.idle.right [0]>
Animator 함수를 구현할 수있게되었습니다.
var current_frame:uint;
function animate(keyframe:Array):void {
// how it works
// just called with a keyframe with direction (each frame),
// if keyframe is what is already playing, its just moved to next frame and got updated (or begning frame for loop)
// other wise, just moved to begining frame of new keyframe
if (current_frame >= keyframe[0] && current_frame <= keyframe[1]) { // check if in bound
current_frame++;
if (current_frame > keyframe[1]) // play back if reached
current_frame = keyframe[0];
} else {
current_frame = keyframe[0]; // start new keyframe from begining
}
// moving Bitmap inside character MovieClip
var character:MovieClip = stage.getChildByName("character") as MovieClip;
var sprite_sheet:Bitmap = character.getChildByName("sprite_sheet") as Bitmap;
sprite_sheet.x = -1 * current_frame * character.width;
}
위 함수의 주석을 읽지 만,이 함수의 주요 작업은 문자 MovieClip
내에서 sprite_sheet Bitmap
이동하는 것 입니다.
우리는 모든 업데이트가 Loop 함수 내에서 수행되어야한다는 것을 알고 있습니다. 그래서 우리는 관련된 키 프레임을 가진 Loop에서이 함수를 호출 할 것입니다. 다음은 업데이트 된 루프 기능입니다.
// speed of character movement
const speed:Number = 8;
var last_keyStat:Object = {u:false, d:false, l:false, r:false}; // used to getting a backup of previous keyboard stat for detecting correct idle direction
// this function will be called on each frame, with same rate as your project fps
function loop(e:Event):void {
if (keys.u) stage.getChildByName("character").y -= speed;
else if (keys.d) stage.getChildByName("character").y += speed;
if (keys.l) stage.getChildByName("character").x -= speed;
else if (keys.r) stage.getChildByName("character").x += speed;
// animation detection
if (keys.u && keys.l) { animate(keyframs.run.up_right); flip(true); }
else if (keys.u && keys.r) { animate(keyframs.run.up_right); flip(false); }
else if (keys.d && keys.l) { animate(keyframs.run.down_right); flip(true); }
else if (keys.d && keys.r) { animate(keyframs.run.down_right); flip(false); }
else if (keys.u) { animate(keyframs.run.up); flip(false); }
else if (keys.d) { animate(keyframs.run.down); flip(false); }
else if (keys.l) { animate(keyframs.run.right); flip(true); }
else if (keys.r) { animate(keyframs.run.right); flip(false); }
else {
// if character dont move, so play idle animation
// what is the best practice to detecting idle direction?
// my suggestion is to sotring previous keyboard stats to determining which idle direction is correct
// do any better thing if possible (absolutely is possible)
// i just simply copy it from above, and replaced (keys) with (last_keyStat) and (run) with (idle)
if (last_keyStat.u && last_keyStat.l) { animate(keyframs.idle.up_right); flip(true); }
else if (last_keyStat.u && last_keyStat.r) { animate(keyframs.idle.up_right); flip(false); }
else if (last_keyStat.d && last_keyStat.l) { animate(keyframs.idle.down_right); flip(true); }
else if (last_keyStat.d && last_keyStat.r) { animate(keyframs.idle.down_right); flip(false); }
else if (last_keyStat.u) { animate(keyframs.idle.up); flip(false); }
else if (last_keyStat.d) { animate(keyframs.idle.down); flip(false); }
else if (last_keyStat.l) { animate(keyframs.idle.right); flip(true); }
else if (last_keyStat.r) { animate(keyframs.idle.right); flip(false); }
}
// update last_keyStat backup
last_keyStat.u = keys.u;
last_keyStat.d = keys.d;
last_keyStat.l = keys.l;
last_keyStat.r = keys.r;
}
주석을 읽으면 키보드 통계를 통해 진정한 키 프레임을 감지합니다. 그런 다음 유휴 애니메이션을 감지하는데도 동일한 작업을 수행하십시오. 유휴 애니메이션의 경우 어떤 방향 문자가 켜져 있는지 감지하기 위해 키 입력이 없으므로 simle 도우미 변수는 키보드의 이전 상태 (last_keyStat)를 저장하는 데 편리합니다.
누락 된 애니메이션을 시뮬레이션하는 데 사용되는 또 다른 도우미 함수 인 새로운 함수 flip
이 있습니다 (left + up_left + down_left) 또한이 함수는 아래에 설명 된 수정 사항을 수행합니다.
// usage of flip function is because of Movieclip registration point, its a fix
// as the registration point of MovieClip is not placed in center, when flipping animation (for non existing directions inside spritesheet)
// character location changes with an unwanted value equal its width, so we have to prevent this and push it back or forward during flip
function flip(left:Boolean):void {
var character:MovieClip = stage.getChildByName("character") as MovieClip;
if (left) {
if (character.scaleX != -1) {
character.scaleX = -1;
character.x += character.width; // comment this line to see what happen without this fix
}
} else {
if (character.scaleX != 1) {
character.scaleX = 1;
character.x -= character.width; // comment this line to see what happen without this fix
}
}
}
우리의 작업은 여기서 끝나고 있습니다. 이 튜토리얼을 더 이해할 수 없도록 만드는 Editor의 특별한 감사. 또한이 튜토리얼의 라이브 데모와 완전한 코드의 외부 링크가 있습니다.