My February game is my CandyJam entry so here is the postmortem.
What Went Right
Despite the rather long time that was theoretically available for the CandyJam entry, I knew that I would probably not have as much time as I expected and planned accordingly. Not counting the time spent playing with Unity (see mixed blessings), the game only took about 8 hours to create though had I been under tighter game-jam rules and had not been able to borrow artwork from some of my other games it would have taken a bit longer. I do not know what has been up with the last few game jams but having only part of the time to work on the game is a trend that seems to be continuing into this year! That said, if I did have more time to work on the game, I would spend the time working on my polishing skills as those are most definitely in need of work.
Mixed Blessings
It is never wise to try and learn something new when you are already under a tight deadline. I originally planned on using this project as my first Unity project as it is a very popular engine which may also be exactly what I need for some of my larger projects. I got bits of this project working and possibly could have finished it but things came up and the next thing I knew the end of the month was upon me. While it may have been possible to finish the game on time with Unity, I didn't want to take any chances so went with Flash. This gleaned enough knowledge of Unity that I should be able to create a simple game with it real soon.
What Went Wrong
Companies abusing IP laws. Technically this is not a problem with the project, yet, but the title of this game very well could result in trademark complaints for using a common English word as part of my title. Far too often patent and trademark offices grant weak patents or trademarks and leave it up to the courts to decide if the patent or trademark are valid. This actually has the result of having the exact opposite effect that the trademark and patent laws intended. Instead of giving small companies the tools to compete against larger companies on a level playing-field, this gives companies with money (large companies) tools to attack companies without money (small companies) preventing them from competing. In the long term this is harmful to both innovation and consumers.
Friday, January 31, 2014
Thursday, January 23, 2014
A Tour of the 6502
Before I start writing code, it would probably be a good idea to quickly go over the 6502 processor that the NES uses. This is just a quick overview. As I have time, I will cover the material here in much more detail. The 6502 processor is fairly simple so if you are already familiar with programming then this should be enough info to let you follow what I am doing.
The 6502 is an 8-bit processor with a 16 bit address range. This means that it works with one byte at a time and can handle 65536 bytes of memory. Techniques like bank-switching allow for more memory but that will be covered later. It has 6 registers to hold the information it is working on. These are the program counter (PC), accumulator (A), X register (X), Y register (Y), the Flag register aka status register (SR), and the stack pointer (SP).
The program counter is a 16-bit register and holds the current address that the processor is executing. The accumulator is a general-purpose register that is the only register that math operations can be applied to. The X and Y registers are index registers, which means they are used for counting. They can also be used for temporary storage. The status register holds flags that hold information about what happened in previous operations. Finally, the stack pointer is an index holding which byte the top of the stack is at.
The flags that are tracked are NV-BDIZC. N stands for negative and is set if the highest bit of the last byte-related operation is set as signed bytes use this bit to indicate if a number is negative. V is for oVerflow and is set when an operation results in a number larger than a byte. The - flag is reserved and I am not sure what the B flag does but it stands for break so I am assuming it is for hard breaks. D is to indicate if the chip is in Binary Coded Decimal mode or not but as the NES doesn't support BCD, the only thing ever done with this is clearing it. The I flag is the interrupt flag and is used to temporarily disable interrupts. The Z flag is set whenever an operation results in a value of zero. Finally, the C flag is used for math operations for dealing with multi-byte numbers.
The 6502 makes no distinction between program memory and data memory so it is possible, though probably not wise, to create self-modifying programs. Certain Commodore 64 copy-protection schemes actually took advantage of this to obfuscate how they worked. Commands take the form of an Operation Code (OP Code) which may be followed by one or two data bytes for holding a number or an address. The Op Code is simply a number which indicates which command should be executed. The nice thing about assembly language is that you do not need to know the numbers for the op codes you just need to use easy to remember 3 letter commands.
This is all easy enough, but to complicate things, some instructions have a number of modes in which they can be used. These are simply different ways of accessing memory. There are 13 different addressing modes. The following shows how the mode is coded in assembly language followed by a brief description of the mode (note that $ indicates a hexadecimal number, % a binary number and no prefix a decimal number):
OPC A ; Accumulator - value in accumulator used for data
OPC $1234 ; Absolute - uses hard coded address
OPC $1234,X ; Absolute, X - hard coded address with value in X Register added to it
OPC $1234,Y ; Absolute, Y - hard coded address with value in Y Register added to it
OPC #$12 ; Immediate - uses provided byte value
OPC ; Implied - no operand
OPC ($1234) ; Absolute Indirect - the hard coded address is used as a pointer to the real address
OPC ($12,X) ; Indirect, X - hard coded zero-page address with value in X Register added to it
OPC ($12),Y ; Indirect, Y - hard coded zero page address and consecutive byte form pointer to the real base address which is then increased by the value in the Y register to form the final address.
OPC $12 ; Relative - signed byte is added to the program counter to determine address. Used for branching instructions.
OPC $12 ; Zero Page - hard coded zero page address
OPC $12,X ; Zero Page, X - hard coded zero page address with value of X register added to it.
OPC $12,Y ; Zero Page, Y - hard coded zero page address with value of Y register added to it.
Not every addressing mode can be used with every instruction. Though generally it is clear what modes work with what instructions. Here is a summary of the instructions that the 6502 supports grouped into types of operations the commands perform.
Memory Manipulation
LDA (LoaD Accumulator)
LDY (LoaD X)
LDY (LoaD Y)
PHA (PusH Accumulator)
PHP (PusH Processor status [flags])
PLA (PulL Accumulator)
PLP (PulL Processor status [flags])
STA (STore Accumulator)
STX (STore X)
STY (STore Y)
TAX (Transfer Accumulator to X)
TAY (Transfer Accumulator to Y)
TSX (Transfer Stack pointer to X)
TXA (Transfer X to Accumulator)
TXS (Transfer X to Stack pointer)
TYA (Transfer Y to Accumulator)
Math and Boolean logic instructions
ADC (ADd with Carry)
AND (logical AND)
ASL (Arithmetic Shift Left)
DEC (DECrement)
DEX (DEcrement X)
DEY (DEcrement Y)
EOR (Exclusive OR with accumulator)
INC (INCrement)
INX (INcrement X)
INY (INcrement Y)
LSR (Logical Shift Right)
ORA (OR with Accumulator)
ROL (ROtate Left)
ROR (ROtate Right)
SBC (SuBtract with Carry)
Comparison Instructions
BIT (BIT test)
CMP (CoMPare with accumulator)
CPX (ComPare with X)
CPY (ComPare with Y)
Branching instructions
BCC (Branch on Carry Clear)
BCS (Branch on Carry Set)
BEQ (Branch on EQual [zero flag set])
BMI (Branch on MInus [negative flag set])
BNE (Branch on Not Equal [zero flag clear])
BPL (Branch on PLus [negative flag clear])
BVC (Branch on oVerflow Clear)
BVS (Branch on oVerflow Set)
JMP (JuMP)
JSR (Jump to SubRoutine)
RTI (ReTurn from Interrupt)
RTS (ReTurn from Subroutine)
Flag Management
CLC (CLear Carry flag)
CLD (CLear Decimal flag)
CLI (CLear Interrupt disable flag)
CLV (CLear oVerflow)
SEC (SEt Carry)
SED (SEt Decimal)
SEI (SEt Interrupt disable)
Debugging aids
BRK (BReaK)
NOP (No OPeration)
The 6502 is an 8-bit processor with a 16 bit address range. This means that it works with one byte at a time and can handle 65536 bytes of memory. Techniques like bank-switching allow for more memory but that will be covered later. It has 6 registers to hold the information it is working on. These are the program counter (PC), accumulator (A), X register (X), Y register (Y), the Flag register aka status register (SR), and the stack pointer (SP).
The program counter is a 16-bit register and holds the current address that the processor is executing. The accumulator is a general-purpose register that is the only register that math operations can be applied to. The X and Y registers are index registers, which means they are used for counting. They can also be used for temporary storage. The status register holds flags that hold information about what happened in previous operations. Finally, the stack pointer is an index holding which byte the top of the stack is at.
The flags that are tracked are NV-BDIZC. N stands for negative and is set if the highest bit of the last byte-related operation is set as signed bytes use this bit to indicate if a number is negative. V is for oVerflow and is set when an operation results in a number larger than a byte. The - flag is reserved and I am not sure what the B flag does but it stands for break so I am assuming it is for hard breaks. D is to indicate if the chip is in Binary Coded Decimal mode or not but as the NES doesn't support BCD, the only thing ever done with this is clearing it. The I flag is the interrupt flag and is used to temporarily disable interrupts. The Z flag is set whenever an operation results in a value of zero. Finally, the C flag is used for math operations for dealing with multi-byte numbers.
The 6502 makes no distinction between program memory and data memory so it is possible, though probably not wise, to create self-modifying programs. Certain Commodore 64 copy-protection schemes actually took advantage of this to obfuscate how they worked. Commands take the form of an Operation Code (OP Code) which may be followed by one or two data bytes for holding a number or an address. The Op Code is simply a number which indicates which command should be executed. The nice thing about assembly language is that you do not need to know the numbers for the op codes you just need to use easy to remember 3 letter commands.
This is all easy enough, but to complicate things, some instructions have a number of modes in which they can be used. These are simply different ways of accessing memory. There are 13 different addressing modes. The following shows how the mode is coded in assembly language followed by a brief description of the mode (note that $ indicates a hexadecimal number, % a binary number and no prefix a decimal number):
OPC A ; Accumulator - value in accumulator used for data
OPC $1234 ; Absolute - uses hard coded address
OPC $1234,X ; Absolute, X - hard coded address with value in X Register added to it
OPC $1234,Y ; Absolute, Y - hard coded address with value in Y Register added to it
OPC #$12 ; Immediate - uses provided byte value
OPC ; Implied - no operand
OPC ($1234) ; Absolute Indirect - the hard coded address is used as a pointer to the real address
OPC ($12,X) ; Indirect, X - hard coded zero-page address with value in X Register added to it
OPC ($12),Y ; Indirect, Y - hard coded zero page address and consecutive byte form pointer to the real base address which is then increased by the value in the Y register to form the final address.
OPC $12 ; Relative - signed byte is added to the program counter to determine address. Used for branching instructions.
OPC $12 ; Zero Page - hard coded zero page address
OPC $12,X ; Zero Page, X - hard coded zero page address with value of X register added to it.
OPC $12,Y ; Zero Page, Y - hard coded zero page address with value of Y register added to it.
Not every addressing mode can be used with every instruction. Though generally it is clear what modes work with what instructions. Here is a summary of the instructions that the 6502 supports grouped into types of operations the commands perform.
Memory Manipulation
LDA (LoaD Accumulator)
LDY (LoaD X)
LDY (LoaD Y)
PHA (PusH Accumulator)
PHP (PusH Processor status [flags])
PLA (PulL Accumulator)
PLP (PulL Processor status [flags])
STA (STore Accumulator)
STX (STore X)
STY (STore Y)
TAX (Transfer Accumulator to X)
TAY (Transfer Accumulator to Y)
TSX (Transfer Stack pointer to X)
TXA (Transfer X to Accumulator)
TXS (Transfer X to Stack pointer)
TYA (Transfer Y to Accumulator)
Math and Boolean logic instructions
ADC (ADd with Carry)
AND (logical AND)
ASL (Arithmetic Shift Left)
DEC (DECrement)
DEX (DEcrement X)
DEY (DEcrement Y)
EOR (Exclusive OR with accumulator)
INC (INCrement)
INX (INcrement X)
INY (INcrement Y)
LSR (Logical Shift Right)
ORA (OR with Accumulator)
ROL (ROtate Left)
ROR (ROtate Right)
SBC (SuBtract with Carry)
Comparison Instructions
BIT (BIT test)
CMP (CoMPare with accumulator)
CPX (ComPare with X)
CPY (ComPare with Y)
Branching instructions
BCC (Branch on Carry Clear)
BCS (Branch on Carry Set)
BEQ (Branch on EQual [zero flag set])
BMI (Branch on MInus [negative flag set])
BNE (Branch on Not Equal [zero flag clear])
BPL (Branch on PLus [negative flag clear])
BVC (Branch on oVerflow Clear)
BVS (Branch on oVerflow Set)
JMP (JuMP)
JSR (Jump to SubRoutine)
RTI (ReTurn from Interrupt)
RTS (ReTurn from Subroutine)
Flag Management
CLC (CLear Carry flag)
CLD (CLear Decimal flag)
CLI (CLear Interrupt disable flag)
CLV (CLear oVerflow)
SEC (SEt Carry)
SED (SEt Decimal)
SEI (SEt Interrupt disable)
Debugging aids
BRK (BReaK)
NOP (No OPeration)
Friday, January 17, 2014
Planning my NES RPG Development Path
How much planning a project requires is largely dependent on the size/complexity of the project and the number of people working on the project. The more complicated the project, the more planning things out will make things go smoothly. It certainly is possible to complete a complex project without any planning, but the time required to do so will be significantly higher than the time that would be saved by proper planning. Planning doesn't need to take the form of formal documents, though if you are working with large teams a formal design specification document is probably a good idea.
I tend to design by working out what screens a game needs then breaking down each of the screens. After breaking down my NES RPG game I quickly realized that I could turn this project into a number of mini-games. These pieces could then be assembled together to form the core of the final game.
The first game is totally unrelated to the RPG but will have common code that will be used by other projects. It will be a simple quiz game. This will let me get my multiple-choice prompts working, number printing, random numbers, and some of the math routines. The 6502 does not have multiplication or division (other than bit shifting) so this has to be done in software.
Next will be a outdoor scrolling maze game. This obviously will be used for the game map screen. This will rapidly followed by a treasure hunt game using a scrolling covered map that only reveals itself as the player explores the map.
The final pair of pre-game mini-games will be the combat engine. This will be developed as a series of arena games. The first arena will simply be the combat with pre-determined characters and monsters. The second game will add the character stat screens and a store where players can buy equipment for upcoming battles.
At this point all the major parts of the RPG exist so they can be combined into a proper RPG. This will be broken into separate chapters so each chapter can be released separately yet be combined into a single larger final game.
I tend to design by working out what screens a game needs then breaking down each of the screens. After breaking down my NES RPG game I quickly realized that I could turn this project into a number of mini-games. These pieces could then be assembled together to form the core of the final game.
The first game is totally unrelated to the RPG but will have common code that will be used by other projects. It will be a simple quiz game. This will let me get my multiple-choice prompts working, number printing, random numbers, and some of the math routines. The 6502 does not have multiplication or division (other than bit shifting) so this has to be done in software.
Next will be a outdoor scrolling maze game. This obviously will be used for the game map screen. This will rapidly followed by a treasure hunt game using a scrolling covered map that only reveals itself as the player explores the map.
The final pair of pre-game mini-games will be the combat engine. This will be developed as a series of arena games. The first arena will simply be the combat with pre-determined characters and monsters. The second game will add the character stat screens and a store where players can buy equipment for upcoming battles.
At this point all the major parts of the RPG exist so they can be combined into a proper RPG. This will be broken into separate chapters so each chapter can be released separately yet be combined into a single larger final game.
Friday, January 10, 2014
Color Smash postmortem
When I heard about the New Year's Game Jam, I thought it would be perfect for my first game of the year. Unfortunately, I had some other plans for New Year's Eve which meant that I had a shorter period of time to develop this game. This is the postmortem for that game.
What went right
When I went to the NYGJ site to find out how the challenge worked, I noticed that they did not specify a theme. Having the option to do anything is nice but if you can't decide what you want to do then there is little chance you will finish a project within such a short timeframe. Having a plan, at least in your head, is important for really time-restricted projects. Thankfully the site had a theme generator (1GAM had not released their January theme yet so I couldn't use that). While the theme I was given, click the button, was rather generic, it was enough to get me to focus on a whack-a-mole style of game.
Mixed blessings
Knowing that I was going to only have a short period of time to work on this game was both a disadvantage and an advantage. By anticipating a short development time I opted to go with a much simpler game than I would have liked to but it also meant that I was able to finish the game and even get a slight polishing pass.
What went wrong
Perhaps anticipating a New Years Eve party is what caused me to make a rookie mistake but a simple order order of execution bug stumped me. With Flash, it is important to remember that the last playhead adjustment is the one that gets executed. This means a gotoAndPlay() followed by a stop() is not going to work well. This is a fairly obvious bug, but in my case things were a slight bit more complicated so obvious sequence was hidden from me.
For the lights I dispatch an event when an animation sequence finished playing then going to the default "off" animation to wait for orders to change the color of the lights. The problem is that the event dispatcher actually calls the event handling functions before returning. This means I tell my main game that it is time to start the next animation, which calls the light to set the color to the new value causing a gotoAndPlay() call to the appropriate color appearing animation. When the dispatcher returns, the light goes to the default off animation. Simply by moving the gotoAndPlay("off") call to before the dispatchEvent call solved the problem.
This bug is an important reminder that it is important to think about when things get executed. As the trend of having more cores instead of faster speeds continue, future speed gains will come from taking advantage of multiple cores, meaning that I expect to have to write more threaded code in the future. This makes order of execution issues even more prevalent.
What went right
When I went to the NYGJ site to find out how the challenge worked, I noticed that they did not specify a theme. Having the option to do anything is nice but if you can't decide what you want to do then there is little chance you will finish a project within such a short timeframe. Having a plan, at least in your head, is important for really time-restricted projects. Thankfully the site had a theme generator (1GAM had not released their January theme yet so I couldn't use that). While the theme I was given, click the button, was rather generic, it was enough to get me to focus on a whack-a-mole style of game.
Mixed blessings
Knowing that I was going to only have a short period of time to work on this game was both a disadvantage and an advantage. By anticipating a short development time I opted to go with a much simpler game than I would have liked to but it also meant that I was able to finish the game and even get a slight polishing pass.
What went wrong
Perhaps anticipating a New Years Eve party is what caused me to make a rookie mistake but a simple order order of execution bug stumped me. With Flash, it is important to remember that the last playhead adjustment is the one that gets executed. This means a gotoAndPlay() followed by a stop() is not going to work well. This is a fairly obvious bug, but in my case things were a slight bit more complicated so obvious sequence was hidden from me.
For the lights I dispatch an event when an animation sequence finished playing then going to the default "off" animation to wait for orders to change the color of the lights. The problem is that the event dispatcher actually calls the event handling functions before returning. This means I tell my main game that it is time to start the next animation, which calls the light to set the color to the new value causing a gotoAndPlay() call to the appropriate color appearing animation. When the dispatcher returns, the light goes to the default off animation. Simply by moving the gotoAndPlay("off") call to before the dispatchEvent call solved the problem.
This bug is an important reminder that it is important to think about when things get executed. As the trend of having more cores instead of faster speeds continue, future speed gains will come from taking advantage of multiple cores, meaning that I expect to have to write more threaded code in the future. This makes order of execution issues even more prevalent.
Friday, January 3, 2014
Welcome to 2014
As some of you already know, Blazing Games has switched to a Monthly release format, while my development blog is switching to a weekly format. This is to allow me to spend my time focusing on larger scale games and hopefully turn my hobby into something that can actually pay my bills.
I will continue to have postmortem articles for my game jam games, and will probably expand this to include all the games that I release. Postmortems are nice to have as they help both myself and other game developers spot issues that they should pay attention to. For me, they also help me keep good habits in mind and remind me to avoid all the bad habits that are far too easy to show up.
Anybody who has followed this blog for a while knows that occasionally there will be some news that gets me annoyed requiring me to write long rambling rants. While these will occasionally show up, I do not expect the number of these posts to increase.
The bulk of the posts will be following my development of a late-eighties commercial quality NES role-playing game. In other words, by the end of the project I want to have a NES game that people would have been willing to pay money for had it come out in the late eighties. This will be made up of very in-depth with side articles on programming 6502 assembly language and the NES.
While I will covering the entire development of this game, I am going to focus more on the theory and logic behind what I am doing rather than the code. My thought is that understanding why something is done is far more important than the actual code. If you understand the theory then you can do the implementation in any language you want. I will try to make my source code available so anybody interested in the nuts and bolts can simply download the source files and look at the code.
I will continue to have postmortem articles for my game jam games, and will probably expand this to include all the games that I release. Postmortems are nice to have as they help both myself and other game developers spot issues that they should pay attention to. For me, they also help me keep good habits in mind and remind me to avoid all the bad habits that are far too easy to show up.
Anybody who has followed this blog for a while knows that occasionally there will be some news that gets me annoyed requiring me to write long rambling rants. While these will occasionally show up, I do not expect the number of these posts to increase.
The bulk of the posts will be following my development of a late-eighties commercial quality NES role-playing game. In other words, by the end of the project I want to have a NES game that people would have been willing to pay money for had it come out in the late eighties. This will be made up of very in-depth with side articles on programming 6502 assembly language and the NES.
While I will covering the entire development of this game, I am going to focus more on the theory and logic behind what I am doing rather than the code. My thought is that understanding why something is done is far more important than the actual code. If you understand the theory then you can do the implementation in any language you want. I will try to make my source code available so anybody interested in the nuts and bolts can simply download the source files and look at the code.
Subscribe to:
Posts (Atom)