QB64 Phoenix Edition
Asteroids Clone - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Games (https://qb64phoenix.com/forum/forumdisplay.php?fid=57)
+---- Thread: Asteroids Clone (/showthread.php?tid=3435)

Pages: 1 2


Asteroids Clone - SierraKen - 02-01-2025

Well, it's not exactly like the original Asteroids. The rocks don't break into smaller ones. I did try to make it that way, but it's a bit too advanced for my learning. Plus I don't want to make it look almost exactly like the arcade version because of copyright infringement. I know there's been a million other versions already made, but ya never know. lol

The rocks change their looks randomly for each level. 

Tell me what you think, I've been working on this for maybe 3 or 4 days. It might still have some flukes, like possibly more than 1 asteroids being gone after shooting one for a different angle. I'm not exactly sure why that happened but I might have fixed it, or at least almost all of it. 

Disclaimer: This game is not intended to replace any other game in existence. It is a labor of love and given out for free like usual. 
Thank you guys for your inspiration. My Mouse Tank game really helped a lot on this code. Plus I got more math code from ChatGPT. Feel free to take as much as you want, as usual. Smile 
Here is a picture, the code is below it.

@bplus
@Pete


[Image: Asteroids-Clone-by-Sierra-Ken.jpg]

Code: (Select All)


'Asteroids Clone by, SierraKen
'February 1, 2025
'Thank you QB64pe Forum for your inspiration!
'Thank you also ChatGPT for a lot of the math.
'This game is not intended to replace any other game already in existence.
'It is a labor of love and given out for free.


_Title "Asteroids Clone - by SierraKen"

Screen _NewImage(800, 600, 32)

Randomize Timer

Dim oldx(100), oldy(100)
Dim llx(300), lly(300)
Dim lx(300), ly(300), ldir(300)
Dim x1 As Single, y1 As Single

Const numPoints = 30
Dim x2(100, 35), y2(100, 35)
Dim xRot(100, 35), yRot(100, 35)
Dim cx2(200), cy2(200), angle2(200)
Dim dx(100), dy(100)
Dim nox(200)

start:

numAsteroids = 8
radius2 = 45 'Asteroids
level = 1
score = 0
health = 50
healthp = 100
rot = -90

Cls
_AutoDisplay

Locate 3, 25: Print "A s t e r o i d s  C l o n e"
Locate 5, 25: Print "By SierraKen"
Locate 10, 25: Print "Move your ship around with the arrow keys."
Locate 11, 25: Print "Turn your ship with the left and right arrow keys."
Locate 12, 25: Print "Go forward with the up arrow key."
Locate 12, 25: Print "Press Space Bar to fire at asteroids."
Locate 13, 25: Print "To pause and un-pause, press Esc."
Locate 14, 25: Print "Press Q anytime to quit."
Locate 16, 25: Print "This game is not intended to replace any other game."
Locate 17, 25: Print "It is a labor of love and given out for free."
Locate 20, 25: Input "Press Enter to Begin.", a$

start2:
Cls

_Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)

numAsteroids = numAsteroids + 2
If numAsteroids > 40 Then numAsteroids = 40
num = numAsteroids
rock = 0
hits = 0
speed2 = 0
laser = 0
ll = 0
numpoints2 = Int(Rnd * numPoints) + 5

For ll3 = 1 To 200
    lx(ll3) = 0
    ly(ll3) = 0
    ldir(ll3) = 0
Next ll3

sx = 400
sy = 300
oldx = 400
oldy = 300
det = 0

r1 = 3 'bullets
r3 = 25 'Your ship
loops = 0

Play "MB"

' Initialize asteroids
For a = 0 To num - 1
    more:
    cx2(a) = Int(Rnd * 680) + 55 ' Random start X
    cy2(a) = Int(Rnd * 480) + 55 ' Random start Y
    If cx2(a) > 250 And cx2(a) < 550 And cy2(a) > 150 And cy2(a) < 450 Then GoTo more:
    angle2(a) = Rnd * 360 ' Random starting rotation
    dx(a) = (Rnd - 0.5) * 2 ' Random speed X (-1 to 1)
    dy(a) = (Rnd - 0.5) * 2 ' Random speed Y (-1 to 1)

    ' Generate random asteroid shape
    For i = 0 To numpoints2
        ang = i * (360 / numpoints2)
        rOffset = radius2 + Int(Rnd * 15 - 7) ' Vary radius randomly
        x2(a, i) = Cos(ang * _Pi / 180) * rOffset
        y2(a, i) = Sin(ang * _Pi / 180) * rOffset
    Next
    rock = rock + 1
Next

Do
    _Limit 100

    k = _KeyHit

    If k = 32 Then
        laser = 1
        ll = ll + 1
        If ll > 100 Then ll = 1
        lx(ll) = x1
        ly(ll) = y1
        ldir(ll) = angle
    End If

    If k = 19200 Then dir = 1 ' Left Arrow (rotate counterclockwise)
    If k = 19712 Then dir = 2 ' Right Arrow (rotate clockwise)
    If k = 18432 Then dir = 3 ' Up Arrow (thrust forward)
    If k = 20480 Then dir = 4 ' Down Arrow (thrust backward)

    If dir = 1 Then
        angle = angle - 1
        If right = 1 Then
            right = 0
            dir = 0
            GoTo nex2
        End If
        left = 1
        right = 0
    End If
    If dir = 2 Then
        angle = angle + 1
        If left = 1 Then
            left = 0
            dir = 0
            GoTo nex2
        End If
        right = 1
        left = 0
    End If
    If dir = 3 Then
        forward = 1
        backward = 0
        speed2 = speed2 + .5
        If speed2 > 3 Then speed2 = 3
        ' Move in the direction of the angle
        sx = sx + speed2 * Cos(angle * _Pi / 180)
        sy = sy + speed2 * Sin(angle * _Pi / 180)
    End If
    If dir = 4 Then
        If forward = 1 Then
            dir = 0
            speed2 = 0
            GoTo nex2:
        End If
    End If
    nex2:
    If sx > 800 Then sx = 0
    If sx < 0 Then sx = 800
    If sy > 600 Then sy = 0
    If sy < 0 Then sy = 600

    If k = 27 Then
        Do: c = _KeyHit
        Loop Until c = 27
    End If

    If k = 81 Or k = 113 Then End

    ' Update and draw each asteroid
    For a = 0 To num - 1
        If nox(a) = 1 Then GoTo skip
        angle2(a) = angle2(a) + 1 ' Rotate
        If angle2(a) >= 360 Then angle2(a) = 0

        ' Rotate asteroid points
        rad = angle2(a) * _Pi / 180
        For i = 0 To numpoints2
            xRot(a, i) = cx2(a) + (x2(a, i) * Cos(rad) - y2(a, i) * Sin(rad))
            yRot(a, i) = cy2(a) + (x2(a, i) * Sin(rad) + y2(a, i) * Cos(rad))
        Next

        ' Draw asteroid
        For i = 0 To numpoints2 - 1
            j = (i + 1) Mod numpoints2
            Line (xRot(a, i), yRot(a, i))-(xRot(a, j), yRot(a, j)), _RGB32(255, 255, 255)
        Next

        ' Move asteroid
        cx2(a) = cx2(a) + dx(a)
        cy2(a) = cy2(a) + dy(a)

        ' Wrap around screen edges
        If cx2(a) < 0 Then cx2(a) = 800
        If cx2(a) > 800 Then cx2(a) = 0
        If cy2(a) < 0 Then cy2(a) = 600
        If cy2(a) > 600 Then cy2(a) = 0
    Next

    skip:

    If laser = 1 Then
        For lz = 0 To ll - 1 Step 10
            lx2 = Cos(ldir(lz) * _Pi / 180) * 10
            ly2 = Sin(ldir(lz) * _Pi / 180) * 10
            lx(lz) = lx2 + lx(lz)
            ly(lz) = ly2 + ly(lz)
            If lx(lz) > 850 Then lx(lz) = 850
            If lx(lz) < -50 Then lx(lz) = -50
            If ly(lz) > 650 Then ly(lz) = 650
            If ly(lz) < -50 Then ly(lz) = -50
            fillCircle lx(lz), ly(lz), r1, _RGB32(255, 0, 5)
            For chk = 0 To num - 1
                distance = Sqr((lx(lz) - cx2(chk)) ^ 2 + (ly(lz) - cy2(chk)) ^ 2)
                If distance <= r1 + radius2 Then
                    DetectCollision = -1 ' True (collision detected)
                Else
                    DetectCollision = 0 ' False (no collision)
                End If

                If DetectCollision = -1 Then
                    For explosion = 1 To 50
                        Circle (lx(lz), ly(lz)), explosion, _RGB32(255, 0, 0)
                        llx(explosion) = lx(lz)
                        lly(explosion) = ly(lz)
                    Next explosion
                    'SOUND frequency!, duration![, volume!][, panPosition!][, waveform&][, waveformParameters!][, voice&]]
                    Sound 800, .4, , , 5
                    Sound 200, .75, , , 6
                    Sound 100, .75, , , 7
                    nox(rock) = 1
                    rock = rock - 1
                    num = num - 1
                    cx2(chk) = -500: cy2(chk) = 1200
                    lx(lz) = -150: ly(lz) = -150: ldir(lz) = 0
                    score = score + 10
                    _Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)
                    hits = hits + 1
                    laser = 0
                End If
            Next chk
            'Detect Level Change
            If hits > numAsteroids - 1 Then
                Cls
                level = level + 1
                For n = 1 To 200
                    nox(n) = 0
                Next n
                For n = 0 To 100
                    cx2(n) = -400: cy2(n) = -400
                    dx(n) = 0: dy(n) = 0
                    angle2(n) = 0
                Next n
                GoTo start2
            End If
        Next lz
    End If

    'Draw ship,
    DrawTriangle sx, sy, 20, angle, x1, y1

    For chk = 0 To numAsteroids - 1
        distance = Sqr((sx - cx2(chk)) ^ 2 + (sy - cy2(chk)) ^ 2)
        If distance <= r3 + radius2 Then
            DetectCollision = -1 ' True (collision detected)
        Else
            DetectCollision = 0 ' False (no collision)
        End If
        If DetectCollision = -1 And nox(chk) <> 1 Then
            det = 1
            health = health - .2
            healthp = Int((health / 50) * 100)
            _Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)
            If health < .01 Then
                health = 0
                For explosion = 1 To 200
                    Circle (sx, sy + 25), explosion, _RGB32(255, 0, 0)
                Next explosion
                For nn = 1 To 200
                    nox(nn) = 0
                Next nn
                Sound 500, 4, , , 8
                Sound 500, 8, , , 5
                Sound 100, 4, , , 7
                Locate 20, 30: Print "G A M E    O V E R"
                Locate 25, 30: Input "Again (Y/N)"; ag$
                ag2$ = LTrim$(RTrim$(ag$))
                If Left$(ag2$, 1) = "y" Or Left$(ag2$, 1) = "Y" Then GoTo start
                End
            End If
        End If
    Next chk
    skip3:
    If det > 0 Then
        det = det + 1
        Paint (sx, sy), _RGB32(255, 0, 0), _RGB32(255, 255, 255)
        If det > 200 Then det = 0
    End If

    If loops < 1000 Then
        loops = loops + 1
    End If
    _Display
    Cls
Loop


'from Steve Gold standard
Sub fillCircle (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
    Dim Radius As Integer, RadiusError As Integer
    Dim X As Integer, Y As Integer
    Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
    If Radius = 0 Then PSet (CX, CY), C: Exit Sub
    Line (CX - X, CY)-(CX + X, CY), C, BF
    While X > Y
        RadiusError = RadiusError + Y * 2 + 1
        If RadiusError >= 0 Then
            If X <> Y + 1 Then
                Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
                Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
            End If
            X = X - 1
            RadiusError = RadiusError - X * 2
        End If
        Y = Y + 1
        Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
        Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
    Wend
End Sub

Sub DrawTriangle (cx As Integer, cy As Integer, size As Integer, angle As Single, x1, y1)
    Dim x2 As Single, y2 As Single
    Dim x3 As Single, y3 As Single
    Dim a1 As Single, a2 As Single, a3 As Single

    ' Define angles of triangle vertices
    a1 = angle
    a2 = angle + 120
    a3 = angle + 240

    ' Convert polar to Cartesian coordinates
    x1 = cx + size * Cos(a1 * _Pi / 180)
    y1 = cy + size * Sin(a1 * _Pi / 180)

    x2 = cx + size * Cos(a2 * _Pi / 180)
    y2 = cy + size * Sin(a2 * _Pi / 180)

    x3 = cx + size * Cos(a3 * _Pi / 180)
    y3 = cy + size * Sin(a3 * _Pi / 180)

    ' Draw triangle
    Line (x1, y1)-(x2, y2), _RGB32(255, 255, 255)
    Line (x2, y2)-(x3, y3), _RGB32(255, 255, 255)
    Line (x3, y3)-(x1, y1), _RGB32(255, 255, 255)

    'Draw Gun
    gx = x1 + Cos(angle * _Pi / 180) * 5
    gy = y1 + Sin(angle * _Pi / 180) * 5
    Line (x1, y1)-(gx, gy), _RGB32(255, 255, 255)
End Sub





RE: Asteroids Clone - TempodiBasic - 02-02-2025

I like it but where do you leave the mouse?


RE: Asteroids Clone - Deciheximal - 02-02-2025

Someone on the Discord forum suggested I share this ChatGPT o3-generated Asteroids clone on this thread. Arrow keys to move, space to fire, B to halt ship.

Code: (Select All)
'--------------------------------------------
' Asteroids Clone in QB64 Phoenix Edition
'--------------------------------------------

' === CONSTANTS AND GLOBAL DECLARATIONS ===
CONST SCREENSIZEX = 352 ' 352 pixels wide
CONST SCREENSIZEY = 256 ' 256 pixels tall
CONST MAX_SPEED = 5 ' Maximum speed for the spaceship

COMMON SHARED viewSizeX, viewSizeY

' --- Game Settings ---
CONST MAX_ASTEROIDS = 5 ' Number of asteroids

' --- Asteroid Variables (arrays are global and sized on declaration) ---
DIM SHARED astX(1 TO MAX_ASTEROIDS), astY(1 TO MAX_ASTEROIDS)
DIM SHARED astDX(1 TO MAX_ASTEROIDS), astDY(1 TO MAX_ASTEROIDS)

' --- Spaceship Variables ---
COMMON SHARED shipX, shipY ' Position of the spaceship
COMMON SHARED shipAngle ' Facing direction (in degrees)
COMMON SHARED shipSpeed ' Current speed

' --- Bullet Variables ---
COMMON SHARED bulletActive ' 0 = no bullet active, 1 = bullet is active
COMMON SHARED bulletX, bulletY ' Bullet position
COMMON SHARED bulletDX, bulletDY ' Bullet velocity

'============================================
' SET UP SCREEN AND INITIAL GAME VARIABLES
'============================================

'_FULLSCREEN
SCREEN _NEWIMAGE(SCREENSIZEX, SCREENSIZEY, 13) ' Third argument MUST be 13
_FONT 8 ' Ensures the font is 8x8 (QB64-specific)

' Set play area (for example, if you want to use ASCII coordinate notions)
viewSizeX = 32
viewSizeY = 24

' Initialize spaceship in the center of the screen.
shipX = SCREENSIZEX / 2
shipY = SCREENSIZEY / 2
shipAngle = 0 ' Facing right (0 degrees)
shipSpeed = 0

' Bullet is initially inactive.
bulletActive = 0

' Randomize and initialize asteroid positions and velocities.
RANDOMIZE TIMER
FOR ii = 1 TO MAX_ASTEROIDS
  astX(ii) = RND * SCREENSIZEX
  astY(ii) = RND * SCREENSIZEY
  ' Give each asteroid a random velocity between -1 and 1 (pixels per frame)
  astDX(ii) = (RND * 2 - 1)
  astDY(ii) = (RND * 2 - 1)
NEXT ii

'============================================
' MAIN GAME LOOP
'============================================
DO
  ' --- Handle Input ---
  ' Rotate spaceship left: key 4 (keycode 52 or alternate 19200)
  IF _KEYDOWN(52) OR _KEYDOWN(19200) THEN
    shipAngle = shipAngle - 5
  END IF

  ' Rotate spaceship right: key 6 (keycode 54 or alternate 19712)
  IF _KEYDOWN(54) OR _KEYDOWN(19712) THEN
    shipAngle = shipAngle + 5
  END IF

  ' Accelerate forward: use key 8 (keycode 56 or alternate 18432) as “up”
  IF _KEYDOWN(56) OR _KEYDOWN(18432) THEN
    shipSpeed = shipSpeed + 0.1
  END IF

  ' Decelerate / Brake: use key 2 (keycode 50 or alternate 20480) as “down”
  IF _KEYDOWN(50) OR _KEYDOWN(20480) THEN
    shipSpeed = shipSpeed - 0.1
    IF shipSpeed < 0 THEN shipSpeed = 0
  END IF

  ' Halt momentum: B key (keycode 66 or alternate 98) sets speed to 0.
  IF _KEYDOWN(66) OR _KEYDOWN(98) THEN
    shipSpeed = 0
  END IF

  ' Enforce maximum speed limit.
  IF shipSpeed > MAX_SPEED THEN shipSpeed = MAX_SPEED

  ' Fire Bullet: space bar (keycode 32)
  IF _KEYDOWN(32) THEN
    IF bulletActive = 0 THEN
      bulletActive = 1
      bulletX = shipX
      bulletY = shipY
      ' Set bullet velocity in the direction the ship is facing.
      bulletDX = COS(shipAngle * 3.14159 / 180) * 4
      bulletDY = SIN(shipAngle * 3.14159 / 180) * 4
    END IF
  END IF

  ' --- Update Game Objects ---
  ' Update spaceship position (using its current speed and angle)
  shipX = shipX + COS(shipAngle * 3.14159 / 180) * shipSpeed
  shipY = shipY + SIN(shipAngle * 3.14159 / 180) * shipSpeed

  ' Screen wrapping for spaceship:
  IF shipX < 0 THEN shipX = SCREENSIZEX
  IF shipX > SCREENSIZEX THEN shipX = 0
  IF shipY < 0 THEN shipY = SCREENSIZEY
  IF shipY > SCREENSIZEY THEN shipY = 0

  ' Update bullet if active:
  IF bulletActive = 1 THEN
    bulletX = bulletX + bulletDX
    bulletY = bulletY + bulletDY
    ' If bullet goes off screen, deactivate it.
    IF bulletX < 0 OR bulletX > SCREENSIZEX OR bulletY < 0 OR bulletY > SCREENSIZEY THEN
      bulletActive = 0
    END IF
  END IF

  ' Update asteroids:
  FOR ii = 1 TO MAX_ASTEROIDS
    astX(ii) = astX(ii) + astDX(ii)
    astY(ii) = astY(ii) + astDY(ii)
    ' Wrap asteroids around the screen.
    IF astX(ii) < 0 THEN astX(ii) = SCREENSIZEX
    IF astX(ii) > SCREENSIZEX THEN astX(ii) = 0
    IF astY(ii) < 0 THEN astY(ii) = SCREENSIZEY
    IF astY(ii) > SCREENSIZEY THEN astY(ii) = 0
  NEXT ii

  ' --- Collision Detection ---
  ' Check collisions between bullet and asteroids.
  IF bulletActive = 1 THEN
    FOR ii = 1 TO MAX_ASTEROIDS
      ' Using 2 as bullet radius and 12 as asteroid radius.
      IF isCollision(bulletX, bulletY, 2, astX(ii), astY(ii), 12) THEN
        ' On collision, reset the asteroid and deactivate the bullet.
        astX(ii) = RND * SCREENSIZEX
        astY(ii) = RND * SCREENSIZEY
        bulletActive = 0
        EXIT FOR
      END IF
    NEXT ii
  END IF

  ' Check collisions between spaceship and asteroids.
  ' Here, we use an approximate collision radius of 10 for the spaceship.
  FOR ii = 1 TO MAX_ASTEROIDS
    IF isCollision(shipX, shipY, 10, astX(ii), astY(ii), 12) THEN
      CALL ESCAPETEXT("GAME OVER")
    END IF
  NEXT ii

  ' --- Draw Everything ---
  CLS ' Clear the screen

  ' Draw the spaceship as a triangle.
  ' Calculate the three vertices based on shipX, shipY and shipAngle.
  shipTipX = shipX + COS(shipAngle * 3.14159 / 180) * 10
  shipTipY = shipY + SIN(shipAngle * 3.14159 / 180) * 10
  shipLeftX = shipX + COS((shipAngle + 140) * 3.14159 / 180) * 8
  shipLeftY = shipY + SIN((shipAngle + 140) * 3.14159 / 180) * 8
  shipRightX = shipX + COS((shipAngle - 140) * 3.14159 / 180) * 8
  shipRightY = shipY + SIN((shipAngle - 140) * 3.14159 / 180) * 8

  LINE (shipLeftX, shipLeftY)-(shipTipX, shipTipY), 15
  LINE (shipTipX, shipTipY)-(shipRightX, shipRightY), 15
  LINE (shipRightX, shipRightY)-(shipLeftX, shipLeftY), 15

  ' Draw bullet if it is active.
  IF bulletActive = 1 THEN
    CIRCLE (bulletX, bulletY), 2, 15
  END IF

  ' Draw each asteroid as a circle.
  FOR ii = 1 TO MAX_ASTEROIDS
    CIRCLE (astX(ii), astY(ii)), 12, 15
  NEXT ii

  _DISPLAY ' Refresh the graphics screen

  ' --- Exit Check ---
  IF _KEYDOWN(27) THEN CALL ESCAPE(0) ' Exit if ESC is pressed

  _LIMIT 30 ' Limit frame rate to 30 FPS
LOOP

'============================================
' COLLISION DETECTION FUNCTION
'============================================
FUNCTION isCollision (obj1X, obj1Y, r1, obj2X, obj2Y, r2)
  DIM dx, dy, distanceSquared, rSumSquared
  dx = obj1X - obj2X
  dy = obj1Y - obj2Y
  distanceSquared = dx * dx + dy * dy
  rSumSquared = (r1 + r2) * (r1 + r2)
  IF distanceSquared <= rSumSquared THEN
    isCollision = 1
  ELSE
    isCollision = 0
  END IF
END FUNCTION

'============================================
' EXAMPLE FUNCTION (for reference)
'============================================
FUNCTION moveCheck (wPosX, wPosY)
  ' A dummy function to illustrate function returns.
  moveCheck = 1
END FUNCTION

'============================================
' HELPER SUBROUTINES (NEVER MODIFY THESE)
'============================================

SUB ZLOCATE (wx, wy)
  ' Adjusts LOCATE since QB64’s LOCATE starts at row 1.
  LOCATE wy + 1, wx + 1
END SUB

SUB ESCAPE (code)
  ' Display an exit message with the given code and end the program.
  COLOR 15
  ZLOCATE 10, 5: PRINT "                              "
  ZLOCATE 10, 6: PRINT "ENDED WITH CODE:"; code; "    "
  ZLOCATE 10, 7: PRINT "                              "
  END
END SUB

SUB ESCAPETEXT (wStr$)
  ' Display an exit message (for example, GAME OVER) and end the program.
  COLOR 15

  xPos = 1
  yPos = 5

  IF wStr$ = "" THEN wStr$ = "No text sent"

  ZLOCATE xPos, yPos: PRINT "                "
  ZLOCATE xPos, yPos + 2: PRINT "                " ' Clear line before text in case of wrap
  ZLOCATE xPos, yPos + 1: PRINT "  "; wStr$; " "

  END
END SUB



RE: Asteroids Clone - bplus - 02-02-2025

Nice, more natural navigation and better shooting in less LOC Big Grin

Welcome to the forum @Deciheximal

Oh hey did that work right out-of-the-box from the AI or did you have to fix a few things?


RE: Asteroids Clone - TempodiBasic - 02-02-2025

@Deciheximal
fine code, I can affirm that chatGPT is better coder than me.
QB64pe IDE found 2 unused variables, wposX and wposY.

Interesting the Helper suborutines set and the MoveCheck example function.
ChatGPT prefers the Pitagora's theorem for collision detecting.
Just a human dubt... is it possible packing all the action done into the many FOR loops into just one FOR loop?

yes it seems to be possible
here my confusional mod that packes all FOR loop of asteroids to one loop
Code: (Select All)

'--------------------------------------------
' Asteroids Clone in QB64 Phoenix Edition
'--------------------------------------------

' === CONSTANTS AND GLOBAL DECLARATIONS ===
Const SCREENSIZEX = 352 ' 352 pixels wide
Const SCREENSIZEY = 256 ' 256 pixels tall
Const MAX_SPEED = 5 ' Maximum speed for the spaceship

Common Shared viewSizeX, viewSizeY

' --- Game Settings ---
Const MAX_ASTEROIDS = 5 ' Number of asteroids

' --- Asteroid Variables (arrays are global and sized on declaration) ---
Dim Shared astX(1 To MAX_ASTEROIDS), astY(1 To MAX_ASTEROIDS)
Dim Shared astDX(1 To MAX_ASTEROIDS), astDY(1 To MAX_ASTEROIDS)

' --- Spaceship Variables ---
Common Shared shipX, shipY ' Position of the spaceship
Common Shared shipAngle ' Facing direction (in degrees)
Common Shared shipSpeed ' Current speed

' --- Bullet Variables ---
Common Shared bulletActive ' 0 = no bullet active, 1 = bullet is active
Common Shared bulletX, bulletY ' Bullet position
Common Shared bulletDX, bulletDY ' Bullet velocity

'============================================
' SET UP SCREEN AND INITIAL GAME VARIABLES
'============================================

'_FULLSCREEN
Screen _NewImage(SCREENSIZEX, SCREENSIZEY, 13) ' Third argument MUST be 13
_Font 8 ' Ensures the font is 8x8 (QB64-specific)

' Set play area (for example, if you want to use ASCII coordinate notions)
viewSizeX = 32
viewSizeY = 24

' Initialize spaceship in the center of the screen.
shipX = SCREENSIZEX / 2
shipY = SCREENSIZEY / 2
shipAngle = 0 ' Facing right (0 degrees)
shipSpeed = 0

' Bullet is initially inactive.
bulletActive = 0

' Randomize and initialize asteroid positions and velocities.
Randomize Timer
For ii = 1 To MAX_ASTEROIDS
    astX(ii) = Rnd * SCREENSIZEX
    astY(ii) = Rnd * SCREENSIZEY
    ' Give each asteroid a random velocity between -1 and 1 (pixels per frame)
    astDX(ii) = (Rnd * 2 - 1)
    astDY(ii) = (Rnd * 2 - 1)
Next ii

'============================================
' MAIN GAME LOOP
'============================================
Do
    ' --- Handle Input ---
    ' Rotate spaceship left: key 4 (keycode 52 or alternate 19200)
    If _KeyDown(52) Or _KeyDown(19200) Then
        shipAngle = shipAngle - 5
    End If

    ' Rotate spaceship right: key 6 (keycode 54 or alternate 19712)
    If _KeyDown(54) Or _KeyDown(19712) Then
        shipAngle = shipAngle + 5
    End If

    ' Accelerate forward: use key 8 (keycode 56 or alternate 18432) as “up”
    If _KeyDown(56) Or _KeyDown(18432) Then
        shipSpeed = shipSpeed + 0.1
    End If

    ' Decelerate / Brake: use key 2 (keycode 50 or alternate 20480) as “down”
    If _KeyDown(50) Or _KeyDown(20480) Then
        shipSpeed = shipSpeed - 0.1
        If shipSpeed < 0 Then shipSpeed = 0
    End If

    ' Halt momentum: B key (keycode 66 or alternate 98) sets speed to 0.
    If _KeyDown(66) Or _KeyDown(98) Then
        shipSpeed = 0
    End If

    ' Enforce maximum speed limit.
    If shipSpeed > MAX_SPEED Then shipSpeed = MAX_SPEED

    ' Fire Bullet: space bar (keycode 32)
    If _KeyDown(32) Then
        If bulletActive = 0 Then
            bulletActive = 1
            bulletX = shipX
            bulletY = shipY
            ' Set bullet velocity in the direction the ship is facing.
            bulletDX = Cos(shipAngle * 3.14159 / 180) * 4
            bulletDY = Sin(shipAngle * 3.14159 / 180) * 4
        End If
    End If

    ' --- Update Game Objects ---
    ' Update spaceship position (using its current speed and angle)
    shipX = shipX + Cos(shipAngle * 3.14159 / 180) * shipSpeed
    shipY = shipY + Sin(shipAngle * 3.14159 / 180) * shipSpeed

    ' Screen wrapping for spaceship:
    If shipX < 0 Then shipX = SCREENSIZEX
    If shipX > SCREENSIZEX Then shipX = 0
    If shipY < 0 Then shipY = SCREENSIZEY
    If shipY > SCREENSIZEY Then shipY = 0

    ' Update bullet if active:
    If bulletActive = 1 Then
        bulletX = bulletX + bulletDX
        bulletY = bulletY + bulletDY
        ' If bullet goes off screen, deactivate it.
        If bulletX < 0 Or bulletX > SCREENSIZEX Or bulletY < 0 Or bulletY > SCREENSIZEY Then
            bulletActive = 0
        End If
    End If

    Cls ' Clear the screen

    ' Update asteroids:
    For ii = 1 To MAX_ASTEROIDS
        astX(ii) = astX(ii) + astDX(ii)
        astY(ii) = astY(ii) + astDY(ii)
        ' Wrap asteroids around the screen.
        If astX(ii) < 0 Then astX(ii) = SCREENSIZEX
        If astX(ii) > SCREENSIZEX Then astX(ii) = 0
        If astY(ii) < 0 Then astY(ii) = SCREENSIZEY
        If astY(ii) > SCREENSIZEY Then astY(ii) = 0

        ' Check collisions between bullet and asteroids.
        If bulletActive = 1 Then
            ' Using 2 as bullet radius and 12 as asteroid radius.
            If isCollision(bulletX, bulletY, 2, astX(ii), astY(ii), 12) Then
                ' On collision, reset the asteroid and deactivate the bullet.
                astX(ii) = Rnd * SCREENSIZEX
                astY(ii) = Rnd * SCREENSIZEY
                bulletActive = 0
            End If
        End If

        ' Check collisions between spaceship and asteroids.
        ' Here, we use an approximate collision radius of 10 for the spaceship.
        If isCollision(shipX, shipY, 10, astX(ii), astY(ii), 12) Then
            Call ESCAPETEXT("GAME OVER")
        End If

        ' Draw each asteroid as a circle.
        Circle (astX(ii), astY(ii)), 12, 15
    Next ii

    ' --- Collision Detection ---
    ' Check collisions between bullet and asteroids.
    'If bulletActive = 1 Then
    '    For ii = 1 To MAX_ASTEROIDS
    '        ' Using 2 as bullet radius and 12 as asteroid radius.
    '        If isCollision(bulletX, bulletY, 2, astX(ii), astY(ii), 12) Then
    '            ' On collision, reset the asteroid and deactivate the bullet.
    '            astX(ii) = Rnd * SCREENSIZEX
    '            astY(ii) = Rnd * SCREENSIZEY
    '            bulletActive = 0
    '            Exit For
    '        End If
    '    Next ii
    'End If

    ' Check collisions between spaceship and asteroids.
    ' Here, we use an approximate collision radius of 10 for the spaceship.
    'For ii = 1 To MAX_ASTEROIDS
    '    If isCollision(shipX, shipY, 10, astX(ii), astY(ii), 12) Then
    '        Call ESCAPETEXT("GAME OVER")
    '    End If
    'Next ii

    ' --- Draw Everything ---
    'Cls ' Clear the screen

    ' Draw the spaceship as a triangle.
    ' Calculate the three vertices based on shipX, shipY and shipAngle.
    shipTipX = shipX + Cos(shipAngle * 3.14159 / 180) * 10
    shipTipY = shipY + Sin(shipAngle * 3.14159 / 180) * 10
    shipLeftX = shipX + Cos((shipAngle + 140) * 3.14159 / 180) * 8
    shipLeftY = shipY + Sin((shipAngle + 140) * 3.14159 / 180) * 8
    shipRightX = shipX + Cos((shipAngle - 140) * 3.14159 / 180) * 8
    shipRightY = shipY + Sin((shipAngle - 140) * 3.14159 / 180) * 8

    Line (shipLeftX, shipLeftY)-(shipTipX, shipTipY), 15
    Line (shipTipX, shipTipY)-(shipRightX, shipRightY), 15
    Line (shipRightX, shipRightY)-(shipLeftX, shipLeftY), 15

    ' Draw bullet if it is active.
    If bulletActive = 1 Then
        Circle (bulletX, bulletY), 2, 15
    End If

    ' Draw each asteroid as a circle.
    'For ii = 1 To MAX_ASTEROIDS
    '    Circle (astX(ii), astY(ii)), 12, 15
    'Next ii

    _Display ' Refresh the graphics screen

    ' --- Exit Check ---
    If _KeyDown(27) Then Call ESCAPE(0) ' Exit if ESC is pressed

    _Limit 30 ' Limit frame rate to 30 FPS
Loop

'============================================
' COLLISION DETECTION FUNCTION
'============================================
Function isCollision (obj1X, obj1Y, r1, obj2X, obj2Y, r2)
    Dim dx, dy, distanceSquared, rSumSquared
    dx = obj1X - obj2X
    dy = obj1Y - obj2Y
    distanceSquared = dx * dx + dy * dy
    rSumSquared = (r1 + r2) * (r1 + r2)
    If distanceSquared <= rSumSquared Then
        isCollision = 1
    Else
        isCollision = 0
    End If
End Function

'============================================
' EXAMPLE FUNCTION (for reference)
'============================================
Function moveCheck (wPosX, wPosY)
    ' A dummy function to illustrate function returns.
    moveCheck = 1
End Function

'============================================
' HELPER SUBROUTINES (NEVER MODIFY THESE)
'============================================

Sub ZLOCATE (wx, wy)
    ' Adjusts LOCATE since QB64’s LOCATE starts at row 1.
    Locate wy + 1, wx + 1
End Sub

Sub ESCAPE (code)
    ' Display an exit message with the given code and end the program.
    Color 15
    ZLOCATE 10, 5: Print "                              "
    ZLOCATE 10, 6: Print "ENDED WITH CODE:"; code; "    "
    ZLOCATE 10, 7: Print "                              "
    End
End Sub

Sub ESCAPETEXT (wStr$)
    ' Display an exit message (for example, GAME OVER) and end the program.
    Color 15

    xPos = 1
    yPos = 5

    If wStr$ = "" Then wStr$ = "No text sent"

    ZLOCATE xPos, yPos: Print "                "
    ZLOCATE xPos, yPos + 2: Print "                " ' Clear line before text in case of wrap
    ZLOCATE xPos, yPos + 1: Print "  "; wStr$; " "

    End
End Sub

it is just a curiosity and not a better code. But it still works.
The game sometimes starts with a GAME OVER. It is probable that sometimes the starship and an asteroid are born in the same place.


RE: Asteroids Clone - aadityap0901 - 02-02-2025

Bro this is a very lovely game!

I made one a year ago, and added some AI after watching a youtube video: Asteroids with NEAT Genetic Algorithm
But implementing that was hard, so I scrapped that idea.

The rock design is pretty good actually!


RE: Asteroids Clone - SierraKen - 02-02-2025

TempodiBasic, mine doesn't use a mouse, it uses the keyboard. Instructions are on the welcome screen.


RE: Asteroids Clone - SierraKen - 02-02-2025

Thank you Deciheximal!! Your game helped me improve mine greatly. Now the keys work every time as soon as you press them. Also, I took up what B+ said about natural flight, so I changed that too, and it's actually a much more fun game to play. Big Grin 

What do you guys think?

Code: (Select All)


'Asteroids Clone by, SierraKen
'February 2, 2025
'Thank you QB64pe Forum for your inspiration!
'Thank you also ChatGPT for a lot of the math.
'This game is not intended to replace any other game already in existence.
'It is a labor of love and given out for free.

'Update: Fixed speed of ship and better response to keys. Thank you Deciheximal for your game code!


_Title "Asteroids Clone - by SierraKen"

Screen _NewImage(800, 600, 32)

Randomize Timer

Dim oldx(100), oldy(100)
Dim llx(500), lly(500)
Dim lx(500), ly(500), ldir(500)
Dim x1 As Single, y1 As Single

Const numPoints = 30

Dim x2(100, 40), y2(100, 40)
Dim xRot(100, 40), yRot(100, 40)
Dim cx2(200), cy2(200), angle2(200)
Dim dx(100), dy(100)
Dim nox(500)

start:

numAsteroids = 8
radius2 = 45 'Asteroids
level = 1
score = 0
health = 50
healthp = 100
rot = -90

Cls
_AutoDisplay

Locate 3, 25: Print "A s t e r o i d s  C l o n e"
Locate 5, 25: Print "By SierraKen"
Locate 10, 25: Print "Move your ship around with the arrow keys."
Locate 11, 25: Print "Turn your ship with the left and right arrow keys."
Locate 12, 25: Print "Go forward with the up arrow key."
Locate 12, 25: Print "Press Space Bar to fire at asteroids."
Locate 13, 25: Print "To pause and un-pause, press Esc."
Locate 14, 25: Print "Press Q anytime to quit."
Locate 20, 25: Input "Press Enter to Begin.", a$

start2:
Cls

_Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)

numAsteroids = numAsteroids + 2
If numAsteroids > 40 Then numAsteroids = 40
num = numAsteroids
rock = 0
hits = 0
speed2 = 0
laser = 0
ll = 0
numpoints2 = Int(Rnd * numPoints) + 10

For ll3 = 1 To 200
    lx(ll3) = 0
    ly(ll3) = 0
    ldir(ll3) = 0
Next ll3

sx = 400
sy = 300
oldx = 400
oldy = 300
det = 0

r1 = 3 'bullets
r3 = 25 'Your ship
loops = 0

Play "MB"

' Initialize asteroids
For a = 0 To num - 1
    more:
    cx2(a) = Int(Rnd * 680) + 55 ' Random start X
    cy2(a) = Int(Rnd * 480) + 55 ' Random start Y
    If cx2(a) > 250 And cx2(a) < 550 And cy2(a) > 150 And cy2(a) < 450 Then GoTo more:
    angle2(a) = Rnd * 360 ' Random starting rotation
    dx(a) = (Rnd - 0.5) * 2 ' Random speed X (-1 to 1)
    dy(a) = (Rnd - 0.5) * 2 ' Random speed Y (-1 to 1)

    ' Generate random asteroid shape
    For i = 0 To numpoints2
        ang = i * (360 / numpoints2)
        rOffset = radius2 + Int(Rnd * 15 - 7) ' Vary radius randomly
        x2(a, i) = Cos(ang * _Pi / 180) * rOffset
        y2(a, i) = Sin(ang * _Pi / 180) * rOffset
    Next
    rock = rock + 1
Next

Do
    _Limit 100


    If _KeyDown(32) Then
        laser = 1
        ll = ll + 1
        If ll > 500 Then ll = 1
        lx(ll) = x1
        ly(ll) = y1
        ldir(ll) = angle
    End If

    If _KeyDown(52) Or _KeyDown(19200) Then dir1 = 1 ' Left Arrow (rotate counterclockwise)
    If _KeyDown(54) Or _KeyDown(19712) Then dir1 = 2 ' Right Arrow (rotate clockwise)
    If _KeyDown(56) Or _KeyDown(18432) Then dir2 = 3 ' Up Arrow (thrust forward)
    If _KeyDown(50) Or _KeyDown(20480) Then dir2 = 4 ' Down Arrow (thrust backward)

    If dir1 = 1 Then
        angle = angle - 1
        If right = 1 Then
            right = 0
            dir1 = 0
            GoTo nex2
        End If
        left = 1
        right = 0
    End If
    If dir1 = 2 Then
        angle = angle + 1
        If left = 1 Then
            left = 0
            dir1 = 0
            GoTo nex2
        End If
        right = 1
        left = 0
    End If
    If dir2 = 3 Then
        speed2 = speed2 + .01
        If speed2 > 2 Then speed2 = 2
        sx = sx + speed2 * Cos(angle * _Pi / 180)
        sy = sy + speed2 * Sin(angle * _Pi / 180)
    End If
    If dir2 = 4 Then
        speed2 = speed2 - .01
        If speed2 < 0 Then speed2 = 0
        sx = sx + speed2 * Cos(angle * _Pi / 180)
        sy = sy + speed2 * Sin(angle * _Pi / 180)
    End If
    nex2:
    If sx > 800 Then sx = 0
    If sx < 0 Then sx = 800
    If sy > 600 Then sy = 0
    If sy < 0 Then sy = 600

    If _KeyHit = 27 Then
        Do
            If _KeyHit = 27 Then GoTo go
        Loop
    End If
    go:
    k$ = ""
    If _KeyDown(81) Or _KeyDown(113) Then End

    ' Update and draw each asteroid
    For a = 0 To num - 1
        If nox(a) = 1 Then GoTo skip
        angle2(a) = angle2(a) + 1 ' Rotate
        If angle2(a) >= 360 Then angle2(a) = 0

        ' Rotate asteroid points
        rad = angle2(a) * _Pi / 180
        For i = 0 To numpoints2
            xRot(a, i) = cx2(a) + (x2(a, i) * Cos(rad) - y2(a, i) * Sin(rad))
            yRot(a, i) = cy2(a) + (x2(a, i) * Sin(rad) + y2(a, i) * Cos(rad))
        Next

        ' Draw asteroid
        For i = 0 To numpoints2 - 1
            j = (i + 1) Mod numpoints2
            Line (xRot(a, i), yRot(a, i))-(xRot(a, j), yRot(a, j)), _RGB32(255, 255, 255)
        Next

        ' Move asteroid
        cx2(a) = cx2(a) + dx(a)
        cy2(a) = cy2(a) + dy(a)

        ' Wrap around screen edges
        If cx2(a) < 0 Then cx2(a) = 800
        If cx2(a) > 800 Then cx2(a) = 0
        If cy2(a) < 0 Then cy2(a) = 600
        If cy2(a) > 600 Then cy2(a) = 0
    Next

    skip:

    If laser = 1 Then
        For lz = 0 To ll - 1 Step 10
            lx2 = Cos(ldir(lz) * _Pi / 180) * 10
            ly2 = Sin(ldir(lz) * _Pi / 180) * 10
            lx(lz) = lx2 + lx(lz)
            ly(lz) = ly2 + ly(lz)
            If lx(lz) > 850 Then lx(lz) = 850
            If lx(lz) < -50 Then lx(lz) = -50
            If ly(lz) > 650 Then ly(lz) = 650
            If ly(lz) < -50 Then ly(lz) = -50
            fillCircle lx(lz), ly(lz), r1, _RGB32(255, 0, 5)
            For chk = 0 To num - 1
                distance = Sqr((lx(lz) - cx2(chk)) ^ 2 + (ly(lz) - cy2(chk)) ^ 2)
                If distance <= r1 + radius2 Then
                    DetectCollision = -1 ' True (collision detected)
                Else
                    DetectCollision = 0 ' False (no collision)
                End If

                If DetectCollision = -1 And nox(chk) <> 1 Then
                    For explosion = 1 To 50
                        Circle (lx(lz), ly(lz)), explosion, _RGB32(255, 0, 0)
                        llx(explosion) = lx(lz)
                        lly(explosion) = ly(lz)
                    Next explosion
                    'SOUND frequency!, duration![, volume!][, panPosition!][, waveform&][, waveformParameters!][, voice&]]
                    Sound 800, .4, , , 5
                    Sound 200, .75, , , 6
                    Sound 100, .75, , , 7
                    nox(rock) = 1
                    rock = rock - 1
                    num = num - 1
                    cx2(chk) = -500: cy2(chk) = 1200
                    lx(lz) = -150: ly(lz) = -150: ldir(lz) = 0
                    score = score + 10
                    _Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)
                    hits = hits + 1
                    laser = 0
                End If
            Next chk
            'Detect Level Change
            If hits > numAsteroids - 1 Then
                Cls
                level = level + 1
                For n = 1 To 200
                    nox(n) = 0
                Next n
                For n = 0 To 100
                    cx2(n) = -400: cy2(n) = -400
                    dx(n) = 0: dy(n) = 0
                    angle2(n) = 0
                Next n
                GoTo start2
            End If
        Next lz
    End If

    'Draw ship,
    DrawTriangle sx, sy, 20, angle, x1, y1

    For chk = 0 To num - 1
        distance = Sqr((sx - cx2(chk)) ^ 2 + (sy - cy2(chk)) ^ 2)
        If distance <= r3 + radius2 Then
            DetectCollision = -1 ' True (collision detected)
        Else
            DetectCollision = 0 ' False (no collision)
        End If
        If DetectCollision = -1 And nox(chk) <> 1 Then
            det = 1
            health = health - .2
            healthp = Int((health / 50) * 100)
            _Title "Score: " + Str$(score) + "    Health: " + Str$(healthp) + "%    Level: " + Str$(level)
            If health < .01 Then
                health = 0
                healthp = 0
                For explosion = 1 To 200
                    Circle (sx, sy + 25), explosion, _RGB32(255, 0, 0)
                Next explosion
                For nn = 1 To 200
                    nox(nn) = 0
                Next nn
                Sound 500, 4, , , 8
                Sound 500, 8, , , 5
                Sound 100, 4, , , 7
                Locate 20, 30: Print "G A M E    O V E R"
                Locate 25, 30: Input "Again (Y/N)"; ag$
                ag2$ = LTrim$(RTrim$(ag$))
                If Left$(ag2$, 1) = "y" Or Left$(ag2$, 1) = "Y" Then GoTo start
                End
            End If
        End If
    Next chk
    skip3:
    If det > 0 Then
        det = det + 1
        Paint (sx, sy), _RGB32(255, 0, 0), _RGB32(255, 255, 255)
        If det > 200 Then det = 0
    End If

    If loops < 1000 Then
        loops = loops + 1
    End If
    _Display
    Cls
Loop


'from Steve Gold standard
Sub fillCircle (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
    Dim Radius As Integer, RadiusError As Integer
    Dim X As Integer, Y As Integer
    Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
    If Radius = 0 Then PSet (CX, CY), C: Exit Sub
    Line (CX - X, CY)-(CX + X, CY), C, BF
    While X > Y
        RadiusError = RadiusError + Y * 2 + 1
        If RadiusError >= 0 Then
            If X <> Y + 1 Then
                Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
                Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
            End If
            X = X - 1
            RadiusError = RadiusError - X * 2
        End If
        Y = Y + 1
        Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
        Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
    Wend
End Sub

Sub DrawTriangle (cx As Integer, cy As Integer, size As Integer, angle As Single, x1, y1)
    Dim x2 As Single, y2 As Single
    Dim x3 As Single, y3 As Single
    Dim a1 As Single, a2 As Single, a3 As Single

    ' Define angles of triangle vertices
    a1 = angle
    a2 = angle + 120
    a3 = angle + 240

    ' Convert polar to Cartesian coordinates
    x1 = cx + size * Cos(a1 * _Pi / 180)
    y1 = cy + size * Sin(a1 * _Pi / 180)

    x2 = cx + size * Cos(a2 * _Pi / 180)
    y2 = cy + size * Sin(a2 * _Pi / 180)

    x3 = cx + size * Cos(a3 * _Pi / 180)
    y3 = cy + size * Sin(a3 * _Pi / 180)

    ' Draw triangle
    Line (x1, y1)-(x2, y2), _RGB32(255, 255, 255)
    Line (x2, y2)-(x3, y3), _RGB32(255, 255, 255)
    Line (x3, y3)-(x1, y1), _RGB32(255, 255, 255)

    'Draw Gun
    gx = x1 + Cos(angle * _Pi / 180) * 5
    gy = y1 + Sin(angle * _Pi / 180) * 5
    Line (x1, y1)-(gx, gy), _RGB32(255, 255, 255)
End Sub





RE: Asteroids Clone - SMcNeill - 02-03-2025

I played around with this and again added joystick support to the game. (Eventually, some dang body is going to notice and comment on whether or not a joystick works for them! LOL!!)

Code: (Select All)
'Asteroids Clone by, SierraKen
'February 2, 2025
'Thank you QB64pe Forum for your inspiration!
'Thank you also ChatGPT for a lot of the math.
'This game is not intended to replace any other game already in existence.
'It is a labor of love and given out for free.

'Update: Fixed speed of ship and better response to keys. Thank you Deciheximal for your game code!

'*** FROM: https://qb64phoenix.com/forum/showthread.php?tid=3424
'*** Joystick Support by Steve!
Type Axis_Type
Active As _Byte
X As Integer
Y As Integer
Vert As Single
Hort As Single
Angle As Single
End Type
ReDim Shared As Axis_Type JoyStick(0)
ReDim Shared As Long Button(0)
'*** END Top of JoyStick Support


_Title "Asteroids Clone - by SierraKen"

Screen _NewImage(800, 600, 32)

Randomize Timer

Dim oldx(100), oldy(100)
Dim llx(500), lly(500)
Dim lx(500), ly(500), ldir(500)
Dim x1 As Single, y1 As Single

Const numPoints = 30

Dim x2(100, 40), y2(100, 40)
Dim xRot(100, 40), yRot(100, 40)
Dim cx2(200), cy2(200), angle2(200)
Dim dx(100), dy(100)
Dim nox(500)

start:

numAsteroids = 8
radius2 = 45 'Asteroids
level = 1
score = 0
health = 50
healthp = 100
rot = -90

Cls
_AutoDisplay

Locate 3, 25: Print "A s t e r o i d s C l o n e"
Locate 5, 25: Print "By SierraKen"
Locate 10, 25: Print "Move your ship around with the arrow keys."
Locate 11, 25: Print "Turn your ship with the left and right arrow keys."
Locate 12, 25: Print "Go forward with the up arrow key."
Locate 12, 25: Print "Press Space Bar to fire at asteroids."
Locate 13, 25: Print "To pause and un-pause, press Esc."
Locate 14, 25: Print "Press Q anytime to quit."
Locate 20, 25: Print "Press Enter or press <Button 1> to Begin."
Do
ReadJoyStick
_Limit 15
Loop Until Button(1) Or _KeyDown(13)
_Delay .2
_KeyClear

start2:
Cls

_Title "Score: " + Str$(score) + " Health: " + Str$(healthp) + "% Level: " + Str$(level)

numAsteroids = numAsteroids + 2
If numAsteroids > 40 Then numAsteroids = 40
num = numAsteroids
rock = 0
hits = 0
speed2 = 0
laser = 0
ll = 0
numpoints2 = Int(Rnd * numPoints) + 10

For ll3 = 1 To 200
lx(ll3) = 0
ly(ll3) = 0
ldir(ll3) = 0
Next ll3

sx = 400
sy = 300
oldx = 400
oldy = 300
det = 0

r1 = 3 'bullets
r3 = 25 'Your ship
loops = 0

Play "MB"

' Initialize asteroids
For a = 0 To num - 1
more:
cx2(a) = Int(Rnd * 680) + 55 ' Random start X
cy2(a) = Int(Rnd * 480) + 55 ' Random start Y
If cx2(a) > 250 And cx2(a) < 550 And cy2(a) > 150 And cy2(a) < 450 Then GoTo more:
angle2(a) = Rnd * 360 ' Random starting rotation
dx(a) = (Rnd - 0.5) * 2 ' Random speed X (-1 to 1)
dy(a) = (Rnd - 0.5) * 2 ' Random speed Y (-1 to 1)

' Generate random asteroid shape
For i = 0 To numpoints2
ang = i * (360 / numpoints2)
rOffset = radius2 + Int(Rnd * 15 - 7) ' Vary radius randomly
x2(a, i) = Cos(ang * _Pi / 180) * rOffset
y2(a, i) = Sin(ang * _Pi / 180) * rOffset
Next
rock = rock + 1
Next

Do
_Limit 100

'*** JOYSTICK SUPPORT BY STEVE!! ***
ReadJoyStick
joybutton = _FALSE
joyleft = _FALSE: joyright = _FALSE
joyup = _FALSE: joydown = _FALSE
If Button(1) Then joybutton = _TRUE

For i = 1 To UBound(JoyStick) 'this checks for each axis of our joystick
If JoyStick(i).Active Then 'check to see if any of the joysticks are active
If JoyStick(i).X < 0 Then joyleft = _TRUE
If JoyStick(i).X > 0 Then joyright = _TRUE
If JoyStick(i).Y < 0 Then joyup = _TRUE
If JoyStick(i).Y > 0 Then joydown = _TRUE
End If
Next


If _KeyDown(32) _Orelse joybutton Then
laser = 1
ll = ll + 1
If ll > 500 Then ll = 1
lx(ll) = x1
ly(ll) = y1
ldir(ll) = angle
End If

If _KeyDown(52) Or _KeyDown(19200) _Orelse joyleft Then dir1 = 1 ' Left Arrow (rotate counterclockwise)
If _KeyDown(54) Or _KeyDown(19712) _Orelse joyright Then dir1 = 2 ' Right Arrow (rotate clockwise)
If _KeyDown(56) Or _KeyDown(18432) _Orelse joyup Then dir2 = 3 ' Up Arrow (thrust forward)
If _KeyDown(50) Or _KeyDown(20480) _Orelse joydown Then dir2 = 4 ' Down Arrow (thrust backward)

If dir1 = 1 Then
angle = angle - 1
If right = 1 Then
right = 0
dir1 = 0
GoTo nex2
End If
left = 1
right = 0
End If
If dir1 = 2 Then
angle = angle + 1
If left = 1 Then
left = 0
dir1 = 0
GoTo nex2
End If
right = 1
left = 0
End If
If dir2 = 3 Then
speed2 = speed2 + .01
If speed2 > 2 Then speed2 = 2
sx = sx + speed2 * Cos(angle * _Pi / 180)
sy = sy + speed2 * Sin(angle * _Pi / 180)
End If
If dir2 = 4 Then
speed2 = speed2 - .01
If speed2 < 0 Then speed2 = 0
sx = sx + speed2 * Cos(angle * _Pi / 180)
sy = sy + speed2 * Sin(angle * _Pi / 180)
End If
nex2:
If sx > 800 Then sx = 0
If sx < 0 Then sx = 800
If sy > 600 Then sy = 0
If sy < 0 Then sy = 600


If _KeyHit = 27 Or (Button(2) And Not oldbutton2.1) Then 'give time for button 2 to release
oldbutton2 = _TRUE
oldbutton2.1 = _TRUE
Do
ReadJoyStick
If _KeyHit = 27 Or (Button(2) And Not oldbutton2) GoTo go
oldbutton2 = Button(2)
_Limit 15
Loop
End If
oldbutton2.1 = Button(2) 'the
go:
k$ = ""
If _KeyDown(81) Or _KeyDown(113) Then End

' Update and draw each asteroid
For a = 0 To num - 1
If nox(a) = 1 Then GoTo skip
angle2(a) = angle2(a) + 1 ' Rotate
If angle2(a) >= 360 Then angle2(a) = 0

' Rotate asteroid points
rad = angle2(a) * _Pi / 180
For i = 0 To numpoints2
xRot(a, i) = cx2(a) + (x2(a, i) * Cos(rad) - y2(a, i) * Sin(rad))
yRot(a, i) = cy2(a) + (x2(a, i) * Sin(rad) + y2(a, i) * Cos(rad))
Next

' Draw asteroid
For i = 0 To numpoints2 - 1
j = (i + 1) Mod numpoints2
Line (xRot(a, i), yRot(a, i))-(xRot(a, j), yRot(a, j)), _RGB32(255, 255, 255)
Next

' Move asteroid
cx2(a) = cx2(a) + dx(a)
cy2(a) = cy2(a) + dy(a)

' Wrap around screen edges
If cx2(a) < 0 Then cx2(a) = 800
If cx2(a) > 800 Then cx2(a) = 0
If cy2(a) < 0 Then cy2(a) = 600
If cy2(a) > 600 Then cy2(a) = 0
Next

skip:

If laser = 1 Then
For lz = 0 To ll - 1 Step 10
lx2 = Cos(ldir(lz) * _Pi / 180) * 10
ly2 = Sin(ldir(lz) * _Pi / 180) * 10
lx(lz) = lx2 + lx(lz)
ly(lz) = ly2 + ly(lz)
If lx(lz) > 850 Then lx(lz) = 850
If lx(lz) < -50 Then lx(lz) = -50
If ly(lz) > 650 Then ly(lz) = 650
If ly(lz) < -50 Then ly(lz) = -50
fillCircle lx(lz), ly(lz), r1, _RGB32(255, 0, 5)
For chk = 0 To num - 1
distance = Sqr((lx(lz) - cx2(chk)) ^ 2 + (ly(lz) - cy2(chk)) ^ 2)
If distance <= r1 + radius2 Then
DetectCollision = -1 ' True (collision detected)
Else
DetectCollision = 0 ' False (no collision)
End If

If DetectCollision = -1 And nox(chk) <> 1 Then
For explosion = 1 To 50
Circle (lx(lz), ly(lz)), explosion, _RGB32(255, 0, 0)
llx(explosion) = lx(lz)
lly(explosion) = ly(lz)
Next explosion
'SOUND frequency!, duration![, volume!][, panPosition!][, waveform&][, waveformParameters!][, voice&]]
Sound 800, .4, , , 5
Sound 200, .75, , , 6
Sound 100, .75, , , 7
nox(rock) = 1
rock = rock - 1
num = num - 1
cx2(chk) = -500: cy2(chk) = 1200
lx(lz) = -150: ly(lz) = -150: ldir(lz) = 0
score = score + 10
_Title "Score: " + Str$(score) + " Health: " + Str$(healthp) + "% Level: " + Str$(level)
hits = hits + 1
laser = 0
End If
Next chk
'Detect Level Change
If hits > numAsteroids - 1 Then
Cls
level = level + 1
For n = 1 To 200
nox(n) = 0
Next n
For n = 0 To 100
cx2(n) = -400: cy2(n) = -400
dx(n) = 0: dy(n) = 0
angle2(n) = 0
Next n
GoTo start2
End If
Next lz
End If

'Draw ship,
DrawTriangle sx, sy, 20, angle, x1, y1

For chk = 0 To num - 1
distance = Sqr((sx - cx2(chk)) ^ 2 + (sy - cy2(chk)) ^ 2)
If distance <= r3 + radius2 Then
DetectCollision = -1 ' True (collision detected)
Else
DetectCollision = 0 ' False (no collision)
End If
If DetectCollision = -1 And nox(chk) <> 1 Then
det = 1
health = health - .2
healthp = Int((health / 50) * 100)
_Title "Score: " + Str$(score) + " Health: " + Str$(healthp) + "% Level: " + Str$(level)
If health < .01 Then
health = 0
healthp = 0
For explosion = 1 To 200
Circle (sx, sy + 25), explosion, _RGB32(255, 0, 0)
Next explosion
For nn = 1 To 200
nox(nn) = 0
Next nn
Sound 500, 4, , , 8
Sound 500, 8, , , 5
Sound 100, 4, , , 7
Locate 20, 30: Print "G A M E O V E R"
Locate 25, 30: Input "Again (Y/N)"; ag$
ag2$ = LTrim$(RTrim$(ag$))
oldbutton = _TRUE
oldbutton2 = _TRUE
Do
ReadJoyStick
If Button(1) And Not oldbutton GoTo start 'make certain button is up before accepting it as a down click
If Button(2) And Not oldbutton2 Then System 'quit with the 2nd button
oldbutton = Button(1): oldbutton2 = Button(2)
k = _KeyHit
Select Case Chr$(k)
Case "Y", "y": GoTo start
Case "N", "n": System
End Select
_Limit 15
Loop
End If
End If
Next chk
skip3:
If det > 0 Then
det = det + 1
Paint (sx, sy), _RGB32(255, 0, 0), _RGB32(255, 255, 255)
If det > 200 Then det = 0
End If

If loops < 1000 Then
loops = loops + 1
End If
_Display
Cls
Loop


'from Steve Gold standard
Sub fillCircle (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
Dim Radius As Integer, RadiusError As Integer
Dim X As Integer, Y As Integer
Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
If Radius = 0 Then PSet (CX, CY), C: Exit Sub
Line (CX - X, CY)-(CX + X, CY), C, BF
While X > Y
RadiusError = RadiusError + Y * 2 + 1
If RadiusError >= 0 Then
If X <> Y + 1 Then
Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
End If
X = X - 1
RadiusError = RadiusError - X * 2
End If
Y = Y + 1
Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
Wend
End Sub

Sub DrawTriangle (cx As Integer, cy As Integer, size As Integer, angle As Single, x1, y1)
Dim x2 As Single, y2 As Single
Dim x3 As Single, y3 As Single
Dim a1 As Single, a2 As Single, a3 As Single

' Define angles of triangle vertices
a1 = angle
a2 = angle + 120
a3 = angle + 240

' Convert polar to Cartesian coordinates
x1 = cx + size * Cos(a1 * _Pi / 180)
y1 = cy + size * Sin(a1 * _Pi / 180)

x2 = cx + size * Cos(a2 * _Pi / 180)
y2 = cy + size * Sin(a2 * _Pi / 180)

x3 = cx + size * Cos(a3 * _Pi / 180)
y3 = cy + size * Sin(a3 * _Pi / 180)

' Draw triangle
Line (x1, y1)-(x2, y2), _RGB32(255, 255, 255)
Line (x2, y2)-(x3, y3), _RGB32(255, 255, 255)
Line (x3, y3)-(x1, y1), _RGB32(255, 255, 255)

'Draw Gun
gx = x1 + Cos(angle * _Pi / 180) * 5
gy = y1 + Sin(angle * _Pi / 180) * 5
Line (x1, y1)-(gx, gy), _RGB32(255, 255, 255)
End Sub


'This sub needs to go at the end of your code, or in a BM file
Sub ReadJoyStick
Static As Long d, LA, LB 'Last Axis, Last Button
If d = 0 Then d = _Devices
If d < 3 Then Exit Sub '3 is joystick. Without one, then there's no reason to waste effort doing anything else.
If LA = 0 Then LA = _LastAxis(3): ReDim JoyStick(1 To 3) As Axis_Type
If LA = 0 Then Exit Sub 'if there's no axis on your joystick, I don't know how to read it!
If LB = 0 Then LB = _LastButton(3): ReDim Button(1 To LB + 2) As Long
If LB = 0 Then Exit Sub 'if there's no buttons on your joystick, then it's not a proper controller. Go buy one!

Dim axis(LA) As Single
Do
di = _DeviceInput
Select Case di
Case 3 'We have joystick input
For a = 1 To LA: axis(a) = Int(100 * _Axis(a)) / 100: Next 'read the input on each axis
JoyStick(1).Hort = axis(1): JoyStick(1).Vert = axis(2) 'left pad is axis 1 and 2
'axis 3 is the botton left/right buttons on the front of my joystick
JoyStick(2).Hort = axis(5): JoyStick(2).Vert = axis(4) 'right pad is axis 5 and 4
JoyStick(3).Hort = axis(6): JoyStick(3).Vert = axis(7) 'd-pad is axis 6 and 7
'right-pad seems to be mapped backwards to the other axis??!!
For i = 1 To LB
Button(i) = _Button(i)
Next
Button(LB + 1) = _FALSE: Button(LB + 2) = _FALSE
Select Case _Axis(3) 'this is an odd axis which reads off the left/right buttons on the front of the gamepad
Case Is > 0.4: Button(LB + 1) = _TRUE
Case Is < -0.4: Button(LB + 2) = _TRUE
End Select
End Select
Loop Until di = 0
For j = 1 To 3
If Abs(JoyStick(j).Vert) <= .01 Then JoyStick(j).Vert = 0 'remove some natural drift from the keypad
If Abs(JoyStick(j).Hort) <= .01 Then JoyStick(j).Hort = 0 'my joystick seldom resets back to perfect 0
' the code below here gives me a simple X/Y value for left/right, up/down of _TRUE/_FALSE
'I personally find it easier for my 2-d style games to process than having to use frational results.
'Feel free to change the threshold as necessary for your own uses. 0.4 works fine for cardinal directions and diagionals for my use.
If Abs(JoyStick(j).Vert) > 0.4 Then JoyStick(j).Y = Sgn(JoyStick(j).Vert) Else JoyStick(j).Y = 0
If Abs(JoyStick(j).Hort) > 0.4 Then JoyStick(j).X = Sgn(JoyStick(j).Hort) Else JoyStick(j).X = 0
'the angle here is just like the one we learned in school with 0/360 to the right, 90 up, 180 left, and 270 down
'Tweak this as needed so it fits the coordinate system of your own stuff as desired.
JoyStick(j).Angle = _Atan2(JoyStick(j).Hort, JoyStick(j).Vert)
JoyStick(j).Angle = (_R2D(JoyStick(j).Angle) + 270) Mod 360
'And below here is what determines if a joystick was active or not
If JoyStick(j).Vert = 0 And JoyStick(j).Hort = 0 Then JoyStick(j).Active = _FALSE Else JoyStick(j).Active = _TRUE
Next
End Sub

Button 1 is Yes/Fire.
Button 2 is No/Pause.
Any axis will rotate the plane and move it.

One thing that I've noticed is if one shoots in one direction (such as always shooting to the right), you'll also blow up asteroids on your left side of the screen sometimes. The bullets don't seem to carry over all the way across the screen, so I'm not certain what's going on here. It might be something you'd want to check to see what's going on with the collision routines (maybe an ABS in affect somewhere it shouldn't be?? I dunno.)

Anywho... You can now pew-pew with your X-box style controllers! Big Grin


RE: Asteroids Clone - SierraKen - 02-03-2025

Thanks Steve! Yeah I've been looking for that glitch for a couple days and will continue to do so. The only ABS command is in your fillcircle SUB so I highly doubt it's that.