This week's lab is an introduction to GameX. We'll learn how to use GameX in the Visual Studio .NET environment and how a basic game is set up by compiling a sample program called ShipDemo, and we'll then modify the ShipDemo sample in various ways to see a few of the things you can do with GameX.
You may work in groups of anywhere between 1 and 1 people. In other words, everyone must work individually, and what you do on the lab must be entirely your own work. This lab is not due during this lab period, but it is to your advantage to work through (and/or learn) as much as you can now, while assistance is immediately available. If you run into problems finishing the lab project later, please free to contact a staff member for help. (Preferably a programming TA)
Getting Started Using GameX with VS.NET
The GameX official homepage is http://www.rchoetzlein.com/gamex/
(but you should use the mirror at www.cs.cornell.edu/projects/game/GameX/).
There you will find an overview of GameX and documentation for it.
For this lab we will be using version 5.08 of GameX (5.09 will work too).
(Use this link for a
version of the lab that works with VS .NET 2005.
Here
is a version that works with 2003, but you shouldn't use it.)
The first time you run Visual Studio .NET, it may prompt you with various questions about what kind of environment you want. I recommend you indicate you are a C++ developer. (NOTE: Visual Studio C++ 6.0 or earlier is not suitable for use in this course - we require you to use 7.1 or later)
We could get started by creating a Visual Studio solution and project file, but ShipDemo should already come with these set up. Because there are a lot of options to set and a lot of little things that can go wrong in the process, when it comes time to create your own projects in this class I recommend you use this project as a template and simply replace its code files with your own rather than trying to set up all the project settings from scratch. In any case, for now you just need to open the .sln file in the ShipDemo or Lab1P folder to get started.
Once the solution is open, you should be able to compile and run the demo by hitting F5. (Ask a TA if this doesn't work.) Here are some other useful commands in Visual Studio, for future reference: Pressing Shift-F5 from VS.NET will immediately stop your game from running even if it has crashed or frozen, F9 adds/removes breakpoints, F10 steps (runs one line), F11 steps into a function call, Shift-F11 steps out of a function, and F5 continues (or starts) running normally (with debugging). Also, Ctrl-Shift-S can be used to save everything at once (all open code files, project settings, etc.).
There should be a list of folders and files on the side of the Visual Studio window (called the Solution Explorer). Expand the folder called "Code" if it's not already expanded, and and from here you should be able to double-click on any of the code files to edit them. Note that this is also where you would remove files from the project (by pressing Delete on a selected file) and add files to your project (by dragging them from Windows onto a folder in this list), and you can create new folders to better organize your files, but for now just leave this as it is and only edit the files.
Lab Project
To help get familiarized with how to use GameX, we're going to be making a series of modifications to ShipDemo. Before doing so, it might be helpful to look through the code and see how it works. The official GameX page details a good chunk of similar code in its examples. After you understand the code to some degree, your assignment for this lab is to make the following changes (in the order given):
1) In shipdemox-main.cpp, add an extra parameter (an int of 60) to the call to GameX.Initialize, right after the parameter that starts as 400. If you run the game now, there should be no change. Now change the 60 to different values such as 100, 30, 600, and 5. Describe briefly what happens, i.e. the effect these have on the gameplay and/or graphics (in a file called readme.txt you will submit later). Leave the extra parameter at 60. If you wish to know more about what GameX is doing here, see the section "About Game Speed and Frame Rate" in the GameX documentation.
2) Modify the size of the game window, which starts as 800x400. Try several sizes, including 400x300, 100x1000, and 2000x2000, to see what happens. Try running the game in both windowed and full-screen mode for the various sizes (pressing Alt-Enter in the game will toggle full-screen), and fly the ship around and fire a little for each one. How do the ships and photons know to change where they bounce off of when you change the size of the screen? Does the game perform worse and/or look worse with some sizes than others? Is there a minimum or maximum size? Is there a "best" width to height ratio, and if so, in what way is it best? (Answer these questions either now or later, in your readme.txt.) Leave the game window set to 800x600 when done.
3) In shipdemox-ship.cpp, in the Ship::Draw method, change the size of the ships by changing each "1.0f" to a different value, and describe what the trend is for what different values do. (Make sure to change the size of the ship's shadow by the same amount you change the size of the ship, or it will look pretty strange.) Change the size of the ship to be the square root (use sqrtf()) of its _weight member value, and also change the collision detection code to correctly use the new radius of each ship. The collision detection code is in the same file, in the Ship::CollideCheck method. After this is done, run the game and observe how well it runs (how smooth the animation seem to be). Now, taking the square root is a somewhat slow operation, and the change you just made probably causes the square root of the ship's weight to be calculated several times for every frame of animation, for a total of several hundred square roots per second, all for recalculating values that don't change. But we want the game to do as little unnecessary work as possible so that it can run faster/smoother, so as an optimization make sure that you do not recalculate the square root of the ship's weight every time, and instead store that value as a private variable in the Ship class that gets set only in the Ship's Initialize function. Compare how well the game ran when you observed it without this optimization to how well it runs with the optimization. How much did the performance improve? Explain why you think it improved as much or as little as it did. Now try purposely slowing the game down by doing various things (math functions, drawing functions, etc.) lots of times per frame. When something has a noticeable effect, try changing the Visual Studio project configuration from Debug to Release and see if that makes any difference. From what you observed, what can you say about optimizing your game code in general?
4) Implement "wrap-around" (this means when an object hits the edge of the playfield, it should keep going and reappear on the other side). To make this work better you may want to modify the lifespan of shots, and you should implement a "max speed" for the ships (perhaps by changing the "drag" to always affect them even when they are accelerating). Also, be aware that there are "clipping" issues: you want a ship that's in the process of wrapping around to be drawn partially on one side, and partially on the other, which you can do by drawing the image twice (once on each side of the screen for each screen edge that it is near). Make sure you get the "corner cases" right, and make sure the ship's shadow doesn't do anything noticeably weird or different in the transitions. And, very importantly, you shouldn't need to add a lot of code to do this. Do not just copy-and-paste the drawing code a few times, instead you should find a pattern and wrap it in a loop, or something like that. Make sure a ship that is not in the process of wrapping around the screen does not draw multiple times, though.
5) In Photons::Draw, you will notice the constant DRAW_ADD is used. Look this constant up in the GameX documentation, and in the code, replace DRAW_ADD one-at-a-time with each of the other nine Image Drawing Mode constants such as DRAW_PLAIN and DRAW_BURN, and try them all out in separate runs of the game. Describe (in readme.txt) what visual effect each one causes as best as you can (including DRAW_ADD). Also, set the ships (but not their shadows) to draw with DRAW_ADD (in the Ship::Draw method of shipdemox-ship.cpp), and describe the effect. You do not need to test any of the constants starting with DRAWOP, although these are useful too. Return the ship to normal and the photon drawing constant to DRAW_ADD when you are done testing the drawing modes, or you can leave them as whatever you think makes the game look better.
6) Change the shots to fade out as they get older, so they don't seem to suddenly disappear when they are removed. Hint: Use "SetDrawShading". Also, make the shots look better by doing some or all of the following: Make them spin using SetDrawAngle. Make certain color components fade out faster than others. Draw each shot an extra time with a larger size (try 4x) and lower or faster-fading opacity to make for a glowing light effect around each photon. Each of these that you do must be visually noticeable - having code that should work but doesn't or is too subtle to notice isn't acceptable.
7) Implement collisions for shots. Make each connecting photon reflect at a reasonable angle upon colliding, and also make it knock back the ship it hit a little. You will probably find it easiest to copy (or generalize) the ship-to-ship collision code and treat photons as having some arbitrary weight value that gives reasonable results. Make sure each ship is immune to its own photons (you might want to wait until part of question #10 below is done before doing this last part).
8) Change the blue ship (but not the red ship) to play its photon firing sound at a slightly lower frequency/pitch, such as 75% of normal. See the GameX documentation about playing sounds for information on how to do this. Also, make the photon sound "pan" (for both ships) depending on the x location of the ship that fired it (make sure it fades smoothly from left to middle to right). Don't worry about the wrap-around for this.
9) Comment out the line of code that draws the background, run the game and describe what happens, then un-comment the background-drawing code to re-enable it. Change the appearance of the background (hopefully to something you think looks better or interesting) by running one or several of GameX's image filters on it when it is first loaded. See section 10 of the GameX documentation for details of how to do this. Also, remove the translation offsets given to DrawTiled and call GameX.SetDrawAngle beforehand with a changing angle value to make the background spin, but *please* make sure it spins very slowly, such as 1/100th of a degree every game loop. You can use GameX.GetGlobalCounter to get a value that increases continuously, which you can modify to be the changing rotation angle. A common problem you may encounter is that it spins jerkily or doesn't seem to spin continuously, in which case you probably forgot to cast to float or double before dividing (and so were unintentionally performing integer division). Finally, make the background drawn shaded with a high degree of transparency (i.e. low alpha such as 32 or 64) and describe what happens and why you think it's happening (it's related to the very first thing you did for this question #9). Leave the background this way, with all of these changes in effect at once. This will probably change the way your photons look, such as by obliterating most visible details inside them, in which case go back and change how photons are drawn so that whatever changes you made to them in step 6 are still noticeable. (Reducing their brightness may be enough.)
10) Change the red ship (but not the blue ship) to fire lasers instead of photons. Do this by giving the Photon struct an extra variable called "type", and changing AddPhoton to take in and assign the photon type. (If you were concerned about memory usage, how would you do this differently?) The red ship should then be made to add photons of a different "type" than the blue ship does. Once this is done, change the Photons::Draw function to draw the photons of the blue ship's type the same way as before, but to draw the photons of the red ship's type in a different way so they look like laser beams. To draw the "lasers", use some variation of the GameX.DrawLine function and any color or drawing mode you think looks best. Note that, since DrawLine takes two sets of (x,y) coordinates, you will need to decide on where the laser beam starts and where it ends. You could change the photons to keep track of some previous positions, but an easier way in this case is to subtract the photon's velocity from it's current position to come up with a second point's coordinates, and draw the line from that point to the current position. If the laser beam is too short, multiply the velocity when you subtract it by a higher number until the beam looks adequately long. If the laser beam is too thin, call GameX.SetDrawScale(width_in_pixels) before the call to DrawLine. Make sure the lasers do not look too ugly in comparison with your photons - you may want to use DrawLineImage (with the photon image and the optional parameter set to "true") instead of DrawLine to help accomplish this.
11) Change either ship's controls so that moving the mouse leftward turns the ship counter-clockwise, moving the mouse right turns the ship clockwise, moving the mouse up accelerates forward, moving the mouse down accelerates backward, and left-click fires the photons. Do not remove the keyboard input from that ship; mouse and keyboard must both work for it. Make sure the ship does not respond hypersensitively to mouse movement, but moving the mouse quickly should cause more of a reaction than moving it slowly. Also make sure that you can hold down the mouse button for rapid-fire as it was before. As much as possible, make the mouse control "feel natural" to use, which can be achieved with some tweaking (changes elsewhere to turning speed etc. may be necessary). See the GameX documentation for information about getting mouse input with GameX.
12) Finally, make one more change of your choice that you think we will find impressive, and explain your reasoning in your readme file.
This might seem like a lot of tasks, but hopefully you will find all of them reasonably simple and straightforward. To turn in the lab, hand in (using CMS) your code which completes all twelve tasks at once, along with a readme that answers questions and describes anything directly asked in the above tasks, and also describes (very briefly) how you handled each task and where in the code it was handled. You should zip up the following files and submit them as "lab1programming.zip":
For this first lab, you may NOT add or change (or submit) any graphics files, sound files, or modifications to GameX.
This lab project is due on CMS by Friday, September 8, 2006 at 11:59pm.