banner



Javascript And How To Make An Animated Walking Sprite Stop Walking?

Let's look at animative a sprite sheet, on an HTML5 sheet, using JavaScript.

A Little Setup

Commencement, let's create the canvass element.

                          <sail              width=              "300"              elevation=              "200"              ></canvas>                      

Enter fullscreen mode Exit fullscreen mode

Add together a edge (so we can come across our usable expanse).

                          sheet              {              edge              :              1px              solid              black              ;              }                      

Enter fullscreen mode Exit fullscreen mode

And load the sprite canvass (https://opengameart.org/content/green-cap-character-16x18). While we're at it, let'southward go access to the canvas and its 2d context.

                          let              img              =              new              Image              ();              img              .              src              =              '              https://opengameart.org/sites/default/files/Green-Cap-Character-16x18.png              '              ;              img              .              onload              =              function              ()              {              init              ();              };              allow              canvas              =              document              .              querySelector              (              '              canvas              '              );              let              ctx              =              canvas              .              getContext              (              '              2d              '              );              part              init              ()              {              // futurity animation code goes here              }                      

Enter fullscreen way Go out fullscreen manner

The init function is called after the image is loaded, via img.onload. This is to ensure the image is loaded earlier nosotros endeavour working with information technology. All of animation code will go in the init function. For the sake of this tutorial, this will work. If we were dealing with multiple images, nosotros'd probably desire to use Promises to wait for all of them to load before doing anything with them.

The Spritesheet

Now that we're prepare, let's take a await at the image.

Character sprite sheet

Each row represents and animation wheel. The first (peak) row is the character walking in a downward direction, the second row is walking up, the 3rd row is walking left, and the fourth (bottom) row is walking right. Technically, the left column is a standing (no animation) while the middle and right columns are animation frames. I think nosotros tin utilize all three for a smoother walking animation, though. 😊

Context'south drawImage Method

Before we get to animating our image, let'south wait at the drawImage context method, as that'due south what we'll use for automatically slicing upward the sprite sail and applying it to our sail.

MDN docs - drawImage

Whoa, there are a lot of parameters in that method! Peculiarly the third form, which is the one nosotros'll be using. Don't worry, it's not as bad as it seems. In that location'south a logical group to information technology.

                          drawImage              (              image              ,              sx              ,              sy              ,              sWidth              ,              sHeight              ,              dx              ,              dy              ,              dWidth              ,              dHeight              )                      

Enter fullscreen mode Exit fullscreen manner

The image argument is the source prototype. The adjacent four (sx, sy, sWidth, and sHeight) relate to the source prototype - the sprite sheet. The last 4 (dx, dy, dWidth, and dHeight) relate to the destination - the canvass.

The "x" and "y" parameters (sx, sy, dx, dy) chronicle to the sprite sheet (source) and canvas (destination) starting positions, respectively. It's essentially a filigree, where the pinnacle left starts at (0, 0) and moves positively to the right and down. In other words, (l, xxx) is l pixels to the right and 30 pixels downward.

The "Width" and "Meridian" parameters (sWidth, sHeight, dWidth, and dHeight) refer to the width and tiptop of the sprite sheet and canvas, starting at their respective "x" and "y" positions. Allow's break information technology downward to i section, say the source image. If the source parameters (sx, sy, sWidth, sHeight) are (10, 15, xx, 30), the the starting position (in grid coordinates) would exist (ten, 15) and stretch to (30, 45). And then catastrophe coordinates are calculated as (sx + sWidth, sy + sHeight).

Drawing The Commencement Frame

Now that we've gone over the drawImage method, let's actually see it in action.

Our sprite sheet's character frame size is conveniently labeled in the file name (16x18), so that gives usa our width and acme attributes. The get-go frame will outset at (0, 0) and end at (16, eighteen). Allow's draw that to the sail. We'll outset with drawing this frame starting at (0, 0) on the canvas and continue the proportions.

                          role              init              ()              {              ctx              .              drawImage              (              img              ,              0              ,              0              ,              16              ,              eighteen              ,              0              ,              0              ,              sixteen              ,              xviii              );              }                      

Enter fullscreen mode Exit fullscreen mode

And we have our beginning frame! Information technology's a fiddling small-scale though. Permit'south scale it up a bit to make information technology easier to see.

Modify the above to this:

                          const              calibration              =              2              ;              part              init              ()              {              ctx              .              drawImage              (              img              ,              0              ,              0              ,              16              ,              18              ,              0              ,              0              ,              16              *              calibration              ,              18              *              scale              );              }                      

Enter fullscreen mode Exit fullscreen mode

Y'all should see the paradigm drawn on the sheet has doubled in size both horizontally and vertically. By irresolute the dWidth and dHeight values, we can calibration the original image to be smaller or larger on the canvas. Exist careful when doing this though, as yous're dealing with pixels, it can offset blurring pretty apace. Try changing the scale value and come across how the output is changed.

Next Frames

To depict a second frame, the merely thing we need to do is modify some values for the source set. Specifically, sx and sy. The width and elevation of each frame are the same, so we'll never have to modify those values. In fact, let'south pull those values out, create a couple scaled values, and draw our next two frames to the correct of our electric current frame.

                          const              scale              =              2              ;              const              width              =              16              ;              const              height              =              18              ;              const              scaledWidth              =              calibration              *              width              ;              const              scaledHeight              =              scale              *              height              ;              function              init              ()              {              ctx              .              drawImage              (              img              ,              0              ,              0              ,              width              ,              height              ,              0              ,              0              ,              scaledWidth              ,              scaledHeight              );              ctx              .              drawImage              (              img              ,              width              ,              0              ,              width              ,              height              ,              scaledWidth              ,              0              ,              scaledWidth              ,              scaledHeight              );              ctx              .              drawImage              (              img              ,              width              *              2              ,              0              ,              width              ,              meridian              ,              scaledWidth              *              ii              ,              0              ,              scaledWidth              ,              scaledHeight              );              }                      

Enter fullscreen mode Get out fullscreen mode

And this is what it looks similar at present:

Now nosotros have the entire top row of the sprite canvass, only in three split frames. If yous look at the ctx.drawImage calls, there are only 4 values that alter now - sx, sy, dx, and dy.

Let'south simplify it a flake. While we're at information technology, let's starting time using frame numbers from the sprite sail instead of dealing with pixels.

Replace all the ctx.drawImage calls with this:

                          office              drawFrame              (              frameX              ,              frameY              ,              canvasX              ,              canvasY              )              {              ctx              .              drawImage              (              img              ,              frameX              *              width              ,              frameY              *              height              ,              width              ,              pinnacle              ,              canvasX              ,              canvasY              ,              scaledWidth              ,              scaledHeight              );              }              function              init              ()              {              drawFrame              (              0              ,              0              ,              0              ,              0              );              drawFrame              (              i              ,              0              ,              scaledWidth              ,              0              );              drawFrame              (              0              ,              0              ,              scaledWidth              *              2              ,              0              );              drawFrame              (              2              ,              0              ,              scaledWidth              *              3              ,              0              );              }                      

Enter fullscreen style Exit fullscreen mode

Our drawFrame office handles the sprite canvas math, so we only need to pass in frame numbers (starting at 0, like an array, then the "x" frames are 0, 1, and 2).

The canvas "x" and "y" values still take pixel values so we take better control over positioning the character. Moving the scaledWidth multiplier inside the function (i.e. scaledWidth * canvasX) would hateful everything moves/changes an unabridged scaled character width at a time. That wouldn't work with a walking animation if, say, the character moves iv or v pixels each frame. So nosotros leave that as information technology is.

At that place'southward also an extra line in that list of drawFrame calls. This is to show what our blitheness cycle volition expect like, rather than but cartoon the tiptop three frames of the sprite sheet. Instead of the animation bicycle repeating "left pace, right step", it volition echo "stand, left, stand, right" - it's a slightly better animation bike. Either is fine though - a number of games in the 80s used two step animations.

This is where we're currently at:

Let'due south Animate This Character!

Now we're gear up to breathing our character! Allow's have a look at requestAnimationFrame in the MDN docs.

This is what we'll employ to create our loop. We could too apply setInterval, but requestAnimationFrame has some nice optimizations in place already, like running at 60 frames per second (or as close equally it can) and stopping the blitheness loop when the browser/tab loses focus.

Essentially, the requestAnimationFrame is a recursive function - to create our animation loop, we'll call requestAnimationFrame again from the function nosotros're passing as the argument. Something like this:

                          window              .              requestAnimationFrame              (              step              );              function              step              ()              {              // do something              window              .              requestAnimationFrame              (              step              );              }                      

Enter fullscreen way Go out fullscreen manner

The lonely call before the walk part starts the loop, then information technology's continuously chosen within.

Before we get to using information technology, at that place's 1 other context method we need to know and utilise - clearRect (MDN docs). When cartoon to the canvas, if we keep calling drawFrame on the same position, information technology'll proceed drawing on top of what'southward already at that place. For simplicity, we'll clear the entire canvas betwixt each draw, rather than just the area we draw to.

Then, our depict loop volition await something like articulate, draw the first frame, articulate, draw the second frame, and and then on.

In other words:

                          ctx              .              clearRect              (              0              ,              0              ,              canvas              .              width              ,              canvas              .              summit              );              drawFrame              (              0              ,              0              ,              0              ,              0              );              // repeat for each frame                      

Enter fullscreen fashion Get out fullscreen fashion

Okay, let's animate this graphic symbol! Let'southward create an array for the wheel loop (0, 1, 0, 2) and something to keep track of where nosotros are in that wheel. And then nosotros'll create our step function, which will human activity as the chief animation loop.

The step function clears the canvas, draws the frame, advances (or resets) our position in the bike loop, then calls itself via requestAnimationFrame.

                          const              cycleLoop              =              [              0              ,              1              ,              0              ,              two              ];              let              currentLoopIndex              =              0              ;              function              step              ()              {              ctx              .              clearRect              (              0              ,              0              ,              canvas              .              width              ,              canvas              .              elevation              );              drawFrame              (              cycleLoop              [              currentLoopIndex              ],              0              ,              0              ,              0              );              currentLoopIndex              ++              ;              if              (              currentLoopIndex              >=              cycleLoop              .              length              )              {              currentLoopIndex              =              0              ;              }              window              .              requestAnimationFrame              (              step              );              }                      

Enter fullscreen manner Leave fullscreen fashion

And to get the animation started, let's update the init function.

                          function              init              ()              {              window              .              requestAnimationFrame              (              step              );              }                      

Enter fullscreen style Go out fullscreen mode

That graphic symbol is going places fast! 😂

Slow Down There!

Looks like our grapheme is a little out of command. If the browser allows it, the character volition be fatigued 60 frames per second, or as close as possible. Let'southward put a limit on that so information technology's stepping every xv frames. We'll need to keep rails of which frame we're on. So, in the stride office, we'll advance the counter every call, but simply depict after 15 frames pass. One time 15 frames pass, reset the counter, and draw the frame.

                          const              cycleLoop              =              [              0              ,              one              ,              0              ,              ii              ];              let              currentLoopIndex              =              0              ;              allow              frameCount              =              0              ;              function              step              ()              {              frameCount              ++              ;              if              (              frameCount              <              15              )              {              window              .              requestAnimationFrame              (              step              );              return              ;              }              frameCount              =              0              ;              ctx              .              clearRect              (              0              ,              0              ,              canvas              .              width              ,              canvas              .              height              );              drawFrame              (              cycleLoop              [              currentLoopIndex              ],              0              ,              0              ,              0              );              currentLoopIndex              ++              ;              if              (              currentLoopIndex              >=              cycleLoop              .              length              )              {              currentLoopIndex              =              0              ;              }              window              .              requestAnimationFrame              (              step              );              }                      

Enter fullscreen mode Get out fullscreen manner

Much amend!

The Other Directions

So far, we've only handled the down direction. How about we modify the animation a bit so the character does a complete four-footstep cycle in each management?

Remember, the "down" frames are in row 0 in our code (showtime row of the sprite sheet), up is row 1, left is row 2, and correct is row 3 (bottom row of the sprite sheet). The wheel remains 0, 1, 0, two for each row. Since nosotros're already handling the cycle changes, the only affair we need to change is the row number, which is the second parameter of the drawFrame function.

We'll add a variable to continue rails of our current management. To proceed it simple, we'll go in the sprite sheet'south order (downwardly, up, left, correct) so it's sequential (0, i, 2, 3, echo).

When the cycle resets, we'll motion to the next direction. And one time we've gone through every management, we'll kickoff over. So, our updated step part and associated variables expect similar this:

                          const              cycleLoop              =              [              0              ,              1              ,              0              ,              two              ];              let              currentLoopIndex              =              0              ;              let              frameCount              =              0              ;              let              currentDirection              =              0              ;              function              step              ()              {              frameCount              ++              ;              if              (              frameCount              <              fifteen              )              {              window              .              requestAnimationFrame              (              step              );              return              ;              }              frameCount              =              0              ;              ctx              .              clearRect              (              0              ,              0              ,              canvas              .              width              ,              canvas              .              superlative              );              drawFrame              (              cycleLoop              [              currentLoopIndex              ],              currentDirection              ,              0              ,              0              );              currentLoopIndex              ++              ;              if              (              currentLoopIndex              >=              cycleLoop              .              length              )              {              currentLoopIndex              =              0              ;              currentDirection              ++              ;              // Next row/direction in the sprite sail              }              // Reset to the "down" management once nosotros've run through them all              if              (              currentDirection              >=              four              )              {              currentDirection              =              0              ;              }              window              .              requestAnimationFrame              (              stride              );              }                      

Enter fullscreen way Exit fullscreen manner

And in that location we take it! Our character is walking in all 4 directions, animated all from a unmarried image.

Source: https://dev.to/martyhimmel/animating-sprite-sheets-with-javascript-ag3

Posted by: baileycoluch.blogspot.com

0 Response to "Javascript And How To Make An Animated Walking Sprite Stop Walking?"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel