These tips are in addition to the general rules of good coding.
The only way to have an acceptable project is to produce intermediate deliverables along the way. The following are required deliverables. They should be sent to the TA.
|Core Animation||Due: Friday, February 19||Working code that demonstrates the most critical graphics and interactions that your game will need, e.g., if you have a maze game, your code draw the maze with walls and a user can't move through a walls, if you have a platform game, your code can draw the platforms to jump to and a user can jump from platform to platform.|
|Core Game Logic||Due: Friday, February 26||Working code that demonstrates the most critical game play, e.g., if you have an asteroid-like game, the user can blow up asteroids and get blown up, if you have a race game, the user(s) can race to some finish line. Play should make sense and work at a usable speed.|
|Scoring||Due: Friday, March 5||Working code that demonstrates a "reasonable" scoring algorithm and game termination (win, lose). "Reasonable" means that the player's subjective feeling of how hard a game was and how well they did should be reflected in the final score. Recommendation: use big scores, e.g., 1000's of points, not 10's. It just feels like more fun that way.|
|In-class Demos||The Week of March 8||Schedule of who demos when will be released soon. Times will be picked randomly.|
Avoid large numbers of global variables. Put most if not all game data in a single game structure object that gets created in the call to big-bang and passed around to the various handlers. By structure, I mean something defined with define-struct, e.g., (define-struct game (...)), and created with (make-game ...)
This structure should contain fields for:
Right now, your game code runs automatically because the file contains a call to big-bang. That means the only way to re-run the game is to reload the file. This means you can't keep scores across multiple games.
This is easy to fix. Put the call to big-bang inside a function, e.g., run or start, like this:
(define (run) (big-bang (new-game) ...))) (run)
This defines run then calls it, so loading the file still starts the game automatically. But now you can just type (run) to run the game again, and it's up to you what is reset when your new-game function is called.
If you find yourself defining several types of objects that are all very similar, like this:
(define-struct rocket (image x y dx dy)) (define-struct asteroid (image x y dx dy)) (define-struct spaceman (image x y dx dy)) (define (new-rocket) (make-rocket rocket-image ...)) (define (new-asteroid) (make-rocket asteroid-image ...))
then you'd be much better off defining one generic object with an additional field to hold a string (or better yet a symbol) specifying the type, like this
(define-struct sprite (image type x y dx dy)) (define (new-rocket) (make-sprite rocket-image 'rocket ...)) (define (new-asteroid) (make-sprite asteroid-image 'asteroid ...))
This makes it much easier to keep a list of all objects and update their locations with one set-sprite-x! function, rather than needing to use a different function to set X for each object. [Sprite is a term commonly used in early computer games.]
It can be inefficient to make copies of a large game object over and over in a game. Therefore it makes sense to use structure modifying functions to change the parts of the game object, like the score, or of objects inside the game object, like the location of a specific player object.
You will need to be in Advanced Student mode to use these functions.
Remember that your on-tick, on-key and on-mouse functions will still need to return the game object, whether it's a new copy or a modified object.
The 2htdp/image library provides functions for rotating and scaling images. Be careful about calling these in loops, since they require substantial calculation of new bit images. It's better to use them to create an initial list of scaled or rotated images, and then have your game select the preconstructed images as needed during the game.
Alternatively use an image editing tool to create your images. There are many free tools out there.
Sometimes you will want your animation to pause or wait, or you will want to animation like an explosion to show for just a certain length of time. Don't go looking for a pause or wait command. That's not what you want to do. Your game should run continuously.
To implement an animation for an amount of time N, or to "pause" for a time N, add a wait counter to your game object. (You don't have to call it wait.) Normally wait should be set to zero. But if you want something to happen or not happen for N seconds, set wait to (roughly) 28 * N. Then in your update function called by on-tick, put this pseudo-logic:
if (game-wait game) > 0 do something (or don't do something) (set-game-wait! game (- (game-wait game) 1)) else do normal processing