Ricerca…


introduzione

[! [inserisci la descrizione dell'immagine qui] [1]] [1] nozioni di base sullo sviluppo del gioco. ------------------------------- Nota : questa serie di esercitazioni / articoli contiene molti concetti che possono essere forniti come argomenti separati prima. dobbiamo rinfrescarli nella mente e imparare un po 'di implementare le parti più critiche di un videogioco tramite actionscript-3. [1]: https://i.stack.imgur.com/CUIsz.png

carattere isometrico che anima + movimento

① i concetti utilizzati in questo esempio:

Classe uso
URLRequest + Loader + Event Caricamento mappa atlante (sprite) dal percorso esterno.
BitmapData + Sprite + beginBitmapFill +
Matrix + stageWidth & stageHeight
disegnare risorse caricate su bitmapdata,
usando bitmap piastrellate, disegnando con la trasformazione.
MovieClip + scrollRect + Bitmap + Rectangle creare e ritagliare un Movie Clip di carattere
usando Bitmap come timeline.
KeyboardEvent rilevamento degli input dell'utente
Event.EXIT_FRAME implementando la funzione Loop del gioco

② Risorse: (nessun permesso per l'utilizzo di queste risorse per scopi commerciali)

posa del personaggio tessera erba

③ Codice e commenti:

Nota: FPS 15 Utilizzato per questo tutorial, è consigliato, ma se necessario, è necessario modificare alcune parti del codice da soli.

all'inizio dobbiamo scaricare le nostre risorse da url esterni.

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 verrà salvato una volta che src_grass_tile_url è stato caricato e pronto per l'uso. in seguito implementando setGround per ottenere risorse e disegnarlo come sfondo del gioco

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

il codice è dannatamente commentato, dopo averlo terminato, è tempo di implementare il personaggio. il carattere contiene anche una risorsa che deve essere caricata allo stesso modo. così alla fine di setGround ci stiamo dirigendo verso il setCharacter che è un altro richiamo completo.

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

ora il personaggio è pronto per il controllo. è visualizzato bene e pronto per il controllo. quindi gli eventi della tastiera sono collegati e ascoltano i tasti freccia come il seguente codice:

// 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 memorizza la variabile booleana di 4 per ogni tasto freccia, il processo di spostamento deve essere eseguito all'interno della funzione di aggiornamento (Loop) del gioco, quindi dobbiamo passare le statistiche della tastiera ad esso. lascia implementare la funzione Loop .

// 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;
}

la velocità è una costante di aiuto, definisce la velocità del carattere. il codice sopra presenta un semplice movimento in 8 direzioni con questa priorità bassa: Up > Down Left > Right . quindi se la freccia su e giù viene premuta nello stesso tempo, il personaggio si sposta solo verso l' alto (non si blocca).

molto bene!!! solo un passo rimanente, l'animazione, la parte più importante di questo tutorial

cos'è veramente l'animazione? un set di fotogrammi chiave che contiene almeno un fotogramma
consente di creare il nostro oggetto keyframs, che contiene il nome dei fotogrammi chiave
e anche alcuni dati sul frame iniziale e finale di questo keyframe
Nota , nei giochi isometrici, ogni fotogramma chiave contiene 8 direzioni (può essere ridotto a 5 con l'uso di flipping)

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]}
};

dovremmo ignorare i fotogrammi rimanenti, questo esempio fornisce solo inattività ed esegue l'animazione
ad esempio il fotogramma iniziale dell'animazione inattiva con direzione destra, è: <keyframs.idle.right [0]>
ora consente di implementare la funzione 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;
}

leggere i commenti della funzione precedente, tuttavia il lavoro principale di questa funzione è lo spostamento di sprite_sheet Bitmap all'interno del personaggio MovieClip .

sappiamo che ogni aggiornamento dovrebbe essere fatto all'interno della funzione Loop, quindi invocheremo questa funzione da Loop con i relativi keyframe. questa è la funzione Loop aggiornata:

// 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;
}

leggi i commenti, ci limitiamo semplicemente a rilevare un vero keyframe tramite le statistiche della tastiera. poi fai anche la stessa cosa per rilevare l'animazione inattiva. per le animazioni inattive non abbiamo input da utilizzare per rilevare quale carattere di direzione è attivo, quindi una variabile helper simle potrebbe essere utile per memorizzare lo stato precedente della tastiera (last_keyStat).


inoltre c'è una nuova funzione flip che è un'altra funzione di aiuto usata per simulare animazioni mancanti (sinistra + up_left + down_left) anche questa funzione fa alcune correzioni che vengono commentate di seguito:

// 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
        }
    }
}

il nostro lavoro sta finendo qui. un ringraziamento speciale per Editor's che rende questo tutorial più irrinunciabile. anche Ecco una demo dal vivo di questo tutorial più un link esterno del codice completo.

④ Riferimenti esterni:



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow