Suche…


Einführung

[! [Bildbeschreibung hier eingeben] [1]] [1] Grundlagen der Spielentwicklung. ------------------------------- Beachten Sie , dass dieser Satz von Tutorials / Artikeln viele Konzepte enthält, die zuvor als separate Themen bereitgestellt wurden. Wir müssen sie im Kopf auffrischen und ein wenig lernen, die wichtigsten Teile eines Videospiels über ActionScript-3 zu implementieren. [1]: https://i.stack.imgur.com/CUIsz.png

isometrische Figur animiert + Bewegung

① die in diesem Beispiel verwendeten Konzepte:

Klasse Verwendungszweck
URLRequest + Loader + Event Atlaskarte (Sprite) wird von extern geladen.
BitmapData + Sprite + beginBitmapFill +
Matrix + stageWidth & stageHeight
Laden von geladenen Ressourcen in Bitmapdata,
Verwenden von gekachelten Bitmaps, Zeichnen mit Transformation.
MovieClip + scrollRect + Bitmap + Rectangle Charakter-Movieclip erstellen und abschneiden
Verwenden von Bitmap als Timeline.
KeyboardEvent Erkennen von Benutzereingaben
Event.EXIT_FRAME Implementierung der Loop-Funktion

② Ressourcen: (keine Erlaubnis zur Nutzung dieser Ressourcen für kommerzielle Zwecke)

Charakter Pose Grasfliese

③ Code und Kommentare:

Hinweis: FPS 15 Wird für dieses Lernprogramm empfohlen. Es wird empfohlen. Wenn Sie jedoch mehr benötigen, müssen Sie einen Teil des Codes selbst anpassen.

Zuerst müssen wir unsere Ressourcen von externen URLs herunterladen.

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 wird kalibriert, sobald src_grass_tile_url geladen und einsatzbereit ist. Im Folgenden wird setGround implementiert, um eine Ressource zu erhalten und sie als Hintergrund des Spiels zu zeichnen

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

Der Code ist verdammt gut kommentiert, nachdem wir mit Ground seine Zeit zur Umsetzung des Charakters getan haben. Zeichen enthält auch eine Ressource, die auf dieselbe Weise geladen werden muss. Am Ende von setGround wir also zu setCharacter , einem weiteren vollständigen Rückruf.

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

jetzt ist der Charakter bereit zu kontrollieren. es wird gut angezeigt und ist zur Kontrolle bereit. so dass Tastaturereignisse angehängt werden und Pfeiltasten als folgenden Code abgehört werden:

// 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 speichert die boolesche Variable von 4 für jede Pfeiltaste. Der Prozess muss innerhalb der Update-Funktion (Loop) des Spiels verschoben werden. Daher müssen Tastaturstatistiken an dieses übergeben werden. lässt die Loop- Funktion implementieren.

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

Geschwindigkeit ist eine Helferkonstante, definiert die Geschwindigkeit des Charakters. Der Code oben zeigt eine einfache Bewegung in 8 Richtungen mit dieser Priorität niedrig: Up > Down Left > Right . Wenn also der Aufwärts- und Abwärtspfeil gleichzeitig gedrückt wird, bewegt sich der Charakter nur nach oben (nicht einfrieren).

gut gemacht!!! Nur noch ein Schritt, die Animation, der wichtigste Teil dieses Tutorials

Was ist eigentlich Animation? eine Reihe von Keyframes, die mindestens ein Frame enthält
können Sie unser Keyframs-Objekt erstellen, das den Namen der Keyframes enthält
sowie einige Daten zum Start- und Endbild dieses Keyframes
Beachten Sie , dass bei isometrischen Spielen jeder Keyframe 8 Richtungen enthält (kann durch Umdrehen auf 5 reduziert werden).

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

Die verbleibenden Frames sollten ignoriert werden. In diesem Beispiel werden nur Leerlauf- und Laufanimationen bereitgestellt
Das Startframe einer Animation im Leerlauf mit der Richtung nach rechts lautet beispielsweise: <keyframs.idle.right [0]>
Jetzt können Sie die Animator-Funktion implementieren

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

Kommentare der obigen Funktion lesen, Hauptaufgabe dieser Funktion ist jedoch das Verschieben der Sprite_sheet- Bitmap im Zeichen MovieClip .

Wir wissen, dass jedes Update innerhalb der Loop-Funktion durchgeführt werden sollte. Daher rufen wir diese Funktion von Loop mit zugehörigen Keyframes auf. Dies ist die aktualisierte Loop-Funktion:

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

Wenn Sie Kommentare lesen, erkennen wir einfach einen wahren Keyframe durch die Tastaturstatistiken. tun Sie dann dasselbe, um eine Animation im Leerlauf zu erkennen. Für Leerlaufanimationen haben wir keine Tasteneingaben, um zu ermitteln, in welche Richtung das Zeichen aktiviert ist. Eine einfache Hilfsvariable könnte zum Speichern des vorherigen Tastaturzustands (last_keyStat) hilfreich sein.


Es gibt auch eine neue Funktion flip , eine weitere Hilfsfunktion, mit der fehlende Animationen simuliert werden können (left + up_left + down_left). Auch diese Funktion führt einige Korrekturen aus.

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

unsere Arbeit endet hier. Besonderer Dank geht an die Redaktion, die dieses Tutorial noch unbeständiger macht. Hier ist auch eine Live-Demo dieses Tutorials sowie ein externer Link zum vollständigen Code.

④ Externe Referenzen:



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow