No wonder it took 3 years to finish Robo-Ninja.
That being said, I'm feeling a bit stuck on Atari Anguna. I haven't been doing much, because when I do, I've been working on my semi-random enemy spawning routines. And I haven't been happy with it.
The issue is that I don't want enemies spawning stuck in walls, which means:
1. I can go completely random on spawns, but then if it's a collision, try again.
1b. To check for a collision, I either have to do the math to figure out if I've collided with a wall (which on the Atari is non-trivial due to the crazy arrangement of the playfield registers) or
1c. Draw the enemy once, check the collision registers, and then move (which means each try eats up a frame, which could be slow and look really tacky)
2. Instead, I could designate "safe spawn" areas in each room layout.
2b. Depending on how I do this, it could eat up quite a few bytes per room. (I potentially need at least 3 safe spots per room. This could be 6 bytes per room, more than I want to spend)
2c. I could cut down on rom usage by having a lookup table of safe spawns, the question is how this would work, and doing the randomization and lookup in a way that's fast and compact.
I spent WAY too long thinking through these, testing ideas, etc, and not being happy with the results. Which made me less interested in working on the game, and thus slower progress.
Finally I have a solution that I think I'm happy with, and seems to be mostly working. I build a lookup table of 8 potential spawn points (16 bytes for the lookup table). Then each room has a byte to tell which of these potential spawns are allowed for each room layout -- a 1 means it's safe to spawn in. Then I pick a random number between 1 and 8, check that bit. If it's not safe, try the next. Once I get a safe bit, spawn an enemy at the position that the bit refers to. Then if there are more enemies to spawn, I just check the next consecutive bits (instead of picking more random numbers, and risking enemies spawning in the same position as each other) until I've spawned them all.
It seems to work, doesn't require much storage, and is relatively fast.
For any 6502 assembly nerds, here's the code I used. You can probably spot all the places that I'm doing things really badly (I end up wasting a lot of cycles saving and loading values -- it seems I just don't have enough registers to track everything I need and still use X and Y for indexed loads (Why oh why didn't the 6502 have TXY and TYX opcodes to move values between X and Y directly?))
LoadPositionsFromSafeSpawnsImpl SUBROUTINE ;TempPF2 should be the number of enemies we want to spawn ;TempPF2 then holds the number of enemies we want to spawn - 1
dec TempPF2 ;load the room index ldy #0 lda (RoomDef),Y tax ;x now has room index ;Store #1 in TempPF1 to bit against.. lda #1 sta TempPF1 ;get random number between 0 and 7 lda Rand and #%00000111 tay iny ;a has safe spawns lda SafeSpawns,X ;rotate the safe spawn based on the random number sty Temp ldx Temp .keepRotating cmp #$80 rol dex bne .keepRotating ldx Temp ;now work our way through seeing if it's safe .checkSpawnLoop ;if it's safe, go to useThisValue (of x) bit TempPF1 bne .useThisValue .beforeRotation ;if not, rotate again, increase X cmp #$80 rol inx ; if X equals start, fail. What then? cpx Temp beq .useThisValue ; if X equals 8, set to 0 cpx #8 bne .checkSpawnLoop ldx #0 jmp .checkSpawnLoop .useThisValue pha ldy TempPF2 ; otherwise, look up X and Y in table lda SpawnsX,X sta Enemy0X,Y lda SpawnsY,X sta Enemy0Y,Y pla dec TempPF2 bmi .allDone jmp .beforeRotation .allDone
No comments:
Post a Comment