Sprite-animation - lav sprite-animation ved hjælp af CSS.

CSS sprites er ikke nye. Siden den blev populær af A List Apart i 2004, er den ydmyge sprite blevet en basisteknik i mange webudviklerværktøjssæt. Men selvom hastighedsfordelene fra sprites er klare, diskuteres deres brug i moderne webanimation sjældent. Princippet forbliver det samme: kombiner flere billeder til én "hoved" grafik, hvorfra kun udvalgte områder vises ad gangen.

I denne artikel vil vi se på en enkel måde at skabe responsive animerede CSS-sprites, der er lette, velegnede til mobile enheder, og er også interaktive. Du behøver ikke nogen speciel grafik software, bare lidt CSS og JavaScript knowhow. Lad os komme i gang.

Oprettelse af en sprite

Sprites kommer fra videospillenes verden, så der skal gives en vis ære til deres arv og animationen af ​​Ryu fra Street Fighter. Det er overflødigt at sige, at alt kunstværk tilhører Capcom.

Vi skal kombinere hvert enkelt billede i vores animation til ét billede. Der er snesevis af værktøjer til at hjælpe dig med at skabe sprites, hvoraf mange endda laver et ledsagende stilark til dig. De indbyggede sprite-kompasfunktioner er kraftfulde, og det er SpritePad godt eksempel web interface generator. Til vores formål, dog en simpel tilgang kommandolinje god pasform.

ImageMagick, den "schweiziske hærkniv" inden for billedbehandling - gratis og open source kildekode billedbehandling, som er ideel til at automatisere opgaver, der ellers kunne blive ret arbejdskrævende, såsom at kombinere billeder. ImageMagick er også tilgængelig for næsten alle operativsystem. Mac-brugere kan installere det ved hjælp af Homebrew, Windows-tilhængere kan downloade installationsprogrammet fra det officielle websted, og Linux-tilhængere behøver sandsynligvis ikke nogen forklaring fra mig.

Gem animationsrammer af identisk størrelse i en mappe som en sekvens i PNG-format. Bryd ud af din kommandolinjeterminal, cd til den mappe, hvor dine billeder bliver gemt, og kør følgende kommando:

Konverter *.png -tilføj resultat-sprite.png

Denne kommando fortæller ImageMagick at lodret flette alle .PNG-filerne i vores mappe ('*' er et almindeligt tegn) til en komplet sprite kaldet "result-sprite.png". Dette er grafikken, vi vil bruge som baggrundsbillede i CSS'en for det element, vi vil animere, og ændre dets position for at iterere over hver frame i sekvensen.

Ren CSS-animation

Vi starter enkelt og animerer vores sprite med CSS3 keyframe-animation. Hvis du ikke er bekendt med den grundlæggende syntaks, vil en artikel af Louis Lazaris på Smashing Magazine bringe dig i gang. Vi vil animere baggrundspositionen af ​​vores element fra 0% til 100% (top til bund), og eksponere hver enkelt frame på skift.

At tillade animationen at blive en kontinuerlig sekvens, som standard, vil få sprite-rammer til at dukke op, og illusionen vil bryde. Men ved at bruge steps()-funktionen kan vi styre antallet af tegnede keyframes. På grund af den måde, baggrundsposition fungerer på, skal vi, givet procenter, indstille et antal handlinger for at producere et billede mindre end det samlede antal billeder i vores animation.

Eksperimenter med følgende CodePen for at få en idé:

Bemærk, at vi har defineret bredden og højden af ​​vores element for nøjagtig match med data fra én frame for at undgå at lække tidligere eller efterfølgende frames. Vi brugte stenografi til at indstille animation-iteration-tælleren til "uendelig" og animationsvarigheden til 3,5 sekunder, hvilket fik Ryu til at udføre sin combo indtil tidens ende.

Det er dog langt fra perfekt. Prøv at ændre højden på vores Ryu-element, eller anvend en baggrundsstørrelse på vores sprite, og effekten vil bryde. Vores animerede element er ikke tilpasset og afhænger af vedvarende pixeldimensioner.

Tilpasning

Hvis vi frit vil ændre størrelsen på en animeret sprite, har vi to muligheder. Nøglen til begge muligheder er, at vores sprite er afhængig af en konstant andel frem for en bestemt størrelse. Vi skal opretholde proportioner for korrekt skalering.

Den første mulighed er at indstille vores sprites baggrundsstørrelse til 100 % af vores elements bredde, konvertere vores elements bredde og højde fra pixels til ems og derefter ændre grundskriftstørrelsen efter behov. Dette vil give dig mulighed for at opnå ønsket effekt, og kan være egnet til de fleste formål, men vil ikke føre til ægte fluiditet.

Vores anden mulighed er at bruge Marc Hinses rene CSS faste proportioner. Dette trick er brugt i demoen nedenfor for at give vores sprite en flydende bredde på 70%.

Denne nem metode og det er velegnet til mobile enheder, hjælper med at opnå adaptiv frame-by-frame animationseffekt. Browser support til CSS animationer fremragende, med den eneste begrænsende faktor - CSS syntaks keyframe. Herunder det passende -webkit- leverandørpræfiks, og effekten virker i alle moderne browsere, herunder Internet Explorer 10 og derover.

JavaScript animation

Hvad hvis vi ønsker mere præcis kontrol over vores animation, hvilket normalt er muligt med CSS3 keyframe-syntaks? Lad os antage, at vi i stedet for en enkelt looping-animation vil kombinere flere sprites og binde triggere og timing-funktioner til rullebjælkens position?

For at opnå dette er vi nødt til at animere vores sprites med JavaScript ved hjælp af kraften fra GreenSock-animationsplatformen med Jan Paepkes fremragende ScrollMagic-bibliotek. For dem, der ikke er bekendt med denne kraftfulde kombination, er de officielle ScrollMagic-demoer et godt sted at starte.

Det er de kraftfulde værktøjer, hvorpå hele serien blev skrevet læremidler. Så i stedet for at dykke ned i detaljerne om, hvordan man arbejder med ScrollMagic, vil vi demonstrere nogle af de kraftfulde synkroniseringsfunktioner, der nu er tilgængelige for os. De grundlæggende sprite-principper forbliver de samme som i den, vi beskrev. ren CSS animationer.

Lad os først udløse en to sekunders animation bestemt stilling rulle. Det skal slutte på det sidste billede og afspilles baglæns, når brugeren ruller tilbage (selvom det sker midt i afspilningen). Vi vil holde os til videospilstemaet og bruge en sprite fra Westwood Studios strategiklassiker Rød alarm, i øjeblikket udgivet som freeware.

Bemærk venligst, at vi bruger ovenstående em-metode, prøv at ændre skriftstørrelsen for at skalere spriten op og ned. Vi definerede TweenMax.to-animationen ved at indstille slutværdien af ​​vores elements lodrette baggrundsposition til 100% og brugte SteppedEase-metoden til at opnå den ønskede ramme-for-ramme-effekt.

Spriten forbliver i en fast position under hele animationen, som i øjeblikket styres af brugerens rulning. For at opnå denne effekt gav vi vores ScrollMagic.Scene en varighed på 1500px og brugte setPin-metoden til at fikse det overordnede element for at få et fuldt billede af handlingen. For en mere detaljeret opdeling af disse metoder, sørg for at tjekke den fremragende dokumentation på ScrollMagic-webstedet.

Browserunderstøttelse af JavaScript sprite-animationer er endnu bedre end vores rene CSS-version. ScrollMagic og GreenSock understøttes i alle moderne browsere, inklusive IE 9+, med browserpræfikser og uoverensstemmelser, der håndteres automatisk. Mobil enhedssupport er også god; Disse metoder fungerer på iOS 8+ uden nogen særlige løsninger, herunder gentegning undervejs og tabsfri rulning.

Konklusion

Dette er blot en dråbe i spanden af, hvad der kan opnås ved at kombinere adaptiv CSS sprites med avanceret animeret rulning, men jeg håber at kunne inspirere andre til at begynde at eksperimentere. Jeg vil gerne se nogle websteder, der bruger variationer af metoderne beskrevet ovenfor. Du kan tage et kig på det personlige projekt, der fik mig til at udforske sprite-animation i første omgang - videospilsfans kan måske bemærke et mønster.

Som Rachel Nabors sagde: "Vær venlig at animere ansvarligt." Bare fordi noget kan animeres, betyder det ikke, at det skal gøres. Og hvis du vælger at animere, så gør det bevidst, smukt og med formål.

Animation baseret på spritesheets bruges ret meget i spil. i lang tid. Inklusiv i så populære spil som Legend of Zelda: A Link to the Past eller Cut the Rope. I denne artikel vil vi tale om, hvordan en sådan animation fungerer, og hvordan man programmerer den. Vi vil bruge JavaScript som et programmeringssprog, men du kan vælge et hvilket som helst, der passer dig.

Før vi taler om, hvordan man programmerer spritesheet-animation, bør vi definere tre udtryk: animation, sprite og faktisk spritesheet.

Animation

Tilbage i 1872 fik den engelske og amerikanske fotograf Eadweard Muybridge til opgave at finde ud af, om en hest hæver alle 4 ben, mens den løber. Muybridge brugte flere kameraer til at gøre dette, som tog et billede, da hesten og rytteren løb forbi. Som følge heraf modtog fotografen 16 billeder af hesten. Forresten, i et par af dem er alle fire hestens ben faktisk i luften.

Muybridge gentog senere eksperimentet, men placerede alle fotografierne i en enhed, der kunne projicere dem i hurtig rækkefølge, så illusionen af ​​en hest, der løb, blev opnået. Denne enhed blev senere kaldt en filmprojektor.

Behandle hurtig ændring billeder for at skabe illusionen om bevægelse blev kaldt animation.

Sprite

Sprite er en grafisk billede, som er placeret på en stor scene. Det vil sige, at spriten er en del af denne scene.

Sprites er populær måde til at skabe store og komplekse scener, da du kan manipulere og kontrollere hvert billede individuelt. Dette giver dig mulighed for bedre at kontrollere, hvad der sker på scenen.

Det er slet ikke ualmindeligt, at der er snesevis eller endda hundredvis af sprites i et spil. At indlæse hver af dem som et individuelt og separat billede vil forbruge meget hukommelse og computerkraft. For at undgå disse gener anvendes spritesheets.

Når du sætter mange sprites i ét billede, får du et spritesheet.

Spritesheets bruges til at fremskynde processen med at vise billeder på skærmen. Det er meget mere effektivt og hurtigere at indlæse en stort billede og vise en del af det end at indlæse en masse sprites og vise dem.

Animation baseret på et spritesheet er ikke andet end et almindeligt spritesheet, som ikke vises helt, men billede for billede. Desuden ændrer disse rammer sig fra helt høj hastighed. Funktionsprincippet for en sådan animation ligner driftsprincippet for filmprojektoren skabt af Muybridge.

Dele af et spriteark

Selve spritearket består af to dele: rammer og løkker.

Rammen er et almindeligt billede (eller sprite). Ved at bruge eksemplet med Muybridges hest: hvert billede af en hest i hele animationen er en ramme.

Hvis du sætter rammerne i den rigtige rækkefølge, får du kontinuerlig bevægelse. Dette er cyklussen.

Programmering af spritesheet-animation

Der er 3 trin i oprettelse af spritesheet-animation:

  1. Oprettelse af et billede.
  2. Opdater billedet for hver animationsramme.
  3. Tegning af animation på skærmen.

Oprettelse af et billede

Lad os starte med at oprette en funktion (eller klasse), der skal håndtere vores spritesheet-animation. Denne funktion vil oprette et billede og angive dets sti, så vi kan bruge det til yderligere tegning på skærmen.

Funktion SpriteSheet(sti, frameWidth, frameHeight) ( var image = new Image(); var framesPerRow; // beregn antallet af frames i en række efter indlæsning af billedet var self = this; image.onload = function() ( framesPerRow = Math.floor( image.width/frameWidth) image.src = sti)

Da spritesheets kan have forskellige størrelser, så kender vi størrelsen af ​​en ramme, og vi kender antallet af rammer i en række og kolonne.

Det er meget vigtigt, at hver frame i animationen har samme størrelse, ellers bliver animationen ikke naturlig.

Opdatering af billeder

For at opdatere spritesheet-animationen skal vi ændre den ramme, der er tegnet ind i øjeblikket. Nedenfor er spritearket opdelt i rammer, som hver er nummereret.

Du skal ændre den aktuelle ramme til den næste ikke øjeblikkeligt, men efter et stykke tid. Det vil sige, at når du viser en af ​​rammerne, skal du lave en vis forsinkelse, før du ændrer den.

Det er vigtigt at bemærke, at ikke alle spriteark har en tilgængelig sprite i hver enkelt af sine rammer. For eksempel mangler den 16. sprite i Muybridges spriteark. Hvis vi ikke havde taget højde for dette, ville animationen være blevet til med et blitz.

Det er nemt at undgå stænk: vi gengiver ikke disse tomme billeder:

Funktion SpriteSheet(sti, frameWidth, frameHeight, frameSpeed, endFrame) ( // kode fjernet for kortheds skyld var currentFrame = 0; // nuværende frame til at tegne var counter = 0; // ventetæller // animation update this.update = function( ) ( // hvis tiden er inde til at ændre rammen, så skift hvis (tæller == (frameSpeed ​​​​- 1)) currentFrame = (currentFrame + 1) % endFrame; // opdatering af ventetælleren = (tæller + 1) % frameSpeed;

Når vi bruger den resterende (%) operation på currentFrame, opretter vi en kontinuerlig løkke af tal fra 0 til endFrame. På denne måde afspiller vi kun de rammer, som vi har billeder for i spritearket.

Tegning animation

Vi beregner linjen med den ønskede ramme ved at tage resten af ​​at dividere nummeret på den aktuelle ramme med antallet af rammer i linjen. Vi beregner også kolonnen med den ønskede frame ved at dividere nummeret på den aktuelle frame med antallet af frames i rækken.

Ved hjælp af de fundne værdier kan vi finde koordinaterne for den ramme, inden for hvilken den ønskede ramme er placeret:

// tegner den aktuelle ramme this.draw = function(x, y) ( // beregner kolonnen og rækken med den ønskede ramme var row = Math.floor(currentFrame / framesPerRow); var col = Math.floor(currentFrame % framesPerRow) ); ctx .drawImage(image, col * frameWidth, row * frameHeight, frameWidth, frameHeight, x, y, frameWidth, frameHeight); )

Nu er vi klar til at lave animationen:

Spritesheet = new SpriteSheet("Walk_Cycle_Image.png", 125, 125, 3, 16); funktion animate() ( requestAnimFrame(animate); ctx.clearRect(0, 0, 150, 150); spritesheet.update(); spritesheet.draw(12.5, 12.5); )

Flere løkker i ét spritesheet

Ovenstående kode vil fungere for ethvert spriteark, der indeholder præcis én løkke. Der er dog spritesheets med flere loops (animationer).

For at arbejde med flere animationer i ét spritesheet skal vi ændre vores arbejdsprincip.

Oprettelse af et billede

Vi gemmer oplysninger om billedet og dets dimensioner:

Funktion SpriteSheet(sti, frameWidth, frameHeight) ( this.image = new Image(); this.frameWidth = frameWidth; this.frameHeight = frameHeight; // beregn antallet af frames i linjen efter indlæsning af billedet var self = this; this.image onload = function() ( self.framesPerRow = Math.floor(self.image.width / self.frameWidth); this.image.src = sti;

Opdatering og tegning af animation

Animation()-funktionen vil være ansvarlig for at opdatere og tegne billedet:

Funktion Animation(spritesheet, frameSpeed, startFrame, endFrame) ( var animationSequence = ; // array holding ordren af animationen var currentFrame = 0; // den aktuelle ramme til at tegne var counter = 0; // holde styr på billedhastighed // oprettelse af en sekvens af animationsrammenumre for (var frameNumber = startFrame; frameNumber<= endFrame; frameNumber++) animationSequence.push(frameNumber); // обновление анимации this.update = function() { // если подошло время смены кадра, то меняем if (counter == (frameSpeed - 1)) currentFrame = (currentFrame + 1) % animationSequence.length; // обновление счетчика ожидания counter = (counter + 1) % frameSpeed; }; // отрисовка текущего кадра this.draw = function(x, y) { // вычисление количества кадров в строке после загрузки изображения var row = Math.floor(animationSequence / spritesheet.framesPerRow); var col = Math.floor(animationSequence % spritesheet.framesPerRow); ctx.drawImage(spritesheet.image, col * spritesheet.frameWidth, row * spritesheet.frameHeight, spritesheet.frameWidth, spritesheet.frameHeight, x, y, spritesheet.frameWidth, spritesheet.frameHeight); }; } spritesheet = new SpriteSheet("Walk_Cycle_Image.png", 125, 125); walk = new Animation(spritesheet, 3, 0, 15); function animate() { requestAnimFrame(animate); ctx.clearRect(0, 0, 150, 150); walk.update(); walk.draw(12.5, 12.5); }

Da spritearket indeholder mange frames til forskellige animationer, skal vi kende antallet af de frames, hvis animation vi vil afspille nu.

Efterord

Vi skrev et simpelt løberspil med den samme animation, som blev diskuteret i artiklen. Du kan se kildekoden i artiklen ved at gå til

Udtrykket "sprites" opstod, da computere lærte at kombinere levende billeder med en stillestående baggrund. Dette skete i midten af ​​halvfjerdserne af forrige århundrede. Det første stykke hardware, der tillod dig at vise et billede med sprites på en skærm, var denne chip fra Texas Instruments, som fandt udbredt brug i hjemmecomputere og spillekonsoller i starten af ​​firserne.

Hvis en sprite er et stillbillede, så udgør en serie af disse billeder, der hurtigt erstatter hinanden, en animation kaldet en sprite. Denne type animation er anderledes ved, at det ikke er hele rammen (rammen), der ændrer sig på skærmen, men kun et lille stykke af den, hvor sprites optræder. Sprite-animation kaldes også nogle gange for software-animation.

Da computere var store, var sprites meget små, målte 8 gange 8 pixels og kunne farves i 4-8 farver. Den mest berømte helt i de første sprite-spil var alles favorit Mario. På grund af spritens lille størrelse var det umuligt at tegne en mund til denne karakter: sådan så Marios karakteristiske overskæg ud, og helten blev genkendelig.

Efterhånden som computerkraften steg, voksede sprites sig større og blev i fuld farve, mere og mere som rigtige tegneseriefigurer. Toppen af ​​spredningen af ​​sprite-animation fandt sted i halvfemserne: både på computere og på spillekonsoller blomstrede genren "kampspil", hvor alle kunne føle sig som Bruce Lee. Den dengang populære "action"-genre i biografen bidrog til dette. Hvem af os har ikke spillet Mortal Kombat, baseret på hvilken en film endda blev lavet! Disse "kampspil" var helt sprite-baserede.

Prøv sprites fra Street Fighter-spil

Men "platform arcade"-genren blev endnu mere populær, hvor sprite-animation virkelig herskede. I begyndelsen af ​​halvfemserne begyndte en renæssance-æra i Disney-studiet: nye mesterværker blev født år efter år. Spil baseret på studiets berømte tegnefilm var i hidtil uset efterspørgsel. Mulighederne for sprite-animation er steget så meget, at du i et arkadespil bogstaveligt talt kunne fordybe dig i tegnefilmens atmosfære. Spillene var sande mesterværker - matchede de originale animerede funktioner! "Aladdin", "Løvernes Konge", "Hercules", "Junglebogen" og mange andre spil med Disney-logoet er for evigt inkluderet i den gyldne arkadesamling. Dette var sprite-animationens virkelige storhedstid.

Arcade Løvernes Konge (1994)

Alle sprites, der udgør animationen, gemmes i en speciel fil i form af ét stort rasterkort (bitmap). Dette billede kaldes et "kort", fordi spilprogrammet konstant søger efter den ønskede sprite ved hjælp af koordinater. Lad os sige, at animationsområdet er 16 gange 32 pixels, og startpunktet på bitmap'et på det krævede tidspunkt er den 84. pixel vandret og den 460. pixel lodret. Disse data indtastes af programmøren i spilkoden. Et bitmap er således en slags "storyboard" af sprite-animation, "hardwired" i programkoden og skjult for den eksterne seer. For eksempel, sådan ser dette "storyboard" ud for spillet "Løvernes Konge", karakteren er lille Simba:

En anden populær genre af spil, der brugte sprites, var animerede quests. Det er værd at bemærke, at i dag er dette måske den eneste genre af desktop-spil, hvor sprite-animation stadig er meget udbredt. Mange af os er bekendt med spillene i serien "Petka og Vasily Ivanovich", "Pilot Brothers" og andre populære indenlandske animerede quests. De er også sprite-baserede.

Skærmbillede fra spillet "Pilot Brothers: In the Footsteps of the Striped Elephant" (1997)

Ved årtusindskiftet begyndte traditionel animation at vige for 3D-animation. Det samme skete i computerspilverdenen - sprite-karakterer begyndte at blive erstattet af polygonale. Selv spil baseret på nye Disney-tegnefilm, lavet ved hjælp af klassiske animationsteknikker, blev overført til tredimensionelt rum. Eksempler - "Tarzan", "Lilo og Stitch", "Brother Bear", "Prinsessen og frøen".

Skærmbillede fra spillet "Brother Bear" (2002)

Sprite-animation forsvinder dog ikke fra scenen lige foreløbig. Det bliver fortsat meget brugt i design (tænk på "assistenterne" fra tidligere versioner af Microsoft Office), i uddannelsesprogrammer og også i spil - primært til mobile enheder. Derudover bruges i disse dage sprites i Flash- og GIF-animation, men de gemmes ikke som en bitmap, men i formatet af en multi-lags grafikfil, hvor hver animationsramme er placeret på et separat lag. I Adobe After Effects kan du placere en animation oprettet ud fra en sekvens af grafikfiler i en scene. Strengt taget er dette også sprite-animation, kun kilden til sprites er ikke et flerlagsbillede eller bitmap, men en separat mappe på computeren. Så sprites, som magiske feer, er altid klar til at komme animatoren til undsætning, især når de laver spil! :)

Sprites fra spillet Angry Birds (2009)


Sprites fra spillet Dust: an Elysian Tail (2012)

rotationsvariabel, som indstiller spritens rotationsvinkel.

Et tryk på A-knappen får spriten til at skifte farve - den nye farve vælges tilfældigt. Et tryk på S-knappen fører til et fald i skala-parameteren, som er ansvarlig for størrelsen af ​​spriten, der vises på skærmen, et tryk på W-knappen fører til en stigning i skala-parameteren og følgelig til et fald i størrelsen af spriten.

Oprindelsesparameteren angiver oprindelsen af ​​koordinaterne for spriten. Som standard svarer spritens positionskoordinat til dens øverste venstre hjørne. I forhold til venstre øverste hjørne, i dette tilfælde roteres spriten også. For at rotationen kan ske omkring midten af ​​spriten, skriver vi til Variablen Origin resultatet af at dividere længden og bredden af ​​spriten med 2. Som et resultat heraf vises spriten for det første på skærmen under hensyntagen til tage højde for den nye oprindelse af koordinater for det, og for det andet, under rotation sprite det gøres omkring midten af ​​sprite, ikke omkring dens øverste venstre hjørne. Lad os se nærmere på Draw-kommandoen, som vi brugte til at vise spriten på skærmen. I tabel 10.1. hver af dens parametre er beskrevet.

Tabel 10.1.
Beskrivelse af Draw-kommandoparametrene Element
Beskrivelse MySprite
Sprite tekstur position
Sprite position spRec
Et rektangel, der afgrænser en sprite i en tekstur, kan bruges til at vise forskellige områder af teksturen farve
Sprite nuance rotation
Sprite rotationsvinkel oprindelse
Oprindelsen af ​​spritekoordinaterne, i forhold til hvilke spriten roteres og vises på skærmen skala
Sprite størrelse. SpriteEffects.None
Sprite output effekt - giver dig mulighed for at rotere spriten 180 grader eller, hvis parameteren er sat til Ingen - påvirker ikke spritens position på nogen måde (flyde) 0

Sprite dybde. Kan variere fra 0 til 1

I fig. 10.1. du kan se spilskærmen for projekt P6_1.

Lad os nu se på sprite-animation.

Sprite animation

Lad os skabe en sprite, som vi vil animere. Vi brugte en sprite til animation, bestående af to billeder (fig. 10.2.). Når den er animeret, vil der blive skabt en blinkende effekt - den sorte farve erstattes af grøn.

Lad os skabe et standard spilprojekt, lad os kalde det P6_2. I notering 10.2. Koden til Game1-klassen vises. I dette projekt gjorde vi uden at oprette yderligere spilkomponenter og implementerede al den nødvendige funktionalitet i hovedspilklassen.

Brug af System; ved hjælp af System.Collections.Generic; bruger Microsoft.Xna.Framework; bruger Microsoft.Xna.Framework.Audio; bruger Microsoft.Xna.Framework.Content; bruger Microsoft.Xna.Framework.GamerServices; bruger Microsoft.Xna.Framework.Graphics; ved hjælp af Microsoft.Xna.Framework.Input; bruger Microsoft.Xna.Framework.Net; bruger Microsoft.Xna.Framework.Storage; navneområde P6_2 ( ///

/// Dette er hovedtypen for dit spil /// offentlig klasse Spil1: Microsoft.Xna.Framework.Game ( GraphicsDeviceManager-grafik; SpriteBatch spriteBatch; Texture2D-tekstur; // Antal rammer i billedet const int frames = 2; // Animation frekvens - frames per second const int framesPerSec = 10; / / Current frame for at vise int numberOfFrame=0 //Tid, hvor en frame vises float timePerFrame //Tid, der er gået siden starten af ​​visningen af ​​den aktuelle frame float totalElapsed; rammeposition i billedet Rectangle sprRec; //Frame width int widthFrame = 64 public Game1() ( graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; ) protected override void Initialize() (position = new Vector2); ( 100, 100); //Tiden for en frame beregnes som resultatet af at dividere 1 sekund //med det angivne antal billeder pr. sekund timePerFrame = (float)1 / framesPerSec = new Rectangle(0, 0, 64, 64); base.Initialiser(); sidste opkald procedurer totalForløbet += elpT; //hvis den samlede visningstid for en sprite er større end tiden //allokeret til en sprite if (totalElapsed > timePerFrame) ( //Hvis framenummeret er lig med antallet af frames-1 if (antalOfFrame == frames- 1) ( //indstil rammenummeret til 0 numberOfFrame = 0; ) //ellers øg rammenummeret med 1 else numberOfFrame++; af rammen er Y 0, længde og bredde er altid 64 sprRec = new Rectangle((int)widthFrame * numberOfFrame, 0, 64, 64 //reset totalElapsed to 0 totalElapsed = 0 ) protected override void Update(GameTime); gameTime) ( // Kalder sprite-animationsproceduren //passer den forløbne tid som en parameter efter //det sidste kald Update ChangeFrame((float)gameTime.ElapsedGameTime.TotalSeconds); base.Update(gameTime); beskyttet tilsidesættelse void Draw( GameTime gameTime) ( graphics.GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin( ); spriteBatch.Draw(tekstur, position, sprRec, Color.White);

spriteBatch.End();

base.Draw(gameTime);

) ) )

Notering 10.2. Spil 1 klasse kode Hvis vi kort beskriver arbejdssekvensen i dette projekt, får vi følgende: i Update()-løkken kalder vi ChangeFrame()-proceduren, hvortil vi overfører den tid, der er gået siden sidste Update()-kald. Inde i denne procedure tjekker vi, om den aktuelle tid er nok

Del 2. Animering af spriten.

En lille teori:

Så vil kun den første ramme være synlig på skærmen. Resten forsvinder. Hvorefter vi blot flytter billedet en ramme frem, og i rektanglet vil der efter hver bevægelse dukke en ny ramme op. Dem. rammerne vises én efter én, og der opstår en animationseffekt.

Så hvis du har forstået teorien, så er det tid til at gå videre til praksis (hvis ikke, kan du stille mig et spørgsmål i PM, jeg svarer på alt =)).

Animering af en sprite:

Så for lad os starte til klassen Sprites og tilføje nogle hjælpevariable.

Efter linjen

200?"200px":""+(this.scrollHeight+5)+"px");">offentlig Vector2 spritePosition;

Lad os skrive ned:

200?"200px":""+(this.scrollHeight+5)+"px");">int FrameCount; //Antallet af alle frames i billedet (vi har 10)
int frame;// hvilken ramme er tegnet i øjeblikket
float TimeForFrame;//Hvor længe skal en frame vises (hastighed)
float TotalTime;//hvor meget tid der er gået siden det forrige billede blev vist

Alt er klart her fra kommentarerne. Det eneste, der kan give dig et spørgsmål, er den sidste variabel. Denne variabel forhindrer værdien " TimeForFrame» ude af rammen, og hvis den går ud, så opdaterer den sidste variabel rammen. For eksempel, hvis vi har et billede, der skal vises på skærmen i nøjagtigt et sekund, og der allerede er gået 1 sekund og et millisekund, så er variablen Samlet tid signalerer, at det er tid til at vise en ny ramme. Og så lad os fortsætte.

Lad os skabe en ny overbelastet konstruktør og skrive en variabel i dens parametre, der vil være ansvarlig for hastigheden. Vi vil også tildele værdier til de resterende variable. Sådan ser koden til den anden konstruktør ud:

200?"200px":""+(this.scrollHeight+5)+"px");">offentlige Sprites(int speedAnimation)
{
ramme = 0;
TimeForFrame = (float)1 / speedAnimation;
TotalTid = 0;
}

Lad os nu tage det hele i rækkefølge.

Som jeg allerede har sagt, er der i parentes (i parametrene) en variabel, der beregner animationshastigheden ud fra den tid, der er allokeret for en frame.

Den første linje tæller, hvilken ramme der i øjeblikket vises på skærmen. Så animationen er endnu ikke begyndt at spille, nul-rammen vises på skærmen (dvs. ingenting).

Den anden variabel beregner animationshastigheden. Dette nummer afhænger af den hastighed, du har indtastet.

Og den sidste variabel er også nul, da animationen ikke afspilles endnu.

Lad os nu tilføje ny metode til vores klasse. Lad dette være metoden Udate(), med parameter spilletid. Umiddelbart efter den anden konstruktør skriv:

200?"200px":""+(this.scrollHeight+5)+"px");">
{

Du kan spørge, hvorfor vi har brug for spilletid? Meget simpelt. Som navnet antyder, er denne variabel ansvarlig for spilletid. Dette er den eneste variabel, jeg kender, hvormed vi kan øge tiden til en handling.
I spilletid Alle tidsværdier for vores spilprojekt er gemt. Hun tæller tidspunktet for at kalde metoden Opdater()(klasse Spil 1), spillets varighed og alt det der. Vores variabel kan også vise denne tid i form af millisekunder, sekunder, minutter osv.
Nøjagtig spilletid hjælper os med at beregne hastigheden af ​​vores animation.

Så først og fremmest i metoden Opdatering vi vil beregne Samlet tid. For at gøre dette, lad os gøre det, så hver gang metoden kaldes Opdatering, denne variabel steg med et vist beløb tid (i vores tilfælde er dette den tid, hvor metoden udføres fuldstændigt Opdatering). For at gøre dette vil vi skrive i vores metode:

200?"200px":""+(this.scrollHeight+5)+"px");">

Lad os derefter tjekke tilstanden hvis Samlet tid mere TimeForFrame(dvs. fremvisningstiden er slut), vil vi øge billedet med én (vis det næste billede), og også mindske Samlet tidTimeForFrame, hvad ville være værdien for den nye ramme Samlet tid var originalen. Sådan ser vores metode ud efter redigering:

200?"200px":""+(this.scrollHeight+5)+"px");">offentlig ugyldig opdatering (GameTime gameTime)
{
FrameCount = spriteTexture.Width / spriteTexture.Height;

TotalTime += (float)gameTime.ElapsedGameTime.TotalSeconds;

Hvis (TotalTime > TimeForFrame)
{
ramme++;

TotalTime -= TimeForFrame;
}

Den første linje i denne metode beregner antallet af frames i en tekstur (hvis du husker, har vi ti frames (animationsrammer) tegnet på teksturen). Alle rammer skal som sagt være lige store, og gerne kvadratiske. Da mine rammer er firkantede, er højden af ​​en af ​​dem lig med dens bredde.
Men højden af ​​en ramme er lig med højden af ​​hele teksturen. Kort sagt, fra ovenstående er det klart, at højden af ​​hele teksturen ( spriteTexture.Højde) er lig med bredden og højden af ​​en ramme. Og da rammerne er de samme, kan du dividere bredden af ​​hele teksturen med bredden af ​​en ramme. Dette vil give os antallet af frames.

Ellers er alt klart, alt er klart, bortset fra stregen

200?"200px":""+(this.scrollHeight+5)+"px");">ramme = frame % (FrameCount - 1);

Det er nødvendigt, så antallet af rammer ikke overstiger det tilladte antal (i vores tilfælde er der kun 10 rammer, og for at den ellevte ikke vises på skærmen, brugte jeg denne linje).

Nå, vi kan sige, at den sværeste del er forbi. Tilbage er kun at tegne vores animation. For at gøre dette vil vi oprette en ny metode kaldet DrawAnimation med parameter spriteBatch. Sådan skal det se ud umiddelbart efter oprettelsen:

200?"200px":""+(this.scrollHeight+5)+"px");">offentlig ugyldig DrawAnimation(SpriteBatch spriteBatch)
{

Lad os nu beregne bredden af ​​en ramme. Men hvis animationsrammerne er firkantede, så behøver du ikke at gøre dette, men for at gøre det tydeligere for dig, tilgiver vi dig stadig. For at gøre dette skal du bare dividere hele teksturens bredde med antallet af rammer. Lad os forestille os, at vores tekstur er 300 pixels bred. Og hvis vi deler disse 300 pixels i de 10 rammer, der er tegnet i dem (og rammerne er de samme), så viser det sig, at bredden af ​​hver frame er 30 pixels. Men jeg gav ikke de rigtige værdier af vores tekstur, men fiktive værdier (faktisk er bredden af ​​min tekstur 960 pixels, men din kan være en hvilken som helst anden). Kort sagt vil dette være den første linje i den nye metode:

200?"200px":""+(this.scrollHeight+5)+"px");">int frameWidth = spriteTexture.Width / FrameCount;

Lad os nu tegne det samme rektangel, der viser en enkelt animationsramme. Jeg talte om ham i begyndelsen af ​​denne del af artiklen. For at gøre dette skal du umiddelbart efter den linje, vi lige har skrevet, skrive:

200?"200px":""+(this.scrollHeight+5)+"px");">Rektangel rektangel = nyt rektangel(rammeBredde *ramme, 0, rammebredde, spriteTexture.Højde);

Her har vi lavet et rektangel rektangel klasse Rektangel. I dens parametre specificerede vi placeringen af ​​hver ramme i teksturen (eller rettere, bevægelse langs teksturen, langs koordinaten X, dvs. sidelæns), bevæger sig langs en koordinat Y(da alle sprites er tegnet fra venstre mod højre, derefter ved koordinat Y vi behøver ikke at flytte, dvs. vi angiver i den anden parameter 0), bredden og højden af ​​hver ramme.

Det eneste der er tilbage er at tegne vores animation. For at gøre dette skriver vi yderligere:

200?"200px":""+(this.scrollHeight+5)+"px");">spriteBatch.Draw(spriteTexture, spritePosition, rektangel, Color.White);

Det er det, vi er færdige med denne klasse!

Lad os nu gå videre til klassen Spil 1 og opret et anden klasses objekt Sprites. For at gøre dette, umiddelbart efter linjen (i begyndelsen af ​​klassen)

200?"200px":""+(this.scrollHeight+5)+"px");">Sprites helt;

Lad os skrive ned:

200?"200px":""+(this.scrollHeight+5)+"px");">Sprites runAnimation;

Dette objekt vil være ansvarlig for den kørende animation i vores spil. Lad os nu initialisere det i konstruktøren Spil 1. Umiddelbart efter linjen

200?"200px":""+(this.scrollHeight+5)+"px");">hero = new Sprites();

Skriv ned:

200?"200px":""+(this.scrollHeight+5)+"px");">runAnimation = new Sprites(10);

Tallet 10 i argumentet betyder hastigheden af ​​vores animation. Her kan du eksperimentere og lave den hastighed, der passer dig.

Lad os nu indstille placeringen af ​​vores animation. Det skal passe til objektets position helt, så det i spillet ser ud til, at det ikke er et separat objekt, der kører, men spilleren. For at gøre dette, umiddelbart efter:

200?"200px":""+(this.scrollHeight+5)+"px");">hero.spritePosition = new Vector2(300, 300);

Lad os skrive ned:

200?"200px":""+(this.scrollHeight+5)+"px");">runAnimation.spritePosition = new Vector2(300, 300);

Så lad os nu tilføje vores animationsfil til mappen Indhold/teksturer og lad os kalde det løbe. Som jeg allerede har sagt, ser min fil sådan ud:

Så efter at have tilføjet filen, lad os skrive den i metoden Indlæs indhold disse linjer:

200?"200px":""+(this.scrollHeight+5)+"px");">runAnimation.LoadContent(Indhold, "Textures//run");

Således indlæste vi vores animation i spillet. Tilbage er blot at kalde metoden Opdatering(klasse Sprites), hvor vi beskrev teknologien til at spille animation og tegne vores animation. Til at begynde med i metoden Opdatering(klasse Spil 1) før linjen:

venstre 200?"200px":""+(this.scrollHeight+5)+"px");">if(states.IsKeyDown(Keys.Left))
{
runAnimation.DrawAnimation(spriteBatch);
}
andet
hero.Draw(spriteBatch);

Denne kode betyder: "Hvis venstre tast trykkes ned, afspilles animationen. Hvis der ikke trykkes på venstre tast, stopper animationen, og resten afspilles."

Nu kan du starte vores spil. Hvis du gjorde alt korrekt, vil du først se vores tidligere hviletekstur for hovedpersonen. Men hvis du trykker på knappen til venstre, vil helten begynde at løbe (omend mens den er på plads).

Her er hvad jeg fik:

Og hvis jeg trykker på en tast venstre, så afspilles animationen:

Så tænk på, at vi allerede har gjort den sværeste del. Sandt nok, nu løber vores helt kun til venstre, men i fremtiden vil vi ordne dette. Det er nok på tide at afslutte dette denne del artikler.