This course will become read-only in the near future. Tell us at community.p2pu.org if that is a problem.

Exercises for Week 3 - Animation and Scrolling


3.1 - Scrolling in Two Dimensions

The scrolling code in Example 3.2 only scrolls left and right.  Modify the scrolling code so that it can scroll in all four dimensions - up, down, left, and right - in response to the player's movement.

You can either integrate a scrolling feature into one of your exercises from week 2, or you can take the code from Example 3.2 and add vertical movement to it.

3.2 - Animating

Take one of your existing projects (could even be 3.1) and jazz it up a little bit by adding either a multi-frame sprite animation (like the one in Example 3.3) or a canvas transformation (like the ones in Example 2.7).

For example, if you're already animating a ghost... give it a couple of frames of bobbing animation as it floats along.  And if you're drawing playing cards to a surface... use Canvas rotation transforms to display the cards at different angles.

Task Discussion


  • Tyler Cipriani   May 30, 2011, 4:42 p.m.

    Finally getting caught up on some stuff. Phew. ANYWHO:

    Week 3 ex. 1 - http://www.tylercipriani.com/html5gamedev/week3_exercise1.html This is my pink invader guy that scrolls through 4 directions in space. Not very clever.

    Week 3 ex. 2 - http://www.tylercipriani.com/html5gamedev/week3_exercise2.html I found a sprite from NES/SNES Sprites.com and used that - it's subzero, fool! Much funner. I figured that since my final game won't have too much in the way of animated sprites that this would be a fun and entertaining excercise. It looks pretty solid ya ask me...

    Also, as always, poking at my final game - http://www.tylercipriani.com/html5gamedev/ikta.html

    That's all, folks!

  • Jono Xia   May 30, 2011, 9:32 p.m.
    In Reply To:   Tyler Cipriani   May 30, 2011, 4:42 p.m.

    LOL at Sub-Zero!  That does indeed look pretty smooth.


    Your prototype is looking good!  One thing I noticed about your code is that you're keeping four different variables for each star, but keeping them all in a single one-dimensional array, which means you have to jump through hoops to access those numbers again when you need them.   It might be easier (and more readable) to use an array of objects.  Instead of:

    starPos.push(posX, posY, starWxH, starWxH);
    // ...
    ctx.fillRect(this.starPos[i], this.starPos[i + 1], this.starPos[i + 2], this.starPos[i + 3]);


    You could push a new anonymous object into the array like this:

    starPos.push({x: posX, y: posY, width: starWxH, height: starWxH});
    //...
    var star = this.starPos[i];
    ctx.fillRect(star.x, star.y, star.width, star.height);

  • Jay   May 26, 2011, 5:46 p.m.

    Week 3 assignments are done.

    Task 1 - I just added vertical scrolling to the example with the constrobot.  It works pretty smoothly.

    Task 2 - I added a scale to change the size of the background if the window size changes.  This should make the program more dynamic so it can be played on anything from laptops to smart phones or tablets.  I also added to the display picture function so you can give it an angle and it will rotate the picture that many degrees.  I have the table cards rotated as if they were played by the player at a table.

    The main issue that I have with it as it stands is that there is a setInterval called every second to get the images displayed if they aren't loaded when they are initally called.  I am guessing that I will not need this when I have a waiting area for people to connect to the server and start the game.  Any ideas?

    Jay

  • Krabat   May 27, 2011, 2:17 a.m.
    In Reply To:   Jay   May 26, 2011, 5:46 p.m.

    Hi Jay,

    I tried your task2 and the idea of scaling the gameCanvas based on the screen size is really great!

    When looking at your code I didn't quite understand the problem with the game image loading mechanism(seems fine), but the init of the cards could be done in a loop (function "dealCards").

    function dealCards(ctx, myHand){
      var startX = 10;
      var dX=76;
      var currX=startX-dX;
      for(var i=0; i<13; i++) {
        myHand.push(new Card("img/"+i+".png", currX+=dX, 380, 0));
      }
      drawCards(ctx, myHand);
    }
    (UNTESTED)

    On a sidenote: I left the task2 open in a browser tab and (although inactive) it consumed a whole CPU core.

  • Jono Xia   May 30, 2011, 9:22 p.m.
    In Reply To:   Jay   May 26, 2011, 5:46 p.m.

    Hi Jay,

    Task 1 looks good.  I see you started trying to add a vertical limiter to drawIfOnScreen() but then commented it out.  I think that code would have worked the way you wanted if you just swapped getUp() and getDown() -- remember you want to see if the bottom of the object is above the top of the screen, or the top of the object is below the bottom of the screen.

     

    Task 2: Very good use of rotate()!  Also, good job responding to window resize events.  My main question here is why do you want to redraw everything once per second?  I assume cards aren't going to move around on their own, meaning the game state only changes in response to the player input (or another player connected to the same server making a play, which would result in a network event).  So you should be able to get away with only redrawing in response to those events, and not on a timed loop.

     

    In regards to your question -- maybe you already figured this out yourself, but when I looked at your code it looks like the image load is initiated only once for each card, when the Card() constructor calls init().  I think this is the right way to do it.  If you have a "waiting area", you might want to call the constructor once for each card image while the players are waiting to start, just to make sure they're all pre-loaded.

  • Anonym   May 25, 2011, 9:54 a.m.

    Here is the exercise for week 3.

    1. Scrolling:

    Page - Code

    Just a small concern. Even though the image i used is an animated gif, when it's drawn it does't show the animation. Any idea to solve this?

    I just finished the second task

    2. Animating:

    Page - Code

  • Jono Xia   May 30, 2011, 9:44 p.m.
    In Reply To:   Anonym   May 25, 2011, 9:54 a.m.

    Hi Shadowplay,

    Drawing an animated GIF into a canvas doesn't work the way you would expect, because drawing an image into a canvas is basically a one-time thing -- it copies a snapshot of the image but doesn't do any further updating after that unless you tell it to.  So in order to make an animated GIF animate properly inside a canvas, you would have to grab each frame and set up a timer that calls drawImage() for each one in sequence.

    Which is kind of a pain, so it's probably better to use animated GIFs only if you're doing a non-canvas-based HTML game, and use the image slicing method described in the example instead, if you're working inside canvas.

    Your second task isn't working for me.  I'm getting an error that says:

    Error: uncaught exception: [Exception... "Index or size is negative or greater than the allowed amount"  code: "1" nsresult: "0x80530001 (NS_ERROR_DOM_INDEX_SIZE_ERR)"  location: "http://shadowplay.github.com/Html5GameDev/jsFile3b.js Line: 159"]

    I'm guessing this is because either spriteOffsetX or spriteOffsetY in the ConstrPlayer.draw() function is coming out to a number that is too big for the image file.  Then again, it might just be for me -- does it work on your computer?

  • Anonym   May 31, 2011, 3:49 p.m.
    In Reply To:   Jono Xia   May 30, 2011, 9:44 p.m.

    Hi Jono Xia,

    Thanks for the input. I'll leave GIFs for non-canvas-based html games, and as you said it will be better to use the slicing animation of the example instead, which actually is the case of the second task.

    Now regarding to the error you comment, probably it is because either the spriteOffsetX or spriteOffsetY and the image size. However, it is working fine with me. I've tested it on the lastest versions of Mozilla Firefox, Google Chrome, and Opera. But, for some reason it is not working on Internet Explorer.

    I'll modify the image size, maybe that will work.

  • Krabat   May 24, 2011, 2:10 p.m.

    Here are my exercises from week3:

    3.1 Scrolling
    http://www.darkfluid.com/html5/week3task1/


    I had a hard time figuring out how to enable Y-axis-scrolling, until I realized that there was some kind of baseline("groundLevel"), then it kept being hard to solve :)
    After inserting my yOffset at various places it worked, but I think my solution is far from perfect... A smarter way would be to draw everything and THEN apply the transformation to the whole scene, I think.

    3.2 Animation
    http://www.darkfluid.com/html5/week3task2/


    I altered my hulk/maze from week2/canvas1 and replaced hulk with the running human from the scrolling lession (sorry for re-using him). The fact that only the hulk(spaceship) object had to be altered is really cool! Now, where do I get cool space taxi animation graphics for my main project?

    On a sidenote: how about trying to achive a constant framerate, so that the animations can be displayed in sync with it?

  • Jono Xia   May 31, 2011, 8:14 p.m.
    In Reply To:   Krabat   May 24, 2011, 2:10 p.m.

    Hi Krabat,

    Yeah sorry about the groundLevel thing.  Basically my code already had a built-in Y offset, so the trick was to make that a variable instead of a constant.

    Drawing everything and then applying a transformation doesn't work -- the transformation only applies to stuff that's drawn after it.  Everything already drawn simply stays in place when you apply a transformation.

    The hulk maze is looking good ("tiny human!").  Of course the next thing to do would be to add frames of animation for vertical travel, and more importantly, to make the walls of the maze actually block your progress.  That will require writing some collision detection (which I cover in one of the lesson 4 examples).  Collision detection will be central to your space taxi gameplay, so you will need to work on some really solid collision detection code.

    A constant framerate is tricky.  When the browser slows down (due to the javascript garbage collector, other tabs, etc.), your game may not get the framerate you ask for.  You have a choice between slowing down the simulation or skipping frames (as I explain in the "dealing with slowdown" video).  But I don't know any way to gurarantee a given framerate.

    Instead, maybe look at syncing the frames of animation to the actual distance covered by the sprite and/or the number of milliseconds elapsed since the last update - whichever makes sense for your game.

  • Kajan Sivaramalingam   May 20, 2011, 8:16 p.m.

    There we go :

    http://kajan.siva.free.fr/game_dev/week3/week3.html

     

    The animation sprite sucks (different size image within an animation, not centred etc...). So it make suck the whole thing.

     

    But my big concern is about the performance. I should try to optimize my code, to improve it.

    Advices are welcome :)

  • Jono Xia   May 30, 2011, 10 p.m.
    In Reply To:   Kajan Sivaramalingam   May 20, 2011, 8:16 p.m.

    Hi Kajan,

    The only thing I can see in your code that might be causing performance to suffer is that you've got three very tight setInterval loops running at the same time. I think you should try combining them into a single function that handles the moving, animating, and drawing.  So that you only have one setInterval() running.  See if that makes it run smoother.

    Also: try to avoid using strings in your setInterval() call, because then javascript has to evaluate the string instead of just running a function (and you can only use variables defined at the top-level...).  Evaluating strings is also a bad habit because it can lead to security holes.  There's always a way to do what you want without putting code in a string to be evaluated.  Instead of:


    setInterval("player.move();", 110);

    try:

    setInterval(function() {player.move();}, 110);

  • Jason   May 20, 2011, 11:52 a.m.

    I have almost finished task 2:

    http://jasonmanners.info/html5gamedev/w3.html

    Animations are working for the most part but my code is sort of a mess. Currently in the process of cleaning it up, well reworking a lot of things. As I am doing that I will put scrolling in as well.

    Background sprite sheet from "Random Rebel Soldier" and animation sprite sheets from "LMS60" and edited down to what I need. 

    Currently in this task  you can move and jump, left right and up. As well as land on the platforms. right now you can infinitely jump and infinitely move to the right or left. So if you get lost just reload the page =).

    Again the version with scrolling will be cleaned up a lot, and I was thinking about doing things a bit differently in terms of updating position, collision detection, and objects defined. Will let you know when I get that up!

     

    Thanks!

  • Jono Xia   May 31, 2011, 7:58 p.m.
    In Reply To:   Jason   May 20, 2011, 11:52 a.m.

    Hi Jason,
    Careful about mozRequestAnimationFrame / webkitRequestAnimationFrame -- if you use these you're restricting yourself to only browsers that implement them  (i.e. it won't run in IE9).  But if that's OK with you, then full speed ahead!

    That said, I really like the idle animation, and the jumping.  If you don't want to allow the player to jump infinitely, just add a boolean to tell whether there is ground / a platform under the character's feet or not, and don't allow jumping if there isn't.

    I really like the ImageLoader object -- that seems very useful!  Would you care to explain to other students how it works?  I think they would benefit.

    Anyway, overall it's looking really good!
    --Jono

  • Jason   June 3, 2011, 1:10 p.m.
    In Reply To:   Jono Xia   May 31, 2011, 7:58 p.m.

    Jono,

    So i have been working and I thought that this had made it in but that must have been in my newest iteration. 

    my requestAnimationFrame now looks like this: 

    var requestAnimationFrame = 
     window.mozRequestAnimationFrame ||
     window.webkitRequestAnimationFrame ||
     function(/* function */ callback, /* DOMElement */ element){
      window.setTimeout(callback, 1000 / 60, new Date());
     };
    

     

    Do you think this would be better? or should I stick with setInterval ?

     

    Ya in another iteration I had the ability to double jump by setting a counter and only allowing them to jump when the counter was less than 2. I do like your boolean though for other reasons, like knowing when to add gravity to yVelocity without having to always do it and set the postion to the platform if i was on a platform.

    How would you go about determining that? Check for y values, and would you consider adding in a threshold of a couple pixels?

    ImageLoader

    So I was having trouble with images not loading before I drew them to an animation canvas that I use so I can prerender evertyhing and just draw the canvas, so nothing was showing up. After doing some research I found out that it was because the images were not loaded. After thinking about it for a while I came up with this image loader class.

    it has the following attributes:

    images         //An array of the images
    sources        //An array of the sources for the images
    totalImages    //Keeps track of total number of images to load
    loadedImages   //Keeps track of currently loaded images
    

    and the following methods:

    /* Adds src to sources with the index of name */
    addImage(name,src) 
    /* Called when we want to loaded all images  */
    loadImages()
    /* Called within a loop(requestAnimationFrame) to show the progress it just returns (loadedImages / totalImages) * 100.0  */
    getProgress()
    
    The main function and most interesting one is loadImages
    • I loop through all of sources creating images and adding them to the images array with the index of name
    • I then set the onload to increment loadedImages and then check if loadedImages = totalImages
      • If they do eqaul I then call startGame
    • I then set the source of the image to start loading the image
      • Order matters, the onload must be set before the image.src is set

    And thats it. It allows us to preload the images with a progress bar and removes the problem of drawing without an image. Sorry for the lengthy post and if you would like the code for it and an example of use just let me know =)

    -Jason