CS349

A1-Canvas

Jan 21, 2024: Setup

cd canvas-a1
npm install simplekit
npm run dev

Modify index.html to only have one child.

Card class, Cat class, bullseye class. Event and SKDrawing things.

Jan28 2024: Stuck on flipping cards and matching them.

Trying to get debugger to work: End result, I’ve failed… Didn’t get to ask Chester about it. Later figured it out.

package.json:

{
	"name": "canvas-a1",
	"private": true,
	"version": "0.0.0",
	"type": "module",
	"scripts": 
	{
	"dev": "vite --port 4000",
	"build": "tsc && vite build",
	"preview": "vite preview"
	},
	
	"devDependencies": 
	{
	"typescript": "^5.2.2",
	"vite": "^5.0.8"
	},
	
	"dependencies": 
	{
	"simplekit": "^0.1.0"
	}
}
  • Gesture
  • Animation: jiggle and rotate
  • Animation slide

What I’ve Learned

How to use simplekit and got an idea of how TypeScript works. It’s so hard, first time using it and having to write a front end game with it.

The program I wrote had a lot of bad designs. Such as the Card class. I realized that I need to store everything in the Card class, for example the states, and even do the animation in the card class.

Also, you can pass gc through functions which I didn’t do. Will make my life easier.

  • Read and familiarize with simplekit source code
  • Study Chester’s organization and how he designed everything

Update: Lost a mark on jiggle???

A1 Example Solution Video

Video Link  password: t9NqrjdaPb

This video is only for students currently enrolled in CS349.
Do not share or download this video.

This is the order of topics I cover:

  1. drawings.ts How I implemented card face drawings
    • parameterized drawing functions (all fit in 80,80 box, centred at 0,0)
    • array of functions with closures for drawing types
  2. card.ts Card class, how it draws
    • constructor needs drawing function, use it in draw
    • how drawing is drawn at card centre
    • how hover, faceup/facedown, and matched are drawn (will talk about jiggle later)
  3. game.ts Game object tracks how many pairs, number flipped to check for matches, and state
  4. main.ts Game and Cards are local module variables (the main “state”)
    • When SimpleKit starts, it sends a resize event first. If game is undefined, it uses canvas size for setupGame.
    • Note resize also saves width and height in the local module state
  5. main.ts setupGame creates a new Game, sorts the face drawings, builds array of cards (2 per pair) and then calls layoutCards
    • note how cards have an “id” for each pair
  6. main.ts layoutCards
    • layout is just math: calc how many cards per row, calc where the top left corner of grid stars, then go through array of cards and assign positions in each row
    • some special rules to centre last row if fewer cards
    • note there’s an animate flag, because you want to just put the cards in layout position on a resize event, but for new game, and starting a game, you want to animate to those positions.
    • returns the vertical margin space (used for centering the message)
  7. main.ts setSKDrawCallback is simple, just draw the message according to game state and draw all cards
  8. main.ts setSKEventListener uses a simple “dispatch” based on Game mode handleStartEvent binds keydown events to actions and updates Game mode accordingly
    • for SPACE: shuffles cards, then calls layoutCards with animation flag set to true (my code can turn all animation off with ANIMATED flag, but it’s set to true by default).
    handlePlayEvent is most complex, with binding to different events
    • for “keydown” and “keyup” with “x”: need to save list of which cards were flipped, then flip all other unmatched cards. On keyup, then flip all unmatched cards down again except the ones that were flipped before
    • “click” has to do quite a few of checks: is it on an unmatched card? were two cards already flipped? if two cards are flipped, do they match? if there was a match, did they win the game?
    handleWinEvent only has to add a pair and move to start mode
  9. main.ts and card.ts jiggle winning animation
  • in animation callback, the jiggle simulation animation is run when in game mode: it sets a yOffset on each Card
  • in Card class, the yOffset is added to y position of Card when the card is drawn
  1. main.ts and card.ts peek timer

    • every Card has it’s own peek timer, the peek timer returns the card to face down after time is up
    • all card timers are updated inside the Card update method called from the animation callback
    • in handlePlayEvent for “gesture” event, if the peek is over an unmatched facedown card, then it calls peek method on Card to start the timer
  2. main.ts and card.ts x,y animations

    • every Card has its own x and y position Animators

    • Card has a setPosition method which has an optional flag to animate: I set it to true when starting a game after a shuffle and when starting a new game or adding/removing a pair. When the window is resized, I don’t animate.

    • if the flag is false, the duration for two Animators is 0, otherwise it’s 400 to show animation

    • all card x,y Animators are updated inside the Card update method called from the animation callback

    You’ll see a bug in my code, the body of the yAnimator callback should be { this._y = p; }.

  3. main.ts and card.ts spin animation

    • every card has its own spin Animator that animates a spin property
    • in handlePlayEvent for “click” event, if this is a match, the card matched setter is set to true and the spin Animator starts
    • in the card draw, a spin property is used with gc.rotate to spin the card