After coding a couple of applications in PureBasic, I decided to turn my attention to the graphical abilities of said programming language. Research time and first port of call was the PureBasic forum where I found a few examples, but they were either too old for my version of PureBasic so wouldn't compile, or were overly complicated with lots of added effects that I didn't want or need.
So I decided to go it alone and code a clean and simple routine that I would then be able to 'plug' into other projects if I needed to fill the background with something interesting, as I did with 'Old School Demo 1'. This is what I came up with to generate a simple 4 layer starfield...
;- INITIALISE ENVIRONMENT ------------------------------------
InitKeyboard()
InitSprite()
;- CREATE VARIABLES ------------------------------------------
#XRES = 800
#YRES = 600
#LAYER1SPEED = 1
#LAYER2SPEED = 3
#LAYER3SPEED = 5
#LAYER4SPEED = 10
;- PROCEDURES ------------------------------------------------
Procedure OpenMainWindow()
OpenWindow(0,0,0,#XRES,#YRES,"Parallax Stars!",#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0),0,0,#XRES,#YRES)
EndProcedure
Procedure CreateStarfields()
Global sp_BKGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_BKGRDSTAR))
For n=0 To 999
Plot(Random(#XRES-1),Random(#YRES-1),RGB(100,100,100))
Next
StopDrawing()
Global sp_MDGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_MDGRDSTAR))
For n=0 To 299
Plot(Random(#XRES-1),Random(#YRES-1),RGB(150,150,150))
Next
StopDrawing()
Global sp_FTGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_FTGRDSTAR))
For n=0 To 199
Plot(Random(#XRES-1),Random(#YRES-1),RGB(200,200,200))
Next
StopDrawing()
Global sp_CLGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_CLGRDSTAR))
For n=0 To 19
Plot(Random(#XRES-1),Random(#YRES-1),RGB(255,255,255))
Next
StopDrawing()
EndProcedure
Procedure DrawStarfields()
Shared bgx1, bgx2, bgx3, bgx4
For s1 = 0 To 1
For s2 = 0 To 1
DisplayTransparentSprite(sp_BKGRDSTAR,#XRES*s1-bgx1,#YRES*s2)
DisplayTransparentSprite(sp_MDGRDSTAR,#XRES*s1-bgx2,#YRES*s2)
DisplayTransparentSprite(sp_FTGRDSTAR,#XRES*s1-bgx3,#YRES*s2)
DisplayTransparentSprite(sp_CLGRDSTAR,#XRES*s1-bgx4,#YRES*s2)
Next
Next
bgx1+#LAYER1SPEED : If bgx1 > #XRES-1 : bgx1=0 : EndIf
bgx2+#LAYER2SPEED : If bgx2 > #XRES-1 : bgx2=0 : EndIf
bgx3+#LAYER3SPEED : If bgx3 > #XRES-1 : bgx3=0 : EndIf
bgx4+#LAYER4SPEED : If bgx4 > #XRES-1 : bgx4=0 : EndIf
EndProcedure
;- BEGIN PROGRAM ----------------------------------------------------
OpenMainWindow()
CreateStarfields()
HideWindow(0,#False)
;- MAIN PROGRAM LOOP ------------------------------------------------
Repeat
WaitWindowEvent(1)
ClearScreen(0)
DrawStarfields()
FlipBuffers()
ExamineKeyboard()
Until KeyboardPushed(#PB_Key_Escape)
;- EXIT PROGRAM ----------------------------------------------------
FreeSprite(#PB_All)
CloseWindow(#PB_All)
End
The above code will actually run stand alone; if you compile it in PureBasic, it will open its own window and display the scrolling starfield until you exit by pressing 'escape'.
What is each part of the code doing?
;- INITIALISE ENVIRONMENT ------------------------------------
InitKeyboard()
InitSprite()
This section does what the comment says and initialises the environment - to be able to detect keypresses you must initialise the keyboard and to be able to use any graphics (including sprites) in a window you must initialise the sprite system.
;- CREATE VARIABLES ------------------------------------------
#XRES = 800
#YRES = 600
#LAYER1SPEED = 1
#LAYER2SPEED = 3
#LAYER3SPEED = 5
#LAYER4SPEED = 10
This section creates variables and assigns number values. Changing the value of #XRES and #YRES alters the size of the window in which the starfield is displayed. The other variables created control the speed of the scroll of each star layer, in this example 'layer 1' is the slowest to simulate being the furthest away and layer 4 the quickest to simulate being the closest, with layers 2 and 3 being somewhere in between.
Next, I've created some procedures.
;- PROCEDURES ------------------------------------------------
Procedure OpenMainWindow()
OpenWindow(0,0,0,#XRES,#YRES,"Parallax Stars!",#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0),0,0,#XRES,#YRES)
EndProcedure
This first procedure opens a window on the users screen with the size of #XRES and #YRES, with a title "Parallax Stars!" and centres the window in the users display. It then opens a 'windowed screen' in this window which enables the use of powerful graphic commands that display images and sprites really quickly, enough so to create fast demos and games.
Procedure CreateStarfields()
Global sp_BKGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_BKGRDSTAR))
For n=0 To 999
Plot(Random(#XRES-1),Random(#YRES-1),RGB(100,100,100))
Next
StopDrawing()
Global sp_MDGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_MDGRDSTAR))
For n=0 To 299
Plot(Random(#XRES-1),Random(#YRES-1),RGB(150,150,150))
Next
StopDrawing()
Global sp_FTGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_FTGRDSTAR))
For n=0 To 199
Plot(Random(#XRES-1),Random(#YRES-1),RGB(200,200,200))
Next
StopDrawing()
Global sp_CLGRDSTAR = CreateSprite(#PB_Any,#XRES,#YRES)
StartDrawing(SpriteOutput(sp_CLGRDSTAR))
For n=0 To 19
Plot(Random(#XRES-1),Random(#YRES-1),RGB(255,255,255))
Next
StopDrawing()
EndProcedure
This next procedure then creates 4 sprites 'on the fly' and then randomly fills them with dots pretending to be stars. Each sprite is held in a named variable starting with 'sp_' to indicate it's a sprite plus a name to indicate it's level in the display, so 'BKGRDSTAR' is the background layer, 'MDGRDSTAR' being the mid layer and so on. This is my naming convention, you could call the sprite whatever you like. Each sprite is created the same size as the window (#XRES, #YRES).
When randomly filling with dots, the closer the layer the fewer the number of dots (n). Also, since the background stars would be further away they are darker, RGB(100,100,100), while the closest layer is brighter, RGB(255,255,255).
Procedure DrawStarfields()
Shared bgx1, bgx2, bgx3, bgx4
For s1 = 0 To 1
For s2 = 0 To 1
DisplayTransparentSprite(sp_BKGRDSTAR,#XRES*s1-bgx1,#YRES*s2)
DisplayTransparentSprite(sp_MDGRDSTAR,#XRES*s1-bgx2,#YRES*s2)
DisplayTransparentSprite(sp_FTGRDSTAR,#XRES*s1-bgx3,#YRES*s2)
DisplayTransparentSprite(sp_CLGRDSTAR,#XRES*s1-bgx4,#YRES*s2)
Next
Next
bgx1+#LAYER1SPEED : If bgx1 > #XRES-1 : bgx1=0 : EndIf
bgx2+#LAYER2SPEED : If bgx2 > #XRES-1 : bgx2=0 : EndIf
bgx3+#LAYER3SPEED : If bgx3 > #XRES-1 : bgx3=0 : EndIf
bgx4+#LAYER4SPEED : If bgx4 > #XRES-1 : bgx4=0 : EndIf
EndProcedure
This next procedure draws the actual sprites (starfields) on the screen when this procedure is called later in the code. The sprites are drawn in 'transparent' mode, whereby everything that is black is transparent so shows everything underneath. Therefore, the stars (dots) on each sprite layer are always visible.
Each sprite layer is moved sideways using the #LAYERSPEED variables, with each layer moving a set number of pixels defined by that variable. The sprites loop around continuously when they have scrolled their #XRES limit.
;- BEGIN PROGRAM ----------------------------------------------------
OpenMainWindow()
CreateStarfields()
HideWindow(0,#False)
Now the program begins to actually execute by calling the 'OpenMainWindow()' procedure to open a window, generate the sprites by calling the 'CreateStarfields()' procedure and then unhides the window so we can see it, if it was hidden in the 'openwindow' procedure (which in this example it actually isn't).
;- MAIN PROGRAM LOOP ------------------------------------------------
Repeat
WaitWindowEvent(1)
ClearScreen(0)
DrawStarfields()
FlipBuffers()
ExamineKeyboard()
Until KeyboardPushed(#PB_Key_Escape)
The code now enter a continuous loop where it waits for a window event, clears the screen each frame, draws the starfields in their new positions, flips to the second prepared screen where the drawing of starfields has taken place out of view to ensure smoothness, then checks the keyboard to see if the user has pressed 'escape'.
;- EXIT PROGRAM ----------------------------------------------------
FreeSprite(#PB_All)
CloseWindow(#PB_All)
End
If the user pressed space, then the program loop exits, erases all sprites and closes the window. Technically, the 'End' command does this automatically but I like to be thorough.
This code can easily be adapted to make the stars scroll the opposite direction, or even up/down.
If you want the raw code to copy and paste into PureBasic, grab it from Pastebin here...