Retrofan Posted January 6, 2008 Share Posted January 6, 2008 Not yet! Dino Eggs is a very hot candidate though! I'll post a "Brainstorming" thread sometime soon, maybe someone has a good idea for Dino Moms foot You know my Dino Eggs mockup, don't you? (more at http://www.haddewig.de/vcs/index.html) Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 6, 2008 Author Share Posted January 6, 2008 You know my Dino Eggs mockup, don't you? Yup, I had planned to hija... I mean bump it Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 7, 2008 Share Posted January 7, 2008 (edited) I removed Robotron and Zoo Keeper from the list, since they're not doable anyway Any other mockups still in the works? I worked up this Robotron SC mockup over the last week... RobotronSC20080106.bin Can shoot with trigger or 2nd joystick. Press RESET to get a different opening screen. Don't hold down RESET, though, or it will crash. Actually, it might crash anyway. And it's kinda jittery. But the basic concept is there. Even without any optimizations, you have a 40x18 playfield with up to 70 enemies (moving independently), collision detection, and, at top speed, they move plenty fast! This binary supports 70 enemies, though you could go higher with some optimization - the limiting factor is CPU time. There is some free kernel time, but to make a real Robotron clone you'd almost certainly need to go to a narrower playfield and use some repositioned sprites for other objects. Right now half the enemies move about every 16 frames, the other half more every 32 frames. They can all move at the same speed, but it looks more interesting if they don't. They could move as fast as every 9 frames. You could probably do this on a regular cart - with 128 bytes RAM - but you'd need to narrow the playfield and all the PF enemies would have to move at the same speed. Plus it might be pretty tight for CPU cycles, since you'd need to move all enemies in a single frame. With the extra SC RAM, I am buffering the screen memory and storing Enemy X/Y coords separately. That way I can move enemies over several frames (and move them independently of their position) and then do a (relatively) fast copy to screen memory in a single frame. Edited January 7, 2008 by vdub_bobby Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 7, 2008 Author Share Posted January 7, 2008 Looks very promising Hm... for a better effect you could probably add a check so that a Grunt won't enter a field that's already occupied, else they're all transmogrified into one after a while Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 7, 2008 Share Posted January 7, 2008 Looks very promising Hm... for a better effect you could probably add a check so that a Grunt won't enter a field that's already occupied, else they're all transmogrified into one after a while Yeah, I know. But Al said that they can overlap, so I let it slide. All grunts are tracked separately, so if you gave them different movement routines they will separate. You can see some of that. Quote Link to comment Share on other sites More sharing options...
ZylonBane Posted January 7, 2008 Share Posted January 7, 2008 If you don't allow them to overlap, you can theoretically (but wouldn't really want to) have as many Grunts as there are playfield pixels. Every frame just scan some subset of the playfield and move any active pixels toward the player. Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 8, 2008 Author Share Posted January 8, 2008 If you don't allow them to overlap, you can theoretically (but wouldn't really want to) have as many Grunts as there are playfield pixels. Every frame just scan some subset of the playfield and move any active pixels toward the player. Sounds cool. That way you need neither to double buffer the screen, nor extra storage of the coords. There you go Bob, fits in 128 Bytes now! Regarding overlap, I don't think it makes sense with the playfield approach. Visually it's just not plausible. On the arcade you see them overlap, but a playfield pixel just disappears from view. Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 8, 2008 Share Posted January 8, 2008 If you don't allow them to overlap, you can theoretically (but wouldn't really want to) have as many Grunts as there are playfield pixels. Every frame just scan some subset of the playfield and move any active pixels toward the player. Sounds cool. That way you need neither to double buffer the screen, nor extra storage of the coords. There you go Bob, fits in 128 Bytes now! Actually, I think you could possibly do the whole screen in one pass if it was in ZP RAM. Quote Link to comment Share on other sites More sharing options...
ZylonBane Posted January 8, 2008 Share Posted January 8, 2008 Perhaps, but you really wouldn't want to. A significant chunk of vblank cycles would also be needed for a good intelligent flicker routine for the sprite-based enemies. Also, if memory serves, even the Grunts in arcade Robotron don't all move at once. They kind of "ripple" toward the player, being moved only a few at a time. The 2600 playfield is so chunky that scanning just one row per frame would probably give a good movement rate. With an 18-row display, that would allow a single unimpeded Grunt to move a little more than three times a second. I think this matches the arcade movement rate reasonably well. The playfield scanning, if done, shouldn't be simply top-to-bottom, left-to-right. That would look far too orderly, and bias movement of large Grunt masses in certain directions. It would probably be best to use some sort of interleaved row order, and alternate scanning left-to-right with right-to-left. This would also eliminate the need to double-buffer the playfield, since processing adjacent rows on the same pass could allow Grunts to move twice in one frame. Another option would be a context-sensitive scanning routine that selects a scan direction depending on where the player is. For example, imagine a solid row of Grunts on the left, and the player on the right. If the scan routine starts at the left, the first Grunt will attempt to move toward the player, but be blocked by the Grunt to his right, and so on down the line so that only the rightmost Grunt can actually move on that frame. On the other hand, if the scan routine started at the right, the rightmost Grunt would move first, clearing a space for the Grunt to his left, who would then also be able to move, and so on down the line so that every Grunt can move on that frame. Both behaviors are viable, the deciding factor would be how fast the author wants the Grunts to swarm the player. It might also be a good idea to disallow diagonal movement. This sort of display would look jumpy enough as is, without allowing Grunts to "teleport" along both axes at once. Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 8, 2008 Share Posted January 8, 2008 Perhaps, but you really wouldn't want to. A significant chunk of vblank cycles would also be needed for a good intelligent flicker routine for the sprite-based enemies. Also, if memory serves, even the Grunts in arcade Robotron don't all move at once. They kind of "ripple" toward the player, being moved only a few at a time. The 2600 playfield is so chunky that scanning just one row per frame would probably give a good movement rate. With an 18-row display, that would allow a single unimpeded Grunt to move a little more than three times a second. I think this matches the arcade movement rate reasonably well. The playfield scanning, if done, shouldn't be simply top-to-bottom, left-to-right. That would look far too orderly, and bias movement of large Grunt masses in certain directions. It would probably be best to use some sort of interleaved row order, and alternate scanning left-to-right with right-to-left. This would also eliminate the need to double-buffer the playfield, since processing adjacent rows on the same pass could allow Grunts to move twice in one frame. Another option would be a context-sensitive scanning routine that selects a scan direction depending on where the player is. For example, imagine a solid row of Grunts on the left, and the player on the right. If the scan routine starts at the left, the first Grunt will attempt to move toward the player, but be blocked by the Grunt to his right, and so on down the line so that only the rightmost Grunt can actually move on that frame. On the other hand, if the scan routine started at the right, the rightmost Grunt would move first, clearing a space for the Grunt to his left, who would then also be able to move, and so on down the line so that every Grunt can move on that frame. Both behaviors are viable, the deciding factor would be how fast the author wants the Grunts to swarm the player. It might also be a good idea to disallow diagonal movement. This sort of display would look jumpy enough as is, without allowing Grunts to "teleport" along both axes at once. An intelligent flicker routine takes a while, but it doesn't take that long. If I did do this, no way would I spread out the PF-update routine over 9+ frames! Spreading things over multiple frames has its own set of problems. And besides, if you have a narrow playfield, and 16-18 rows, you're talking 64+ bytes for the playfield alone; that doesn't leave a ton of RAM for a million other sprites. Lots of good ideas, though. Quote Link to comment Share on other sites More sharing options...
ZylonBane Posted January 8, 2008 Share Posted January 8, 2008 If I did do this, no way would I spread out the PF-update routine over 9+ frames! I may not have explained myself clearly. I'm not suggesting that the playfield would be static for some large number of frames, then update all at once. I'm suggesting that the playfield by updated progressively, a little bit each frame. This would have several benefits-- 1. Reduce per-frame cycle cost 2. Make the Grunt movement appear more organic, as they wouldn't all move simultaneously 3. Eliminate the need to double-buffer the playfield Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 8, 2008 Share Posted January 8, 2008 If I did do this, no way would I spread out the PF-update routine over 9+ frames! I may not have explained myself clearly. I'm not suggesting that the playfield would be static for some large number of frames, then update all at once. I'm suggesting that the playfield by updated progressively, a little bit each frame. This would have several benefits-- 1. Reduce per-frame cycle cost 2. Make the Grunt movement appear more organic, as they wouldn't all move simultaneously 3. Eliminate the need to double-buffer the playfield Nah, I understood what you were saying. Spreading things over 8+ frames is just overkill. I'd say 2-4 frames max. Directly manipulating the playfield would be complicated enough as it is, spreading the process over lots of frames would just increase that unnecessarily. All my opinion, of course. Quote Link to comment Share on other sites More sharing options...
Gorf Posted January 8, 2008 Share Posted January 8, 2008 Nah, I understood what you were saying. Spreading things over 8+ frames is just overkill. I'd say 2-4 frames max. Directly manipulating the playfield would be complicated enough as it is, spreading the process over lots of frames would just increase that unnecessarily. All my opinion, of course. It's looking rather good so far. But I have to agree with the other folks that say you should move them seperately across frames. Im guessing that Jarvis did so in robotron to avoid any frame drops, especially in the GRUNT levels where I think it's like 100 grunts attacking. I'd be intersted to se what you do with the brain waves. Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 8, 2008 Author Share Posted January 8, 2008 Nah, I understood what you were saying. Spreading things over 8+ frames is just overkill. I'd say 2-4 frames max. Directly manipulating the playfield would be complicated enough as it is, spreading the process over lots of frames would just increase that unnecessarily. You're probably approaching it from a wrong thinking angle. ZB is not talking about a task that has to be *completed* in X frames. He's just suggesting to do a fraction of an eternally ongoing task each frame. That should actually simplify things a lot and not complicate them Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 8, 2008 Share Posted January 8, 2008 (edited) Nah, I understood what you were saying. Spreading things over 8+ frames is just overkill. I'd say 2-4 frames max. Directly manipulating the playfield would be complicated enough as it is, spreading the process over lots of frames would just increase that unnecessarily. You're probably approaching it from a wrong thinking angle. ZB is not talking about a task that has to be *completed* in X frames. He's just suggesting to do a fraction of an eternally ongoing task each frame. That should actually simplify things a lot and not complicate them Hmmm. Good point. I just added code to prevent overlap in the SC binary and it is really a drain on CPU time. Had to reduce to 50 enemies for it to be (mostly) stable. Also went to narrow playfield, which gives you an idea of how the proposed standard-cart version would look. RobotronSC20080108.bin Edited January 8, 2008 by vdub_bobby Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 9, 2008 Author Share Posted January 9, 2008 Marvellous! Bring it down to a standard-cart next and then we can see how to optimze it Quote Link to comment Share on other sites More sharing options...
Retrofan Posted January 9, 2008 Share Posted January 9, 2008 You know my Dino Eggs mockup, don't you? Yup, I had planned to hija... I mean bump it You're welcome ... Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 9, 2008 Share Posted January 9, 2008 Marvellous! Bring it down to a standard-cart next and then we can see how to optimze it Okee dokee. Cut number of rows to 16 (so playfield is 16x16) for speed and RAM. Processing one row per frame. But...processing a row takes too long. For rows with many enemies, it takes (I think) 50+ scanlines. Could split the routine in half between Vblank and overscan, but if you want intelliflickered sprites you probably need one or the other for them. Could spread the routine over more frames, but then the updates would come too slowly - right now a typical grunt is moved once every 16 frames. And I took out diagonal movement, so effective diagonal movement is even slower. Here's the 32-frame routine: Take player Y position. Start at that row and move down, processing one row per frame. For each row, move enemies L/R towards player, starting with enemies closest to player and moving out. After reaching the bottom of the screen, go back to the original row and move up, processing one row per frame until we reach the top of the screen. Then get the player Y position again, and repeat, but this time move enemies U/D towards player. Robotron20080108.bin This binary is fairly stable at 268 scanlines, but to get it I had to set the Vblank at something like 60 lines long. Here's an example of the code that needs to be optimized: ldy #31 ;number of playfield pixel positions (minus one) MoveEnemyDownLoop ;process: 1. if enemy exists, erase it and goto 2. else goto 4. ; 2. if room to move enemy, update Y coord. Else, leave Y coord same. ; 3. draw enemy at new/old position ; 4. repeat ;--X holds row number we are looking at sty Temp+2 ;--look for enemy to move first, since should be less likely and make it run faster. lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 beq NoEnemyToMoveDown ;--found enemy, now erase it. lda Temp+3 eor #$FF and $00,Y sta $00,Y ;--now, is there an enemy in the way of moving down? dex ;decrement row index ldy Temp+2 lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 beq RoomToMoveEnemyDown ;--no room, so draw in old position iny RoomToMoveEnemyDown ;--draw enemy back into screen mem lda Temp+3 ora $00,Y sta $00,Y inx ;restore row index NoEnemyToMoveDown ldy Temp+2 ;restore index dey bpl MoveEnemyDownLoop Quote Link to comment Share on other sites More sharing options...
+Nathan Strum Posted January 9, 2008 Share Posted January 9, 2008 Wow... that's impressive! You know, even if you didn't want to turn this into a full-fledged Robotron port, it has the makings of a game in and of itself. (To me, it looks like some guy being attacked by a giant virus or amoeba or something.) Very, very cool. Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 9, 2008 Author Share Posted January 9, 2008 Let's start with a question first. Now the screen is represented by a solid block of 64 bytes. Is that data organized in 4 columns or 16 rows? Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 9, 2008 Share Posted January 9, 2008 (edited) Let's start with a question first. Now the screen is represented by a solid block of 64 bytes. Is that data organized in 4 columns or 16 rows? 4 columns. I can attach the source if that's easier. EDIT: I think I see where you're going...trying to optimize this bit? txa clc adc PFCourseLookupTable,Y tay EDIT II: Just realized...I have that (10-cycle) snip of code twice inside the loop. I can probably save....hm...well, not many cycles. Nevermind, that won't help that much. EDIT III: Also just realized that I can save 7 cycles per loop by cutting out one of these unnecessary snips: lda PFFineLookupTable,Y sta Temp+3 Edited January 9, 2008 by vdub_bobby Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 9, 2008 Share Posted January 9, 2008 (edited) Okay, continuing my thoughts above, I optimzed the move-up and move-down code so they seem to run fast enough. Now I need to optimize the similar move L/R code, which still takes too long in bad cases: MoveEnemiesHorizontally ;--move one row at a time. row number in X. ;--first, count left from player position and move enemies right ldy Temp+2 MoveEnemyRightLoop ;--X holds row number we are looking at ;--look for enemy first, since should be less likely and make it run faster. sty Temp+2 dey bmi AtLeftBorder lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 beq NoEnemyToMove ldy Temp+2 lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 bne NoRoomToMoveEnemyRight ;--ok, move the enemy now ;--first, draw new enemy lda Temp+3 ora $00,Y sta $00,Y ;--now, erase old enemy ldy Temp+2 dey lda PFFineLookupTable,Y eor #$FF sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 sta $00,Y NoRoomToMoveEnemyRight AtLeftBorder NoEnemyToMove ldy Temp+2;restore index dey bpl MoveEnemyRightLoop ldy Temp ;restore PlayerX coord to Y MoveEnemyLeftLoop ;--X holds row number we are looking at ;--look for enemy first, since should be less likely and make it run faster. sty Temp+2 iny cpy #32 beq AtRightBorder lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 beq NoEnemyToMoveLeft ldy Temp+2 lda PFFineLookupTable,Y sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 bne NoRoomToMoveEnemyLeft ;--ok, move the enemy now ;--first, draw new enemy lda Temp+3 ora $00,Y sta $00,Y ;--now, erase old enemy ldy Temp+2 iny lda PFFineLookupTable,Y eor #$FF sta Temp+3 txa clc adc PFCourseLookupTable,Y tay lda $00,Y and Temp+3 sta $00,Y NoRoomToMoveEnemyLeft AtRightBorder NoEnemyToMoveLeft ldy Temp+2;restore index iny cpy #32 bne MoveEnemyLeftLoop Edited January 9, 2008 by vdub_bobby Quote Link to comment Share on other sites More sharing options...
Cybergoth Posted January 9, 2008 Author Share Posted January 9, 2008 I'm just trying to get things straight in my head. Source code would only make me lose my concentration right now Here's an idea for something funky: You have to compute all these 64 bytes every once in a while. For example you could just use a counter. Compute a Byte - Inc the counter - Repeat. Now, my idea would be using a LSFR dedicated for a sequence. Just call the next value, AND %00111111 it and compute the byte from the RAM block that's indexed by it. (One single byte of the block has a little less a chance for updates, since the LSFR can't be zero.) Now, my second idea would be having two 64 byte ROM tables, that would give you the middle y value for the playfield block of grunts and the middle x value for the first grunt in that block. When scanning for grunts in that byte, you just add 4 to the xpos for each scanned bit and so you immediately have x and y values to compare with the player pos. My third idea would be priorising X movement over Y since there's more horizontal steps, i.e. when a grunt can make an X move, do it and if not check if it can move Y instead. My fourth idea would be only checking moves towards the player, i.e. check 2 directions at max, e.g. check right, check down, done. So far so good. I will think some more on how to process a single byte now. Quote Link to comment Share on other sites More sharing options...
vdub_bobby Posted January 9, 2008 Share Posted January 9, 2008 Here's an idea for something funky: You have to compute all these 64 bytes every once in a while. For example you could just use a counter. Compute a Byte - Inc the counter - Repeat. Now, my idea would be using a LSFR dedicated for a sequence. Just call the next value, AND %00111111 it and compute the byte from the RAM block that's indexed by it. (One single byte of the block has a little less a chance for updates, since the LSFR can't be zero.) I like the funkiness but I don't think it helps the problem much and in some respects makes it worse. The real killer is still processing the byte. Now, my second idea would be having two 64 byte ROM tables, that would give you the middle y value for the playfield block of grunts and the middle x value for the first grunt in that block. When scanning for grunts in that byte, you just add 4 to the xpos for each scanned bit and so you immediately have x and y values to compare with the player pos. This would help in your scenario but I don't see how it is an improvement over my scenario since I compare X,Y coords once and then process the whole line in one go. So I only make one compare per line (i.e., up to 32 grunts); your way looks like one compare per grunt or maybe one compare per byte (a group of up to 8 grunts). My third idea would be priorising X movement over Y since there's more horizontal steps, i.e. when a grunt can make an X move, do it and if not check if it can move Y instead. This is a good idea but this would slow the routine down rather than speeding it up, since my routine hard codes which direction to move the grunts. My fourth idea would be only checking moves towards the player, i.e. check 2 directions at max, e.g. check right, check down, done. Excellent idea! I do this already. Well, actually, I only process a single direction at a time, since I do horizontal and vertical movement on separate frames. So far so good. I will think some more on how to process a single byte now. Quote Link to comment Share on other sites More sharing options...
supercat Posted January 10, 2008 Share Posted January 10, 2008 I like the funkiness but I don't think it helps the problem much and in some respects makes it worse. The real killer is still processing the byte. To move a byte worth of pixels vertically (from src,y to dest,y), use: lax dest,y ora src,y sta dest,y lda src,y sax src,y To move a byte horizontally is a little trickier, but it shouldn't be too hard. If you can spare eight bytes of temp space, it would probably be easiest to compute a row shifted left and right a pixel, and then have that available for the movement routines. Both directions of shifted data are needed whether data is being moved left or right, so you may as well compute them. When moving leftward, each pixel should be [(left_neighbor and self) or right_neighbor]. When moving rightward, [(right_neighbor and self) or left_neighbor]. Not sure the best way of merging those expressions to handle split moves. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.