This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

My first foray into making cinema displays

My first foray into making cinema displays
by on (#39345)
Well, my newest game has an intro cinema, and I thought I'd show you guys a video of it. It's not that long. There's no text during the cinema because I don't really think it needs it too badly, as the story itself is simple. Anyway, here is a link to the vid:

by on (#39348)
Uh... Resident Evil style isn't my pretty thing.

by on (#39349)
Cool zombie at the end.

by on (#39352)
strat wrote:
Cool zombie at the end.

Thanks man, though it's not my work. Crap, I forgot to post in the original post that my buddy Dave did the art for the screens. He did the intro screen graphics, plus the title screen zombie. He also did the art for the boss, and has so far done one of the ending screens for the game. He's pretty damn pimpin' : )

haha I love working with him, too. He's really laid back about it all, but whips out the graphics quick as hell. Great guy!

by on (#39355)
Don't like the glitches from turning the screen on at the wrong time.

by on (#39357)
Yeah, as far as I know, it only appears that one time, right?

Also, how do I avoid that? What I'm doing at that point is changing the sprites from the 'guy on the phone' to the 'guy looking at the screen'. For some reason though, no matter what I do, I get this jumping thing happening, and don't know how to work around it. If you have some advice, don't hold back : )

by on (#39358)
Don't turn the screen off during rendering if it's on, wait for an NMI before you turn it off.
If you have the screen off, wait for an NMI before you turn it back on.

I'll just assume you use that really simple NMI handler that just increments a byte in memory then immediately returns, and you are spinwaiting on that number to not be zero to wait for an NMI. Then of course reset the number back to zero right before the wait.

;'vblanked' is an equate which points to a byte somewhere in the zeropage
   inc vblanked
;example of code to wait for NMI
   lda #$00
   sta vblanked
   lda vblanked
   beq waitloop

by on (#39360)
Well, here is how I wait for a frame to happen:

wait_for_nmi:                  ; A routine that we can use
   lda nmi_num                  ; in the main loop to make
:   cmp nmi_num                  ; sure we are doing a frame
   beq :-                     ; through each loop.

See, this is something that I find confusing though. I've had a few people tell me 'wait for an NMI,' but I don't really understand how. See, this code above just says that an NMI happened already, not that things are ready to be changed and updated, etc. It's not like I'm in a blanking stage, if you know what I mean. Maybe I am and just don't understand the full gist of it, though. I don't know :/

by on (#39361)
Roth wrote:
See, this code above just says that an NMI happened already

Yes, the NMI routine is over, but whether VBlank is over depends on what was done in the NMI routine. If all you do is change that number, there certainly is a lot of VBlank time left.

It's not like I'm in a blanking stage, if you know what I mean.

You could be, depending on how much CPU time was used by the NMI routine. Many people don't do anything but change a flag or a counter, in which case the next 2200 or so cycles are still VBlank time.

I'm assuming you are only using that loop to prevent the game logic from jumping to the next frame before the current one is rendered, right? Well, if you do a lot of things in your NMI routine and can't be sure you're still in VBlank when returning from it (as could be the case if you called your music engine from it, for example), you have to find some other way to safely disable and enable rendering.

Maybe instead of directly manipulating the PPU register in the main code, you could use a flag to indicate if you want rendering on or off. Then, somewhere in your NMI routine you are sure is still during VBlank, read that flag and write to the PPU accordingly. That way, your program can issue orders to enable or disable rendering at any time, but they will only be executed at safe times.

by on (#39370)
This looks kind of Ninja-Gaiden style.
It's pretty cool btw but the graphics could be better.

by on (#39381)
I think it looks good, and I like the style of graphics.
How did you do that scroll part. Are some of those sprites?


by on (#39382)
This was heavily influenced by Ninja Gaiden, actually. Though of course it's not as stellar as that is, but once again, my baby steps hehe

Thanks Al, and yeah, the main character is all sprites actually. The nametable at $2000 is all blue with the black top and bottom, and and the nametable at $2400 is all blue with the black top and bottom, but also with the zombie shadows in there. This way they can be offscreen, and I can push them onto the screen and also have the main character as sprites to move around. I didn't have any plans to try and insert words during it, so I didn't have to bother with figuring out how to do some zero hit stuff to scroll the cinema area; the entire screen scrolling worked fine for this case : )

by on (#39383)
Nice intro for your first one. I can't wait to make some too! First I need to make the artwork thought (...).

by on (#39386)
This was heavily influenced by Ninja Gaiden, actually.

Yeah, this is really obvious, but still looks cool. I think the main character doesn't look very good, it could be improved I guess, but it's just my guess. The zombie on title screen looks really impressive tough.

I'd like to come with a method of making cinematics on the NES that would be a little different than what Ninja Gaiden does, but for now I can't do any in my current game (else I'd have to change the mapper it uses to get more CHRROM). I'd probably do it in further games.

by on (#39393)
tough crowd around here, roth. i say great work!

by on (#39397)
I think it's pretty cool! Although I'd add more to it, and like you said, explain what's happening a little more. But pretty cool!

I like the parallax, and it does remind me of Ninja Gaiden. But really the only thing that reminds me of Ninja Gaiden is the fact that it's a cutscene with parallax effects. It's not like a huge rip-off or anything because the content really isn't really Ninja Gaiden-esque at all.

I'll be having two kinds of sequences in my game. One kind is polygon based (Both BG and Sprites) with support for 60- pre-defined tiles for sprites, but I could probably do it for BG too (I have quite a bit of work to do to support pre-defined tiles and for sprites in general, but the polygonal BG part works). The other kind is all pre-defined, like yours is. This works better for story parts than the 3D does.

However, when the game play stops for characters to speak to each other, it remains with in-game graphics and a text box with their portrait comes up. So I guess that counts as like 3 types of cutscenes. I like having them all together and not just one type of cutscene.

by on (#39402)
Looks like a nice start. :) However, I think the main character looks a bit too cartoony for what I assume to be a survival horror game, especially if you compare it to the zombie in the title screen. A pen on his shirt appears when he calls his cell phone, but it's not there in his other poses. The cell phone is hard to see, and I think it should be a different color, maybe by using an extra sprite.

It looks like you used color $18 for the guy's skin tone, but I don't think that's a good color, especially since it could either be biased towards orange or green depending on the palette or TV. If I were you, I'd use either colors $17, $06, or $07, which are all decent African skin tones, but $17 can be a bit too red in some palettes.

But considering the impressive zombie in the title screen, I'm sure you guys could touch it up a bit. It looks really promising so far. :D Just curious, but have you ever thought about the gameplay yet?

by on (#39403)
They're really fun to make, because there are always ideas that can come to your head for them.

Yeah, I had to upgrade my mapper. Luckily it was only going from NROM to CNROM though, so it wasn't a headache hehe Can't wait to see what kind of ideas you have for a cinema system : )

That's a good thing though, it's always nice to have some people that will give some different ideas that I may have never thought of.

Dude, that sounds really cool to have all those different means of propelling a story forward! If I could code more tightly, I would probably think of employing something like that sometime. Maybe one of these days ; )

Thanks for the suggestions on the palettes! I really don't want him to be so light skinned, and be more of a dark skin color. I'll fiddle with the colors that you recommended for sure.

As for gameplay, it is an overhead game. Basically on every screen two zombies will pop up from the ground and break out after you. As the clock counts down further to the time that your friend picks you up, the zombies will gain more speed. It's not going to be an incredibly long game, but there will be things like real-time item selection via the menu at the top of the screen. There's not alot to it honestly, but it's the most complex game I've been working on so far hehe We'll see how this turns out : )

Thanks for all the really great comments guys. It's appreciated.

by on (#39404)
Seconding the cool zombie comment. It works pretty well with the palette transition.

by on (#39406)
The cardinal rule of having a token black guy in a story is that he can do anything except survive all the way to the end. To do otherwise would be most avant-garde. :wink:

And please, please, please have a scene where all the zombies bust out the Thriller dance as they come to eat your brains. That would take your game way over the top and make it the stuff of legend.

by on (#39411)
Yeah, I had to upgrade my mapper. Luckily it was only going from NROM to CNROM though, so it wasn't a headache hehe Can't wait to see what kind of ideas you have for a cinema system : )

I didn't say I have ideas, I said I'd like to have ideas ! That's not the same thing.
It's good you get cinema cutscenes with only CNROM. My game uses CNROM, I've planned to take 3 CHR banks for gameplay, and one for title screen. The second half for title screen is still free, so maybe I could come with some really basic cinama cutscenes if I can get economical in tiles enough for them to fit, but I don't think this will happen unless I really have a lot of free space when the game is completed. I'll probably use that space for a map that shows between levels instead, or just for the ending.
My game won't have that much story exept textboxes with the portrait, which is already good, considering most NES games doesn't have that. Even RPGs often have textboxes without the portrait !

PS : About cutscenes, I was saying I wondered if there is another way than the "horizontal blue band" thing. Doing them fullscreen waste tiles, and doing a blue horizontal blue bar is too much Ninga Gaiden like.

by on (#39413)
Roth wrote:
They're really fun to make, because there are always ideas that can come to your head for them.

Yeah, I know. The last time I did something similar was while I was still learning programming, under dos while coding in pascal. That was 15 years ago. Since then, I didn't do much game programming.

Now I'm restarting to do programming at home so I hope to make something interesting again.

by on (#39430)
Firstly, it seems that palette color $07 is exactly what I was wanting, so thanks a ton for the recommendation strangenesfreak : )

The main problem I'm running into now is this: Is there a way to completely destroy sprites so they are no longer needed? I have some strays that appear because the amount of sprites used in the cutscene are greater than those going on during initial gameplay. Any advice would be awesome.

by on (#39431)
Roth wrote:
The main problem I'm running into now is this: Is there a way to completely destroy sprites so they are no longer needed? I have some strays that appear because the amount of sprites used in the cutscene are greater than those going on during initial gameplay. Any advice would be awesome.

Can you set their y-positions to some value that puts them below the visible display area, such as $FE?

EDIT: The y-position stored in sprite RAM is actually one less than the y-position displayed onscreen. That's why I suggested $FE instead of $FF. If you set the y-position to $FF in sprite RAM, the sprite will actually be displayed at y=0. This shouldn't matter on NTSC because the first 8 scanlines aren't displayed anyway, but using $FE strikes me as a better way to ensure the sprite is completely offscreen.

by on (#39432)
Well, the problem with that solution is that there are some sprites that will already be residing in that position, as well as up at 00,00. I suppose I can just find a way to throw them somewhere, though I would rather be able to just get rid of all that are able to be seen. It seems even if I try something like this first:

   lda #$00
:   sta left_p_head,x
   cpx #$00
   bne :-

... it doesn't do much good either. So I don't even know if it's possible to do something 'destroying' sprites, but it would be nice if there were a way.

by on (#39433)
Lots of games do something like this at the end of the frame to 'destroy' all sprite tiles. If they can't be seen that's all that matters.

   LDA #$EF   ;to store
   LDY #$00
   STA OAM_YPOS,y   ;store offscreen
   INY      ;next sprite
   BNE @loop


by on (#39434)
So is your problem that some sprites during the cutscenes are carried over to gameplay because there aren't enough ones used during gameplay to completely overwrite the ones from the cutscenes? If so, that sounds like you're not clearing the sprite page before you write to it, which would be wise to do when switching "modes". Also, you should use the concept of "objects" to draw sprites on the screen every frame, overwriting all 256 bytes of the sprite page each frame. If you use this, you can have a list of all the objects that will be drawn on screen, and once they're drawn, clear out the rest with $FF. Sorry if that doesn't make any sense.

EDIT: smkd, that seems like a pretty wasteful way to do it. Why not overwrite the sprites previously displayed during the sprite drawing routine and when all that are meant to be displayed are drawn, then clear the remainder?

by on (#39436)
The MSX has an interesting sprite feature: When a sprite is placed at a certain coordinate, all sprites after it are ignored. The NES doesn't have such a feature, so you have to manually hide all the sprites you're not using, preferably at the bottom of the screen.

Now, I'm not sure I got what your sprite problem is, but it seems to me like you are using them in a very hardcoded way. Most games have dynamic ways of drawing sprites (routines to draw them based on coordinates of objects and sprite definitions), because of sprite cycling and many other reasons, and they are rendered every frame, even if they didn't change since the last frame. Just look at the OAM area of any game in FCEUXD and you'll see how crazy that part of the memory is.

Anyway, a quick fix for you would probably be to clear all the sprites before using them, with code similar to what smkd suggested. However, as Celius observed, the optimal solution would be to use all the sprites you want and only clear the remaining ones, but that may require you to do heavier changes to your program.

by on (#39439)
Alright, thanks for the input guys. I was able to get it to work by just clearing the ones that aren't going to be used at the time. Though, I'm still unsure of what you guys mean by 'objects' in this case.

by on (#39441)
It's wasteful, sure. In my game I take the current 'OAMINDEX' (next available tile) and work backwards so that only used sprites get cleared.

by on (#39446)
Roth wrote:
Though, I'm still unsure of what you guys mean by 'objects' in this case.

By "object" I meant an entity that exists in the world modeled in the program. In SMB, Mario is an object. In most games, objects require multiple sprites to be represented. Rendering each sprite individually by hand would consume a lot of ROM and would be a very tedious task for the programmer.

Because of this, routines that render sprites will typically transform some sort of sprite definition (list of individual sprites that compose the visual representation of an object) into OAM data based on the position and attributes of the object they represent.

Such routines make it easy for the programmer to handle sprites, because it's possible to draw an object anywhere on the screen, just by informing the routine about the coordinates of the object and the sprite definition it uses. You could even have the routine automatically handle the flipping of each sprite based on the flipping of the object, as well as other attributes (such as palette changes because of water, and so on).

This makes it all very dynamic, in a way that a certain object is not tied to specific OAM positions, which is really bad for sprite cycling, for example.

by on (#39450)
Yes, yes, Tokumaru is completely right. An object is just a thing in the game world, usually made of sprites. A candle or an enemy in Castlevania is an object. It's usually something that has it's own mind/purpose.

But it's great for sprite cycling to do it this way, and it's a lot less coding. For example, you could tell the sprite drawing routine to place an enemy at coordinates 143,102, and have that enemy's sprite data defined somewhere in RAM. This data tells the arrangement of the sprites + the attribute data. The routine will also have to be told if the object is flipped in any way.

Oh, and just so you know, these routines can take a very long time to execute! There's just a lot it has to compute. Though you can optimize the routine to take less time, obviously. There was actually a topic about it:

Where before posting, I'd always thought of having to work with sprites in a hardcoded way. But it's so much easier when you don't have to.

by on (#39452)
I'm unsure if I'm doing what you guys are referring to or not haha

What I have for my enemies, is they are all loaded offscreen from my initial sprite positions and attributes that are stored in ROM:

   .byte $80,$08,$00,$80 ; Player left head
   .byte $80,$09,$00,$88 ; Player right head
   .byte $88,$1a,$00,$80 ; Player left chest
   .byte $88,$1b,$00,$88 ; Player right chest
   .byte $00,$84,$01,$00 ; Zombie1 left head

; ... etc.

The way I load is to use a routine to store everything from $200 up to the rest of the sprites that are to be used, and do the sprite cramming routine every NMI, which just updates the 200 page.

If this is what you guys are referring to, then I do that for sure.

by on (#39453)
Just to clarify if there's a difference between what you do and what we do:

Assuming the OAM page is $200-$2FF, to move an enemy one pixel to the right, do you do something like this?

lda $203
adc #1
sta $203

In other words, do you take existing values from the OAM page and alter them slightly as enemies move and whatnot?

by on (#39454)
Yes, that is what I do. If you mean you guys specify groups of sprites together to make an object, I have no clue how to do that. Like, how to 'lock them together' as one thing, I guess is what I'm saying.

by on (#39456)
Roth wrote:
Yes, that is what I do. If you mean you guys specify groups of sprites together to make an object, I have no clue how to do that. Like, how to 'lock them together' as one thing, I guess is what I'm saying.

Specify a location in RAM (somewhere other than the OAM page) to hold the position of each group of sprites.

Assuming you have an object that's 1 sprite wide and 4 sprites tall, and that the object's position is stored in the variables objectPosX and objectPosY, you can "lock" the sprites together with code like this:

; Set x-position
lda objectPosX
sta $203
sta $207
sta $20B
sta $20F

; Set y-position
lda objectPosY
sta $200
adc #8
sta $204
adc #8
sta $208
adc #8
sta $20C

With this setup, you only have to change two variables (objectPosX and objectPosY) to move the entire object. The other sprites will move along automatically.

Note: This method still doesn't allow for easy sprite cycling, but it's a start. It's all about baby steps.

by on (#39462)
Roth wrote:
Yes, that is what I do. If you mean you guys specify groups of sprites together to make an object, I have no clue how to do that. Like, how to 'lock them together' as one thing, I guess is what I'm saying.

Ah, I understand. It can be rather complex. Generally, object placement is specified by one set of X,Y coords which is used as a reference point for the metasprite definition. Here's how I define metasprites:

  .db NumberOfSprites, FlipXAdjustment, FlipYAdjustment

  .db RelativeXCoord, RelativeYCoord, Tile, Attribute
  .db RelativeXCoord, RelativeYCoord, Tile, Attribute

It goes on for how ever many sprites make up the object. Take a look at this definition:

   .db $03,$08,$18
   .db $00,$00,$B1,$03
   .db $08,$02,$B3,$03
   .db $10,$03,$B5,$03

This definition says that 3 sprites make up the Medusa Head. The FlipX and FlipY adjustment are there to adjust the position of the sprites if it is flipped horizontally or vertically, as sometimes it needs to be adjusted because it looks weird when you flip back and forth. So if this metasprite is flipped horizontally, it will be placed 8 pixels to the right to make it look like a smooth flipping.

Now say I wanted to place the medusa head at 123, 42. My drawing routine would take this metasprite, and assuming there's no flipping, would put the sprites at the following coords:

123, 42
131, 44
139, 45

Because those relative coords place the sprites relative to the object's X/Y coords.

Don't get confused by my order of definitions. I know it's standard to have Y, Tile, Attribute, then X, but it makes no difference performance-wise the way I have it set up, and it's just easier for me to read as X, Y, Tile, Attribute for me.

Does that make any more sense? That's how I specify groups of sprites as one object.

You should have a sprite drawing routine that takes a list of objects that are going to be drawn on screen, and use metasprite definitions like this to have it draw them accordingly.

by on (#39467)
Heh, that's fun because I'm re-doing all sprites for my game those times.
Yes, the way Roth did it is really terrible, you shouldn't rely on any value in OAM, it's just a buffer to be sent to the PPU via $4014, nothing else. You should have a system that handles objects and all, and a routine that maze the object's sprites in OAM. This routine itself call the general-purpose sprite mazing, which can maze any sprite in OAM instantly. Basically it just compies data for a few sprites to OAM, but changin the Y and X position to add their relative componant (and check if there is overflow), and optionally change the color and flipping.

To handle sprite cycling, I have the routine that update sprites in RAM, and I toggle some instructions every so frame, so that when I call that routine to maze object it will maze the sprite forwards every so frame and backward every other frame. This is needed because I need the objects to be drawn in a sorted order, as my game has top-down view. The sorting routine takes very long by the way, but it's doable.
For sprites that needs to be mazed in a specific order, I just call the ROM version of the routine so that it doesn't do sprite cycling as the RAM version does.

I could share my routine if you guys want it, but this could discourage some people to write their own, which isn't good.

by on (#39475)
Thanks for the advice guys! I'll read these posts a few more times to get an idea of how I need to do something like this. Sounds a bit hairy!

SecretServiceDude wrote:
It's all about baby steps.

So true ; )

by on (#39485)
Roth, I know this can be overwhelming at first. In fact, you can get away with simpler sprite handlers depending on the type of your game.

What we've been presenting you is very useful in dynamic setups, when a variable number of objects can be active at once, in which case some sort of sprite handler is necessary to cycle them, hide the unused ones and so on.

If your game is so simple that it allows you to hardcode individual objects to certain OAM locations (should only happen when you have a small constant number of objects on screen), there is no problem with that. Just keep in mind that this is not the standard way to do it (as in "not many commercial games did it"), because it's very limited.