|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
![]() |
Goto page 1, 2, 3 Next |
Author | Message |
---|---|
|
Frontier Force (another fixed screen shoot-em-up)
![]() Last edited by badcomputer on Sun Sep 29, 2024 11:56 am; edited 1 time in total |
I've uploaded an Alpha Demo of the game HERE if you'd like to try it and give any feedback.
----------------- Original post: I've started prototyping a new SMS game in Python. I think it will end up saving me a lot of time if I can iron out all the design issues before moving to SMS dev (using the devkit again). Here's a brief gif of an early feel for the game. I have a lot of the design written out, it's not too complicated but I think it'll be a deeper and longer game than my previous game Blast Arena.[/img] |
|
![]() ![]() ![]() |
|
|
![]() |
It looks already very Master System-ish!!
Looking forward to it! |
|
![]() ![]() |
|
|
![]() |
That's a good trick. I've been idly thinking about the possibility of a code generator to fasttrack converting high level prototypes into assembly code which I might try one day in the future. Are you using Pygame, or something else? |
|
![]() ![]() ![]() |
|
|
![]() |
Thanks kusfo!
I'm using Pyxel which is like Pico-8 for Python. It has a lot of nice features to speed up making 8-16bit era games. Pygame is good too but is more of a general game library. https://github.com/kitao/pyxel |
|
![]() ![]() ![]() |
|
|
![]() |
Oh fun! I'll definitely check that out, thanks! | |
![]() ![]() ![]() |
|
|
![]() |
Finalising the look of the game and starting to add weapons and enemies.
|
|
![]() ![]() ![]() |
|
|
![]() |
More work on this today. Adding enemy and collision behaviours and more graphics stuff. A very work in progress title screen, but the colours and style of the game is beginning to take shape.
|
|
![]() ![]() ![]() |
|
|
![]() |
More basic functionality and design tweaks today, things coming together now. I'll be able to start testing gameplay soon, which was the whole point of this prototype.
|
|
![]() ![]() ![]() |
|
|
![]() |
More work on this today beginning to add enemy spawning and some new stage stuff. Resisting the feature creep.
|
|
![]() ![]() ![]() |
|
|
![]() |
This looks class mate, another home run. Well done, looking forward to trying it | |
![]() ![]() ![]() |
|
|
![]() |
Begun transferring the prototype to SMS now (or the emulator), and playing with palette cycling to make the background more interesting.
Will the SMS manage with all this palette stuff when all the sprites are moving around? Stay tuned to find out! |
|
![]() ![]() ![]() |
|
|
![]() |
Playing with a different art style for the enemies, I think a softer and brighter style stands out more.
|
|
![]() ![]() ![]() |
|
|
![]() |
Looks great. | |
![]() ![]() |
|
|
![]() |
Starting to stress test collisions and such now. I feel like this already performs much better than Blast Arena.
My initial mistake making Blast Arena was thinking sprite drawing would be the bottleneck and so I made a lot of small sized sprites, but the bottleneck by far was collision detection. This time I intend to use bigger individual enemies in general so that will automatically help by reducing the enemy count but still fill up the screen nicely. The direction of enemy and player shots are more predictable too so I can remove a lot of checking there. I also intend to care less about reducing flicker this time, unless it really impacts visibility I don't care for small bits of flicker if it means more action. Edit: Also as a gift to my future self, posting this thread by Kagesan on Flight of Pigarus development. Very useful info about collision detection, design, scrolling maps etc. https://www.smspower.org/forums/17278-FlightOfPigarusDevelopmentQA |
|
![]() ![]() ![]() |
|
|
![]() |
Doing the laser/lightning type weapon.
|
|
![]() ![]() ![]() |
|
|
![]() |
Title screen is work in progress and main menu fully working today.
I plan to do a bit more with the title screen so splitting them up for now seems like a good idea. |
|
![]() ![]() ![]() |
|
|
![]() |
Continuing to make progress. I implemented an score drawing routine in assembly (thanks to sverx thread here) and begun adding the laser weapons.
The laser tile drawing is surprisingly quick so I can probably allow it to be used more often. |
|
![]() ![]() ![]() |
|
|
![]() |
Decided to redesign some elements of the game, got rid of some clunky mechanics, and felt it needed more character so added a guy.
As an (unplanned) bonus, getting rid of the side lasers will allow me to optimise enemy bullet to player collisions a lot more. |
|
![]() ![]() ![]() |
|
|
![]() |
What is the standard way to set up my main loop to ensure music is uninterrupted by slowdown?
I've split my logic and rendering, and split all types of entities, so I can easily skip parts if needed but I'm not sure how to structure this properly to guarantee 100% that music won't slow down. Do I need to be checking if the previous frame slowed down and then updating music twice in the next? I can't get my head around it. Any help or advice? Very much appreciated! |
|
![]() ![]() ![]() |
|
|
![]() |
the *only* safe way to ensure that music won't *ever* slow down is to tie that to an interrupt - for instance if you're not already using the line interrupt, that's the best option.
basically in your handler (that will run once a frame) you have to make sure that you first save and later restore the ROM banking so to do not disrupt other processes (say some VRAM updates from data in ROM) and call the PSGFrame/PSGSFXFrame functions. example: void my_handler (void) {
SMS_saveROMBank(); SMS_mapROMBank(your_music_bank); PSGFrame(); // update music SMS_mapROMBank(your_sfx_bank); PSGSFXFrame (void); // update SFX (if active) SMS_restoreROMBank(); } then you need to 'link' the handler, and configure and enable the interrupt - example: SMS_setLineInterruptHandler(my_handler);
SMS_setLineCounter(140); // any value over 96 is good because it fires only once every frame SMS_enableLineInterrupt(); |
|
![]() ![]() ![]() |
|
|
![]() |
Working perfectly, thank you! This was so much simpler than the solutions I read elsewhere, particularly for N*S. |
|
![]() ![]() ![]() |
|
|
![]() |
What if you wanted to use line interrupts for multi-segmented scrolling? | |
![]() ![]() |
|
|
![]() |
You can do it in the frame interrupt, like most games do, but then you waste valuable VBlank time. If you put it at the end of the vblank then it might get a little jittery, but probably not noticeable; and if your vblank handler ends up taking more than a frame, you’ll get slowdown again. The classic “wait for vblank” loop has the same issues. | |
![]() ![]() ![]() |
|
|
![]() |
Another somewhat random newbie question.
I was stress testing the game and caused major slowdown (16 enemies + bullets) and managed to read uninitialised memory according to Emulicious, the game panicked and reset itself. I don't intend to have more than 8 enemies normally or that level of slowdown ever, should I be concerned about managing to crash/reset/halt the game in this way, or just avoid that amount of slowdown? The answer may seem obvious (avoid it), but I would've expected the game to remain stable albeit incredibly slow until the frame completed? |
|
![]() ![]() ![]() |
|
|
![]() |
Indeed slowdown should not cause that. In assembly I’d guess you’re messing up the stack somehow but in C I suppose that ought not to be possible. | |
![]() ![]() ![]() |
|
|
![]() |
So I seem to have found the issue but I'm not sure why it's doing this.
When a metasprite of an enemy is drawn it's putting the sprite index ($50) into the memory address ($C3F7) of the variable num_player_shots, this tracks the number of active shots to update/draw. The maximum num_player_shots is 2, so obviously the causes all kinds of crazy stuff if its suddenly increased to 80. This only seems to be happening when the player gets hit by an enemy bullet and theres greater than around 10 enemies (see in-game shot). The num_player_shots variable was initialised previously to 0. Any ideas why it might be doing this? (Sidenote, I'm using metasprite to draw the 3-wide enemy sprite rather than the SMS_addThreeAdjoiningSprites as I was intending to make it bigger later.) |
|
![]() ![]() ![]() |
|
|
![]() |
the most common reason for such behavior is buffer overrun. i see no checks for the maximum possible sprites count in the metasprite rendering routine on your screenshot, so it continues to render SAT over the random variables in memory which happen to land after those shadow SAT buffers (SpriteTableY[], and SpriteTableXN[]). |
|
![]() ![]() |
|
|
![]() |
I suspect toxa is right - if you're using the sprites with the library compiled with NO_SPRITE_CHECKS then there's no 'safety net' to avoid trying to place more than 64 sprites on screen - the code will just overwrite whatever happens to be in RAM after the SAT buffers.
You can use the library compiled with default settings or you have to make sure yourself that there's no way more than 64 sprites will be placed on screen. |
|
![]() ![]() ![]() |
|
|
![]() |
you could do that too by checking a counter variable to see if it's the right moment to drive the music - of course that counter needs to be reset in the vertical interrupt handler, but that's a small waste compared to updating the music during vblank |
|
![]() ![]() ![]() |
|
|
![]() |
why there even such an option? just to have ability to shoot your leg? :) the case when you never need that (because of the guaranteed small amount of sprites on screen, for instance) unlikely require that paranoic cycle saving, and possible checks in the outer code most likely be just slower. |
|
![]() ![]() |
|
|
![]() |
I'm not using anything non-standard, I've never compiled SMSlib myself. Looking at the version I'm using NO_SPRITE_CHECKS is also not defined, so I'm not sure why it's using that version? I'll investigate further. |
|
![]() ![]() ![]() |
|
|
![]() |
devkitSMS improvement is mostly driven by user desire so - believe it or not - that option is there because somebody wanted to avoid wasting cycles knowing they weren't going to even get close to using 64 sprites |
|
![]() ![]() ![]() |
|
|
![]() Last edited by badcomputer on Wed Mar 27, 2024 8:36 am; edited 1 time in total |
I noticed in SMSlib_metasprite.c it seems to be the wrong way around, only if NO_SPRITE_CHECKS is set then it checks if it's using too many sprites, or am I getting confused?
https://github.com/sverx/devkitSMS/blob/master/SMSlib/src/SMSlib_metasprite.c |
|
![]() ![]() ![]() |
|
|
![]() |
Whoopsy. My mistake. Let me fix the code... |
|
![]() ![]() ![]() |
|
|
![]() |
No, you're right - I just noticed it myself. :facepalm: It's recompiling and I'm pushing an update in a minute! edit: done. Sorry! |
|
![]() ![]() ![]() |
|
|
![]() |
Thanks sverx! At least the stress testing was worth it and I'm glad I found the reason. |
|
![]() ![]() ![]() |
|
|
![]() |
Thank you for finding a bug! I suspect many more are around... |
|
![]() ![]() ![]() |
|
|
![]() |
Don't say that! haha |
|
![]() ![]() ![]() |
|
|
![]() |
I don't want to get too philosophical but I'm just a man and the only program assured to have no bugs is an empty program ;) | |
![]() ![]() ![]() |
|
|
![]() |
mostly, people not know what they really need. they think they saved 20 cycles, but instead wasting 2000 cycles trying to workaround limitations they produced by that previous "optimization". not that i am teaching you how to design the libraries or giving any suggestions though... just my thoughts on the subject. |
|
![]() ![]() |
|
|
![]() |
Back to progress, added a new sprite and bg.
|
|
![]() ![]() ![]() |
|
|
![]() |
Not necessarily they need to check they're not using too many sprites - some games I've seen never even get close to 50 on screen at the same time. But I do not suggest this option in general - as you said, it's possible to shoot yourself in the foot. (also, for context, an SMS_addSprite call takes 162 CPU cycles, down to just 116 for the NO_SPRITE_CHECKS version) |
|
![]() ![]() ![]() |
|
|
![]() |
What would be the best way to scroll the sky on this stage in 3 horizontal strips? I already have a line interrupt set up on 140 for the audio update but I can't seem to get scrolling to work properly with further interrupts without seriously messing up the screen. I guess I'm interrupting screen updates or something but I don't know the best way around this? I don't need to load any new tiles, just adjust/wrap scroll x in 3 stages continuously. Although I am writing a column of black tiles on the right to even the screen, so I guess I'd need to update that. |
|
![]() ![]() ![]() |
|
|
![]() |
First of all, when you do horizontal scroll you usually turn off the leftmost column of BG on screen so that it doesn't display garbage - you can use SMS_VDPturnOnFeature(VDPFEATURE_LEFTCOLBLANK);
Also the line interrupt triggers after N lines, N being the value you set using SMS_setLineCounter, so if you set this to 40 for example, your handler will be called after 40 lines, again after 80 lines, then after 120 lines and also after 160 lines. Handling multiple strips of different height is more complex unfortunately, and can be done in different ways: the simpler is breaking down the strips into more strips of the same height, say every 20 instead of 40 if you want the interrupt to also fire at 100th line. The other approach is more complex and it involves updating the line counter value during the interrupt, which will unfortunately result in the value being updated *after* the *next* interrupt invocation... and it's a pain, honestly, so I suggest you the former approach. edit: You might also want to lock the HUD in place using SMS_VDPturnOnFeature(VDPFEATURE_LOCKHSCROLL); which will make the topmost two rows on screen not be affected by the X scroll value.
|
|
![]() ![]() ![]() |
|
|
![]() |
Thanks sverx. Testing this, is there a reason the interrupt seems to be firing late? Say if I use SMS_setLineCounter(16) and use my own counter to tell which number interrupt I'm on. I set scroll x in the interrupt it will set it starting from line 18, and the period to the next one is 17 lines but should be 16. What could be causing this or is this expected? I've attached a short gif to show. It should be scrolling at different speeds at line 16-31 (just below HUD), line 32-103 and line 104-119. |
|
![]() ![]() ![]() |
|
|
![]() |
Yes. The issue basically is that there's very very VERY little time to do anything before it's too late to update the scroll register in time for the next row. Basically the only way to be sure this will work is to have an handler in this form: void my_handler (void) {
so that the VDP scroll register gets updated as early as possible.
INLINE_SMS_setBGScrollX (parallax_scroll_X); // do this immediately! // prepare the value for the next parallax_scroll_X here, // then do the rest of the work you need to do } Of course you have to set the initial value for the global variable parallax_scroll_X in your vblank, for instance doing something like parallax_scroll_X=my_scroll_table[0]; and using a global variable as an index to your my_scroll_table to pull the next value in your line interrupt handler.
It's not that straightforward, but it works :) edit: I forgot to mention that to have it fire after line #15 (and after every 16 lines) you have to use SMS_setLineCounter(15); because 0 means 'after every line', 1 is after every second line, and so on...
|
|
![]() ![]() ![]() |
|
|
![]() |
Using 15 on the line counter got it to trigger every 16 lines but I couldnt get it to start on line 16, only 17. I settled for that anyway and adjusted my tile graphics 1 pixel as it was still triggering every 16 like I wanted. I scrolled the ground too. I haven't decided yet if it causes motion sickness or not! |
|
![]() ![]() ![]() |
|
|
![]() |
Uhm, I suspect I know why this is happening. If you want to experiment with this, you could move everything apart for the INLINE_SMS_setBGScrollX call into a separate function so that your handler remains absolutely minimal like this: void my_handler (void) {
INLINE_SMS_setBGScrollX (parallax_scroll_X); // do this immediately! line_handler_process(); // here we process everything we have to do } This way SDCC won't have to create any stack frame handling code for the my_handler function and I suppose it'll work. |
|
![]() ![]() ![]() |
|
|
![]() |
It worked, thanks! |
|
![]() ![]() ![]() |
|
|
![]() |
Good! :D |
|
![]() ![]() ![]() |
![]() |
Goto page 1, 2, 3 Next |