수색…


소개

[! [여기에 이미지 설명을 입력하십시오.] [1]] [1] 게임 개발의 기초 . ------------------------------- 본 튜토리얼을 참고 / 기사 전에 분리 주제로 제공 할 수 많은 개념이 포함되어 있습니다. 우리는 마음 속에 그들을 새롭게하고 actionscript-3를 통해 비디오 게임의 가장 중요한 부분을 구현하는 방법을 배워야합니다. [1] : https://i.stack.imgur.com/CUIsz.png

아이소 메트릭 문자 애니메이션 + 움직임

①이 예에서 사용 된 개념들 :

수업 용법
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의 특별한 감사. 또한이 튜토리얼의 라이브 데모와 완전한 코드의 외부 링크가 있습니다.

④ 외부 참조 :



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow