SNES game programing help

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

SNES game programing help

Post by Drew Sebastino »

First off, I want to say that I am sorry for not well contributing to this website. I feel that if I had a better understanding of how to make and SNES game, I could actually write something decent. The purpose of this page is to inform me (and other beginners possibly) how to do game programming basics such as collision detection and map updating. I don't care if you link a page, or if you try and explain something yourself, but I am just going to say that I tried to go on line and look up some simple things on how to make a game using asm, but ultimately, I had no luck. I know that what I am about to write is not limited to the SNES, but it is the only thing I have ever worked with. Now I am going to start a list of things that are essential to a game, but I do not understand to pull off.

1. Collision Detection.

This is something that has never been very well explained that I see. I know that most video games use hit boxes, but I do not know how to set something like that up. It be easy if there were greater than and less than functions, as you could see if any part of an object was in a position greater than the left side, but less than the right side, (same with up and down respectively) but unfortunately, I know that the only kind of comparisons are equal or not equal. (you could do collision detection pixel by pixel by checking each individual pixel of the sprite with BEQ, but it would by VERY slow) By the way, like I said, I have looked over other people code for collision detection, but usually I get something that is nearly impossible to read like this...
collision.png
2. Level Maps.

I don't get this either, because say if you had 50 characters that would appear throughout the level, you have to check 50 different spots every frame to see if you were in any of them. The way you would check to see if anything was in the right place or not, I would assume that every time the screen moved, you would add to the BG scrolling registers, but you would also use another register to see where you are in the level vertically and horizontally, as these could be a 16 bit value large enough to hold an entire level. Something like this would be much easier on an auto-scrolling shooter, as you could just set a "timer" for everything to happen, as you can't actually change the direction of the screen in game.
Last edited by Drew Sebastino on Mon Dec 01, 2014 7:35 pm, edited 1 time in total.
93143
Posts: 1371
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES game rograming help

Post by 93143 »

Notepad
That could be half your problem right there.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game rograming help

Post by Drew Sebastino »

93143 wrote:
Notepad
That could be half your problem right there.
What's bad about notepad? What do you use? And even if you did use some sort of different tool, you would still need to have some basic understanding as what you are supposed to do.

Although completely irrelevant, sorry if you felt that I was trying to force you to help me on the other topic, as you said you were busy. And just out of curiosity, what are you busy from?
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: SNES game rograming help

Post by psycopathicteen »

Code: Select all

object_collision:

lda {width}			// grab object #1 width using direct page
clc
adc.w {width},x			// add object #2 width using x-indexed
sta {temp}			// store the sum at "temp"

lda {x_position}
sec
sbc.w {x_position},x		// calculate the distance between objects center point
cmp {temp}
bcc +				// if distance < width, collision on x-axis has occured
clc
adc {temp}
bcc no_collision		// if distance + width < 0, no collision has occured
+;

lda {height}			// now do the same in the y direction
clc
adc.w {height},x
sta {temp}

lda {y_position}
sec
sbc.w {y_position},x
cmp {temp}
bcc +
clc
adc {temp}
bcc no_collision
+;
lda #$0001			// if collision has occured, A=1
rts


no_collision:
lda #$0000			// if collision has not occured, A=0
rts
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES game rograming help

Post by koitsu »

The problem is that the code you've loaded into Notepad was written on a UNIX or OS X system, not an MS-DOS or Windows system (or was written on a MS-DOS or Windows system with an editor that used only LF).

As such, line endings only contain LF (line feed), while MS-DOS/Windows expects CR+LF (carriage return + line feed).

Editors (like Notepad, doesn't matter which Windows version you're using) which do not understand LF-only line endings result in throwing everything into one long line, making it unreadable.

It's been like this for over 30 years. :-)

Either use another editor (like Notepad++, which you don't even have to install (there is a standalone version that comes in a .zip file; you just extract and go)) or use Wordpad (which comes with Windows).

Details and verification: https://en.wikipedia.org/wiki/Newline
93143
Posts: 1371
Joined: Fri Jul 04, 2014 9:31 pm

Re: SNES game rograming help

Post by 93143 »

Espozo wrote:What do you use?
Notepad++. It doesn't know 65816 out of the box, but it at least knows that ";" in an ASM file means a comment, and uses a different colour for such.
And just out of curiosity, what are you busy from?
Computational fluid dynamics. I'm trying to finish a degree, and currently I am snowed under with calculus. It's mostly my fault...
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game rograming help

Post by Drew Sebastino »

psycopathicteen wrote:

Code: Select all

object_collision:

lda {width}			// grab object #1 width using direct page
clc
adc.w {width},x			// add object #2 width using x-indexed
sta {temp}			// store the sum at "temp"

lda {x_position}
sec
sbc.w {x_position},x		// calculate the distance between objects center point
cmp {temp}
bcc +				// if distance < width, collision on x-axis has occured
clc
adc {temp}
bcc no_collision		// if distance + width < 0, no collision has occured
+;

lda {height}			// now do the same in the y direction
clc
adc.w {height},x
sta {temp}

lda {y_position}
sec
sbc.w {y_position},x
cmp {temp}
bcc +
clc
adc {temp}
bcc no_collision
+;
lda #$0001			// if collision has occured, A=1
rts


no_collision:
lda #$0000			// if collision has not occured, A=0
rts
What is bcc? I have never heard of it before. Also, what is the + for? I even see that it is by itself on one line, as if it were an opcode or something. Just thinking, did you actually do a less than or greater than comparison with bcc? (this would be extremely useful) And is {temp} just a regular register you are using for this code? Another thing I don't understand is the the .w in adc.w and sbc.w. What does it do?
93143 wrote:Computational fluid dynamics. I'm trying to finish a degree, and currently I am snowed under with calculus. It's mostly my fault...
Dang... So you're at that point in life. (I'm only a 15 year old in 9th grade, taking honor's geometry. :oops:)
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: SNES game rograming help

Post by psycopathicteen »

BCC means "branch if carry clear." CMP sets carry if accumulator => memory, and clears carry if accumulator < memory. The "+" after the bcc means branch ahead to the next "+" label. The ".w" just means it uses a 16-bit address. I use bass assembler, so some of these features might not work with other assemblers.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES game rograming help

Post by koitsu »

bcc stands for branch-if-carry-clear. A common alias/synonym opcode for it is blt, which stands for branch-if-less-than. Likewise, a common alias for bcs (branch-if-carry-set) is bge (branch-if-greater-than-or-equal-to).

Recommended reading (because the above two opcodes are 6502/65c02 as well): http://www.6502.org/tutorials/compare_beyond.html

The + entry you see is called a label, but more specifically a "forward" label. It's analogous to making a label called snakes and then referring to that, but with the added bonus that you're telling the assembler "go to the label called + but in code that's further down/below where I am now". You can nest these as well, e.g. ++, +++, and so on. There is also - (and -- and so on) which refers to labels/addresses "before" or "behind" the current place of code. This is purely a feature of the assembler chosen/used. There is no universal standard for things like this. Read the documentation of whatever assembler you're using. If the code in question doesn't say what assembler it's intended for, then the programmer should be repeatedly punched in the head. Some code examples to show what I'm referring to (this code is just for example, it's silly/ridiculous):

Code: Select all

   ldy #3
-- ldx #0
-  lda $9000,x
   cmp #$3f
   beq +
   lda #0
+  sta $0800,x
   inx
   bne -
   dey
   bne --
Compare that to:

Code: Select all

      ldy #3
loop1 ldx #0
loop2 lda $9000,x
      cmp #$3f
      beq loop3
      lda #0
loop3 sta $0800,x
      inx
      bne loop2
      dey
      bne loop1
I have no idea what the braces around a label/variable, i.e. {y_position}, do. I have never seen this syntax before. I'm sure psychopathicteen can explain it, and I'd like to know what assembler uses that. My gut feeling is that it's a way to differentiate actual variables from labels, but that's speculative.

The .w suffix is an "addressing modifier" for an opcode, to force the assembler to refer to the 16-bit address of the label/value rather than default to the 8-bit direct page version, and use the appropriate opcode for that addressing mode. Different assemblers support different ways of doing this; some offer the .w modifier, others use things like >. Anyway, more clearly:

Code: Select all

dp00  .dw $1234   ; DP location $00
myvar .dw $feed   ; DP location $02

    rep #$20      ; 16-bit accumulator
    lda myvar     ; Assembler might optimise this to bytes $A5 02 (LDA $02, 2 bytes, DP addressing, 4 cycles)
    lda.w myvar   ; Assembler will assemble this to bytes $AD 02 00 (LDA $0002, 3 bytes, absolute addressing, 5 cycles)
Assemblers are usually quite good about knowing when to do this on their own, but there are times where you may want something very specific and thus want absolute addressing (remember: computers are supposed to do what we tell them ;-) ). Other times it's purely habitual/a code style of the programmer, e.g. "I prefer doing it this way because then it's extra clear to the reader that I want absolute addressing here".

If you're already familiar with the 6502/65c02, get yourself a copy of Western Design Center's book. See the bottom of our wiki for a copy: http://wiki.nesdev.com/w/index.php/Programming_guide
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES game rograming help

Post by tepples »

Espozo wrote:
93143 wrote:
Notepad
That could be half your problem right there.
What's bad about notepad?
As koitsu pointed out, Windows Notepad is probably the only popular text editor that can't correctly interpret UNIX newlines ($0A). Instead it needs Windows newlines ($0D $0A). You're hitting the same deficiency that 3gengames used to hit before he switched to Linux.
What do you use?
On Windows I use Notepad++, and on Linux I use gedit.
Espozo wrote:What is bcc? I have never heard of it before.
In 6502 family assembly language, "BCC" is "branch if carry clear". It has a synonym "BLT" meaning "branch if less than". The opposite is "BCS" or "BGE", meaning "branch if carry set" or "branch if greater or equal". (On 68000 these are distinct, with BCC used for unsigned comparisons and BLT used for signed comparisons.)

(I am so ninja'd.)
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES game rograming help

Post by Sik »

koitsu wrote:Either use another editor (like Notepad++, which you don't even have to install (there is a standalone version that comes in a .zip file; you just extract and go)) or use Wordpad (which comes with Windows).
Gosh no, don't use Wordpad at all, that's asking for trouble (the likelihood of accidentally saving in the wrong format even by accident is so big it's better to not consider it). Just stick to Notepad++, which as a bonus will preserve indentation when pressing Enter.
Kannagi
Posts: 100
Joined: Sun May 11, 2014 8:36 am
Location: France

Re: SNES game programing help

Post by Kannagi »

It's a bad idea to code in assembly with notepad, I'm on linux and I use geany and I have this:
http://img4.hostingpics.net/pics/674197Capturedcran.png
I have a button to compile and run (and another button to execute with other emulator).
My hitbox test are special, I'm just dot / square test.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

Well... I tried to make my own collision detection code between two squares on the screen, but this was the result...
Collision.zip
(228.94 KiB) Downloaded 83 times
(If you want to assemble it, just use command prompt, go to that directory, and type in wla walker)

The code was just meant to change the center square color from green to blue, but It clearly doesn't work. I don't really understand why as it makes sense to me but anyway, here is the collision code part of the game for your convenience. The values for the sizes of the hitboxes are constant, as everything is 16x16 pixels large.

Code: Select all

CollisionCode:
	lda	SpriteBuf1+0
	sec
	sbc	SpriteBuf1+4	; calculate the distance between objects center point
	cmp	#$10
	bcc	_y_check	; if distance < width, collision on x-axis has occured
	clc
	adc	#$10
	bcc	_no_collision	; if distance + width < 0, no collision has occured

_y_check:
	lda	SpriteBuf1+1
	sec
	sbc	SpriteBuf1+5	; calculate the distance between objects center point
	cmp	#$10
	bcc	_collision		; if distance < width, collision on y-axis has occured
	clc
	adc	#$10
	bcc	_no_collision	; if distance + width < 0, no collision has occured

_collision:
	lda #$34			
	sta SpriteBuf1+7
	rts

_no_collision:
	rts
By the way, thanks for showing me notepad++ (I no longer have to count lines! :D )
KungFuFurby
Posts: 264
Joined: Wed Jul 09, 2008 8:46 pm

Re: SNES game programing help

Post by KungFuFurby »

Time to put my hex lookup skills to good use. No debugger required for this job.

I gave your test a go, and I triggered the collision, but not in the way you would expect.

The blue square was invisible in my emulator (SNES9X 1.53) after flashing on for a single frame. The box flashed various colors when the collision code detected a collision. I was ultimately able to determine that the collision is detected by X coordinate only, and it only triggers between the center of the box and the left edge.

I do have a minor complaint that may be the cause of your bug... your compiled code, which I determined via a combination of SNES9X's variable lookup feature and a lookup of the compiled code via hex editor, indicates you are using 8-bit X and Y coordinates. Your collision code, on the other hand, mistakenly compiles using 16-bit values, and therefore I would recommend adding a few of lines of code: a line to make the accumulator 8-bit at the very start of the routine (sep #$20), and a line before that to save your previous processor status (php). Then, before the end of the routine (before the each RTS opcode in your collision code), restore your original processor status on the line above (plp).

That repairs the invisible box. So now...

The box turns out to be instantly blue in the first place, meaning your collision code in theory instantly fires. However, I decided to take a look at your parameters that you were using to set the box color in the first place. Turns out your collision code is fully functional, but the box color is not green to start with. Your attributes set the box color to blue, and I manually determined how to set the box to green.

I also decided to add one more feature: set the box back to green when there is no collision going on. With this feature, your box is fully functional, and you can tell when the code determines there's a collision and when there's not a collision. When no collision occurs, the box is green. When there is a collision, the box is blue.

By the way, I compiled the program with my own build of WLA DX because I don't use Windows programs on my computer (My computer may be an Intel Mac, but I stay away from Windows emulation software. I make an exception for DOS, though, through DOSBox.) (It also means I didn't use the batch file to compile it, instead manually executing the commands from the Terminal).
Attachments
CollisionBF.zip
Bugfix by KungFuFurby: Collision code requires 8-bit parameters, and the wrong palette was used to start with.

Plus... for added usability, I set the box back to green when the collision goes from on to off.
(227.9 KiB) Downloaded 101 times
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES game programing help

Post by Drew Sebastino »

WOW! It actually works! :mrgreen: Just thinking, what would be the best way to make the code work with any combination of sprites? Would you change any spot of the code that has any registers that are used by sprites and instead make them regular registers that store the information for whatever sprite you are using? Also, in order to check collision for all 128 sprites, wouldn't you have to run through the code several hundred times? (I'm too lazy to do the math) If so, It seems that you would start hitting slowdown pretty quickly. I'm not really sure though. (3 megahertz might as well be 3 gigahertz to me :roll: ) And lastly, how would you assign collision detection to the background? I guess you could do it this way, but I would imagine that there would be a hell of a lot of hitboxes.
Post Reply