Newcomer to NES programming

A place for your artistic side. Discuss techniques and tools for pixel art on the NES, GBC, or similar platforms.

Moderator: Moderators

User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: Newcomer to NES programming

Post by Myask »

The previous one and this one share a problem; really the only bar to getting this on NROM. The ground-green and the dirt underneath are (Technically, you could change the scroll 8px below ground, I guess?) Okay, nevermind, I see that there are still only four colors. Well done with just dark-brown+black there.

To illustrate sprite usage possibility there...
BG color: black
BG0: Foliage/Groundgrass: Green, dark green, dark brown, [black]
BG1: House: Light red, red, dark red, [black.]
BG2: Sky/mountain/statusbar: gray, white, light blue, [black]
BG3: ocean: dark blue, light blue, white, [black]
BG4 (drat, thought it was closer): grass over ocean: green, dark green, dark blue, black
Spr0: (Hero): White, light red, dark red
Spr1: (Foliage over): Green, dark green, black
Spr2: (Tree trunk over): red, dark red, black
Spr3: (unused)

You can maintain the bright colors you had earlier, *almost*- like so, with both layers; you just need to get rid of the grass-over-sea:
(hmm, mind, the gradient-to-black-instead means the tree-trunk-base doesn't need to go to sprite layer.)

Alternately, you could just have a patch of grass over the ocean here or there as sprites, but you might have other needs at ground-level (like the hero and tree-trunk-corners; maybe people?).

e: Nice title-screen, btw.
Attachments
options.gif
options.gif (5.57 KiB) Viewed 6512 times
User avatar
tokumaru
Posts: 12645
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Newcomer to NES programming

Post by tokumaru »

The title screen is much better!
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: Newcomer to NES programming

Post by Alp »

Myask wrote:The previous one and this one share a problem; really the only bar to getting this on NROM. The ground-green and the dirt underneath are (Technically, you could change the scroll 8px below ground, I guess?) Okay, nevermind, I see that there are still only four colors. Well done with just dark-brown+black there.

To illustrate sprite usage possibility there...
BG color: black
BG0: Foliage/Groundgrass: Green, dark green, dark brown, [black]
BG1: House: Light red, red, dark red, [black.]
BG2: Sky/mountain/statusbar: gray, white, light blue, [black]
BG3: ocean: dark blue, light blue, white, [black]
BG4 (drat, thought it was closer): grass over ocean: green, dark green, dark blue, black
Spr0: (Hero): White, light red, dark red
Spr1: (Foliage over): Green, dark green, black
Spr2: (Tree trunk over): red, dark red, black
Spr3: (unused)

You can maintain the bright colors you had earlier, *almost*- like so, with both layers; you just need to get rid of the grass-over-sea:
(hmm, mind, the gradient-to-black-instead means the tree-trunk-base doesn't need to go to sprite layer.)

Alternately, you could just have a patch of grass over the ocean here or there as sprites, but you might have other needs at ground-level (like the hero and tree-trunk-corners; maybe people?).

e: Nice title-screen, btw.
Hmm... I'll take those suggestions into consideration. I don't really want to go too crazy with sprites in the background, as the towns are to have wandering NPCs, as in Zelda II. Hell, I may just keep the ground grass in towns, and throw away the long grass, and keep for the wilderness. Most towns are well kept, why not? :P
tokumaru wrote:The title screen is much better!
Oh, thanks! The original was really just a tile memory allocation. I wasn't expecting people to critique it so harshly, haha! :P

I haven't really had too much time for pixel work, lately, so I'll show the current mock-ups of tile-work for Cat Quest 2 and 3. These were started on the side, just to amuse myself while working on the code for the 1st game. Both of these future games will use mappers, and (possibly) CHR ram.

The dungeon walls in the 3rd game mock-up are *very* unfinished. They were intended as a throwback to the original game, with more of a Zelda 3 style of depth. I messed up my measurements for the bricks, at some point, so they don't line up quite right. Oops.

Image
Last edited by Alp on Sat May 21, 2016 2:54 pm, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Newcomer to NES programming

Post by Drew Sebastino »

Cat Quest 2 and 3. These were started on the side, just to amuse myself while working on the code for the 1st game.
It sounds like we're moving into DLC territory here...
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Newcomer to NES programming

Post by tepples »

Isn't Mega Man 2 mostly just "DLC" for the original Mega Man? See tropes Mission Pack Sequel and Episodic Game.
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: Newcomer to NES programming

Post by Alp »

tepples wrote:Isn't Mega Man 2 mostly just "DLC" for the original Mega Man? See tropes Mission Pack Sequel and Episodic Game.
On the subject on Megaman 2--
Once I've made a game or two, I'm considering making a mapper-based DX version of Cat Quest, with the content I had to cut, to memory restriction.

I finally had some free time this evening, so I blocked out dungeon tiles for that zelda-like game from a while back. 143 map tiles, with some extra tiles left for fun. This assumes 16KB of CHR.

Image
Last edited by Alp on Sat May 21, 2016 2:54 pm, edited 1 time in total.
lidnariq
Site Admin
Posts: 11716
Joined: Sun Apr 13, 2008 11:12 am

Re: Newcomer to NES programming

Post by lidnariq »

Alp wrote:This assumes 16KB of CHR.
Once you have more than 8 KiB, you're either going to be using RAM for CHR, and so there's no need to have any specific size limitation; or else the smallest cheapest available ROM now is 128 KiB. So once you add any mapper at all, don't feel like you need to hold back. :)
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: Newcomer to NES programming

Post by Sik »

Don't forget about what chip is acting as the mapper though, e.g. if the mapper is just a 74xx flip flop you'll still be able to only use two banks =P (although a 74xx latch may be better at this point, I suppose)
lidnariq
Site Admin
Posts: 11716
Joined: Sun Apr 13, 2008 11:12 am

Re: Newcomer to NES programming

Post by lidnariq »

You can't even use any of the single-bit latches without support circuitry anyway. So if you're going to use a discrete logic mapper you may as well use a 74'161 or 74'377 to get a bit more space.
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: Newcomer to NES programming

Post by Alp »

After playing around with tiles for a bit, I settled on a solution similar to Myask's suggestion. (minus the sprites.) I just removed the blue, and transitioned using a "weighted" gradient. (bands of colour, of varying thickness to give an effect of depth.) I'll add the water shine back later.

Also, I decided the full-black title background was boring, so I threw a background onto it, and changed up the text palette.

Image
EDIT: Oops! The Nametable version of the town background, has no chimney on that house.

I *actually* started to code this one! ...and learned some new things I'll be adding to Cat Quest. >.>
(Better map tile collision, and a number of other optimizations.)
Image
I am now programming two games! ...Ugh.

This was, of course after some (rather-amusing) problems:
Image
(I elbow-bumped ONE line of code, can you guess what it was? :P)

I started working on a scrolling engine, so I wrote some Ruby script, to prototype sprite offset calculation. Maybe somebody here will find it useful? I'll be porting it to 6502, and releasing source, later.

Code: Select all

;----------------------------------------
;SET SPRITE OFFSETS
;----------------------------------------
Player_X, = Player_Set_X
Player_X, * 16
Player_X, + 8
Player_Y, = Player_Set_Y
Player_Y, * 16
Player_Y, + 8
;----------------------------------------

;----------------------------------------
;SET MAP BORDERS
;----------------------------------------
Map_Width_X, = Map_X(Tiles)
Map_Width_X, * 16
Map_Height_Y, = Map_Y(Tiles)
Map_Height_Y, * 16
Map_Scroll_Right, = Map_Width_X
Map_Scroll_Right, - 128
Map_Scroll_Bottom, = Map_Height_Y
Map_Scroll_Bottom, - 120
;----------------------------------------
Map_Offset_X, = Map_Width_X
Map_Offset_X, / 2
Map_Offset_Y, = Map_Width_Y
Map_Offset_Y, / 2
;----------------------------------------
Map_North_Edge, = 8
Map_West_Edge, = 8
Map_South_Edge, = Map_Height_Y
Map_South_Edge, - 8
Map_East_Edge, = Map_Width_Y
Map_East_Edge, - 8
;----------------------------------------

;----------------------------------------
;CACULATE MAP SCROLL OFFSET
;----------------------------------------
Player_X > Map_R_Border
	Player_Offset_X, = Player_X
	Player_Offset_X, - Map_Width_X
	Player_Offset_X, + 256
	Map_Offset_X, = Map_Width_X
	Map_Offset_X, / -2
	Map_Offset_X, +256
Else
Player_X < 128
	Player_Offset_X, = Player_X
	Map_Offset_X, = Map_Width_X
	Map_Offset_X, / 2
Else
	Player_Offset_X
	Map_Offset_X, = Map_Width_X
	Map_Offset_X, / 2
	Map_Offset_X, - Player_X
	Map_Offset_X, + 128
;----------------------------------------
Player_Y < Map_B_Border
	Player_Offset_Y, = Player_Y
	Player_Offset_Y, - Map_Height_Y
	Player_Offset_Y, + 240
	Map_Offset_Y, = Map_Height_Y
	Map_Offset_Y, / -2
	Map_Offset_Y, +240
Else
Player_Y < 120
	Player_Offset_Y, = Player_Y
	Map_Offset_Y, = Map_Height_Y
	Map_Offset_Y, / 2
Else
	Player_Offset_Y
	Map_Offset_Y, = Map_Height_Y
	Map_Offset_Y, / 2
	Map_Offset_Y, - Player_Y
	Map_Offset_Y, + 120
;----------------------------------------

;----------------------------------------
;CALCULATE SPRITE OFFSET
;----------------------------------------
Player_X > Map_R_Border
	Enemy_Offset_X, = Enemy_X
	Enemy_Offset_X, - Map_Width_X
	Enemy_Offset_X, + 256
Else
Player_X < 128
	Enemy_Offset_X, = Enemy_X
Else
	Enemy_Offset_X, = Enemy_X
	Enemy_Offset_X, - Player_X
	Enemy_Offset_X, + 128
;----------------------------------------
Player_Y > Map_B_Border
	Enemy_Offset_Y, = Enemy_Y
	Enemy_Offset_Y, - Map_Height_Y
	Enemy_Offset_Y, + 240
Else
Player_Y < 120
	Enemy_Offset_Y, = Enemy_Y
Else
	Enemy_Offset_Y, = Enemy_Y
	Enemy_Offset_Y, - Player_Y
	Enemy_Offset_Y, + 120
;----------------------------------------
Last edited by Alp on Sat May 21, 2016 2:56 pm, edited 1 time in total.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Newcomer to NES programming

Post by Drew Sebastino »

Is the title screen going to use parallax scrolling with the mountains? I think it would look good that way. Also, do you not know 6502 ASM, or do you just not feel like using it?
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: Newcomer to NES programming

Post by Alp »

Espozo wrote:Is the title screen going to use parallax scrolling with the mountains? I think it would look good that way. Also, do you not know 6502 ASM, or do you just not feel like using it?
I have some of the 6502 basics down, background drawing, getting meta-sprites to load, room-switching, and the basics of sprite-to-map collision.

I'm really still learning at this point, that code above, was a test for something, better explained below.

Parallax? I didn't think of that! Good idea! I'll try putting that to use, here.
Alp wrote:Ruby script to prototype sprite offset calculation.
Perhaps I should have been more specific with this part? My fault, for posting at 3am, heh.

On my initial read-up on 6502, I found examples, stating that sprite-objects were annoying to keep track of, once you started scrolling the screen. So I wrote a re-alignment code in a language I'm more familiar with, to get a better idea of what to do, so sprites don't jitter/collide with blocks incorrectly when the player is moving the screen. I've seen that effect in a number of games. So avoiding it, is ideal. (That platformer will have left/right horizontal scrolling.)
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: Newcomer to NES programming

Post by Alp »

I managed re-format Cat Quest's metasprite/tile data to %60 of it's original size, and realized that it would require re-programming the data. I took the opportunity to re-organize the entire CHR file.

Image

I shifted some sprites that I somehow managed to miss, and threw out some background tiles, that were (basically) useless. I freed up enough space to:
1. Add 4 8x8 sprites to the player, to show her carrying a shield, when she has one.
2. Added two tiles for the top of a cave entrance, to make it less blocky looking.
3. Added a 16x16 tile for "shutter blocks", that slide into the floor, when you clear a room.
4. Generally re-organized the SH*T out of the CHR. Haha.

So... collision storage? How do other people do it? I have my own technique, which I will show here, and it works quite nicely in my engine, so far.

I calculate my sprites from-center, in order to make the code easier to read, and as a nice bonus, in 6502 ASM, this manages to save a fair-sized bit of space, at least, in larger games it would!

Image

The sprite offset data listed below, is compiled from the *two!* collision bytes that are listed below the sprite area boxes at the top. Can you see how the data is shifted? ;D

The data is calculated, and stored into RAM, on loading a new screen, so it's actually pretty fast!

I *would* have charted my metasprite/tile format as well, but I have not slept in close to a week, due to spring break, and loud children. Ugh. (Both formats are practically identical, sitting comfortably at 5 bytes/per)
Attachments
CQ_NewGFX.chr
(8 KiB) Downloaded 261 times
Last edited by Alp on Sat May 21, 2016 2:57 pm, edited 1 time in total.
Nameguy
Posts: 15
Joined: Sun Nov 02, 2014 3:27 pm

Re: Newcomer to NES programming

Post by Nameguy »

I apologize for not having posted in this thread - or forum - for several months. I've been caught up with real life and other projects.


Currently, I'm trying to design a very small reasonably-usable music engine. Disregarding the restrictions imposed by the NROM nature of Cat Quest, I am simply trying to design it to be as small as possible as a personal challenge.

Firstly, I will address the period table. Instead of the naive method of storing a 128-byte 64-note table, it's probably a better idea in this case to store a 56-byte 28-note table, addressable using only 5 bits. How is this done?

To use the same key for each track sounds like a limitation, but establishes a throughline for the soundtrack. By doing that, 3 chromatic scales can be stored, spanning C2-C4. However, the diminished second, the tritone, and the major seventh all seem a bit misplaced in a fantasy setting in my opinion; these can be thrown out, yielding 12 - 3 = 9 notes per octave. 9 * 3 = 27, + C4 = 28 notes seem sufficient for a plethora of musical content based on what I've yet written.

The representation of notes only needs 3 values: note duration, note pitch, and note volume. Note duration is rather simple; it is probably 6 bits, in units of frames, meaning a duration of 63 yields a note just over a second long. (63 would actually be 64 frames, because a 0-length note is useless.) Note volume is just 0-15 as usual, which is 4 bits. (Volume is still used for the triangle channel, because a volume of 0 is still semantically useful, and not creating exceptions simplifies the design.)

This means that each note is 6 + 5 + 4 = 15 bits, aligned to 2 bytes to simplify reading the data a little bit, decreasing program size.

Individual tracks are identified using a label in the assembly, which is essentially "compiled away" (need confirmation on this). The metadata header for each track holds a byte that stores the duty cycle for each pulse channel and a "does this song loop" bit, followed by a few bytes holding the length of the track in frames (debating over this - it would be a really big number), and then by pointers to 4 note tables - one per channel. The note tables simply store the notes, each entry being a word holding the 6+5+4 values.

This means only a few bytes of RAM are needed: A global tick counter, one note table index per channel (using 1 byte would limit song length to 256 notes), and one duration counter per channel. (If the duration counters are 6 bits, four can be stored in 3 bytes.) Few CPU cycles are taken on average per frame, because it only takes a few instructions to check whether any of the counters are equal to 0, which is usually false. If one of them is, read the corresponding channel's next entry in the table, send the data to the registers, and increment the index for the modified channel's table. Then, if the global tick counter has hit 0, check the loop bit and reset the note table indices and counters appropriately.

Final note: I nowhere mentioned patterns, which many engines use (presumably; I know little about them) to greatly compress song size. I've omitted them, not only to simplify the design, but to encourage minor variations in my composition of repetitive tracks, rendering the resultant music more interesting.

Is my approach a bad idea? Am I forgetting an integral component? Is there a smaller way of storing music? Is this whole thing a mess? I'd love to hear some feedback.
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Newcomer to NES programming

Post by tepples »

Go ahead and implement your ideas, and see if you can pack as much into 3K as I did. My music engine, used in three NROM games in each of the first two volumes of Action 53, uses a 64-note or 80-note table (size configurable at build time). Each pattern can see a 25-note window of this table, which can be shifted with a "change transposition" command. This lets me pack a duration and a pitch into one byte.
However, the diminished second, the tritone, and the major seventh all seem a bit misplaced in a fantasy setting in my opinion
In a minor key, you'll need both the "natural" seventh for III chords and the "harmonic" major seventh for V chords. For example, E minor needs E, F#, G, A, B, C, D, D#, and E.
Post Reply