Jump to content
IGNORED

Parallax Starfield - demo released


sometimes99er

Recommended Posts

parac.bin

 

 

 


- - - - -


In the footsteps of managing 32 sprites, without more than 4 sprites per horizontal line, as explored in Bubbles (demo).

If you keep a number of sprites stacked vertically (no vertical overlap), they will only occupy 1 of the 4 sprites allowed horizontally.

In Bubbles I made each stack move up with their own individual speed. This time I will try and move the 4 stacks in any direction.

Here's a quick setup of 4 stacks.

parallax.starfield.0001.png

Oh, and instead of having 8 sprites in each stack (or plane), it's 6 + 7 + 9 + 10 = 32 sprites. The number of stars close to you are less than those far away. ;)

 

 

Edited by sometimes99er
Cartridge made V9938/58 friendly.
  • Like 5
Link to comment
Share on other sites

parallax.starfield.0002.png

This has the vertical position slightly adjusted to get a better initial distribution.

The different sized stars are evenly distributed vertically. The 6 biggest stars separated by 192 / 6 = 32 pixels. The smallest by 192 / 10 = roughly 19 pixels.

parallax.starfield.0003.png

Tursi supplied me with an excellent random number routine here. And I've been using it ever since. Distributing stars horizontally and randomly did however expose a weakness. Or maybe I'm doing something wrong.

The numbers may seem random, but the bit manipulation appear weak.

Generally I'd like to use fixed seeds to populate scenarios ( procedural level generation *) ). Then I know what is presented instead of getting perhaps somewhat "strange effects" from more random generation.

Manually I changed the fixed seed about 20 times, and did find one with a good initial appearance. It still has exponential growth telltale though.

parallax.starfield.0004.png

*)
1982 Pitfall for the Atari 2600: Innovative techniques were used to keep the code space within the 4K limit, including a polynomial counter to create 256 screens within 50 bytes of code.

 

Edited by sometimes99er
  • Like 1
Link to comment
Share on other sites

Yeah, I've had issues with the randomness of that algorithm at times as well. But I've often got better results when I don't use the entire word by taking bits from the middle of the word instead of the least significant ones. Might have been my imagination but I liked the results more. ;)

  • Like 1
Link to comment
Share on other sites

Stars

 

Apart from the 32 hardware sprites, I'm adding 16 software sprites. That's 48 sprites in 5 planes.

 

Randomness

 

I tried to shift the random number 3, 4 and 5 places to the left, but still there's traces of exponential growth. More or less. Tried up to 10 different initial seeds without being fully satisfied. Now, I simply preset all sprites.

 

parallax.starfield.0005.png

 

To do list

 

Splash screen.

Music.

Joystick input and movement of planes.

Adjustments. Colors etc.

 

;)

 

Edited by sometimes99er
  • Like 2
Link to comment
Share on other sites

You might have better luck using the modulo operation on the values from your PRNG to determine x/y positions. Its more complex that a simple shift and bitwise AND, but you should get better results.

 

I did not use modulo operations (DIV), since y-positions are not random and x-positions use a width of 256 pixels. Using DIV with the random routine to get a range is however creme de la creme.

 

Dividing the random word (range >0000 to >FFFF) by 256 returns the low byte as remainder, so no gain in some cases (power of 2).

 

DIV may be a key ingredient to better random number generation. Simply adding contents from console ROM (same for all TI-99/4A) may be a quick and dirty fix.

 

Somehow I think I may have to control randomness in my Restless II game to avoid a screen full of aliens all starting out in the same corner. I did some controlling in my Snake Plissken game. With my Minesweeper game you could get all the bombs in one corner, though the random routine is probably not that random.

 

;)

Edited by sometimes99er
Link to comment
Share on other sites

I do not know whether the PRNG I use in fbForth (same as TI Forth) is any better than what you are using, but here it is:

 

The _RNDW function first retrieves the seed from >83C0. Then the result of seed * 6FE5h + 7AB9h is shifted right circularly by 5 bits. This result is stored at >83C0 as the new seed and also returned to the user:
_RNDW  LI   R0,>6FE5       calculate new pseudo-random
       MPY  @>83C0,R0      seed * >6FE5
       AI   R1,>7AB9       seed * >6FE5 + >7AB9
       SRC  R1,5           (seed * >6FE5 + >7AB9) SRC 5
       MOV  R1,@>83C0      store new seed
       B    *R11           return to caller
 
The _RND function returns a positive integer less than the number supplied to it, which includes 0. _RND invokes _RNDW , takes the absolute value of the result and divides by the user-supplied number and returns the remainder. Obviously, for your case, retrieving only the LSB would be a much more economical operation than division:
_RND   MOV  R11,R15        save return
       BL   @_RNDW         get random number into R1
       ABS  R1             force positive
       CLR  R0             set up for division
       DIV  *R9,R0         divide number in R0--R1 by num on stack
       MOV  R1,*R9         return remainder on stack
       B    *R15           return to Forth inner interpreter

 

The above done your way:

RND256 MOV  R11,R15        save return
       BL   @_RNDW         get random number into R1
       ANDI R1,255         extract right byte (MOD 256)
       MOV  R1,*R9         return remainder on stack
       B    *R15           return to Forth inner interpreter

 

I have puzzled over changing RND for fbForth 2.0 to not do the ABS and to just return all but the MSb, i.e., leftmost bit. I should compare the distribution of pseudo-random numbers done both ways, I suppose.

 

One final note, randomizing the seed in both TI Forth and fbForth counts from zero until a VDP interrupt is detected. Though it sets up a race condition, I have never had any trouble with it:
_RNDMZ MOVB @>8802,R0      get VDP status byte
       CLR  R0             discard it
       CLR  R1             clear counter
RZLOOP INC  R1             increment counter
       MOVB @>8802,R0      get VDP status byte
       ANDI R0,>8000       VDP interrupt?
       JEQ  RZLOOP         no, increment counter
       MOV  R1,@>83C0      yes, store new seed
       B    *R11           return to caller
 
...lee

 

  • Like 2
Link to comment
Share on other sites

The _RNDW function first retrieves the seed from >83C0. Then the result of seed * 6FE5h + 7AB9h is shifted right circularly by 5 bits.

 

The _RND function returns a positive integer less than the number supplied to it, which includes 0. _RND invokes _RNDW , takes the absolute value of the result and divides by the user-supplied number and returns the remainder.

 

Excellent. At least I can switch to something else if there's a problem with patterns. Also found that Tursi's random function is the same as in Spectra2 from retroclouds. retroclouds avoids zero by adding one, while Tursi requires the seed to be anything but zero.

 

I have puzzled over changing RND for fbForth 2.0 to not do the ABS and to just return all but the MSb, i.e., leftmost bit. I should compare the distribution of pseudo-random numbers done both ways, I suppose.

 

Interesting. I have to get hold of my old homemade random routine, document it and run all through a few obvious tests.

 

One final note, randomizing the seed in both TI Forth and fbForth counts from zero until a VDP interrupt is detected. Though it sets up a race condition, I have never had any trouble with it:

 

Nice one. I'm a bit worried about emulators that could fail somewhat on that (with whatever conditions you have there). Adding it to the "system seed" (>83C0) should be more safe. And perhaps adding or solely relying on a race at a splash screen until the user hits Enter (insuring that the user is not already holding Enter before the race begins).

 

:)

Edited by sometimes99er
  • Like 1
Link to comment
Share on other sites

For what it's worth, my routine is not mine, but borrowed from a fellow named Albert Veli who shared it with the Dreamcast homebrew group (and I thought he attributed some book before him ;) ). One of its properties is that it never returns 0 (and also you can never seed with 0, it gets stuck)... another is that it returns /all/ values in the sequence before repeating. So for 16 bit numbers, you will see each value from 1 to 65535 in a pseudo-random order before they repeat -- but this is obviously not the same as each element being a random /number/ - for that you'd expect a duplicate to at least be possible. :)

 

Psuedo-randomness is of course a huge field of research, but I've always been happy with simple enough, so I can't say much more about it. ;)

Edited by Tursi
  • Like 1
Link to comment
Share on other sites

And perhaps adding or solely relying on a race at a splash screen until the user hits Enter (insuring that the user is not already holding Enter before the race begins).

 

And then I'm worried about testing Enter directly (CRU) and constantly in a race on real hardware.

 

The rather long time delay in the SCAN is invoked every time a key is pressed or released. If you hold a key, this time delay is not called. SCAN is the console ROM keyboard scanning routine. The loop counter in the delay (@>0498) is a staggering 1250.

 

The interesting part is, that even though SCAN is run and all the lines are tested, the delay only runs when there's a new key state. I guess that's how they thought one could debounce. Since Enter is not used on the menu selection screen, and it's on different lines and columns, I think direct hits without delays could be okay.

 

Within my games and demos, I test all keys of interest (CRU) once per frame update.

 

:)

Edited by sometimes99er
Link to comment
Share on other sites

For what it's worth, my routine is not mine, but borrowed from a fellow named Albert Veli who shared it with the Dreamcast homebrew group (and I thought he attributed some book before him ;) ).

 

Thought so. We could call it the B4 for now. ;)

 

One of its properties is that it never returns 0 (and also you can never seed with 0, it gets stuck)... another is that it returns /all/ values in the sequence before repeating.

 

Excellent properties. It's short. It's quick (no DIV). Takes almost no space. With the long insured range, you can seed at a certain point and do "procedural level generation". You can separate level progress and other random stuff (like aliens attacking) with the same routine but handling different seeds. Later the same level could have another attack formation.

 

So for 16 bit numbers, you will see each value from 1 to 65535 in a pseudo-random order before they repeat -- but this is obviously not the same as each element being a random /number/ - for that you'd expect a duplicate to at least be possible. :)

 

With most use cases the return range would probably not be the full range. Rolling a dice should be safe / random. Wider ranges like screen height and width is something I'll include in future tests.

 

Psuedo-randomness is of course a huge field of research, but I've always been happy with simple enough, so I can't say much more about it. ;)

 

Yeah, it's interesting but perhaps also a time consuming detour.

 

;)

Link to comment
Share on other sites

Thought so. We could call it the B4 for now. ;)

Hehe, but that would be limiting - the mask depends on how many bits you want out of it. I found the original message, the source was the book "Graphic Gems".

 

For different widths just use a different mask. Below is a table of masks
and widths.

Mask (hex)	Width (bits)
03		2 
06		3
0C		4
14		5
30		6
60		7
B8		8
110		9
240		10
500		11
CA0		12
1B00		13
3500		14
6000		15
B400		16
12000		17
20400		18
72000		19
90000		20
140000		21
300000		22
400000		23
D80000		24
1200000		25
3880000		26
7200000		27
9000000		28
14000000	29
32800000	30
48000000	31
A3000000	32
Very handy little function :)
  • Like 1
Link to comment
Share on other sites

Hehe, but that would be limiting - the mask depends on how many bits you want out of it. I found the original message, the source was the book "Graphic Gems".

 

Yes, that might be handy. Repeating series of random numbers with a length of the power of 2 minus 1. Also you can reset seed after a certain count (to have any length of random numbers).

 

;)

 

Link to comment
Share on other sites

I tried plotting random points on a 256x192 screen using Tursi's and Lee's algorithms in Java. I plotted 1000 points, then cleared the screen and looped.

 

Tursi's "Graphic Gems" algorithm is just displaying two tilted lines across the screen, which is not very random unless I programmed it wrong in Java (which is very likely).

    public int randTursi() {
        boolean carry = (randNo & 1) != 0;
        randNo = (randNo >> 1) & 0xFFFF;
        if (carry) {
            randNo ^= 0xB400;
        }
        return randNo;
    } 

Lee's is a lot better, but changing the shift from 5 to 7 makes it look truly random, like static on a TV screen, and very similar to Java's own algorithm.

These are just screenshots of a single frame. It's better to judge the randomness from multiple frames, but it should be clear what I mean.

 

post-35226-0-74606300-1481752962_thumb.png

Tursi

 

post-35226-0-47948700-1481752969_thumb.png

Lee shift 5

 

post-35226-0-36270500-1481752976_thumb.png

Lee shift 7

 

post-35226-0-15517900-1481752985_thumb.png

Java

 

EDIT: Note that I only used the lower 8 bits to plot the points.

  • Like 2
Link to comment
Share on other sites

Since the routine was originally (supposedly) used to do a pseudo-random pixel dissolve, I suspected you did have an error in your conversion. The shift should be handled as unsigned, so the 0xffff mask is correct (although I don't understand why it was necessary? Right shift shouldn't be adding any higher bits...)

 

I ported the routine to Blassic as a quick test... and I get much the same result as you - pretty much a line. I've never really tested the output of that function before, I find that pretty interesting. ;)

 

I may need to switch over to your tweaked version of Lee's myself... ;)

  • Like 2
Link to comment
Share on other sites

Tursi's "Graphic Gems" algorithm is just displaying two tilted lines across the screen, which is not very random ...

 

Very nice.

 

I thought something wrong since I previously got lines of exponential growth instead of straight lines. But I do get the same result (lines but not exactly the same). This is pretty revealing. Major blow. Good catch. Not saying that this random routine is no good, but one has to be careful and watch out for unwanted patterns. And filling the screen with random dots is something we tend to do from time to time.

.setPixel(rand(256), rand(192),

 

parallax.starfield.0006.gif

 

My distribution was not alternating the use of the random numbers for x- and y-coordinates. I only used it for the x-coordinates.

 

.setPixel(rand(256), i++,

 

parallax.starfield.0007.gif

 

Exponential growth is evident, but not as significant as when I tried multiple seeds to get something that looked like random stars. Some seeds were better than others.

 

Again, I can't believe the difference. Maybe I'm doing something wrong.

 

:)

Edited by sometimes99er
  • Like 1
Link to comment
Share on other sites

I double-checked Tursi's implementation (outputting and filling 24K RAM from 8K ROM and dumping RAM (F10)) against my and Rasmus' implementation, and they're all returning the same numbers. So that's not explaining why I get more lines than Rasmus. I tried different variations, and ended up tilting the lines to get the same result.

 

.setPixel(rand(256), rand(256)*256/341,

 

parallax.starfield.0009.png

 

 

Link to comment
Share on other sites

And here's the demo.

You're looking out into space from your little spaceship. Turn the ship in any direction using your joystick controller.

:)


 

Stars move in 5 planes. 4 planes of stacked hardware sprites and 1 plane of software sprites. The planes has 6, 7, 9, 10 and 16 stars.

 

 

Also ...

Respecting the original TMS9918A limitation of only 4 sprites per horizontal raster line.

No need for 32K RAM Memory Expansion.

Fits the FlashROM99.

Label in post #29.

Edited by sometimes99er
  • Like 7
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...