LoginLogin
Might make SBS readonly: thread

Simple Space Game Tutorial

Root / Talk About Programs / [.]

IonesqueCreated:
First post, pretty cool to see a community revolving around this. Brings back a lot of memories of learning programming on an Atari 800XL computer as the syntax is nearly the same. Anyways hopefully this guide will help some people, I may have missed some specific features to smile basic as I just downloaded and tossed this together over the past two days. It's under 100 lines of code and trys to cover most all the basics needed to make a simple game. It also shows both ways to start a game, and ways to improve it.. As I get time I'll edit this post to break it all down, show ways to improve performance, how parts of it work, how less code isn't always better, etc. DISCLAIMER: Information in here might not be 100% accurate, or the best way to handle things. ===================================== Tutorial Part One: https://smilebasicsource.com/page?pid=269 Code: 5DW43NKE What is Included: -Program with emphasis on being under 100 lines of code -Main program loop -Sprite rendering/placement -Simple graphical effects (Flickering) -Sprite based collision detection -Use of subroutines -Enemy generation Problems that need solved: -No safeties for variable protection -Variables written in confusing shorthand -Checks for all subsections are written into the loop and not the subroutine -No way to verify how well the program runs ===================================== Tutorial Part Two: http://smilebasicsource.com/page?pid=276 Code: 74H3LJ What is Included: -Manual declaration of variables (OPTION STRICT) this is something required by nearly any programming language, provides typo protection, and is a good habit. -Subroutine checks are handled within subroutine, not the main loop -Main loop design is much more modular showing how easy it is to add/remove new code -Background star generation -Variables are clearly labeled in a logical manner -Method to measure performance of code -Shows how more code can not only make a program easier to edit, but faster Problems that need solved: -General optimization (Benchmark mode gets 45FPS on O3DS. Granted this is also processing over 300 sprites at once, an impossible scenario but always room for improvement) -Rendered objects clip over the scoreboard -No user-interface (UI) to speak of ===================================== ANALYZING OUR WORK First we need a way to make sure what we are doing is having an impact, for that we need to add a benchmark tool which will run the game in a worse case scenario. For this mode we'll do the following based losely on a post by 12Me21 (Source: http://smilebasicsource.com/forum?ftid=310/): -Add a timer -Draw a many ships as possible (200 at once!) -Leave collision detection on but disable the effects of it to prevent variation -Disable VSYNC (Run as fast as possible) New variables:
' Framerate Counter place this prior to any loop
VAR G_PERFRUN%=1 ' Toggle that can track if we want to performance test
VAR FPS_LOOP%=0   ' Used to track current frame at end of game loop
VAR FPS_COUNT%=0  ' Tracks how many samples were obtained
VAR FPS_SIZE%=300  ' Sample size to poll before reporting
VAR FPS#  ' Total frames minus the tracking frame
VAR FPS_NOW# ' Calculated framerate

' Add this code to a subroutine or end of the main loop
INC FPS_COUNT%  ' INCREMENT CHECK COUNTER
IF FPS_COUNT% >= FPS_SIZE% THEN  ' CHECK IF SAMPLE TOTAL HAS BEEN REACHED
 FPS#=MAINCNT-FPS_LOOP%  ' FRAMES TOTAL DURING BENCHMARK LOOP
 FPS_NOW#= (1/FPS#/FPS_SIZE%))*60  ' CALCULATE FRAMERATE
 CLS
 PRINT "FPS: "; FPS_NOW#  ' PRINT FRAMERATE
 FPS_COUNT%=0  ' RESET THE COUNTER
 FPS_LOOP%=MAINCNT  ' GET STARTING FRAME COUNT
ENDIF
While this isn't 100% perfect this code will provide an idea how well it's working. I took it a step further by forcing it to render an impossible number of ships (200) at once, and disabled explosions to prevent variation. Currently while rendering 200 moving ship sprites with collision, 100 moving star sprites, and some other items I'm getting 42.8FPS considering I can't get past level 18 I'll never see that number but I can use this to see if my changes are having an impact. (If I set it up for a realistic scenario of 20 ships I'm getting closer to 125FPS well over that magical 60 number) Next up... performance profiling PERFORMANCE BASICS: Simply put SmileBASIC will never be a speed demon, it has a lot of overhead against it and interpreted (non-compiled) languages are slow. That said, as a 2-D engine it is still very powerful, simple to use, and in many respects can put 16-bit era consoles to shame. Back on topic, the above program was written with the intent of staying under 100 lines of code. But adding code really improve performance, the idea seems backwards but it is true. Running this you also may be thinking "It's already running at 60FPS, why bother". Well as you add more and more logic, you begin to run the danger of slowing things up. The way the program is written, if we were to constantly miss V-SYNC this would result in choppiness, and possible screen tearing. Let's use the following block of code as a way to improve things: (Corrected: Thanks SquareFingers, and NeatNit)
@FLICKER
IF L_T == 1 THEN BEEP 122
IF (L_T MOD 2) == 1 THEN SPCOLOR 0,RGB(0,0,0,0)
IF (L_T MOD 2) == 0 THEN SPCOLOR 0,RGB(255,255,255,255)
IF L_T >= 240 THEN L = 1
INC L_T     ' INCREMENT THE TIMER
RETURN	 ' RETURN TO THE MAIN LOOP
Currently we are doing the same math operation twice, it's unnecessary work and it's being done once per frame. This means that every second you are performing 60 unnecessary operations. Not only that but we are also doing an extra conditional check that isn't needed. So how can we fix this.
@FLICKER
IF L_T == 1 THEN BEEP 122
IF (L_T MOD 2) == 1 THEN 
  SPCOLOR 0,RGB(0,0,0,0)
ELSE THEN
  SPCOLOR 0,RGB(255,255,255,255)	
ENDIF
IF L_T >= 240 THEN L = 1
INC L_T
RETURN
With three extra lines we made sure to only do that math operation once, and now we only have a single conditional check as this check is a true-false scenario. With that bit of change we just prevented 120 more complex operations from being performed per second. In the big scheme of things, that might not sound like much. But let's say we left something like that in @PROCROCK, and we have 10 ships on screen. Suddenly that 120 became 1200 wasted operations, and that is where we will look next...

I would recommend using OPTION STRICT, and VAR-ing all the variables, so that you have a convenient variable list at the beginning: VAR L 'GAME STATE: 0 = SPAWNING, 1 = PLAYING, 2 = DEAD/RESET VAR I 'PLAYER INERTIA NEGATIVE MOVES LEFT, POSITIVE MOVES RIGHT. etc. EDIT: also, instead of BUTTON()==4, you should do BUTTON() AND #LEFT. EDIT2: also, I reccomend using SPVAR instead of tons of arrays, and X and Y can be checked using SPOFS number OUT <x>,<y> A few weeks ago, I tried to port GAME3 from PTC to smilebasic, and ended up improving it a lot. It's not even close to being done yet, and I'm working on an upgrade system.

if we were to constantly miss V-SYNC and wait for the next one suddenly we'll be sitting at 30FPS
Not so. If you had used WAIT, then yes, there would be a jump from 60fps to 30fps (or, even worse, some game iterations taking 1 frame and others taking 2, leading to inconsistent speeds) (and no flicker), but since you're using VSYNC, it could go down from 60fps to 59.98fps, or 52fps, or any value (with flicker).

I completely agree with the declaration of variables ahead of time, I couldn't keep it under 100, but I'll add a section discussing it and why it's helpful. As for VSYNC from what I read/understood it would pause until the specified number vertical syncs had been achieved. If your code ran too slowly and missed that timeframe on every pass wouldn't it need to wait until the next one? Granted I'm new to SmileBASIC so I have a lot to learn regarding the specifics.

from what I read/understood it would pause until the specified number vertical syncs had been achieved. If your code ran too slowly and missed that timeframe on every pass wouldn't it need to wait until the next one? Granted I'm new to SmileBASIC so I have a lot to learn regarding the specifics.
No, it only waits until the specified number of frames has passed since the LAST vsync. If you use "VSYNC 1", then the program will try to run at 60fps, but if it can't keep it up, it won't be slowed down by the VSYNC operation. http://smilebasic.com/en/reference/#data "Unlike WAIT, the VSYNC count starts from the last VSYNC" By the way, from what I can tell, WAIT actually is based on vertical sync, it will never stop waiting mid-frame. The documentation even agrees: "Stops the program until the specified number of vertically synchronized frames has been reached"

Code for part two of this tutorial is up. Focus of part two is to provide an example of nicer coding, declaration of variables, maintainable code, and showing that extra code is not a bad thing. Also added corrections from my mis-interpretation of the VSYNC command. Additional sections will go up as I obtain time.