Jump to content
IGNORED

FlyGuy II


matthew180

Recommended Posts

After playing Codex's FlyGuy submission to the 30-line XB contest, I decided it would be fun to rewrite it in assembly language. I have secured permission from Codex to remake his new classic, and Owen has agreed to provide some in-game music, so it should sound good too! :-)

 

Codex suggested I start a WIP thread, so here it is. :-) I guess the primary motivation for doing this was because the original XB game, as mentioned in the original thread, is as slow as a glacier and it was recommended that you play in Classic99's CPU overdrive mode. I found this to be the case and wondered what it would be like in assembly.

 

I'm not much of an "original concept" person when it comes to games, so starting with a working model really helps. I already have a playable code-base, but it is ripped right the moment while I add the next little bit. If anyone is interested I'll post a screen shot and the WIP code in a little bit (once it is stable again.)

 

I intend to publish the code as part of my unofficial "series" on programming in general, and the TI in particular. I have never actually *finished* a 100% complete game in any language (it is a tall feat!), so this will hopefully be my first!

 

Uhm, let's see, getting up to date. I'll try to talk about what I have done so far, then post as I go along.

 

First I had to dissect the original game to see how the spider AI and level generation was done. Since the contest only allows 30 lines of code, there is heavy use of multiple statements on one line and things are very dense and hard to follow in the BASIC listing. For the analysis I strung everything out in a text editor, replaced the target line numbers of GOTO statements with labels, and renamed some variables for better clarity. In my opinion BASIC is *very* hard to read since you cannot indent or use an decent structure, so reformatting makes it a lot easier to understand.

 

The method Codex used to to "generate" the levels was pretty slick. Instead of programming in all the data for the level (which would have taken up way to much space for a 30-line program), he used random numbers. While there is nothing unusual about random level generation per-se, what Codex did was take advantage of the fact that you can seed the random number generator (RNG) and always get the same sequence of numbers for any given seed. Usually you want to seed the RNG, uh, randomly, as to not get the same sequence each time the game is played. But for building the levels, this is exactly what was needed. Also, since each level gets a little harder (less intersecting and more single width grids), the choice of seed values needed to be determined. I suspect that Codex just started feeding seed values to the RNG until he found certain seeds that gave levels that were satisfactory, then those seed values were coded into a single DATA statement. Thus, for 5 completely different, progressively harder levels that are the same each time you play, all that was needed were 5 numbers. Sweet.

 

My problem was that I can't use XB's RNG in my assembly program, so I modified Codex's original game to spit out the random data used to generate the levels and I use that in my assembly program to reproduce the original levels. I'm not sure yet how I will deal with levels if I decide to take this further than the original 5.

 

Speaking of random number generators, the one I'm using is straight out of Tombstone City and it pretty week. Any suggestions or code examples would be greatly appreciated. I'm looking for code that does not rely on anything in ROM or GROM. The current code I have has an unfortunate side affect of the 2 least-significant bits are always 00, 01, 10, 11 and cycle in that order. I always forget that and it bites me every time I go to get a random number between 0 and 3 (like picking a random direction of up, down, left, right.)

 

The spider AI was simple, yet effective and gave me plenty to work with as far as the "speed up" and such.

 

The initial coding consisted of getting the screen initialized and reading the level data to get something on the screen. I'm not using any console routines, i.e. nothing from ROM or GROM, so I had to write it all. I like it this way though, and is one of the reasons for doing things in assembly. Hell, if you are not going to code direct to the hardware, you might as well not use assembly. All the console stuff uses BLWP anyway, which is slow! Plus it uses that GLP workspace stuff which I never learned, and GPL takes huge bites out of the precious scratch pad RAM. So, LIMI 0 and never look back. Something you do lose in doing that is the nice VDP interrupt, but we can work around that by polling, which for a game works just as good (if not better.)

 

After the levels were up, I needed to get the FlyGuy on the screen and moving. Hmm, keyboard and joystick input... ugh. I hate the way the 4A does the keyboard, hell, I hate the whole CRU idea. Ah well. So, off Thierry's Tech pages to read up on the 9901, keyboard, and joysticks. Actually, since I don't need the keyboard (and no, I will not use the KSCAN routine in ROM), getting to the joystick data is really actually very easy and fast. You can set the keyboard read-column to joystick 1 and just leave it that way. Since interrupts are disabled the console ISR won't mess it up, then all you have to do is use the TB (test bit) command in assembly to check the 4 directions. It was super simple and I was very happy about it. :-) Here is the code to set up to read the joystick:

 

      LI   R1,>0600          * Keyboard column 6 (joystick 1)
      LI   R12,>0024         * Set the CRU port
      LDCR R1,3              * Load the CRU, setting the column latch
.
.
.
* Test the joystick
      LI   R12,>0006         * Base CRU address for joystick 1
      TB   1                 * Left

 

That's it! After the TB command, you can use JNE or JEQ to do whatever you need. For Right you use TB 2, for Down use TB 3, for Up use TB 4. Fire would be TB 0. Now how simple is that!?

 

Okay, got FlyGuy on the grid, but damn, he is too fast! Pressing and releasing the left or right arrow as fast as I can moves FlyGuy from one side of the level to the other. This is why I love assembly. :-) Gotta slow him down. This is where I see a lot of dumb code in assembly. Slowing things down was typically done with busy-wait loops, but all that does it waste the time and also locks your game to a particular CPU can clock speed.

 

To control the speed of the game it is best to use a counter. Since it is a pain to use the 9901's counters, and since I have interrupts disabled, I have to poll the 9918A for the vsync signal, which if interrupts were enabled would cause the console's ISR to execute. Even though the vsync is only every 60th of a second, this is plenty fast enough for a human, and it does not matter if our game misses a sync or two because we took too long with game logic. The vsync also gives us a nice way to make a game timer in seconds. Last, it lets us control the speed of objects easily. So, for the FlyGuy I decided I would poll user input every vsync (60th of a second), and if there was a move then wait 1/12th of a second (5 vsync signals) before testing user input again. This makes for a pretty snappy FlyGuy, but still controllable and you can get him where you want him (at least I could). We'll see what the beta testers think though.

 

I also use this method for the spider speed. Something different about FlyGuy II over the original is that the spiders will move even if you don't. The spiders get to move every half a second, which is pretty quick. I had them every 10th of a second, then every quarter of a second, but those both seemed to fast. Like in the original, when a spider eats a pill they get a boost of speed. For this I used a ramp-up, so basically after one of them eats a pill they moved again in 1/60th of a second, then I increase the ramp-up by two, so they move again in 3/60th of a second, then in 1/12th of a second, and so on until their delay is back to half a second.

 

Currently I'm working on the part when FlyGuy gets captured. Things need to pause to a few seconds so the player can see what happened, and I'm thinking about some sort of "caught" animation, like most arcade games. Since this is all being written with little planning, I'm doing a lot of rework at this point to make things more logical and flow better, separating out functions and such.

 

So that's where it is at right the moment. Comments, questions, feedback, etc. are welcome and appreciated.

 

Matthew

Link to comment
Share on other sites

Speaking of random number generators, the one I'm using is straight out of Tombstone City and it pretty week. Any suggestions or code examples would be greatly appreciated. I'm looking for code that does not rely on anything in ROM or GROM. The current code I have has an unfortunate side affect of the 2 least-significant bits are always 00, 01, 10, 11 and cycle in that order. I always forget that and it bites me every time I go to get a random number between 0 and 3 (like picking a random direction of up, down, left, right.)

 

Yes, I found the same problem when I did my conversion of TI-Trek, Tombstone's random number generator always does even/odd patterns. I broke it up by using a SRC with an odd value to throw it off.

 

My current random routines in my CRPG are as follows:

* Random number generator (Range in R3, Result in R4)
* Returns 0 to R3-1
RNDNUM DATA VWS,RAND2
RAND2  BL   @RNDGEN
      MOV  @>0006(R13),R3
      INC  R3
      CLR  R4
      DIV  R3,R4
      MOV  R4,@>0008(R13)
      RTWP
* Random number generator 16-bit (Result in R4)
* returns 0-65535
RANDOM DATA VWS,RAND1
RAND1  BL   @RNDGEN
      MOV  R4,@>0008(R13)
      RTWP
* Random number generation
RNDGEN LI   R4,23729
      MPY  @>83C0,R4
      AI   R5,31871
      MOV  @CLOCK,R0
      ANDI R0,>000F
      SRC  R5,0
      MOV  R5,@>83C0
      RT

 

The @CLOCK value is just a word that increments from 0 to 65535. I was using the interrupt counter for this, but you can do it any way you want, so long as a value of some kind that changes periodically is there. I still use @>83C0 for the heck of it, but you could drop that as well if you wanted. The main thing I like is the random SRC which nicely jumbles things.

 

The initial coding consisted of getting the screen initialized and reading the level data to get something on the screen. I'm not using any console routines, i.e. nothing from ROM or GROM, so I had to write it all. I like it this way though, and is one of the reasons for doing things in assembly. Hell, if you are not going to code direct to the hardware, you might as well not use assembly. All the console stuff uses BLWP anyway, which is slow! Plus it uses that GLP workspace stuff which I never learned, and GPL takes huge bites out of the precious scratch pad RAM. So, LIMI 0 and never look back. Something you do lose in doing that is the nice VDP interrupt, but we can work around that by polling, which for a game works just as good (if not better.)

 

Are you referring to the REF utilities like VMBW, KSCAN, and so forth? Yeah, you can write your own of these... KSCAN still would use the SCAN routine, though, unless you circumvent it.

 

The "BLWP is slow" creed is getting increasingly prolific these days. And not for good reasons. Yes, it's slower than using BL... and it's definitely slower than just hitting the VDP ports directly yourself. But there are times you need speed and times you need convenience of access. Try maintaining register contents between 4-5 levels of subroutine calls!

 

For most of my CRPG display work, I build up everything in CPU memory first and then just blast the whole thing to the screen in one go. That saves a LOT of time. I also don't use any subroutines at all, I just hit the VDP port directly. And fortunately, most of my stuff is just to the screen buffer, not the color or pattern buffers.

 

Adamantyr

Link to comment
Share on other sites

Fantastic! Very informative, and I hope you'll continue posts of this detail. The assembler is especially interesting to me, as it's been ages since I've done it and would love to get back into it, and seeing how you convert things makes it quite clear.

 

Sounds like you've made impressive progress all ready. Do put up screenshots when you get to certain milestones as well, please, as visuals can enhance the content. And I like the modifications you are making, such as giving the spiders more autonomy. (Their reactive nature and lack of any discernable AI is mostly down to the 30-line limits, and it does makes the original game a bit more tactical than arcadey. It'll be great to see this play with real arcade behaviours in place.)

 

I'll say again that I'm flattered you thought my game was a worthwhile project, though I don't know if it's quite "a new classic". ;) Looking forward to more of these information-rich posts, and good luck with the game!

Link to comment
Share on other sites

Hmmm... if everything's that fast in assembly, would there maybe be a way to display Fly Guy and the spiders as sprites and move them in 1-pixel steps instead of 8 pixels at a time?

 

Absolutely. That is part of my plan once I get the original functionality working. It is easy to get carried away with features and changes, and then find yourself in the middle of a huge mess and nothing working. Smooth animation will be coming at some point.

 

Matthew

Link to comment
Share on other sites

Adamantyr, thanks for the info and the RNG code. I'll have to mess with that a bit.

 

The "BLWP is slow" creed is getting increasingly prolific these days. And not for good reasons. Yes, it's slower than using BL... and it's definitely slower than just hitting the VDP ports directly yourself. But there are times you need speed and times you need convenience of access. Try maintaining register contents between 4-5 levels of subroutine calls!

 

The problem with BLWP is that it was primarily designed for multi-tasking, which we really don't need to do. TI's primary reason for RAM-based registers was fast context switching in their 990 minicomputer line. It requires (usually) that you also have another 32 bytes for the new workspace, otherwise the call is a waste (just use BL if you BLWP to the same workspace as the current workspace.) And maintaining 4 or 5 workspaces seems more daunting than managing register contents between calls.

 

I used to (a long time ago) look at registers like variables and I tried to say "R3 is such and such" through-out my code. Now I know better and understand that my variables all need a place in RAM (or ROM) and that the registers are just a working set. When you apply the same rules of data use to assembly that you would use in a higher level language, a lot of the problems go away. It all depends on a lot of factors though, the design, the requirements, etc. Every situation is different.

 

For an arcade game, BLWP used without regard can ruin the game. For example, until I really got back into assembly on the TI (about 4 years ago) and learned better, I thought the *way* to clear the screen was like this example:

 

  CLR R0
  LI R1,>2000
LP BLWP @VSBW
  INC R0
  CI R0,768
  JLT LP

 

This is straight from Lottrup's book, and it is probably the most inefficient way to clear the screen in assembly. I have seen plenty of other examples that are similar, and all just as bad. So, it's not that BLWP is bad, it is just that it exists for something that we really don't need, i.e. context switching, and IMHO it is abused more than anything in the 4A.

 

Matthew

Link to comment
Share on other sites

Absolutely. That is part of my plan once I get the original functionality working. It is easy to get carried away with features and changes, and then find yourself in the middle of a huge mess and nothing working. Smooth animation will be coming at some point.

 

Er, how were you going to accomplish that? Replacing them all with sprites? Or using bitmap mode?

 

On the challenging side, if you wrote a sprite rotation system to make sure that every sprite remains visible regardless of the horizontal limitation, that would be quite lovely to see... I don't know of any commercial TI programs that ever tried this, only the Konami MSX platform titles that used the TMS9928A chip.

 

Adamantyr

Link to comment
Share on other sites

The problem with BLWP is that it was primarily designed for multi-tasking, which we really don't need to do. TI's primary reason for RAM-based registers was fast context switching in their 990 minicomputer line. It requires (usually) that you also have another 32 bytes for the new workspace, otherwise the call is a waste (just use BL if you BLWP to the same workspace as the current workspace.) And maintaining 4 or 5 workspaces seems more daunting than managing register contents between calls.

 

Quite true. I only have two workspaces in my CRPG, a primary and an alternate. None of my BLWP calls ever occur on the same layer, so I can reuse the alternate workspace for each one. That's the best way to make use of it, a quick way to get a fresh set of registers, sort of an improved subroutine call. You COULD avoid it by just keeping your data in pre-determined arrays, but that's almost as messy and the trade-off isn't really worth it.

 

I used to (a long time ago) look at registers like variables and I tried to say "R3 is such and such" through-out my code. Now I know better and understand that my variables all need a place in RAM (or ROM) and that the registers are just a working set. When you apply the same rules of data use to assembly that you would use in a higher level language, a lot of the problems go away. It all depends on a lot of factors though, the design, the requirements, etc. Every situation is different.

 

Exactly, every situation is different. What worries me is any budding 99'er programmer may just automatically NOT use BLWP because he keeps reading bad things about it with no context as to why it's bad.

 

This is straight from Lottrup's book, and it is probably the most inefficient way to clear the screen in assembly. I have seen plenty of other examples that are similar, and all just as bad. So, it's not that BLWP is bad, it is just that it exists for something that we really don't need, i.e. context switching, and IMHO it is abused more than anything in the 4A.

 

Yeah, that is a bad clear routine. The one I have in my CRPG is thus:

 

* Clears the entire screen
CLRSCR MOV  @ACTSCR,R0
      ORI  R0,>4000
      SWPB R0
      LI   R1,>2000
      LI   R2,768
      MOVB R0,@VDPWA
      SWPB R0
      MOVB R0,@VDPWA
CS1    MOVB R1,@VDPWD
      DEC  R2
      JNE  CS1
      RT

 

I have two screens, the address of which is stored in @ACTSCR, so this clears the current active screen. VDPWA and VDPWD are, of course, symbolic addresses to the VDP read/write ports in the scratchpad. I could replace those with indirect registers but this routine is fast enough as is. :)

 

However, I also have a video routine that just writes the high-byte content of R1 for R2 times at R0 address called VMBS (Video Multiple Byte Stream). So I could drop my clear routine entirely and replace calls to it with this:

 

      MOV  @ACTSCR,R0
      LI   R1,>2000
      LI   R2,768
      BLWP @VMBS

 

Not as fast, of course, with the BLWP overhead, but the need for speed here is low, and this routine would save me a couple dozen bytes. Hopefully I don't get to a stage where I'm scrimping bytes THAT closely.

 

Adamantyr

Link to comment
Share on other sites

Adamantyr, I think we have had this conversation in the past about the VDP routines. I too developed a VSBW "single byte multiple write" routine that is the same as your VMBS routine. It is a logical progression from VSBW to VMBW and good for initializing blocks of VDP RAM (like clearing the screen.)

 

Also, I'm not sure if you were reading the TI994A list when we determined that the delay for the VDP is unnecessary in the 4A, primarily because reading or writing to the 9918A causes the wait-state circuitry to be in effect. Also, the memory access of the 9900 is just not faster than the 9918A in its worst case scenario. Thus, the SWPB and NOP's you see in typical VDP routines are unnecessary. For raw VDP speed, my routines use the fact that I know the workspace addresses and I skip using SWPB:

 

WRKSP  EQU  >8300             * Workspace
R0LB   EQU  WRKSP+1           * R0 low byte reqd for VDP routines

.
.
.

VSBW   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
      MOVB R1,@VDPWD         * Write byte to VDP RAM
      B    *R11

VMBW   MOVB @R0LB,@VDPWA      * Send low byte of VDP RAM write address
      ORI  R0,>4000          * Set read/write bits 14 and 15 to write (01)
      MOVB R0,@VDPWA         * Send high byte of VDP RAM write address
VMBWLP MOVB *R1+,@VDPWD       * Write byte to VDP RAM
      DEC  R2                * Byte counter
      JNE  VMBWLP            * Check if done
      B    *R11

 

Slightly faster, but it probably does not matter since your SWPB does not happen inside any loop. I used to have an ANDI R0,>3FFF in there to restore R0, but decided I didn't care and if the calling routine wanted to preserve the original value in R0 that I would just do the ANDI at that point.

 

Matthew

Link to comment
Share on other sites

Er, how were you going to accomplish that? Replacing them all with sprites? Or using bitmap mode?

 

On the challenging side, if you wrote a sprite rotation system to make sure that every sprite remains visible regardless of the horizontal limitation, that would be quite lovely to see... I don't know of any commercial TI programs that ever tried this, only the Konami MSX platform titles that used the TMS9928A chip.

 

Adamantyr

 

Well, without knowledge of where the horizontal trace is, I don't know how you would do sprite rotation. Or I don't understand what you mean by "rotation".

 

FlyGuy will at least be a sprite, and maybe I'll convert the spiders to sprites when they move. Since the graphics are all 8x8 in this game, it is also possible to stagger sprites so that they are on different lines yet appear in the same locations. I have not really thought about solving that problem yet though. I'll have to go see what the MSX guys came up with since they really push the limits of the 9918A.

 

Matthew

Link to comment
Share on other sites

Well, without knowledge of where the horizontal trace is, I don't know how you would do sprite rotation. Or I don't understand what you mean by "rotation".

 

Basically, a sprite management engine that rotates which sprites are in positions 1-4 so that a sprite has at least 1 frame of visibility. You would just swap around the 4-bytes of sprite attribute data for a period of time.

 

The main challenge of such an engine is that it introduces a new layer of abstraction. Your FlyGuy sprite, for example, may not always be sprite #1, you would have to use some kind of ID table to store which sprite is the FlyGuy at any given time.

 

A good engine can even expand the number of sprites. The max number of sprites is 32, but if you rotate sprite data in and out, then it's however many the engine can handle. (Although to be honest, I can't foresee a game where I really need to have more than 32.)

 

Adamantyr

Link to comment
Share on other sites

Also, I'm not sure if you were reading the TI994A list when we determined that the delay for the VDP is unnecessary in the 4A, primarily because reading or writing to the 9918A causes the wait-state circuitry to be in effect. Also, the memory access of the 9900 is just not faster than the 9918A in its worst case scenario. Thus, the SWPB and NOP's you see in typical VDP routines are unnecessary. For raw VDP speed, my routines use the fact that I know the workspace addresses and I skip using SWPB:

 

Interesting, I'll have to remove the NOP and SWPB code from my utilities. It won't make a huge difference in speed, but if it scrapes up some spare bytes, so much the better. Like yourself, I always know where my workspace is as well, so I can replace it with static memory calls.

 

Adamantyr

Link to comment
Share on other sites

Hmm. I guess I don't know how long a sprite currently gets a frame when you have more than 4 on a line. From what I remember of things like munchman and all, the 5th sprite will "flicker", but now that I think about it I don't even know how that is possible since the VDP won't even process the 5th sprite.

 

The VDP draws 60 frames/sec, and you need at least 30 FPS to get decent animation, so even with rotation of all 4 top priority sprites, you only get 8 on a line at 30FPS.

 

Frame 1 1-4

Frame 2 5-8

Frame 3 9-12

Frame 4 13-16

Frame 5 17-20

Frame 6 21-24

Frame 7 25-28

Frame 8 29-32

 

60FPS / 8 groups == 7FPS per group. I don't think that would be an acceptable frame rate per sprite. However, with a 1-pixel stagger, i.e. you would have 16x15 pixel sprites, and rotating sprites 1-4, you could get an "apparent" 16 sprites on a line at 30FPS.

 

I'll have to mess with that a little and see what 30FPS looks like for a sprite.

 

Matthew

Link to comment
Share on other sites

Okay, the code is stable again so I figured I would take the opportunity to make a screen shot. A few things have changed, probably the most notable is the addition of a timer. I'm not sure why I added it, maybe because most classic games have a timer, and I'm not sure if it will just be a bonus to the score at the end of the level or if FlyGuy will bite the dust if it reaches zero.

 

Other than that, I just moved things around a little and added a fontset. The score is on the top left, title in the middle, timer on the top right. The bottom left shows the current level and the bottom right shows the number of lives.

 

I'm currently adding code to do something once FlyGuy is eaten. What you can't see is how fast the spiders move and eat up your pills! So far I only get about 5 or 6 per level!

 

Matthew

post-24952-1268110054_thumb.png

Link to comment
Share on other sites

Though not a big deal for most of us, be aware that direct polling will not work on the TI's bigger brother, the Geneve. On the plus side, the Geneve joystick port is 9901-driven, just different CRU base and IO bits. The keyboard is not CRU-driven, being that it is a byte-wise device. If/when you post the source, and if I have time, I'll give it a whirl and send you the changes needed in case you wish to add compatibility. ;) So much fun stuff to try, so little time...

 

      LI   R1,>0600          * Keyboard column 6 (joystick 1)
      LI   R12,>0024         * Set the CRU port
      LDCR R1,3              * Load the CRU, setting the column latch
.
.
.
* Test the joystick
      LI   R12,>0006         * Base CRU address for joystick 1
      TB   1                 * Left

 

That's it! After the TB command, you can use JNE or JEQ to do whatever you need. For Right you use TB 2, for Down use TB 3, for Up use TB 4. Fire would be TB 0. Now how simple is that!?

 

Link to comment
Share on other sites

Cool thread !

 

Hmmm... if everything's that fast in assembly, would there maybe be a way to display Fly Guy and the spiders as sprites and move them in 1-pixel steps instead of 8 pixels at a time?

Absolutely. That is part of my plan once I get the original functionality working. It is easy to get carried away with features and changes, and then find yourself in the middle of a huge mess and nothing working. Smooth animation will be coming at some point.

 

Matthew

One has to remember the 4 sprite horizontal hardware limitation.

 

I guess the fly would take one sprite, so the spiders would have to agree not to be more than 3 overlapping any pixels horizontally. That’s not all impossible to control. Hehe, I like the idea of a spider getting stuck in an alley.

 

One could go for controlled rotation of sprite priorities, that would make “overlapping” pixels flash. One could use “soft sprites”, that’s moving patterns within characters, but some color clash or bits of disappearing track would have to occur.

 

:cool:

Link to comment
Share on other sites

Excellent stuff!

 

Oh, and for what it is worth, you might want to take a peek SPECTRA (shameless plug :D) as it offers some routines for task scheduling, handling sprites, etc.

 

Anyway, great to see a new assembler game in development!

.. and what is even better hope to see this as a full tutorial ;)

Edited by retroclouds
Link to comment
Share on other sites

Layout your sprite list in RAM and access it there (sprite #5 is always sprite #5). The routine for pushing the list into VDP could do the rotate while moving. One could decide not to rotate the main player sprite. Having the sprite list available in RAM is actually rather convenient (unless you plan to work with ScrathPad only).

 

Since the hardware can display "only" 4 sprites horizontally, the worst case scenario is all 32 sprites on the same lines. All sprites would display once every eighth frame (4*8=32).

 

The sprite list in VDP will always display sprite #1, #2, #3 and #4. So we have to pop all sprites to these locations. To get an even spread of priority, one would have 4 barrels rotating. One barrel being #1, #5, #9, #13, #17, #21, #25 and #29. One rotate could look like this #29, #1, #5, #9, #13, #17, #21 and #25. Careful layout of sprite list in RAM to accommodate for this can speed up the move/rotate bit.

 

:cool:

Link to comment
Share on other sites

Speaking of random number generators, the one I'm using is straight out of Tombstone City and it pretty week. Any suggestions or code examples would be greatly appreciated. I'm looking for code that does not rely on anything in ROM or GROM. The current code I have has an unfortunate side affect of the 2 least-significant bits are always 00, 01, 10, 11 and cycle in that order. I always forget that and it bites me every time I go to get a random number between 0 and 3 (like picking a random direction of up, down, left, right.)

This is the one I've used for years. It's a loose copy of the one I did in the early eighties. Not pretty as such, but it served me good.

 

; *** RAMDOM GENERATOR ***
; R8, R9 and R13 are reserved
; R9 holds random number and can be read
; uses/reads ROM located at >0000->1fff
; call this routine many times while waiting for user to press any key

rand	ai	r13,>8761	; add to extra seed
a	r13,r9		; add extra seed to seed
ai	r9,>3456	; add to seed
mov	r9,r8		; copy seed
andi	r8,>1fff	; adjust to ROM
a	*r8,r9		; add ROM to seed
rt

:cool:

Link to comment
Share on other sites

Sprite rotation is pretty easy, like Sometimes suggested, layout your sprite table in CPU memory, and do your updates there. The simplest routine is just a little function that runs on the user interrupt and copies the sprite table to VDP. For my Coleco work, I just rotate the start point of the copy by four sprites each frame.. that way the worst case is one in 8 frames (which doesn't look that good on its own.. if you can use fewer sprites it's better. 16 sprites gives a worst case of 1 in 4, which isn't bad).

 

To do an offset copy like that, you just set the VDP address, copy from the start point to the end of the table, then copy from the beginning back to your start point. So, in the first frame:

 

-Set VDP address

-Copy sprite entries 0-31

-Nothing left to copy

-add 4 sprites (16 bytes) to start point

 

Second frame:

 

-Set VDP address

-Copy sprite entries 4-31

-Copy sprite entries 0-3

-add 4 sprites (16 bytes) to start point

 

Third frame:

 

-Set VDP address

-Copy sprite entries 8-31

-Copy sprite entries 0-7

-add 4 sprites (16 bytes) to start point

 

... etc.. looping when you hit the end of the table. It's a simple and effect rotation. If you have multicolor sprite characters, and space them out nicely, you can have some part of them visible most of the time even in the worst case.

Link to comment
Share on other sites

Looking great, I like the changes. The timer has potential as well. Perhaps it could act like the timer in old classics like Bubble Bobble - when it runs out, the enemies go into "hyper" mode? In this case, the spiders could either move faster, or more intelligently hunt down the player. (A color change signalling this new state is useful too.) Alternately, when it runs out, a new enemy could appear that mercilessly pursues the player, like Baron von Blubba from Bubble Bobble.

 

These are just ideas. I fully endorse you owning the structure of your version of the game so definitely do what you want to do with it. In the meantime, these posts are proving very enlightening, and I'll be researching all these acronyms so that I can better understand the discussion. Good work!

Link to comment
Share on other sites

I like that "hyper" idea. When the timer runs out the spiders could become "enraged" and the AI could change from random movement to pursuit. Seems logical anyway, since every time my kitchen timer goes off I'm attacked by all the spiders in my house. :-)

 

By the way, what acronyms are you not sure about?

 

Matthew

Link to comment
Share on other sites

Sprite rotation is pretty easy, like Sometimes suggested, layout your sprite table in CPU memory, and do your updates there. The simplest routine is just a little function that runs on the user interrupt and copies the sprite table to VDP.

 

Actually the simplest solution is to just use Classic99 since I know you ignore the 4 sprite limit. ;-) Keeping the sprite list in RAM and rotating through it does seem to be the easiest and more practical given the limits of the machine. Thanks for the info guys, now I don't have to work it out myself, I just have to code it. :-)

 

Matthew

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...