2afc63542ca1691000dcd043e885e5b0e474cfd0
haas/fall2026/cprog/projects/fwg5.md
| ... | ... | @@ -0,0 +1,405 @@ |
| 1 | +# CSCS1320 C/C++ Programming |
|
| 2 | + |
|
| 3 | +# PROJECT: FUN WITH GAMES (fwg5) |
|
| 4 | + |
|
| 5 | +## OBJECTIVE |
|
| 6 | + |
|
| 7 | +Using your Vircon32 build and installation from fwg0, the `v32c++` |
|
| 8 | +transpiler from fwg4, and the game foundations from fwg1 through fwg4, |
|
| 9 | +implement a simple 2D game in which your classes are related by |
|
| 10 | +**inheritance**. Rather than writing out the same position, velocity, |
|
| 11 | +and size members in every class, you will factor what your game objects |
|
| 12 | +have in common into a **base class**, and let **derived classes** build |
|
| 13 | +on it. |
|
| 14 | + |
|
| 15 | +Your game can be a continuation of your fwg4 game (refactored to use |
|
| 16 | +inheritance), or something entirely new. The demo `fwg5.cpp` (see |
|
| 17 | +`make copy`) shows the idea in miniature; do not just rename it. |
|
| 18 | + |
|
| 19 | +This project is **single inheritance only**: each derived class has |
|
| 20 | +exactly ONE base class. (Multiple inheritance is a topic for another |
|
| 21 | +day.) |
|
| 22 | + |
|
| 23 | +## BACKGROUND |
|
| 24 | + |
|
| 25 | +### Why inheritance? |
|
| 26 | + |
|
| 27 | +In fwg4 you might have noticed your `Player` and your `Ball` (or |
|
| 28 | +`Enemy`, or `Bullet`) carrying very similar data: an `x`, a `y`, a |
|
| 29 | +`dx`, a `dy`, a `width`, a `height`. Copy/pasting that into each class |
|
| 30 | +works, but now a bug (or a change) has to be fixed in every copy. |
|
| 31 | + |
|
| 32 | +Inheritance lets one class **acquire** the members of another: |
|
| 33 | + |
|
| 34 | + * the **base class** (also: parent, superclass) holds what is common |
|
| 35 | + * the **derived class** (also: child, subclass) gets all of that for |
|
| 36 | + free, and adds or specializes what makes it different |
|
| 37 | + |
|
| 38 | +The litmus test is the **"is-a"** relationship. A `Player` IS-A |
|
| 39 | +`Entity`. A `Banner` IS-A `Entity`. If you cannot say "a ___ is a ___" |
|
| 40 | +with a straight face, inheritance is probably the wrong tool (a `Player` |
|
| 41 | +HAS-A `score`; that is just a data member, not a base class). |
|
| 42 | + |
|
| 43 | +### Syntax |
|
| 44 | + |
|
| 45 | +``` |
|
| 46 | +class Entity // the base class: nothing special |
|
| 47 | +{ |
|
| 48 | + ... |
|
| 49 | +}; |
|
| 50 | + |
|
| 51 | +class Player : public Entity // Player is derived from Entity |
|
| 52 | +{ |
|
| 53 | + ... |
|
| 54 | +}; |
|
| 55 | +``` |
|
| 56 | + |
|
| 57 | +The `: public Entity` part says "Player inherits from Entity". Use |
|
| 58 | +`public` inheritance: it preserves the is-a relationship, and it is the |
|
| 59 | +only flavor we will use. |
|
| 60 | + |
|
| 61 | +Everything that is accessible in `Entity` (see below) is now usable |
|
| 62 | +through a `Player` object: |
|
| 63 | + |
|
| 64 | +``` |
|
| 65 | +Player me(20, 20); |
|
| 66 | +me.move(); // move() is defined in Entity, not in Player |
|
| 67 | +``` |
|
| 68 | + |
|
| 69 | +### Access control: who gets to see what? |
|
| 70 | + |
|
| 71 | +You met `public` and `private` in fwg4. Inheritance introduces a third |
|
| 72 | +level, `protected`, and now the question "who can touch this?" has |
|
| 73 | +three audiences: **the class itself**, **its derived classes**, and |
|
| 74 | +**everyone else**. |
|
| 75 | + |
|
| 76 | +| member declared | the class itself | derived classes | everyone else | |
|
| 77 | +|-----------------|:----------------:|:---------------:|:-------------:| |
|
| 78 | +| `public` | yes | yes | yes | |
|
| 79 | +| `protected` | yes | yes | **no** | |
|
| 80 | +| `private` | yes | **no** | **no** | |
|
| 81 | + |
|
| 82 | +The key line is the `private` row: **a derived class does NOT get to |
|
| 83 | +touch its parent's private data**, even though it inherited it (the |
|
| 84 | +data is there inside every child object, but the child cannot name it). |
|
| 85 | +As we say in class: |
|
| 86 | + |
|
| 87 | +> **Children cannot access their parent's private parts.** |
|
| 88 | + |
|
| 89 | +If that sounds harsh, consider why: if any child could rummage through |
|
| 90 | +the parent's private data, then "private" would protect nothing; all |
|
| 91 | +anyone would need to do is write a derived class. Access control is only |
|
| 92 | +worth anything if inheritance cannot be used to get around it. |
|
| 93 | + |
|
| 94 | +So how does a child get at what it needs? |
|
| 95 | + |
|
| 96 | + 1. **Through the parent's public interface**: accessors ("getters") and |
|
| 97 | + mutators ("setters") such as `getX()` and `setPosition()`. This is |
|
| 98 | + the best default. |
|
| 99 | + 2. **Through `protected` members**: data the parent is willing to |
|
| 100 | + share with its descendants (but not with the world). Use sparingly: |
|
| 101 | + every `protected` member is a promise to all current and future |
|
| 102 | + children. |
|
| 103 | + |
|
| 104 | +A good rule of thumb: start everything `private`. Loosen to `protected` |
|
| 105 | +only when a derived class has a real need, and to `public` only when |
|
| 106 | +outside code does. |
|
| 107 | + |
|
| 108 | +``` |
|
| 109 | +class Entity |
|
| 110 | +{ |
|
| 111 | + private: |
|
| 112 | + int x, y; // only Entity's own functions touch these |
|
| 113 | + |
|
| 114 | + protected: |
|
| 115 | + int dx, dy; // Entity and its children may touch these |
|
| 116 | + |
|
| 117 | + public: |
|
| 118 | + int getX() { return x; } |
|
| 119 | + void setPosition(int nx, int ny) { x = nx; y = ny; } |
|
| 120 | +}; |
|
| 121 | + |
|
| 122 | +class Player : public Entity |
|
| 123 | +{ |
|
| 124 | + public: |
|
| 125 | + void stop() { dx = 0; dy = 0; } // OK: protected in Entity |
|
| 126 | + void home() { x = 0; y = 0; } // ERROR: x and y are private |
|
| 127 | + void home2() { setPosition(0, 0); } // OK: via the public interface |
|
| 128 | +}; |
|
| 129 | +``` |
|
| 130 | + |
|
| 131 | +### Constructors and inheritance |
|
| 132 | + |
|
| 133 | +When you create a `Player`, the `Entity` part of it has to be built too, |
|
| 134 | +and it is built **first**: base class constructor, then the derived |
|
| 135 | +class constructor. If the base class constructor needs arguments, the |
|
| 136 | +derived constructor supplies them using an **initializer list** (the |
|
| 137 | +part after the colon): |
|
| 138 | + |
|
| 139 | +``` |
|
| 140 | +class Player : public Entity |
|
| 141 | +{ |
|
| 142 | + private: |
|
| 143 | + int score; |
|
| 144 | + |
|
| 145 | + public: |
|
| 146 | + Player(int startx, int starty) |
|
| 147 | + : Entity(startx, starty, 10, 20) // build the Entity part first |
|
| 148 | + { |
|
| 149 | + score = 0; // then our own extras |
|
| 150 | + } |
|
| 151 | +}; |
|
| 152 | +``` |
|
| 153 | + |
|
| 154 | +If the base class has a constructor that takes arguments and the derived |
|
| 155 | +constructor does not say which one to call, the build fails; there is |
|
| 156 | +nothing sensible for the compiler to guess. |
|
| 157 | + |
|
| 158 | +### What a derived class can do |
|
| 159 | + |
|
| 160 | + * use everything `public` and `protected` from its base |
|
| 161 | + * **add** its own data members and member functions |
|
| 162 | + * be used wherever the base class is expected (a `Player` is an |
|
| 163 | + `Entity`) |
|
| 164 | + |
|
| 165 | +### The class hierarchy |
|
| 166 | + |
|
| 167 | +Draw it. Arrow from child to parent: |
|
| 168 | + |
|
| 169 | +``` |
|
| 170 | + Banner Player |
|
| 171 | + \ / |
|
| 172 | + \ / |
|
| 173 | + Entity |
|
| 174 | +``` |
|
| 175 | + |
|
| 176 | +Two derived classes, one base: that is the minimum for this project. |
|
| 177 | + |
|
| 178 | +## PROGRAM |
|
| 179 | + |
|
| 180 | +You are to write a Vircon32 C++ game that: |
|
| 181 | + |
|
| 182 | + * has a player-controlled object, moved via the gamepad |
|
| 183 | + * has at least one other kind of game object (enemy, obstacle, |
|
| 184 | + ball, projectile, pickup, etc.) that acts on its own |
|
| 185 | + * detects collisions between objects, with a meaningful result |
|
| 186 | + * keeps a score (or lives, or timer) displayed or otherwise tracked |
|
| 187 | + * has a win and/or lose condition (even if it simply resets) |
|
| 188 | + * is built around **inheritance**: |
|
| 189 | + * define a **base class** holding the data and behavior common to |
|
| 190 | + your game objects (at minimum: position and size, plus a |
|
| 191 | + constructor, accessors, and a mutator or two) |
|
| 192 | + * the base class must have **private** and/or **protected** data; |
|
| 193 | + use `protected` only where a derived class truly needs it |
|
| 194 | + * define at least **TWO derived classes**, each inheriting |
|
| 195 | + **publicly** from the base, from a **single** base class |
|
| 196 | + * each derived class **constructor must invoke the base class |
|
| 197 | + constructor** (initializer list) |
|
| 198 | + * each derived class must **add** something of its own: data and/or |
|
| 199 | + member functions that the base class does not have |
|
| 200 | + * derived classes **must not** touch the base class's private data; |
|
| 201 | + go through the public interface (or `protected` members) |
|
| 202 | + * create objects of your derived classes and let them do the work |
|
| 203 | + through their member functions; `main()` coordinates |
|
| 204 | + |
|
| 205 | +NOTE: you are free to use arrays of objects if your game calls for it, |
|
| 206 | +but the emphasis of this project is on the design of the class |
|
| 207 | +hierarchy and on respecting access control while using it. |
|
| 208 | + |
|
| 209 | +NOTE: if `v32c++` rejects a C++ feature, **keep it simple** and ask in |
|
| 210 | +the class chat; learning where the tool's limits are is part of the |
|
| 211 | +experience. |
|
| 212 | + |
|
| 213 | +NOTE: the first gamepad by default is mapped to the keyboard |
|
| 214 | + |
|
| 215 | +## CLASS REMINDERS |
|
| 216 | + |
|
| 217 | +A skeleton of the sort of thing we are after (yours will differ): |
|
| 218 | + |
|
| 219 | +``` |
|
| 220 | +class Entity // BASE CLASS |
|
| 221 | +{ |
|
| 222 | + private: |
|
| 223 | + int x; // position: private |
|
| 224 | + int y; |
|
| 225 | + |
|
| 226 | + protected: |
|
| 227 | + int dx; // motion: shared with children |
|
| 228 | + int dy; |
|
| 229 | + int width; |
|
| 230 | + int height; |
|
| 231 | + |
|
| 232 | + public: |
|
| 233 | + Entity(int startx, int starty, int w, int h); |
|
| 234 | + int getX(); |
|
| 235 | + int getY(); |
|
| 236 | + void setPosition(int newx, int newy); |
|
| 237 | + void move(); |
|
| 238 | +}; |
|
| 239 | + |
|
| 240 | +class Enemy : public Entity // DERIVED CLASS |
|
| 241 | +{ |
|
| 242 | + private: |
|
| 243 | + int strength; // new data, only Enemy has it |
|
| 244 | + |
|
| 245 | + public: |
|
| 246 | + Enemy(int startx, int starty) |
|
| 247 | + : Entity(startx, starty, 10, 20) // call the base constructor |
|
| 248 | + { |
|
| 249 | + strength = 3; |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + void update(); // new behavior |
|
| 253 | +}; |
|
| 254 | +``` |
|
| 255 | + |
|
| 256 | +Remember: a derived object **is** a base object, so every public member |
|
| 257 | +function of the base class works on it, too. |
|
| 258 | + |
|
| 259 | +## CARTRIDGE BUILD PROCESS |
|
| 260 | + |
|
| 261 | +All of these steps can be automated with the use of the provided |
|
| 262 | +`Makefile` simply by typing `make` at the prompt. Use `make copy` to |
|
| 263 | +pull the demo project (`fwg5.cpp`, `Makefile`) into your repository |
|
| 264 | +directory, and `make update` to refresh the `Makefile`. |
|
| 265 | + |
|
| 266 | +The build is the same as for fwg4: `v32c++` transpiles your `.cpp` and, |
|
| 267 | +as a side effect, generates the cartridge XML and the build script |
|
| 268 | +(`Make.sh`), which then compiles, assembles, converts any textures and |
|
| 269 | +sounds, and packs the ROM. |
|
| 270 | + |
|
| 271 | +### TRANSPILE |
|
| 272 | + |
|
| 273 | +``` |
|
| 274 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ v32c++ fwg5.cpp -o fwg5.c |
|
| 275 | +``` |
|
| 276 | + |
|
| 277 | +Do **NOT** track the generated `.c` file; it is a build product. |
|
| 278 | + |
|
| 279 | +### COMPILE |
|
| 280 | + |
|
| 281 | +``` |
|
| 282 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ compile fwg5.c -o fwg5.asm |
|
| 283 | +``` |
|
| 284 | + |
|
| 285 | +### ASSEMBLE |
|
| 286 | + |
|
| 287 | +``` |
|
| 288 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ assemble fwg5.asm -o fwg5.vbin |
|
| 289 | +``` |
|
| 290 | + |
|
| 291 | +### IMAGE CONVERSION |
|
| 292 | + |
|
| 293 | +``` |
|
| 294 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ png2vircon sprites.png -o sprites.vtex |
|
| 295 | +``` |
|
| 296 | + |
|
| 297 | +Note that you'll also want to ensure the XML file has been updated to |
|
| 298 | +reference these textures: |
|
| 299 | + |
|
| 300 | +``` |
|
| 301 | + <binary path="fwg5.vbin" /> |
|
| 302 | + <textures> |
|
| 303 | + <texture path="sprites.vtex" /> |
|
| 304 | + </textures> |
|
| 305 | +``` |
|
| 306 | + |
|
| 307 | +### AUDIO CONVERSION |
|
| 308 | + |
|
| 309 | +``` |
|
| 310 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ wav2vircon soundfx.wav -o soundfx.vsnd |
|
| 311 | +``` |
|
| 312 | + |
|
| 313 | +``` |
|
| 314 | + <sounds> |
|
| 315 | + <sound path="soundfx.vsnd" /> |
|
| 316 | + </sounds> |
|
| 317 | +``` |
|
| 318 | + |
|
| 319 | +### PACKING THE ROM |
|
| 320 | + |
|
| 321 | +``` |
|
| 322 | +yoursystem:~/src/SEMESTER/cprog/fwg5$ packrom fwg5.xml |
|
| 323 | +``` |
|
| 324 | + |
|
| 325 | +## SUBMISSION |
|
| 326 | + |
|
| 327 | +To be successful in this project, the following criteria (or their |
|
| 328 | +equivalent) must be met: |
|
| 329 | + |
|
| 330 | + * Project must be submit on time, by the deadline. |
|
| 331 | + * Late submissions will lose 33% credit per day, with the submission window closing on the 3rd day following the deadline. |
|
| 332 | + * Executed programs must display in a manner similar to provided output |
|
| 333 | + * output formatted, where applicable, must match that of project requirements |
|
| 334 | + * Processing must be correct based on input given and output requested |
|
| 335 | + * Output, if applicable, must be correct based on values input |
|
| 336 | + * Code must be nicely and consistently indented |
|
| 337 | + * Code must be consistently written, to strive for readability from having a consistent style throughout |
|
| 338 | + * Code must be commented |
|
| 339 | + * Any "to be implemented" comments **MUST** be removed |
|
| 340 | + * these "to be implemented" comments, if still present at evaluation time, will result in points being deducted. |
|
| 341 | + * Sufficient comments explaining the point of provided logic **MUST** be present |
|
| 342 | + * No global variables (without instructor approval), no goto statements, no calling of main()! |
|
| 343 | + * Track/version the source code in your private semester repository |
|
| 344 | + * Submit a copy of your source code to me using the **submit** tool by the deadline. |
|
| 345 | + |
|
| 346 | +Do **NOT** track or submit generated or binary files (`obj/`, `bin/`, |
|
| 347 | +`.c`, `.vbin`, `.v32`); `make submit` cleans these for you. |
|
| 348 | + |
|
| 349 | +### SUBMIT TOOL USAGE |
|
| 350 | + |
|
| 351 | +``` |
|
| 352 | +lab46:~/src/SEMESTER/cprog/fwg5$ make submit |
|
| 353 | +``` |
|
| 354 | + |
|
| 355 | +You should get some sort of confirmation indicating successful submission |
|
| 356 | +if all went according to plan. If not, check for typos and or locational |
|
| 357 | +mismatches. |
|
| 358 | + |
|
| 359 | +## COURSE OBJECTIVES ASSESSED |
|
| 360 | + |
|
| 361 | +Some of the rubric line items below also serve as evaluations of the |
|
| 362 | +course objectives listed on the course homepage: |
|
| 363 | + |
|
| 364 | + * *Implement functional programs ... that satisfy automated test requirements*: |
|
| 365 | + the cartridge builds with no warning or error |
|
| 366 | + * *Label data structures as either scalar or composite*: |
|
| 367 | + the base class and its data/interface |
|
| 368 | + * *Construct modular code by defining functions and utilizing different |
|
| 369 | + methods of passing data into them*: derived constructors passing data |
|
| 370 | + up to the base class |
|
| 371 | + * *Select the most appropriate control structure or data type to solve a |
|
| 372 | + specific, predefined logical problem*: modeling shared and |
|
| 373 | + specialized game objects with a class hierarchy |
|
| 374 | + * *Utilize built-in language libraries*: texture and sound use |
|
| 375 | + |
|
| 376 | +Not every line item maps to an objective, and that is fine. |
|
| 377 | + |
|
| 378 | +### RUBRIC |
|
| 379 | + |
|
| 380 | +I'll be evaluating the project based on the following criteria: |
|
| 381 | + |
|
| 382 | +``` |
|
| 383 | +286:fwg5:final tally of results (286/286) |
|
| 384 | +*:fwg5:code modified to meet project specifications [26/26] |
|
| 385 | +*:fwg5:screenshot or video posted to class DISCORD of game running [26/26] |
|
| 386 | +*:fwg5:base class defined with protected/private data and a public interface [26/26] |
|
| 387 | +*:fwg5:at least two classes derive publicly from a base (single inheritance) [26/26] |
|
| 388 | +*:fwg5:derived constructors invoke the base class constructor [26/26] |
|
| 389 | +*:fwg5:access control respected: children do not touch their parent's private parts [26/26] |
|
| 390 | +*:fwg5:derived classes add their own data and/or behavior [26/26] |
|
| 391 | +*:fwg5:working collision detection between game objects [26/26] |
|
| 392 | +*:fwg5:game has scoring (or lives/timer) and a win/lose condition [13/13] |
|
| 393 | +*:fwg5:game uses at least one texture and one sound [13/13] |
|
| 394 | +*:fwg5:source file, XML file, build script, and cartridge submit [13/13] |
|
| 395 | +*:fwg5:code compiles, cartridge builds with no warning or error [13/13] |
|
| 396 | +*:fwg5:committed project related changes to private semester repo [13/13] |
|
| 397 | +*:fwg5:grabit of project data by Sunday prior to duedate [13/13] |
|
| 398 | +``` |
|
| 399 | + |
|
| 400 | +Additionally: |
|
| 401 | + |
|
| 402 | + * Solutions not abiding by spirit of project will be subject to a 50% overall deduction |
|
| 403 | + * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction |
|
| 404 | + * Solutions not utilizing indentation to promote scope and clarity or otherwise maintaining consistency in code style and presentation will be subject to a 25% overall deduction |
|
| 405 | + * Solutions not organized and easy to read (assume a terminal at least 90 characters wide, 40 characters tall) are subject to a 25% overall deduction |
haas/fall2026/cprog/projects/fwg6.md
| ... | ... | @@ -0,0 +1,404 @@ |
| 1 | +# CSCS1320 C/C++ Programming |
|
| 2 | + |
|
| 3 | +# PROJECT: FUN WITH GAMES (fwg6) |
|
| 4 | + |
|
| 5 | +## OBJECTIVE |
|
| 6 | + |
|
| 7 | +Using your Vircon32 build and installation from fwg0, the `v32c++` |
|
| 8 | +transpiler from fwg4, and the class hierarchy skills from fwg5, |
|
| 9 | +implement a simple 2D game built around **polymorphism**: a collection |
|
| 10 | +of different kinds of game objects, all handled through a **base class |
|
| 11 | +pointer**, each responding to the same function call in its own way via |
|
| 12 | +**virtual functions**. Along the way you will also try out |
|
| 13 | +**overloading**: functions that share a name, and operators (`+`, `==`, |
|
| 14 | +...) taught how to work on your own classes. |
|
| 15 | + |
|
| 16 | +Your game can be a continuation of your fwg5 game or something entirely |
|
| 17 | +new. The demo `fwg6.cpp` (see `make copy`) shows the ideas in miniature; |
|
| 18 | +do not just rename it. |
|
| 19 | + |
|
| 20 | +As in fwg5, inheritance is **single inheritance only**. |
|
| 21 | + |
|
| 22 | +## BACKGROUND |
|
| 23 | + |
|
| 24 | +### The problem polymorphism solves |
|
| 25 | + |
|
| 26 | +Suppose your game has a player, some drifting rocks, and a chaser. They |
|
| 27 | +are all "things that update and draw themselves", but each one does so |
|
| 28 | +differently. With plain inheritance you can write: |
|
| 29 | + |
|
| 30 | +``` |
|
| 31 | +Player me; |
|
| 32 | +Drifter rock1, rock2; |
|
| 33 | +Chaser stalker; |
|
| 34 | + |
|
| 35 | +me.update(); |
|
| 36 | +rock1.update(); |
|
| 37 | +rock2.update(); |
|
| 38 | +stalker.update(); |
|
| 39 | +``` |
|
| 40 | + |
|
| 41 | +...and every new kind of object means more lines in `main()`. What you |
|
| 42 | +want is **one list of game objects, one loop, one call**, with each |
|
| 43 | +object doing the right thing for what it actually is: |
|
| 44 | + |
|
| 45 | +``` |
|
| 46 | +for (i = 0; i < NUM_ACTORS; i++) |
|
| 47 | +{ |
|
| 48 | + actors[i]->update(); |
|
| 49 | +} |
|
| 50 | +``` |
|
| 51 | + |
|
| 52 | +That is **polymorphism** ("many forms"): the same call, many behaviors, |
|
| 53 | +the right one chosen **at run time** by the real type of the object. |
|
| 54 | + |
|
| 55 | +### Two ingredients |
|
| 56 | + |
|
| 57 | +**1. A base class pointer can point at a derived object.** A `Drifter` |
|
| 58 | +IS-A `Actor`, so an `Actor *` is allowed to hold the address of a |
|
| 59 | +`Drifter`: |
|
| 60 | + |
|
| 61 | +``` |
|
| 62 | +Drifter rock(200, 100, 2, 1); |
|
| 63 | +Actor *a = &rock; // address-of (&): a points at rock |
|
| 64 | +a->update(); // arrow (->): call through the pointer |
|
| 65 | +``` |
|
| 66 | + |
|
| 67 | +This is where your pointer skills (address-of `&`, the arrow `->`) pay |
|
| 68 | +off. Since every element of an array has to be the same type, an array |
|
| 69 | +of `Actor *` can hold the addresses of a mixed bag of derived objects: |
|
| 70 | + |
|
| 71 | +``` |
|
| 72 | +Actor *actors[3]; |
|
| 73 | +actors[0] = &me; // a Player |
|
| 74 | +actors[1] = &rock; // a Drifter |
|
| 75 | +actors[2] = &stalker; // a Chaser |
|
| 76 | +``` |
|
| 77 | + |
|
| 78 | +**2. Virtual functions.** By default, `a->update()` on an `Actor *` |
|
| 79 | +calls **`Actor`'s** version of `update()`, because that is the type |
|
| 80 | +the pointer claims. Mark the function `virtual` in the base class and |
|
| 81 | +C++ instead looks at what the pointer **really** points at: |
|
| 82 | + |
|
| 83 | +``` |
|
| 84 | +class Actor |
|
| 85 | +{ |
|
| 86 | + public: |
|
| 87 | + virtual void update(); // "derived classes may replace this" |
|
| 88 | +}; |
|
| 89 | + |
|
| 90 | +class Drifter : public Actor |
|
| 91 | +{ |
|
| 92 | + public: |
|
| 93 | + virtual void update(); // Drifter's own version: an OVERRIDE |
|
| 94 | +}; |
|
| 95 | +``` |
|
| 96 | + |
|
| 97 | +Now `a->update()` runs `Drifter::update()` when `a` points at a |
|
| 98 | +`Drifter`, `Player::update()` when it points at a `Player`, and so on. |
|
| 99 | +(The word `virtual` is optional on the override once the base declares |
|
| 100 | +it, but writing it keeps your intentions clear; do so.) |
|
| 101 | + |
|
| 102 | +### Pure virtual functions and abstract base classes |
|
| 103 | + |
|
| 104 | +Sometimes the base class cannot say anything sensible about how a |
|
| 105 | +function should work: what does it mean to `draw()` a generic `Actor`? |
|
| 106 | +Declare it **pure virtual** by giving it `= 0` instead of a body: |
|
| 107 | + |
|
| 108 | +``` |
|
| 109 | +class Actor |
|
| 110 | +{ |
|
| 111 | + public: |
|
| 112 | + virtual void update() = 0; // pure virtual: no body here |
|
| 113 | + virtual void draw() = 0; |
|
| 114 | +}; |
|
| 115 | +``` |
|
| 116 | + |
|
| 117 | +A class with at least one pure virtual function is an **abstract base |
|
| 118 | +class**: |
|
| 119 | + |
|
| 120 | + * you **must not** create an object of it (`Actor a;` is an error in |
|
| 121 | + standard C++; `v32c++` does not check this for you, so the |
|
| 122 | + discipline is yours, and it will be checked when your project is |
|
| 123 | + evaluated): it is a promise, not a thing |
|
| 124 | + * every derived class that wants to be instantiated **must override** |
|
| 125 | + all of the pure virtuals, i.e. it must keep the promise |
|
| 126 | + * you **can** still have pointers to it (`Actor *a;`), which is what |
|
| 127 | + makes the polymorphic array above possible |
|
| 128 | + |
|
| 129 | +Think of the abstract base class as a contract: "anything that wants to |
|
| 130 | +be an `Actor` must know how to `update()` and `draw()`." |
|
| 131 | + |
|
| 132 | +### Overriding is not overloading |
|
| 133 | + |
|
| 134 | +These two words sound alike and mean different things: |
|
| 135 | + |
|
| 136 | + * **overriding**: a derived class re-implements a **virtual** function |
|
| 137 | + from its base, with the **same name and same parameters**. Resolved |
|
| 138 | + at run time by the object's real type. |
|
| 139 | + * **overloading**: two or more functions with the **same name but |
|
| 140 | + different parameters**, in the same scope. Resolved at compile time |
|
| 141 | + from the arguments you pass. |
|
| 142 | + |
|
| 143 | +### Function overloading |
|
| 144 | + |
|
| 145 | +``` |
|
| 146 | +class Actor |
|
| 147 | +{ |
|
| 148 | + public: |
|
| 149 | + void setPosition(int newx, int newy); // two ints... |
|
| 150 | + void setPosition(Vec2 newpos); // ...or one Vec2 |
|
| 151 | +}; |
|
| 152 | + |
|
| 153 | +actor.setPosition(10, 20); // picks the first |
|
| 154 | +actor.setPosition(Vec2(10, 20)); // picks the second |
|
| 155 | +``` |
|
| 156 | + |
|
| 157 | +You have already used this without noticing: a class can have more than |
|
| 158 | +one **constructor**, as long as their parameter lists differ. |
|
| 159 | + |
|
| 160 | +### Operator overloading |
|
| 161 | + |
|
| 162 | +Operators like `+` and `==` already work on `int`s. For your own classes |
|
| 163 | +you can teach them what to do. An operator is just a function with an |
|
| 164 | +odd name: `operator+`, `operator==`, `operator+=`, ... |
|
| 165 | + |
|
| 166 | +``` |
|
| 167 | +class Vec2 |
|
| 168 | +{ |
|
| 169 | + private: |
|
| 170 | + int x; |
|
| 171 | + int y; |
|
| 172 | + |
|
| 173 | + public: |
|
| 174 | + Vec2(int newx, int newy) { x = newx; y = newy; } |
|
| 175 | + |
|
| 176 | + Vec2 operator+(Vec2 other) // a + b |
|
| 177 | + { |
|
| 178 | + return Vec2(x + other.x, y + other.y); |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + void operator+=(Vec2 other) // a += b |
|
| 182 | + { |
|
| 183 | + x += other.x; |
|
| 184 | + y += other.y; |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + int operator==(Vec2 other) // a == b |
|
| 188 | + { |
|
| 189 | + return (x == other.x && y == other.y); |
|
| 190 | + } |
|
| 191 | +}; |
|
| 192 | + |
|
| 193 | +Vec2 pos(10, 10); |
|
| 194 | +Vec2 vel(2, 1); |
|
| 195 | + |
|
| 196 | +pos += vel; // same as pos.operator+=(vel) |
|
| 197 | +if (pos == vel) { ... } // same as pos.operator==(vel) |
|
| 198 | +Vec2 next = pos + vel; // same as pos.operator+(vel) |
|
| 199 | +``` |
|
| 200 | + |
|
| 201 | +Guidelines: overload an operator only when its meaning is **obvious** |
|
| 202 | +(adding two vectors with `+` is; making `+` launch a missile is not). |
|
| 203 | +Overloading changes how your own types are *written*, not what the |
|
| 204 | +operator can do for built-in types: `3 + 4` is unaffected. |
|
| 205 | + |
|
| 206 | +*Access control reminder (from fwg5):* notice that `operator+` reads |
|
| 207 | +`other.x`. That is legal because the function is a member of `Vec2` and |
|
| 208 | +may touch the private data of any `Vec2`, not just its own object. |
|
| 209 | +Children still cannot access their parent's private parts, though. |
|
| 210 | + |
|
| 211 | +### A word about destructors |
|
| 212 | + |
|
| 213 | +In ordinary C++ a class that is meant to be used through a base pointer |
|
| 214 | +also gets a `virtual` destructor so deleting a derived object through a |
|
| 215 | +base pointer cleans up correctly. Our cartridges never `new`/`delete` |
|
| 216 | +objects (all of ours live as local variables in `main()`), so we need no |
|
| 217 | +destructors for this project. |
|
| 218 | + |
|
| 219 | +## PROGRAM |
|
| 220 | + |
|
| 221 | +You are to write a Vircon32 C++ game that: |
|
| 222 | + |
|
| 223 | + * has a player-controlled object, moved via the gamepad |
|
| 224 | + * has at least two other kinds of game object that act on their own |
|
| 225 | + * detects collisions between objects, with a meaningful result |
|
| 226 | + * keeps a score (or lives, or timer) displayed or otherwise tracked |
|
| 227 | + * has a win and/or lose condition (even if it simply resets) |
|
| 228 | + * is built around **polymorphism**: |
|
| 229 | + * define an **abstract base class** with at least one **pure |
|
| 230 | + virtual** function (for example `update()` and `draw()`) |
|
| 231 | + * define at least **TWO derived classes** (publicly, single |
|
| 232 | + inheritance) that **override** the virtual function(s) with |
|
| 233 | + behavior of their own |
|
| 234 | + * keep your game objects in an **array of base class pointers**, |
|
| 235 | + holding **at least two different derived types** |
|
| 236 | + * use a **loop** to call the virtual function(s) through those |
|
| 237 | + pointers (`->`) |
|
| 238 | + * demonstrates **overloading**: |
|
| 239 | + * overload at least **one operator** for a class of yours (for |
|
| 240 | + example a vector or point class), and **use it** meaningfully in |
|
| 241 | + your game code |
|
| 242 | + * demonstrate **function overloading** with at least one set of |
|
| 243 | + functions (or constructors) sharing a name but differing in |
|
| 244 | + parameters |
|
| 245 | + |
|
| 246 | +NOTE: the first gamepad by default is mapped to the keyboard |
|
| 247 | + |
|
| 248 | +NOTE: the transpiler emits an empty function body for each pure virtual |
|
| 249 | +function, and the Vircon32 compiler reports `argument "this" is not |
|
| 250 | +used` for it. That specific warning is expected and is **not** held |
|
| 251 | +against you for the "no warning or error" line item; any other warning |
|
| 252 | +or error is. |
|
| 253 | + |
|
| 254 | +NOTE: `v32c++` is not a full C++ compiler. If a feature is rejected, |
|
| 255 | +simplify and ask in the class chat; documenting what works and what |
|
| 256 | +does not is valuable. |
|
| 257 | + |
|
| 258 | +## CARTRIDGE BUILD PROCESS |
|
| 259 | + |
|
| 260 | +All of these steps can be automated with the use of the provided |
|
| 261 | +`Makefile` simply by typing `make` at the prompt. Use `make copy` to |
|
| 262 | +pull the demo project (`fwg6.cpp`, `Makefile`) into your repository |
|
| 263 | +directory, and `make update` to refresh the `Makefile`. |
|
| 264 | + |
|
| 265 | +The build is the same as for fwg4 and fwg5: `v32c++` transpiles your |
|
| 266 | +`.cpp` and generates the cartridge XML and build script (`Make.sh`), |
|
| 267 | +which compiles, assembles, converts textures and sounds, and packs the |
|
| 268 | +ROM. |
|
| 269 | + |
|
| 270 | +### TRANSPILE |
|
| 271 | + |
|
| 272 | +``` |
|
| 273 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ v32c++ fwg6.cpp -o fwg6.c |
|
| 274 | +``` |
|
| 275 | + |
|
| 276 | +### COMPILE |
|
| 277 | + |
|
| 278 | +``` |
|
| 279 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ compile fwg6.c -o fwg6.asm |
|
| 280 | +``` |
|
| 281 | + |
|
| 282 | +### ASSEMBLE |
|
| 283 | + |
|
| 284 | +``` |
|
| 285 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ assemble fwg6.asm -o fwg6.vbin |
|
| 286 | +``` |
|
| 287 | + |
|
| 288 | +### IMAGE CONVERSION |
|
| 289 | + |
|
| 290 | +``` |
|
| 291 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ png2vircon sprites.png -o sprites.vtex |
|
| 292 | +``` |
|
| 293 | + |
|
| 294 | +``` |
|
| 295 | + <binary path="fwg6.vbin" /> |
|
| 296 | + <textures> |
|
| 297 | + <texture path="sprites.vtex" /> |
|
| 298 | + </textures> |
|
| 299 | +``` |
|
| 300 | + |
|
| 301 | +### AUDIO CONVERSION |
|
| 302 | + |
|
| 303 | +``` |
|
| 304 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ wav2vircon soundfx.wav -o soundfx.vsnd |
|
| 305 | +``` |
|
| 306 | + |
|
| 307 | +``` |
|
| 308 | + <sounds> |
|
| 309 | + <sound path="soundfx.vsnd" /> |
|
| 310 | + </sounds> |
|
| 311 | +``` |
|
| 312 | + |
|
| 313 | +### PACKING THE ROM |
|
| 314 | + |
|
| 315 | +``` |
|
| 316 | +yoursystem:~/src/SEMESTER/cprog/fwg6$ packrom fwg6.xml |
|
| 317 | +``` |
|
| 318 | + |
|
| 319 | +## SUBMISSION |
|
| 320 | + |
|
| 321 | +To be successful in this project, the following criteria (or their |
|
| 322 | +equivalent) must be met: |
|
| 323 | + |
|
| 324 | + * Project must be submit on time, by the deadline. |
|
| 325 | + * Late submissions will lose 33% credit per day, with the submission window closing on the 3rd day following the deadline. |
|
| 326 | + * Executed programs must display in a manner similar to provided output |
|
| 327 | + * output formatted, where applicable, must match that of project requirements |
|
| 328 | + * Processing must be correct based on input given and output requested |
|
| 329 | + * Output, if applicable, must be correct based on values input |
|
| 330 | + * Code must be nicely and consistently indented |
|
| 331 | + * Code must be consistently written, to strive for readability from having a consistent style throughout |
|
| 332 | + * Code must be commented |
|
| 333 | + * Any "to be implemented" comments **MUST** be removed |
|
| 334 | + * these "to be implemented" comments, if still present at evaluation time, will result in points being deducted. |
|
| 335 | + * Sufficient comments explaining the point of provided logic **MUST** be present |
|
| 336 | + * No global variables (without instructor approval), no goto statements, no calling of main()! |
|
| 337 | + * Track/version the source code in your private semester repository |
|
| 338 | + * Submit a copy of your source code to me using the **submit** tool by the deadline. |
|
| 339 | + |
|
| 340 | +Do **NOT** track or submit generated or binary files (`obj/`, `bin/`, |
|
| 341 | +`.c`, `.vbin`, `.v32`); `make submit` cleans these for you. |
|
| 342 | + |
|
| 343 | +### SUBMIT TOOL USAGE |
|
| 344 | + |
|
| 345 | +``` |
|
| 346 | +lab46:~/src/SEMESTER/cprog/fwg6$ make submit |
|
| 347 | +``` |
|
| 348 | + |
|
| 349 | +You should get some sort of confirmation indicating successful submission |
|
| 350 | +if all went according to plan. If not, check for typos and or locational |
|
| 351 | +mismatches. |
|
| 352 | + |
|
| 353 | +## COURSE OBJECTIVES ASSESSED |
|
| 354 | + |
|
| 355 | +Some of the rubric line items below also serve as evaluations of the |
|
| 356 | +course objectives listed on the course homepage: |
|
| 357 | + |
|
| 358 | + * *Implement functional programs ... that satisfy automated test requirements*: |
|
| 359 | + the cartridge builds with no warning or error |
|
| 360 | + * *Define the relationship between a variable's value and its memory |
|
| 361 | + address using basic pointer syntax*: base class pointers holding the |
|
| 362 | + address (`&`) of derived objects, called through `->` |
|
| 363 | + * *Distinguish between collections of the same data type (arrays) and |
|
| 364 | + collections of differing data types (structures)*: the array of base |
|
| 365 | + pointers holding different derived objects |
|
| 366 | + * *Construct modular code by defining functions and utilizing different |
|
| 367 | + methods of passing data into them*: overriding virtual functions |
|
| 368 | + * *Select the most appropriate control structure or data type to solve a |
|
| 369 | + specific, predefined logical problem*: an abstract base class as the |
|
| 370 | + contract for your game objects |
|
| 371 | + * *Utilize built-in language libraries*: texture and sound use |
|
| 372 | + |
|
| 373 | +Not every line item maps to an objective, and that is fine. |
|
| 374 | + |
|
| 375 | +### RUBRIC |
|
| 376 | + |
|
| 377 | +I'll be evaluating the project based on the following criteria: |
|
| 378 | + |
|
| 379 | +``` |
|
| 380 | +312:fwg6:final tally of results (312/312) |
|
| 381 | +*:fwg6:code modified to meet project specifications [26/26] |
|
| 382 | +*:fwg6:screenshot or video posted to class DISCORD of game running [26/26] |
|
| 383 | +*:fwg6:abstract base class declares virtual and pure virtual functions [26/26] |
|
| 384 | +*:fwg6:at least two derived classes override a virtual function [26/26] |
|
| 385 | +*:fwg6:virtual functions invoked through base class pointers [26/26] |
|
| 386 | +*:fwg6:array of base pointers holds different derived objects, driven by a loop [26/26] |
|
| 387 | +*:fwg6:at least one operator overloaded in a class [26/26] |
|
| 388 | +*:fwg6:overloaded operator is used meaningfully by the game code [13/13] |
|
| 389 | +*:fwg6:function (or constructor) overloading demonstrated [13/13] |
|
| 390 | +*:fwg6:working collision detection between game objects [26/26] |
|
| 391 | +*:fwg6:game has scoring (or lives/timer) and a win/lose condition [13/13] |
|
| 392 | +*:fwg6:game uses at least one texture and one sound [13/13] |
|
| 393 | +*:fwg6:source file, XML file, build script, and cartridge submit [13/13] |
|
| 394 | +*:fwg6:code compiles, cartridge builds with no warning or error [13/13] |
|
| 395 | +*:fwg6:committed project related changes to private semester repo [13/13] |
|
| 396 | +*:fwg6:grabit of project data by Sunday prior to duedate [13/13] |
|
| 397 | +``` |
|
| 398 | + |
|
| 399 | +Additionally: |
|
| 400 | + |
|
| 401 | + * Solutions not abiding by spirit of project will be subject to a 50% overall deduction |
|
| 402 | + * Solutions not utilizing descriptive why and how comments will be subject to a 25% overall deduction |
|
| 403 | + * Solutions not utilizing indentation to promote scope and clarity or otherwise maintaining consistency in code style and presentation will be subject to a 25% overall deduction |
|
| 404 | + * Solutions not organized and easy to read (assume a terminal at least 90 characters wide, 40 characters tall) are subject to a 25% overall deduction |