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

Exercises for Week 2 - Drawing with Canvas



If you're positive that you're not going to use Canvas <i>at all</i> in your game, then you can skip the canvas assignment and work on your main project instead.  But you still might want to do the canvas assignments anyway, in order to learn about and try out the capabilities.  Up to you.

1. Take your previous assignment, where the player can move a character using the mouse or keyboard (choose one) and modify it so the character is drawn inside a canvas with context.drawImage().  Use the canvas drawing methods to create a background for the character to move around in.  This could be a top-down view or a side-view, green hills or outer space or a cityscape or whatever else you want.

Use an object for the character, to encapsulate the x and y position and the image object you're using, like in this example.

You'll need to erase the character each time it moves, so make sure to redraw the part of the background where it just was!


2. Make a canvas with a "game board" background and several "game piece" objects inside it.  (You can use image files, or just draw circles or squares).  Make it so the player can click on any of these game pieces and drag it around. Listen for mousedown, mousemove, and mouseup events on the canvas -- on a mousedown, check if there's a piece in that location, and if so, begin dragging it.   On a mouseup, release any piece that's being dragged.  On a mousemove, if there's a piece being dragged, make it move along with the mouse.

Each game piece should be an object; you'll need to have a list containing all the objects.  The object should have width and height properties in addition to x and y properties, so that you can write a method to check whether a given mouse location is inside the piece or not.  Then you can use that to write a function which takes the x and y coordinates of a mouse click and looks through the list of all objects to find one that matches.

 

Task Discussion


  • Tiago Garcia   May 21, 2011, 8:35 a.m.
  • Kajan Sivaramalingam   May 20, 2011, 4:39 a.m.

    There is the task 2  : http://kajan.siva.free.fr/game_dev/week2/task2/week2b.html

     

    But there are some problems :

    - The page take a long time to load.

    - I still didn't detect collision when we finish to drag a piece.

    - When we drag a piece, if we go outsite of the gameBoard and we leave the mouseclick, the piece disappear.

    - My code is messy

     

    So I will fix all that as soon as possible.

     

    P.S. : I solved the loading pb, it was caused by the resize of the 2 jpg I used for the gameBoard.

  • Kajan Sivaramalingam   May 20, 2011, 5:25 p.m.
    In Reply To:   Kajan Sivaramalingam   May 20, 2011, 4:39 a.m.

    I did fix some little things.

     

    My code is still a little messy and I don't recognize if there are already a piece at the spot we leave a piece.

     

    But I think the code is functionnal, so I prefer to move up to other exercices.

  • Mike Edwards   May 19, 2011, 5:38 p.m.

    Hi, all!

    I'm joining the class a bit late, but here are my exercises for week 2 (and week1).

  • Krabat   May 18, 2011, 3:44 a.m.

    Both Canvas Exercises have been completed:

    Task1
    http://www.darkfluid.com/html5/week2canvas1/


    Task2
    http://www.darkfluid.com/html5/week2canvas2/


    Still, I encountered some of my code, which I don't like as it is right now:

    • 'krb.updateGameState' is my callback, which has to be called periodically and references data from the global 'krb' object, so I had to store 'self' and return a sub function, which seems somehow clumsy.
    • Same goes for the 'handleinput' method...
    • Method 'krb.inputState.mpos()' is even worse, because I had to reference the 'krb' object itself to make it work.

    Any suggestions on how I can improve my code?

  • Jason   May 18, 2011, 4:30 p.m.
    In Reply To:   Krabat   May 18, 2011, 3:44 a.m.

    I am newer to javascript and so this may sound stupid.

    How come you return functions for updateGameState and handleInput?

    Wouldn't you just be able to execute the code within the function instead of returning it? If you do change it to just execute the code you wouldn't have to do this.updateGameState()(); becuase this.updateGameState(); would just run the code within the function.

    I may be missing something and if so please enlighten me =)

  • Tyler Cipriani   May 18, 2011, 10:48 p.m.
    In Reply To:   Krabat   May 18, 2011, 3:44 a.m.

    I am not a javascript guru of any kind (and, as a matter of fact, this class has gotten me doing javascript that I thought was totally out of my depth as recently as a few weeks ago); however, I think I'm inclined to agree with Jason and would take his comment a step further - does the updategame function need to be part of an object?

    I think updategame could be a setInterval that progresses the game by calling other object's functions in a meaningful order.

    Caveat emptor - I am not as intimately familiar with your code so there may have been compelling reasons why you've set it up in the way you have - this is just after spending 15mins or so staring at it. Also, I'm not well versed in object oriented javascript so my thoughts on it may not amount to a hill of beans in this crazy, mixed-up world.

  • Krabat   May 19, 2011, 1:46 a.m.
    In Reply To:   Krabat   May 18, 2011, 3:44 a.m.

    @Jason: In the beginning I executed only the function itself, but I use references to properties of the object 'krb' by calling "this". The tricky part is (and it took some time to understand it) that updateGameStat is a callback in the setTimeout method. Then, if updateGameState executes there, the "this" keyword becomes the callback function itself and is not the krb object anymore.

    @Tyler: I think the main problem is that I am trying to encapsulate everything in my global 'krb' object in order to don't risk namespace issues. You are right, I'll try to separate the updateGameState method from the main object and maybe create a new one, only dedicated to user input.

    Thanks for looking at my code!

    PS: I also updated the HTML with a direct link to the JS file for simpler access

  • Jason   May 19, 2011, 11:39 a.m.
    In Reply To:   Krabat   May 19, 2011, 1:46 a.m.

    Ah, I knew there was some reason.

    On that note though because krb is an object literal couldn't you set the callback like this:

    window.setTimeout(krb.updateGameState, 25); 

    And remove the "return function"?

    And if someone more knowledgable comes along is it possible to do the krb.updateGameState() even if it wasn't an object literal?

    -Jason 

  • Jono Xia   May 19, 2011, 12:52 p.m.
    In Reply To:   Krabat   May 18, 2011, 3:44 a.m.

    A pattern you may find helpful is to stash the value of "this" in a local variable -- I like to call it "self" since I'm a python programmer -- and then make a very small anonymous function that just calls a function on self.  Like this:

     

    ImportantGameObject.prototype = {

        doStuff: function() {

             // function uses "this" so it MUST be called via

             // ImportantGameObject instance

         },

         setUpCallbackForDoStuff: function() {

             var self = this;     // temporarily save value of "this"

             window.setInterval( function() {   self.doStuff(); },  250);

         }

    };

  • Krabat   May 19, 2011, 2:48 p.m.
    In Reply To:   Krabat   May 18, 2011, 3:44 a.m.

    @Jason: You are right, I could use it directly "window.setTimeout(krb.updateGameState, 25);", but I think it is not the best practise, as you would have to rename that line if the name of the krb object changes. Sorry for not being able to describe it better...

    @Jono: That's a cool idea, thanks.

    I also asked a colleague and he showed my another option: it seems that you can define how "this" is treated in a function, so apparently by calling "bind" on the function you pass the variable you want to be "this" for the execution of the method:

    updateGameState: function() {
        this.drawGrid();
        for(i in this.tokens) {
            this.tokens[i].checkActive(this.inputState);
            this.tokens[i].move(this.inputState);
            this.tokens[i].draw(this.ctx);
        }
        window.setTimeout(this.updateGameState.bind(this), 50);
    },

    I tested it and it works!

  • Jason   May 19, 2011, 5:11 p.m.
    In Reply To:   Krabat   May 19, 2011, 2:48 p.m.

    Ah, never considered that but that is an incredibly good point.

    So is bind a defualt of the language? or do you have to write the function yourself?

  • Krabat   May 20, 2011, 3:10 a.m.
    In Reply To:   Jason   May 19, 2011, 5:11 p.m.

    bind is a new addition to JavaScript v1.8.5, so it would only be supported in FireFox4 and IE9, but there's a workaround and more information here:

    https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

  • Kajan Sivaramalingam   May 16, 2011, 6:38 p.m.

    There we go for the first task :

    http://kajan.siva.free.fr/game_dev/week2/task1/week2a.html

     

    There is no animation, only the moving.

     

    To be continued...

  • Anonym   May 15, 2011, 4:42 p.m.

    I just completed both tasks.

    First part:

    Page - Code

    Second part:

    Page - Code

    There is a bug, which I'm working on, in Task B. When the images are too close to each other, they move together.

    UPDATE:

    The bug is no longer there.

  • Krabat   May 16, 2011, 2:02 a.m.
    In Reply To:   Anonym   May 15, 2011, 4:42 p.m.

    hmm.. I'd like to try out your tasks, but how can I access them right away? If I go on the html file I see the source and if I click 'raw', again the html source is displayed fullscreen...

    any ideas?

  • Anonym   May 16, 2011, 12:39 p.m.
    In Reply To:   Krabat   May 16, 2011, 2:02 a.m.

    What you are pointing is correct, you can only see the code at GitHub. Unfortunately, at the moment I do not have where to host the files.

    For now, if you want, you can copy the code locally on your pc. I will try to find a solution to this.

  • Tyler Cipriani   May 16, 2011, 12:42 p.m.
    In Reply To:   Anonym   May 16, 2011, 12:39 p.m.

    Actually all of the content I've posted is 'hosted' on github (e.x. http://www.tylercipriani.com/html5gamedev/) The domain registrar was through Media Temple but other than that everything I've posted has been through github. You can read the 'how to' on this here

  • Anonym   May 16, 2011, 1:54 p.m.
    In Reply To:   Tyler Cipriani   May 16, 2011, 12:42 p.m.

    Hi,

    Thank you very much! I did not know that this was possible. I will update all my comment entries in order to share code with everybody.

  • Krabat   May 15, 2011, 11:52 a.m.

    Here is a Canvas Cheat Sheet I came across while searching how to thicken the stroke lines (which is done by "context.lineWidth = 5"):

    http://www.nihilogic.dk/labs/canvas_sheet/HTML5_Canvas_Cheat_Sheet.png

  • Jason   May 12, 2011, 12:47 a.m.

    I am still working on the background and general movement for Task 1

     

    I have completed Task 2: http://jasonmanners.info/html5gamedev/w2_e2.html

    It allows you to snap the pieces to the grid and will not allow you to place the piece in the same grid space, instead if you try to do that it will place it at spot to the right, unless you are on the right most side in which it will be placed at the first column position in the same row.

    You can view my Javascript code by clicking the link. The HTML is pretty sparse and simple. I find that with HTML5 and canvas a lot of the code will be in the javascript file.

  • Jono Xia   May 13, 2011, 2 a.m.
    In Reply To:   Jason   May 12, 2011, 12:47 a.m.

    Hi Jason,

    Yeah I find that too.  My HTML files are basically just skeletons.

    I checked out your code; looking good (yay for well-documented code)!  I like the snap-to-grid feature.  A couple of comments:

    Your function rect() duplicates the built-in function context.fillRect() - why not use that instead?

    draw_grid() seems like it should be a method of the Game_Board() object.

    The requestAnimationFrame technique is pretty cool!  I hadn't heard of it but I researched it after reading your code.  Unfortunately it seems like it's only implemented in Firefox and Chrome -- your code won't run for me at all in Safari and I don't think IE or Opera support it either.  Here's hoping all the browsers will implement that standard in the future - it would make controlling flicker and animation speed (a topic I'm working on a tutorial for) a lot easier.

  • Jay   May 11, 2011, 1:12 p.m.

    I have completed both tasks.  I drew a very colorful background similar to what I will need for the card game table.  

    Task 2A - the man follows the mouse

    Task 2B - click on any card to move it around.  I feel like some of the code here is clunky.  I plan to clean up the hard coded numbers of the background canvas and make it relative so the background can be resized.  Any other recommendations for optimization are welcome.

  • Rick E   May 11, 2011, 1:29 p.m.
    In Reply To:   Jay   May 11, 2011, 1:12 p.m.

    Jay,

    2B works well. I wonder if two enhancements might help:

    1. Is there a way to make the focal point of each card in the center, rather than the upper left corner of the card? That would help to more accurately place each card when released.
    2. What about having designated card resting locations in the upper 3 colored zones...so that when a card is released it 'snaps' into the closest pre-defined location?  That might be a major task...I don't know.

    Nice work!

    Rick

  • Tiago Garcia   May 11, 2011, 3:42 a.m.

    I just finished the task A. I had not that much patience to draw a realistic background, I just wanted to sybolize it and play with the API for a while.

    Demo: http://tgarcia.net78.net/task3a/index.html

    Code: https://github.com/tiagorg/HTML5/tree/master/task3a

  • Tyler Cipriani   May 9, 2011, 11:36 a.m.

    I've posted these exercises up on my github @ http://www.tylercipriani.com/html5gamedev/ - you'll be able to try my code live, view html source and js source.

    Overall I thought the exercises were great and very helpful in conceptualizing how I'm going to be able to code a whole game. 

    The players interaction with the canvas background is my biggest challenge as far as conceptualizing the best way to do this. Would it be a better idea to make a background object? I didn't do that on either of these exercises, although it probably made sense to do so in the first exercise.

  • Jono Xia   May 9, 2011, 3:30 p.m.
    In Reply To:   Tyler Cipriani   May 9, 2011, 11:36 a.m.

    I tend to make something into an object as soon as there's more than two pieces of data to be encapsulated, so I almost always have a "background" or "map" or "world" object, but that's just me.  Whether such an object makes sense or not depends on the individual game, but i think in most cases it will.

    If you're doing a Galaga-style starfield background, then you could have a background object that manages a list of star positions and colors, generating them at the top, scrolling them downwards, and removing or re-allocating them when they hit the bottom.

  • Jono Xia   May 9, 2011, 3:41 p.m.
    In Reply To:   Tyler Cipriani   May 9, 2011, 11:36 a.m.

    You said:  "I'm not sure why one piece seems to always move over the game board and the other one under."

     

    It's because you call drawBoard() inside GamePiece.draw().  That means when you draw the two pieces, the result is to draw the board, draw piece 1, draw the board again (over the top of piece 1) and then draw piece 2.  That's why piece 1 appears under the board.  Call drawBoard() only once, before drawing either piece, and this will stop happening.

  • Tyler Cipriani   May 9, 2011, 3:55 p.m.
    In Reply To:   Jono Xia   May 9, 2011, 3:41 p.m.

    Thanks for taking a look at that - updated now.

  • Tyler Cipriani   May 13, 2011, 4:33 p.m.
    In Reply To:   Tyler Cipriani   May 9, 2011, 11:36 a.m.

    Finally got a chance to futz with making a background object this afternoon. Also, made a little ship and limited its movement from side to side. Check 'er: http://www.tylercipriani.com/html5gamedev/week3_scrollingBG.html

  • Krabat   May 15, 2011, 11:08 a.m.
    In Reply To:   Tyler Cipriani   May 13, 2011, 4:33 p.m.

    Looks cool! I'm still on drawing my muppet head on the canvas background, which is somehow static compared to yours. I'm kind of jealous :)