Zoeken…


Invoering

[! [voer hier de afbeeldingsbeschrijving in] [1]] [1] basisprincipes van game-ontwikkeling. ------------------------------- Merk op dat deze set tutorials / artikelen veel concepten bevat die eerder als afzonderlijke onderwerpen konden worden aangeboden. we moeten ze in de geest verfrissen en een beetje leren de meest kritische delen van een videogame te implementeren via actionscript-3. [1]: https://i.stack.imgur.com/CUIsz.png

isometrische tekenanimatie + beweging

① de concepten die in dit voorbeeld worden gebruikt:

Klasse Gebruik
URLRequest + Loader + Event Atlaskaart (sprite) laden vanaf extern pad.
BitmapData + Sprite + beginBitmapFill +
Matrix + stageWidth & stageHeight
geladen bronnen tekenen naar bitmapdata,
betegelde bitmaps gebruiken, tekenen met transformatie.
MovieClip + scrollRect + Bitmap + Rectangle filmclip maken en knippen van tekens
Bitmap gebruiken als tijdlijn.
KeyboardEvent gebruikersinvoer detecteren
Event.EXIT_FRAME spel Loop-functie implementeren

② Bronnen: (geen toestemming voor het gebruik van deze bronnen voor commerciële doeleinden)

karakter pose gras tegel

③ Code en opmerkingen:

Opmerking: FPS 15 wordt voor deze zelfstudie gebruikt, maar wordt aanbevolen, maar als u meer nodig hebt, moet u een deel van de code zelf aanpassen.

eerst moeten we onze bronnen downloaden van externe URL's.

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 zal worden src_grass_tile_url zodra src_grass_tile_url is geladen en klaar is voor gebruik. in volg implementatie van setGround om hulpbron te krijgen en het te tekenen als de achtergrond van het spel

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

de code is verdomd becommentarieerd, nadat we het met grond hebben gedaan, is het tijd om karakter te implementeren. character bevat ook een resource die op dezelfde manier moet worden geladen. dus aan het einde van setGround we naar de setCharacter wat weer een complete call-back is.

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

nu is het karakter klaar om te controleren. het wordt goed weergegeven en is klaar om te worden bediend. dus toetsenbordgebeurtenissen gekoppeld en luisterend naar pijltjestoetsen als volgende code:

// 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 slaat 4 booleaanse variabele per pijltjestoets op, het verplaatsen moet gebeuren binnen de update (Loop) functie van het spel, dus we moeten er toetsenbordstatistieken aan doorgeven. laat het implementeren van Loop- functie toe.

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

snelheid is een helperconstante, definieert snelheid van karakter. bovenstaande code presenteert een eenvoudige beweging in 8 richtingen met deze prioriteit laag: Up > Down Left > Right . dus als de pijl omhoog en omlaag tegelijkertijd worden ingedrukt, gaat het teken alleen omhoog (niet bevriezen).

goed gedaan!!! nog maar één stap over, animatie, het belangrijkste deel van deze tutorial

wat is echt animatie? een set hoofdframes die ten minste één frame bevat
laten we ons keyframs-object maken, dat de naam van keyframes bevat
en ook enkele gegevens over het begin- en eindframe van dit hoofdframe
Let op , in isometrische spellen bevat elk hoofdframe 8 richtingen (kan worden teruggebracht tot 5 met behulp van flippen)

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

we moeten de resterende frames negeren, dit voorbeeld biedt alleen inactieve en startanimatie
Het startframe van inactieve animatie met richting rechts is bijvoorbeeld: <keyframs.idle.right [0]>
laat nu de implementatie van de Animator-functie toe

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

lees de opmerkingen van bovenstaande functie, maar de belangrijkste taak van deze functie is het verplaatsen van sprite_sheet Bitmap binnen het teken MovieClip .

we weten dat elke update binnen de Loop-functie moet worden uitgevoerd, dus we zullen deze functie vanuit Loop met bijbehorende keyframes aanroepen. dit is de bijgewerkte Loop-functie:

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

lees opmerkingen, we detecteren eenvoudigweg een echt keyframe via toetsenbordstatistieken. doe dan ook hetzelfde voor het detecteren van inactieve animatie. voor inactieve animaties hebben we geen belangrijke invoer om te detecteren welk richtingsteken is ingeschakeld, dus een eenvoudige helper-variabele kan handig zijn om de vorige status van het toetsenbord op te slaan (last_keyStat).


ook is er een nieuwe functie flip die een andere helperfunctie is die wordt gebruikt voor het simuleren van ontbrekende animaties (links + omhoog_links + omlaag_links) ook deze functie doet enkele fixes die hieronder wordt becommentarieerd:

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

ons werk eindigt hier. speciaal bedankt voor de Editor's die deze tutorial onbegrijpelijker maken. ook Hier is een Live-demo van deze tutorial plus een externe link met volledige code.

④ Externe referenties:



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow