Levi D. Smith holds a Bachelor of Science in Computer Science from the Georgia Institute of Technology and a Master of Science in Industrial Engineering from the University of Tennessee. He has been developing games in his spare time for over twenty years.
Each time a piece is filled, the checkWinner method is called which loops through the board array, and returns true if all of the GamePieceLight objects are filled. Learned that “is” is the equivalent of “instanceof” in Java, which I use to determine if the piece at a given cell is a GamePieceLight type. Created method to return the number of pieces on the board. The fewer pieces used, the higher the score. Created method to return the luminosity of all lights on the board, which is the sum of the Elex values of all the GamePieceLight objects in the board array. The higher the value, the more points. A perfect bonus is awarded if the luminosity value is sum of all the bust values of all the lights. Added bust instance variable to the GamePieceLight object and added a getter method. Created drawWinner method which displays the end of level statistics, which the player is graded upon. I think I’ll just use the classic S, A, B, C leveling system for now, and I will need to set point values for each grade in each level. The drawWinner method is only called if the hasWon variable is true. Later, I will also add the level time as a third statistic which the player can earn points. However, this may not be a good stat since time is somewhat related to the pieces used.
When the user has completed the level, a confirm button press will create a new GameLevel object instance, which will start a new level. For now, it is just the same level, but in the future the level layouts will be different, and the GameLevel constructor will probably take an ID parameter that determines which level will be loaded.
For the first test, I set the bust values for all the lights to 8, which is the same value as the starting wire. Therefore, I got a perfect score for just connecting all the lights without any resistors. Next, I used a few resistors to lower the overall luminosity value. It correctly returned the sum of the luminosity of all the lights which was 20 (6 for the first two lights, and 4 for the lower two lights), so I didn’t get a perfect luminosity score this time which required 32.
Luminosity and Bust values displayed
Added the bust numbers to the lights in red when the light is not filled. When the light has filled, the number changes to its luminosity value and is displayed in blue.
Used Mario Paint Composer to create simple title music and stage music. It really gives the game a classic feel, and it’s really easy to create great sounding tracks with the tool. I spent about 30 minutes trying to record the audio from the desktop (tried Fraps, then tried connecting the audio jack output to the mic input), but I couldn’t get Audacity to record even though I saw activity from the mic input in the Control Panel sound control. Finally, I just held my USB headset mic up against the PC speaker and recorded using Audacity. The sound is a little muffled, but it does the job for now. At first, I used Audacity export the music into MP3 format. I did some research to see what what other formats XNA accepts, because I don’t want to risk the possibility of paying royalties for using MP3.
I’ve never had a formal class in music writing, but I was in the band for many years in high school, so this will be my first attempt at writing music for a game. The GameBoy icon beeps make a nice sound, but I found that too many of them can get annoying, especially when the song is on a loop. The GameBoys should be using sparingly, like sprinkles on a cupcake. I found that the Piranha Plant, Coin, ShyGuy, and Boo make the most soothing sounds, so I’ll probably be using those the most. On my stage 1 song, I have an intro and four melodies. The intro has the most GameBoy beeps, then it is phased out in the later melodies. Using the Arranger in Mario Paint Composer, the sequence I used looks like this: Intro, Intro, Melody A, Melody A, Melody B (transition), Melody C, Melody C, Melody D, Melody D, Melody C, Melody D, Melody C, Melody D.
To be on the safe side, I went ahead and converted my raw music files to WAV using Audacity. I can convert them to WMA later if needed. I don’t want to run the risk of having to pay royalties by using the MP3 format. One trick is that in the IDE you have to change the ContentProcessor of the WAV resource to Song, otherwise it will default to SoundEffect which will cause the program crash when it is played as a Song object.
Created the GamePieceLight class which subclasses the GamePiece class. Currently, it just overrides the draw method, so that it draws a light (circle sprite) instead of a wire. The lights are not playable pieces, meaning that the user can not place lights on the game board. The lights are placed on the board at the start of the level. Learned that you can’t just simply override a method by redefining it like in Java. In order to override, the method in the superclass must be declared virtual, and the override keyword must be used when defining the method in the subclass. After fixing those, I was able to add the light pieces by using the setPiece method in the constructor of the GameLevel class, for each light to be placed on the board. The great thing about OO (object oriented) programming is that the light automatically inherits all of the properties of the GamePiece class, which was the basic wire. Therefore, it automatically fills the adjacent wires just as a regular wire does, and its isFilled property is automatically set when the FILL_MAX value is reached. In order to make these superclass instance variables accessible to the subclass, they must be declared protected instead of private. This makes the variables visible to subclasses, but not visible to other classes. Before the light is filled, I use White as the color parameter for drawing the light sprite. Yellow is used as the color parameter when it is filled. The maximum “bust” value for the light will be added later.
Adding Resistor Pieces
Added instance variables to the GameLevel class, which hold the currently selected piece and the maximum number of selectable pieces. The current selected piece is initialized to zero (wire) and the maximum number of pieces is set to two. In the future, I will just create a list or array of piece types, and derive the maximum value from the array length. I am currently incrementing the selected piece value when the user presses the action button (default X). If the selected piece value is greater than or equal to the number of pieces, then it is set back to zero. In the future, I may have one button specifically for laying wires and another button for placing other pieces like resistors. Changing the selected piece will also probably be handled by the shoulder buttons. I have displayed the selected piece index on the game screen for now. When the selected index is 1, then pressing the confirm button will lay down a 2-resistor. I made the Elex variable in the superclass protected, so that I can subtract 2 from its value. A method was created in the GamePiece called pieceFilled which is called when the piece’s fill value reaches the max value. This method is overridden in the GamePieceResistor subclass, calling base.pieceFilled() first to set the filled boolean values in the superclass. Then I subtract the Elex value in the sublcass, since that is specific to the GamePieceResistor subclass. When the resistor is not filled, I use the Gray as the third color value in the blit method, giving the resistor a faded appearance. When the resistor is filled, White is used as the color parameter which makes it appear brighter. Currently, there is no check to see if the resistor will lower the Elex value below zero. I could make it so that the flow stops when the flow gets to zero.
As the screenshot shows, the Elex value is lowered from 8 to 6 as it passes through the first resistor, because the 2-resistor lowers it by 2. When it passes through the second resistor, it lowers again to 4.
Noticed a problem that when the direction pad was pressed, sometimes the cursor would not move until the pad was pressed multiple times. Increased the PAUSE_FRAMES to 360 (6 seconds at 60 fps) to make this obvious to help with debugging the problem. The PAUSE_FRAMES constant is the number of frames to wait before moving the cursor again, if the directional button his held down. The pause value should be set back to zero after a direction button is released, because the cursor should be moved immediately when the button is first pressed down. As I mentioned in my previous post, the pause value is necessary to keep the cursor from zipping across the screen (moving one cell for each frame).
Used this article to add text to the game, which allowed me to print out the pause value. Found that I needed to check if the velocity was non-zero before doing the bounds checking for moving the cursor. The velocity should be non-zero if the cursor is moving. An x velocity of -1 means the cursor is moving left and 1 means the cursor is moving right. A y velocity of -1 means the cursor is moving up and -1 means the cursor is moving down. When the pause value hits zero, then the velocities are added to the cursor’s row and column values. The problem was that it was doing the bounds checking even if the velocity was zero, then moving the cursor using a zero velocity value, and then setting the pause value back to the PAUSE_FRAMES value. Therefore, the pause value was continually being set back to the maximum value. Fixing that problem, along with setting the pause value to zero on direction button released got the cursor working properly. Set PAUSE_FRAMES value back to 10, so that it doesn’t wait 6 seconds to move the cursor when a direction button is held down.
Filling the Wires
Added a variable in the GameLevel class to track how many frames to wait until filling the first piece on the board. Also added variables to define the start cell (where the flow begins) and end cell (must be connected to win).
Created new images for filled wires and resistors, which will be overlayed on top of the objects on the game board. Hopefully, I can specify just a portion of the sprite to blit on top of the existing piece, so I can give the appearance of the piece filling, using the piece’s fill value. I made these overlays white, so that I can programmatically change the color using the color parameter of the SpriteBatch draw method. For now, I am just displaying the I-wire as the default piece, and I overlay the I-wire fill using the yellow parameter to designate a filled wire. The starting piece is currently set to row 0 column 5. I could change the starting location for each level, or make the starting location random.
I changed the SpriteFont variable to an array of SpriteFonts, so that multiple fonts can be easily defined and used. Currently, I’m passing the SpriteBatch object, textures (sprite) array, and font array to each object that needs to draw itself. It may have been simpler to just have those defined as public variables (or use getter methods) so that any object that needs to draw can obtain it from the main ResistorGame object. However, then every object that needs to draw would need a reference to the ResistorGame object, so it may be more efficient to just create a GameContent object which holds all of the textures and fonts, which could be passed to each drawable object.
After I got the filling to work for the first piece, I had another decision to make. Once a piece has been filled, should the piece call beginFill on its neighbors? In order to do that, then the current piece would need a reference to all of the adjacent (neighbor) pieces. Alternatively, the GameLevel could check for all pieces that have been filled, and then start filling the neighbors of those pieces. In that case, the GameLevel would need to keep track of the filled/unfilled status of each piece before calling update. That sounded like the simplest solution, so that is the approach that I took. For testing purposes now, a piece will fill all of its neighbors (north, south, east, west). I will start checking for the actual output side variables soon. I created a fillAdjacentPieces method in the GameLevel class, which takes the current piece’s row and column as paramters, and begins filling the adjacent pieces. Each piece now displays its fill value in green and Elex value in blue in the cell. A check has also been added to ensure that a piece is not being added to a cell that is already occupied (not null).
After doing testing with the current setup, the game seemed really fun (to me at least) just laying the wires and seeing them fill. The queue of pieces and rotating may be too complex, since this is supposed to be an educational game. I’m now thinking that the player should just have a generic wire, and the wire will shape and split ifself as needed, based upon its neighbors (adjacent cells). Think of how the roads in SimCity would “fix” their shape based on the layout of the road peices, so that the player didn’t have to worry about using curve or intersection pieces of road. Just giving the player the ability to cycle through a wire and the available resistors may be the best way to implement this. If it seems too simple in the end, then I can always add a piece queue or rotation after. The action button or shoulder button can be used for selecting the component to place.
Changing Wire Images
Decided that the GameLevel class should determine the wire image to use for each piece, since it can find the adjacent cells for a specified cell. Created wire images for each case. I can rotate the I, T, and L wire images later for efficiency if needed, but for now each rotation is a separate image. Created methods for accessing the piece to the north, south, east, and west of a specified cell, which handles all of the array bounds checking. Using those methods, I created a method for returning the image ID that should be used for the piece at a specified cell based on the adjacent pieces. This image ID is passed to the draw method of the piece. Therefore, a piece never knows its image ID value because it is derived each time the board is drawn. I did this because there are too many cases to handle to keep track of the adjacent cells in the GamePiece objects themselves. I changed the color of the wire sprites to white, that way I could make them any color (including black) eliminating the need for separate filled and unfilled sprites.
Go With the Flow
Below are flowcharts illustrating the main state transitions in the game.
The following flow chart shows the transitions in the main game loop.
Due to the simplicity of the game, it may make a good mobile game. I know I have more than enough screen real estate just at 640×480. I’m not sure how difficult it would be to port the code to Andriod or iOS. I know there is the capability to build a Windows Phone 7 app from the Visual C# Express IDE.
More GamePad Tweaks
Added code to handle the Start button press on the title screen, which currently just transitions to the game loop, just as the confirm button does. I created a simple test on the title screen to display the X value of the left thumbstick. This told me that it registers a float value between -1.0 and 0 when pressing the stick left, and between 0 and 1.0 when pressing the stick right. I pressed the stick about half way left, and it displayed a value close to -0.5. Therefore, I’m going to use 0.5 as a threshold for registering a directional press. I know some of my older controllers will register a move when the stick is not moved, just because the controller is old. When that happens, the controller is probably reporting a very small value (like 0.00001), but the game sees that as a non-zero value and will move whatever object is being controlled. That’s why I believe there needs to be some threshold value defined, and not create an action until that threshold is passed. Noticed that the Y axis is inverted from the standard screen coordinate system, so that pressing up on the stick actually returns a positive value and pressing down returns a negative value.