VBfx / Common / Starfield Extension



Now that you made a starfield you probably noticed the stars (pixels) are more or less invisible when flying fast. That's because they skip a large distance depending on the speed value.

If you ever played a space game you surely know what the solution is: lines instead of single pixels. But does this work with our concept? Well, yes it does! We're going to use the API again to draw lines and the rest is fairly easy.

Ludicrous speed I say!

API declares again

Similar to SetPixel the Win32 API also provides a LineTo function. Unfortunately it comes without a color parameter and the only way to set the line color is creating a pen and brush. I don't know why Microsoft made this a pain in the ass but here's a little wrapper function that does the work for you. You can use it to draw lines without worrying about the API stuff behind it.

Here's the neccessary API declares:

    Public Type tPoint
        x as Long
        y as Long
    End Type

    Public Declare Function LineTo Lib "gdi32" ( ByVal iDC as Long, ByVal x as Long, ByVal y as Long ) as Long
    Public Declare Function MoveToEx Lib "gdi32" ( ByVal iDC as Long, ByVal x as Long, ByVal y as Long, _
       iPoint as tPoint ) as Long
    Public Declare Function DeleteObject Lib "gdi32" ( ByVal iObject as Long ) as Long
    Public Declare Function SelectObject Lib "gdi32" ( ByVal iDC as Long, ByVal iObject as Long ) as Long
    Public Declare Function CreateSolidBrush Lib "gdi32" ( ByVal iColor as Long ) as Long
    Public Declare Function CreatePen Lib "gdi32" ( ByVal iStyle as Long, ByVal iWidth as Long, _
        ByVal iColor as Long ) as Long

And here's the function:

Public Function DrawLine( DC as Long, x1 as Long, y1 as Long, x2 as Long, y2 as Long, iColor as Long )
    Dim Pen as Long
    Dim Brush as Long
    Dim OldPen as Long
    Dim OldBrush as Long
    Dim TempPos as tPoint
    'Create pen
    Pen = CreatePen( 0, 1, iColor )
    OldPen = SelectObject( DC, Pen )
    'Create brush
    Brush = CreateSolidBrush( iColor )
    OldBrush = SelectObject( DC, Brush )
    'Draw line
    MoveToEx DC, x1, y1, TempPos
    LineTo DC, x2, y2
    'Restore brush
    SelectObject DC, OldBrush
    DeleteObject Brush
    'Restore pen
    SelectObject DC, OldPen
    DeleteObject Pen
End Function

If you had a look at the 3rd example from the last page you'll surely remember the function - in fact it's the same except we have no size property but a second pair of coordinates instead. That's because this time we truly want to draw lines and not just bigger points.

Implementing the lines

Yet we just calculated the current point's position from .x / .z and .y / .z but now we need 2 points to draw the lines. Well let's think about what happens between two frames; the MoveStars function is called and it subtracts Speed from each z-value. So basically we can just add Speed again to get the position of a point in the last frame. And that's exactly what we're going to do now:

Public Sub DrawStars( DC as Long )
    Dim A as Long
    Dim FromPos as tPoint
    Dim ToPos as tPoint
    Dim Brightness as Single
    For A = 0 To StarCount
        With Star( A )
            'Get start point
            FromPos.x = ( .x / .z ) + OffsetX
            FromPos.y = ( .y / .z ) + OffsetY
            'Get end point (adding speed to the .z value)
            ToPos.x = ( .x / ( .z + Speed ) ) + OffsetX
            ToPos.y = ( .y / ( .z + Speed ) ) + OffsetY
            'Get brightness from distance (here ^2 just smooths the fading of darker stars)
            Brightness = ( 1 - ( .z / SpreadZ ) ) ^ 2 * 255
            'Draw the line
            DrawLine DC, FromPos.x, FromPos.y, ToPos.x, ToPos.y, RGB( Brightness * 1, Brightness * 0.8, Brightness * 0.4 )
        End With
End Sub

As you can see this is nothing too fancy. To make the code clear I used the FromPos and ToPos variables to store the positions of both points. For a minor speed gain you can put those 4 formulas directly into the DrawLine call.

If you're coming straight from the first starfield tutorial you'll notice two new things here: The ^2 statement in the Brightness calculation and the multipliers within the RGB() function. Both are easy to explain: Since the Brightness is calculated as a value between 0 and 1 we can use any exponent to tween the results. Try using ^3 or higher to clearly see the effect; the higher the value is the longer the stars stay dark. This makes them appear more smoothly but on the other hand you'll need more stars to fill up the screen. Talking about speed vs. quality here; as always in graphics programming.

The second thing is nothing more than a tint value, I used some kind of yellow in the example. If you prefer white stars you can leave it out, if you want blue stars use 0 / 0 / 1 as the multipliers.


Hard to believe but we're done already. Now the starfield shows up more clearly because the stars are stretched to lines depending on the Speed value. The higher the speed is, the bigger the skipping distance between two frames is and thus the longer the lines are. As on the last page the second download is a little advanced, showing how to combine the lines with a background picture. Nothing that'd need further explanation though.