Glitches on MMC3 chr switch

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
RJM
Posts: 55
Joined: Mon Jul 27, 2020 11:56 am
Location: Rzeszów, Poland

Glitches on MMC3 chr switch

Post by RJM »

Hello,

I'm working on a new game and decided to use a MMC3 with nesdoug.h for the first time.
I followed his amazing tutorial: https://nesdoug.com/2019/11/11/23-advanced-mapper-mmc3/ and it worked for me just fine.

I had to make some adjustments as in my case I have to do chr switching outside irq loop.
Basically, I'm working on 2 player fighting game and the upper half of sprite chrs is for player 1 and the lower half for player 2.
This way I can switch tiles for both players independently. This works fine and dandy - for most of the time.

Here is sample code I use for displaying consecutive metatiles for kick animation:

Code: Select all

const unsigned char *get_kick_animation() {
  switch (get_animation_frame_from_timer()) {
    case 0:
      switch_player_sprites_to(SPRITES_00_QUADRANT_2, currentPlayer);
      return get_sided_array(DudeSomersaultsBackward0SprFlipped, DudeSomersaultsBackward0Spr);
    case 1:
      switch_player_sprites_to(SPRITES_00_QUADRANT_2, currentPlayer);
      return get_sided_array(DudeKicksSprFlipped, DudeKicksSpr);
    case 2:
      switch_player_sprites_to(SPRITES_01_QUADRANT_0, currentPlayer);
      return get_sided_array(DudeStandsOnLeftLegSprFlipped, DudeStandsOnLeftLegSpr);
  }
}
What is happening here is I have little logic inside get_animation_frame_from_timer(), which tells me when to switch animation to the next meta tile. When is time to do so, I always do 2 things: change the portion of the tileset for the one that will be matching my next metatitle and return the actual metatile. get_sided_array() just returns a mirrored version, depending on which side of the screen player is on. Chr switching works like this:

Code: Select all

void switch_player_sprites_to(unsigned char spriteFileBank, unsigned char currentPlayer) {
  if (currentPlayer) {
    set_chr_mode_1(spriteFileBank);
  } else {
    set_chr_mode_0(spriteFileBank);
  }
}
Again, this works as expected - well most of the time. If you look at the switch cases, the transition from 0 -> 1 is correct.
The problem appears on the transition from 1 -> 2, as there I'm also switching the tileset.

There is very short 1 frame long glitch. Tileset is already correctly showing switch_player_sprites_to(SPRITES_01_QUADRANT_0, currentPlayer), but the sprites are still taken from get_sided_array(DudeKicksSprFlipped, DudeKicksSpr);. So I get tileset from state 2 ( correct ) but with the old metasprite from state 1.

I'm quite puzzled by this and checked my code couple times - the logic looks legit. I do not understand how despite switching metasprite and chr bank together in a single frame only chr switch happens and metasprite change is delayed to the next frame.

Has anyone had this problem?
Joe
Posts: 650
Joined: Mon Apr 01, 2013 11:17 pm

Re: Glitches on MMC3 chr switch

Post by Joe »

The CHR bank switch happens instantly, but the sprite tiles aren't updated until the next NMI.

You need to rework your code so that the CHR bankswitch will happen at approximately the same time as the tile update. You might be able to move the CHR bank switch to immediately after the ppu_wait_nmi call, but there may be graphical glitches if the NMI handler doesn't finish before the end of vblank.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Glitches on MMC3 chr switch

Post by tokumaru »

Normally, when running the game's logic we only decide which banks to load, we don't switch banks right away because the current frame is still rendering and you don't want to interfere with that.

The common thing to do is have variables in RAM indicate which banks are supposed to be loaded, which the game logic can modify freely. Then, during vblank, you just map whatever banks are indicated by those variables.

I'm not sure how you'd have these bank switches happen during vblank using nesdoug.h... You'll probably have to make changes to the NMI handler.
User avatar
RJM
Posts: 55
Joined: Mon Jul 27, 2020 11:56 am
Location: Rzeszów, Poland

Re: Glitches on MMC3 chr switch

Post by RJM »

Joe wrote: Sat Feb 05, 2022 5:36 pm The CHR bank switch happens instantly, but the sprite tiles aren't updated until the next NMI.

You need to rework your code so that the CHR bankswitch will happen at approximately the same time as the tile update. You might be able to move the CHR bank switch to immediately after the ppu_wait_nmi call, but there may be graphical glitches if the NMI handler doesn't finish before the end of vblank.
That did the job ;).
Thanks for your help guys!
Post Reply