It is currently Mon Sep 25, 2017 11:54 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 124 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next
Author Message
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Sun Jul 16, 2017 11:43 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6184
Location: Seattle
You have about 2200 cycles per vblank (113⅔ · 20) for everything that needs to be sent to the PPU. Updating sprites (OAM DMA) takes roughly 1/4 of that. CC65 is probably adding some overhead, too.

Almost every game I've looked at has some kind of scheduler for PPU updates. Sometimes they're really minimal (e.g. some flip-screen games just change between "PPU is off, uploading new data" and "game is running")


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 12:07 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5650
Location: Canada
tschak909 wrote:
Yup, looks like that's the problem. The question I have is, is it safe to call set_vram_update(); when the PPU is on?

I need to literally update different areas of the screen, depending on different events, score change, worrior doors opening, teleport happening, etc. If I can't call set_vram_update() while ppu is active, will I need to roll my own little queuing and scheduler code for this?

I think you can call set_vram_update whenever you want, that's just setting a pointer and the actual PPU update is done by neslib's NMI handler (which will run at the next vblank). The problem is just that the NMI handler has no way of safeguarding against or helping to diagnose the "too much data takes too long" case.

I believe all the neslib functions you can't call while rendering is on are labelled as such in the header.

If you need all of those updates to happen in one frame, you could write a custom assembly NMI handler that can get that much data through in a single vblank, but the library's generic one is not efficient enough to do it.

Otherwise you can break up your updates so you only have to do some of it each frame.


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 2:50 am 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 348
The solution is making your vram_update array dynamic, fill it with what you need to change for that frame, and let the NMI handler do its magic. I'm sure you don't need to update everythign every frame, so this just works.

Reserve a big enough update buffer in BSS, such as:

Code:
unsigned char update_buffer [UPDATE_LIST_SIZE*3+1];
unsigned char *ul;


Here, UPDATE_LIST_SIZE = 32 works for me (more than enough!). ul is a pointer we'll use to update the buffer.

Then your main loop would do something in the lines of:

Code:
set_vram_update (update_buffer);
while (game_on) {
    ul = update_buffer; // Point to the beginning of the buffer
    ... do your game logic, write to ul, increment ul.
    *ul = NT_UPD_EOF;
    ppu_wait_frame ();
}
set_vram_update (0);
...


In your game logic, whenever you need to update something, just write everything to the update buffer through *ul, for example:

Code:
*ul ++ = MSB(NTADR_A(1,19))|NT_UPD_HORZ,LSB(NTADR_A(1,19));
*ul ++ = 3;
*ul ++ = 0x74;
*ul ++ = 0x63;
*ul ++ = 0x75;


I'm sure just a number of things will fill the buffer each frame, so this will work and you won't run out of VBlank time.

_________________
http://www.mojontwins.com


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 7:43 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Ok. I'm already basically doing this, I'll just not try to do it in one big buffer. (I was doing 256 bytes in BSS and just creating update records for every single thing to update, and simply just changing the data...) :)

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 9:49 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19015
Location: NE Indiana, USA (NTSC)
tschak909 wrote:
I'm just curious as to how the result frames on a tube.

It depends on how the tube is calibrated. It's often joked that NTSC stands for Never The Same Color, but it can also mean Never The Same Crop. There's a diagram at "Overscan" on the wiki. For convenience and in case something happens to the wiki:

  • Danger Zone (outside Action Safe Area)
  • Action Safe Area, 256x224, (0, 8)-(255, 231)
  • PocketNES Safe Area, 240x212, (8, 16)-(247, 227)
  • Title Safe Area, 224x192, (16, 24)-(223, 215)

An LCD will probably show the Action Safe Area almost exactly. The average CRT will show somewhere between the PocketNES Safe Area and the Action Safe Area, but older CRTs or CRTs in poor shape might not show much out of the Title Safe Area or might show some of the Danger Zone.


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 10:15 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
oh I know the joke, all too well. :) It's especially poignant on the VCS, because you're literally generating half of the video signal yourself! :) (I have literally made black and white pixels artifact into color on the VCS on accident.) ;)

I just wanted to see how tight my tolerances are, since I do not have a toaster or a tube anymore. :)

(and yes, I have the overscan chart for the NES, I'm really bunching up against it. I just want to see how close to the cliff I actually am.)

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 17, 2017 10:18 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Code that currently opens/closes the worrior doors

Code:
/**
 * set_door(player, openclose)
 * player = Player 0 (blue) or Player 1 (yellow) door.
 * openclose = 0 for open, 1 for close.
 */
void set_door(unsigned char player, unsigned char openclose)
{
  // Clear the update buffer
  clear_update_buffer();

  // Set the addresses for the two rows of tiles that make up the door
  update_buffer[0]=(player==0?MSB(NTADR_A(1,18))|NT_UPD_HORZ:MSB(NTADR_A(28,18))|NT_UPD_HORZ);
  update_buffer[1]=(player==0?LSB(NTADR_A(1,18)):LSB(NTADR_A(28,18)));
  update_buffer[2]=3;

  update_buffer[6]=(player==0?MSB(NTADR_A(1,19))|NT_UPD_HORZ:MSB(NTADR_A(28,19))|NT_UPD_HORZ);
  update_buffer[7]=(player==0?LSB(NTADR_A(1,19)):LSB(NTADR_A(28,19)));
  update_buffer[8]=3;

  // And then set the tiles for each update depending on desired door state.
  if (openclose==0 && player==0)
    {
      update_buffer[3]=0x65;
      update_buffer[4]=0x00;
      update_buffer[5]=0x00;
      update_buffer[9]=0x65;
      update_buffer[10]=0x00;
      update_buffer[11]=0x00;
    }
  else if (openclose==1 && player==0)
    {
      update_buffer[3]=0x76;
      update_buffer[4]=0x64;
      update_buffer[5]=0x64;
      update_buffer[9]=0x74;
      update_buffer[10]=0x63;
      update_buffer[11]=0x63;
    }
  else if (openclose==0 && player==1)
    {
      update_buffer[3]=0x00;
      update_buffer[4]=0x00;
      update_buffer[5]=0x66;
      update_buffer[9]=0x00;
      update_buffer[10]=0x00;
      update_buffer[11]=0x66;
    }
  else if (openclose==1 && player==1)
    {
      update_buffer[3]=0x64;
      update_buffer[4]=0x64;
      update_buffer[5]=0x77;
      update_buffer[9]=0x63;
      update_buffer[10]=0x63;
      update_buffer[11]=0x75;
    }

}


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Tue Jul 18, 2017 4:08 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Doors and teleports working, next, scores... but I need to make a little queuing mechanism and make the functions use it... still thinking... in the mean time, a bit of silliness:

Image


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Sun Jul 23, 2017 10:28 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Scoring now implemented. I had a bit of a trip trying to be a bit too clever with the algorithm,

https://www.youtube.com/watch?v=tsEfDJwiPNY

but it's there, and now I'm implementing the sprite structure, a simple array of 8 entries with 5 elements each (40 bytes) containing:

Code:
/**
 * 8 objects on screen, two players and 6 enemies.
 *
 * [0] - Sprite X position
 * [1] - Sprite Y position
 * [2] - Sprite Type - (worrior, burwor, thurwor, gorwor... Bit 7 means empty.)
 * [3] - Dungeon X coordinate
 * [4] - Dungeon Y coordinate
 */


So, onward and upward. (and yes, this structure will change, i'm slowly adding what I need and then rewriting it if I need to tighten it up)

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Tue Jul 25, 2017 11:36 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Needed more room in my sprite chr bank, so I used NESST's remove duplicates feature. Fantastic feature indeed..in fact, both YYCHR and NESST are fantastically indispensable tools, hats off to Shiru and YY.

Now, assembling metasprites is like a twisted jigsaw puzzle. yay.

Image


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Sat Jul 29, 2017 3:02 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Now status is slowing down a bit, as I slog through entering tables and data for animation and start implementing the frame loop for the main part of the game.

I am currently using a very naive vram update scheduler, interleaving parts of the vram to be updated across four different frames, as it isn't critical that all of these updates happen with hair-pin precision:

Code:
     // VRAM update scheduler

      a=frame_cnt&0x03;
      switch (a)
        {
        case 0:
          update_doors();
          break;
        case 1:
          update_scores();
          break;
        case 2:
          set_teleport(teleport_state);
          break;
        case 3:
          break;
        }

      // End VRAM update scheduler


I will spread the radar update into 4 sections, so that I can have enough cycles to update the radar screen (really not much to do other than just read the dungeon x and y boxes for the 6 enemy state slots, and set the tile to use accordingly, but there are 10x6 tiles to update, so I have to slice it up a bit)

If I need something better, i'll write it.

I've gone through and implemented almost all of the sprites, save for the wizard, as I am needing to decode the bitplane format used on the Astrocade, to grab the blitter object for the wizard from the ROM, as I have tried to frame grab it, like the other character objects, and as it happens, the blitter object is munged a bit to blur it, making extraction of anything other than the one visible object on the character description page difficult. Worst case, I will just #@($#@ draw it based on the blurry (#@$#@ images.

I've also implemented the animation state tables which will be used to select which metasprites to use for a given state, and have enough to where I can do a naive first pass at filling in the OAM on each frame. I believe the game uses four frames for each animation state, with two of those frames duplicated to create a sort of slight cyclical pause that is visible at slower speeds...it certainly makes it easy to cycle through the frames in the state list, as I just have to AND it off.

I've also implemented the red/blue dungeon palette swap that is needed to differentiate certain states (worluk/wizard visible, or you're now in a Worlord dungeon), as well as the Double Score win color cycling.

Slowly but surely...

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Sat Jul 29, 2017 11:56 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Implemented first passes of initial enemy placement, and radar. (need to re-order radar bits a bit to make it easier to select the correct tile for display.)

Image

Radar is implemented as background tiles, saw no need to waste perfectly good sprites for it.

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Sun Jul 30, 2017 4:31 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
A quick video status report:

https://www.youtube.com/watch?v=BLIaZFthB5o

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Mon Jul 31, 2017 10:31 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
Looks like enemy collision detection needs a bit of work (I need to add a bit of fudging to allow the monsters to go into the box, as well as some bias correction, but if you look at the radar output, you'll see the collision code is acting as expected)

https://www.youtube.com/watch?v=QV_Tf5Mdmf4

-Thom


Top
 Profile  
 
 Post subject: Re: WIP: Wizard of Wor
PostPosted: Wed Aug 02, 2017 12:19 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 93
A little refactoring of my macros to make them saner, and the collision code became simpler to write, and the bugs went poof:

Code:
#define STAMP_NUM_FIELDS     8                    // Number of fields in each stamp slot
#define STAMP_NUM_SLOTS      8                    // Number of slots in stamp structure
#define STAMP_CENTER_BIAS_X  12                   // Offset to apply to box multiply to center sprite (X)
#define STAMP_CENTER_BIAS_Y  10                   // Offset to apply to box multiply to center sprite (Y)
#define STAMP_NUM(x)         (x*STAMP_NUM_FIELDS) // Stamp Number
#define STAMP_X(x)           (STAMP_NUM(x)+0)     // Stamp Field: X pixel position
#define STAMP_Y(x)           (STAMP_NUM(x)+1)     // Stamp Field: Y pixel position
#define STAMP_TYPE(x)        (STAMP_NUM(x)+2)     // Stamp Field: Type
#define STAMP_STATE(x)       (STAMP_NUM(x)+3)     // Stamp Field: state (which frames to use).
#define STAMP_FRAME(x)       (STAMP_NUM(x)+4)     // Stamp Field: Current frame
#define STAMP_DELAY(x)       (STAMP_NUM(x)+5)     // Stamp Field: Delay
#define STAMP_FINE_X(x)      (STAMP_NUM(x)+6)     // Stamp Field: Fine X offset relative to box (not used, will be repurposed)
#define STAMP_FINE_Y(x)      (STAMP_NUM(x)+7)     // Stamp Field: Fine Y offset relative to box (not used, will be repurposed)

#define PIXEL_BOX_X(x)       ((x*24)+STAMP_CENTER_BIAS_X)             // Convert Box X coordinates to pixels
#define PIXEL_BOX_Y(x)       ((x*24)+STAMP_CENTER_BIAS_Y)             // Convert Box Y coordinates to pixels
#define BOX_PIXEL_X(x)       (div24(x-STAMP_CENTER_BIAS_X))           // Convert Stamp X coordinates to Box X
#define BOX_PIXEL_Y(y)       (div24(x-STAMP_CENTER_BIAS_Y))           // Convert Stamp Y coordinates to Box Y


and the relevant bit of collision code:

Code:
/**
 * move_monsters()
 * Move the monsters
 */
void move_monsters()
{
  for (i=2;i<STAMP_NUM_SLOTS;++i)
    {
      // Get the box in a&b
      a=div24(stamps[STAMP_X(i)]-8);
      b=div24(stamps[STAMP_Y(i)]-8);
      c=(b*10)+a; // C is now the box #
      d=dungeon[c];

      if (stamps[STAMP_STATE(i)] == STATE_MONSTER_RIGHT)
        {
          if (d&1<<4)
            {
              if (stamps[STAMP_X(i)]==PIXEL_BOX_X(a))
                {
                  stamps[STAMP_STATE(i)]=rand8()&0x03;
                }
              else
                {
                  stamps[STAMP_X(i)]++;
                }
            }
          else
            {
              stamps[STAMP_X(i)]++;
            }
        }
      else if (stamps[STAMP_STATE(i)] == STATE_MONSTER_LEFT)
        {
          if (d&1<<6)
            {
              if (stamps[STAMP_X(i)]==PIXEL_BOX_X(a))
                {
                  stamps[STAMP_STATE(i)]=rand8()&0x03;
                }
              else
                {
                  stamps[STAMP_X(i)]--;
                }
            }
          else
            {
              stamps[STAMP_X(i)]--;
            }

        }
      else if (stamps[STAMP_STATE(i)] == STATE_MONSTER_UP)
        {
          if (d&1<<7)
            {
              if (stamps[STAMP_Y(i)]==PIXEL_BOX_Y(b))
                {
                  stamps[STAMP_STATE(i)]=rand8()&0x03;
                }
              else
                {
                  stamps[STAMP_Y(i)]--;
                }
            }
          else
            {
              stamps[STAMP_Y(i)]--;
            }
        }
      else if (stamps[STAMP_STATE(i)] == STATE_MONSTER_DOWN)
        {
          if (d&1<<5)
            {
              if (stamps[STAMP_Y(i)]==PIXEL_BOX_Y(b))
                {
                  stamps[STAMP_STATE(i)]=rand8()&0x03;
                }
              else
                {
                  stamps[STAMP_Y(i)]++;
                }
            }
          else
            {
              stamps[STAMP_Y(i)]++;
            }
        }
    }
}


This implements a very simple "patrol my space" AI, which I may want to change to alter direction when there are no walls in a box, or something similar.

Have posted a quick build for others to enjoy looking @, code is on github, and am continuing on.

Attachment:
wow-monster-to-wall-collision-test.nes [40.02 KiB]
Downloaded 19 times


-Thom


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 124 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group