(PB Demo safe) Yet another brickbreaker clone

Advanced game related topics
Nituvious
Addict
Addict
Posts: 999
Joined: Sat Jul 11, 2009 4:57 am
Location: United States

(PB Demo safe) Yet another brickbreaker clone

Post by Nituvious »

I posted this in another thread some time ago with the intention of bringing it to this forum after fixing it up, but I never got around to that.
It has a fixed time step and a probably really poor use and abuse of CallFunctionFast for pseudo objects. And it should compile and run in the demo version of purebasic(because that's all I had)! I've been away from this beautiful language for some time now so I expect there to be many mistakes or errors that I haven't encountered yet.

Image

Code: Select all

Enumeration 0
  #Ball
  #Brick0
  #RGB1
  #RGB2
  #RGB3
  #RGB4
  #Text1
  #Text2
  #Text3
  #Paddle1
  #Particle1
  #Sheet1
  #Paddle2
  #Paddle3
  #Pickup0; base image
  #Pickup1
  #Pickup2;
  #Pickup3;
  #Pickup4;
  #Pickup5;
  #Pickup6;
  #Pickup7;
  #Pickup8;
  #Pickup9;
  #Pickup10;
  #Font1
EndEnumeration
;-Object structure
Structure Object
  id.l ; set when added to object list
  type.l ;
  *parent;
  x.d;
  y.d;
  w.d; ; width/height
  h.d;
  dir.d[2] ; movement vector
  speed.l
  intangible.b ; used for skipping objects that don't need collision testing such as particles
  removed.b; removes from object list
  expires.l ; similar to removed.b but delayed!
  ; function pointers
  *fnConstructor ; unused, but could probably replace type
  *fnDestructor
  *fnRender
  *fnStep
  *extends  ; pointer to an object defining structure
EndStructure
Structure BrickObject
  hp.l
  gfx.b;
EndStructure
Structure PickupObject
  type.l
EndStructure
Structure PaddleObject
  size.l
  x2.d
EndStructure
Structure BallObject
  lastParticleCreation.l
EndStructure
Structure ParticleObject
EndStructure
Structure Vector
  x.d
  y.d
EndStructure
Structure Profile
  tps.l
  tpsc.l
  tpst.l
  count1.l
  count2.l
EndStructure
;-Game state structure
Structure State
  width.l ; window dimensions
  height.l
  dt.l
  now.l
  running.b
  active.b ;
  score.l
  balls.l
  bricks.l
  gameover.b;
  continues.l
  drawing.Profile
  logic.Profile
  uuidAccumulator.l ; used to create unique id's for objects
  *paddle.Object; used for
  Array objectsList.Object(1)
  Array objectsQueue.Object(1)
EndStructure

;-Object declarations
Declare Paddle(*state.State, *ref.Object, x.l, y.l) ; constructor
Declare paddleDestructor(*ref.Object)
Declare paddleDraw(*ref.Object, dt.l)
Declare paddleStep(*ref.Object, *state.State)

Declare Ball(*state.State, *ref.Object, x.l, y.l)
Declare ballDestructor(*ref.Object)
Declare ballDraw(*ref.Object, dt.l)
Declare ballParticleSpawner(*this.Object, *state.State, delay.l=100, reverseDir.b = 1)
Declare ballStep(*ref.Object, *state.State)

Declare Brick(*state.State, *ref.Object, x.l, y.l, hp.l)
Declare brickDestructor(*ref.Object)
Declare brickDraw(*ref.Object, dt.l)
Declare brickStep(*ref.Object, *state.State)
Declare brickParticleSpawner(*this.BallObject, *state.State, delay.l=100, reverseDir.b=1)

Declare Particle(*state.State, *ref.Object, *parent.Object, x.l, y.l, *direction.Vector, expires.l)
Declare particleDestructor(*ref.Object)
Declare particleDraw(*ref.Object, dt.l)
Declare particleStep(*ref.Object, *state.State)

Declare Pickup(*state.State, *ref.Object, x.l, y.l, type.l)
Declare pickupDestructor(*ref.Object)
Declare pickupDraw(*ref.Object, dt.l)
Declare pickupStep(*ref.Object, *state.State)

; boring things declarations
Declare intersect2d(*a.Object, *b.Object)
Declare addObject(*game.State, *ref);
Declare flush(*state.State)
Declare update(*state.State)
Declare render(*state.State)
Declare isOffScreen(*this.Object, *state.State)
;Declare

;-Particle definition
Procedure Pickup(*state.State, *ref.Object, x.l, y.l, type.l);, reverseDir.b = 1)
  *ptr.PickupObject = AllocateStructure(PickupObject)
  *ptr\type = #Pickup0 + type
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x
    \y = y
    \w = 32 ;
    \h = 32
    \expires = 0
    \dir[0] = 0.0
    \dir[1] = 1.0
    \speed = 3
    \fnRender = @pickupDraw()
    \fnStep = @pickupStep()
    \fnConstructor = @Pickup()
    \fnDestructor = @pickupDestructor()
    \intangible = 0
  EndWith
EndProcedure
Procedure pickupDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure pickupApply(*state.State, *this.Object, type.l=-1)
  *self.PickupObject = *this\extends
  *thatObj.PaddleObject = *state\paddle\extends
  If (type.l = -1)
    type = *self\type
  EndIf
 
  Select type
    Case #Pickup1:
      *state\continues+1
    Case  #Pickup4:
      *thatObj\size + 1
    Case  #Pickup5:
      If (*thatObj\size > 1)
        *thatObj\size - 1
      EndIf
    Case  #Pickup7:
      Ball(*state, obj.Object, *this\x, *this\y)
      addObject(*state, obj);
    Case  #Pickup8:
      pickupApply(*state, *this, Random(9,0));
    Default: *state\score = *state\score / 0.25
  EndSelect
  ProcedureReturn 0
EndProcedure
   
Procedure pickupStep(*this.Object, *state.State);
  *self.PickupObject = *this\extends
  With *this
    \x = \x + (\dir[0] * \speed)
    \y = \y + (\dir[1] * \speed)
  EndWith
  If (isOffScreen(*this, *state))
    *this\removed = 1
  EndIf
    *that.Object = *state\paddle
    collision = intersect2d(*this, *that)
    If (*that\fnConstructor = @Paddle() And collision > 0 And *this\removed = 0)
      pickupApply(*state, *this)
      *this\removed = 1
      ballParticleSpawner(*this, *state, 10, 1)
    EndIf
EndProcedure
Procedure pickupDraw(*this.Object, dt.l)
  *self.PickupObject = *this\extends
  DisplayTransparentSprite(#Pickup0, *this\x, *this\y);
  DisplayTransparentSprite(*self\type, *this\x, *this\y);
EndProcedure
;-Paddle definition
Procedure Paddle(*state.State, *ref.Object, x.l, y.l)
  *ptr.PaddleObject = AllocateStructure(PaddleObject)
  *ptr\size = 5
  With *ref
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0
    \w = 100
    \h = 16
    \fnRender = @paddleDraw()
    \fnStep = @paddleStep()
    \fnConstructor = @Paddle()
    \fnDestructor = @paddleDestructor()
  EndWith
EndProcedure
Procedure paddleDestructor(*ref.Object)
EndProcedure
Procedure paddleParticleSpawner(*this.Object, *state.State, side.b);
  For k = 1 To 2
    dir.Vector\y = 1.0 + ((-4 + Random(8, 0)) / 10);;
    dir\x = 0.0  + ((-4 + Random(8, 0)) / 10)
    pos.Vector\x = *this\x + 5
    pos\y = *this\y + *this\h;
    If (side = 1)
      pos\x = *this\x + *this\w - 10
    EndIf
    Particle(*state, particleObj.Object, *this, pos\x, pos\y, dir, *state\dt + Random(300, 100))
    addObject(*state, particleObj)
  Next
EndProcedure
Procedure paddleStep(*this.Object, *state.State);
  *state\paddle = *this
  *self.PaddleObject = *this\extends
  *this\x = MouseX()
  *this\w = 32 + (*self\size * 6 * 2)
  If (*this\x + *this\w > *state\width)
    *this\x = *state\width - *this\w 
  EndIf 
  paddleParticleSpawner(*this, *state, 0)
  paddleParticleSpawner(*this, *state, 1)
EndProcedure
Procedure paddleDraw(*this.Object, dt.l)
  *self.PaddleObject = *this\extends
  size = *self\size * 2
  DisplayTransparentSprite(#Paddle1, *this\x, *this\y)
  DisplayTransparentSprite(#Paddle3, *this\x + 17 + (6 * size), *this\y)
  For l = 1 To size
    DisplayTransparentSprite(#Paddle2, *this\x + 5 + ( 6 * l), *this\y + 1)
  Next
EndProcedure
;-Ball definition
Procedure Ball(*state.State, *ref.Object, x.l, y.l)
  *state\balls + 1;
  *ptr.BallObject = AllocateStructure(BallObject)
  *ptr\lastParticleCreation = 0
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x;width / 2
    \y = y;height - 50 ;
    \removed = 0        ;
    \speed = 10
    \w = 16 ;
    \h = 16
    \dir[0] = 0.0
    \dir[1] = -1.0
    \fnRender = @ballDraw()
    \fnStep = @ballStep()
    \fnConstructor = @Ball()
    \fnDestructor = @ballDestructor()
  EndWith
EndProcedure
Procedure ballDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure ballParticleSpawner(*this.Object, *state.State, delay.l=100, reverseDir.b = 1)
  *self.BallObject = *this\extends
  If (*state\dt > *self\lastParticleCreation + delay)
    *self\lastParticleCreation = *state\dt
    For i = 0 To 10
      dir.Vector\x = -((-8 + Random(16, 0)) / 10)
      dir\y = -((-8 + Random(16, 0)) / 10)
      Particle(*state, particleObj.Object, *this, *this\x, *this\y, dir, *state\dt + Random(300, 50))
      addObject(*state, particleObj)
    Next
  EndIf
EndProcedure
Procedure ballStep(*this.Object, *state.State);
  *self.BallObject = *this\extends
 
  If (*state\active = 1)
    tmp.Object
    With tmp ; a copy of the object with future transforms applied to it
      \x = *this\x + (*this\dir[0] * *this\speed)
      \y = *this\y + (*this\dir[1] * *this\speed)
      \w = *this\w
      \h = *this\h
    EndWith
   
    For i = 0 To ArraySize(*state\objectsList()) - 1
      that.Object = *state\objectsList(i);
      ; todo: collision will fail if a balls speed is more than the width or height of an object it could collide with
      collision = intersect2d(tmp, that)
      If (*this\id <> that\id And that\intangible = 0 And collision > 0)
        If (that\fnConstructor = @Brick())
          *ptr.BrickObject = that\extends
          *ptr\hp = *ptr\hp - 1
         
          top.Object\x = that\x + 1
          top\y = that\y - that\h
          top\w = that\w - 2
          top\h = that\h + 5
          bot.Object\x = that\x + 1;
          bot\y = that\y + that\h - 5
          bot\w = that\w - 2
          bot\h = that\h
         
         
          left.Object\x = that\x - that\w
          left\y = that\y + 1
          left\w = that\w + 5
          left\h = that\h - 2
          right.Object\x = that\x + that\w - 5;
          right\y = that\y + 1
          right\w = that\w
          right\h = that\h - 2
          If (intersect2d(tmp, top) Or intersect2d(tmp, bot))
            *this\dir[1] = -(*this\dir[1])
          ElseIf (intersect2d(tmp, left) Or intersect2d(tmp, right))
            *this\dir[0] = -(*this\dir[0])
          Else
            *this\dir[0] = -(*this\dir[0])
            *this\dir[1] = -(*this\dir[1])
          EndIf
         
          *state\score + 1
          ballParticleSpawner(*this, *state, 10, 1)
        ElseIf (that\fnConstructor = @Paddle())
          rads = (((that\x - *this\x) + ((that\w - *this\w )/2) )) * #PI
          *this\dir[0] = -Sin(rads / 180)
          *this\dir[1] = -Cos(rads / 180)
          ballParticleSpawner(*this, *state, 10, 1)
        EndIf
      EndIf
    Next
   
    c2 = isOffScreen(tmp, *state)
    If (c2 = 4) ; bottom of the screen hit
      *this\removed = 1
      *state\balls - 1
    ElseIf (c2 = 3) ; top
      *this\dir[1] = -(*this\dir[1])
    ElseIf (c2 > 0)
      *this\dir[0] = -(*this\dir[0])
    EndIf
   
    If (c2 > 0)
      ballParticleSpawner(*this, *state, 10, 0)
    EndIf
   
    With *this
      \x = \x + (\dir[0] * \speed)
      \y = \y + (\dir[1] * \speed)
    EndWith
  Else
    With *this
      \x = *state\paddle\x + ((*state\paddle\w/2) - (\w/2))
      \y = *state\paddle\y - 20
    EndWith
  EndIf
EndProcedure
Procedure ballDraw(*this.Object, dt.l)
  DisplayTransparentSprite(#Ball, *this\x, *this\y);
EndProcedure
;-Particle definition
Procedure Particle(*state.State, *ref.Object, *parent.Object, x.l, y.l, *direction.Vector, expires.l);, reverseDir.b = 1)
  *ptr.ParticleObject = AllocateStructure(ParticleObject)
  s = Random(5, 1)
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x
    \y = y
    \w = s ;
    \h = s
    \expires = expires
    \dir[0] = *direction\x
    \dir[1] = *direction\y
    \speed = Random(10, 0);
    \fnRender = @particleDraw()
    \fnStep = @particleStep()
    \fnConstructor = @Particle()
    \fnDestructor = @particleDestructor()
    \intangible = 1
  EndWith
EndProcedure
Procedure particleDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure particleStep(*this.Object, *state.State);
  *self.ParticleObject = *this\extends
  With *this
    \x = \x + (\dir[0] * \speed)
    \y = \y + (\dir[1] * \speed)
    \speed = \speed/2; - \speed
  EndWith   
EndProcedure
Procedure particleDraw(*this.Object, dt.l)
  ZoomSprite(#Particle1, *this\w, *this\h)
  DisplayTransparentSprite(#Particle1, *this\x, *this\y);
EndProcedure
;-Brick definition
Procedure Brick(*state.State, *ref.Object, x.l, y.l, hp.l)
  *state\bricks + 1
  *ptr.BrickObject = AllocateStructure(BrickObject)
  *ptr\hp = hp
  *ptr\gfx = Random(3, 0);
  With *ref
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0;
    \w = 64;
    \h = 32;
    \fnRender = @brickDraw()
    \fnStep = @brickStep()
    \fnConstructor = @Brick()
    \fnDestructor = @brickDestructor()
  EndWith
EndProcedure
Procedure brickDestructor(*ref.Object)
  ;*self.BrickObject = *ref\extends
  ;FreeMemory(*self\ptrtest)
  FreeStructure(*ref\extends)
EndProcedure

Procedure brickStep(*this.Object, *state.State);
  *self.BrickObject = *this\extends
  If (*self\hp < 1 And *this\removed = 0)
    *this\removed = 1
    *state\bricks - 1
    Pickup(*state, obj.Object, *this\x, *this\y, Random(10, 1))
    addObject(*state, obj);
  EndIf
EndProcedure
Procedure brickDraw(*this.Object, dt.l)
  *self.BrickObject = *this\extends
  DisplayTransparentSprite(#Brick0, *this\x, *this\y);
  DisplayTransparentSprite(#RGB1 + (*self\gfx), *this\x, *this\y);
EndProcedure
Procedure update(*state.State)
  size = ArraySize(*state\objectsList());
  For k = 0 To size - 1
    If (*state\objectsList(k)\removed = 1)
      Continue
    EndIf
   
    *addr = *state\objectsList(k)\fnStep;
    CallFunctionFast(*addr, *state\objectsList(k), *state);
  Next
  If (*state\bricks = 0 And *state\gameover = 0)
    *state\gameover = 1
  EndIf
  If (*state\balls = 0 And *state\gameover = 0)
    If (*state\continues = 0)
      *state\gameover = 2
    Else
      *state\continues - 1
      *state\active = 0
      Ball(*state, obj.Object, 0, 0)
      addObject(*state, obj)
    EndIf
  EndIf
  If (*state\active = 0 And MouseButton(#PB_MouseButton_Left) = 1)
    *state\active = 1
  EndIf
EndProcedure
Procedure render(*state.State)
  ClearScreen(RGB(50, 100, 140));

  For k = 0 To ArraySize(*state\objectsList()) - 1
    item.Object = *state\objectsList(k)
    If (item\removed = 1)
      Continue
    EndIf
    *addr = *state\objectsList(k)\fnRender;
    CallFunctionFast(*addr, *state\objectsList(k), *state);
    ;StartDrawing(ScreenOutput()) ; show bounds
    ;Line(item\x, item\y, item\w, 1, #Red)
    ;Line(item\x, item\y+item\h, item\w, 1, #Red)
    ;Line(item\x, item\y, 1, item\h, #Red)
    ;Line(item\x+item\w, item\y, 1, item\h, #Red)
    ;Line(item\x, item\y, item\w, item\h, #Red)
    ;StopDrawing()
  Next 

  DisplayTransparentSprite(#Text1, *state\width - 150, 10);
  DisplayTransparentSprite(#Text2, 10, 10)
  If (*state\gameover > 0 Or *state\active = 0)
    DisplayTransparentSprite(#Text3, *state\width/2 - 150, *state\height/2)
  EndIf
   
  FlipBuffers();
EndProcedure
Procedure addObject(*game.State, *ref)
  o = ArraySize(*game\objectsQueue());
  ReDim *game\objectsQueue.Object(o+1);
  CopyStructure(*ref, @dref.Object, Object)
  *game\objectsQueue(o) = dref;
EndProcedure
Procedure remakeHUDSprite(*state.State) 
  If (IsSprite(#Text2))
    FreeSprite(#Text2)
  EndIf
 
  CreateSprite(#Text2, 200, 75);
  StartDrawing(SpriteOutput(#Text2))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(0, 0, "Score: " + Str(*state\score), RGB(1, 0, 0));
  DrawText(0, 20, "Continues: " + Str(*state\continues), RGB(1, 0, 0));
  StopDrawing()
EndProcedure
Procedure remakeGameOverSprite(*state.State, msg.s)
  If (IsSprite(#Text3))
    FreeSprite(#Text3)
  EndIf
  CreateSprite(#Text3, 300, 100);
  StartDrawing(SpriteOutput(#Text3))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawingFont(FontID(#Font1))
  DrawText(0, 0, msg, RGB(1, 0, 0));
  StopDrawing()
EndProcedure
Procedure remakeProfileSprite(*state.State) 
  If (IsSprite(#Text1))
    FreeSprite(#Text1)
  EndIf
 
  CreateSprite(#Text1, 200, 120);
  StartDrawing(SpriteOutput(#Text1))

  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(0, 0, Str(*state\drawing\tps) + " - frames per sec", RGB(1, 0, 0));
  DrawText(0, 20, Str(*state\logic\tps) + " - steps per sec", RGB(1, 0, 0));
  DrawText(0, 40, Str(*state\logic\count1) + " - object count", RGB(1, 0, 0)) ;
  DrawText(0, 60, Str(*state\logic\count2) + " - queue count", RGB(1, 0, 0)) 
  DrawText(0, 80, Str(*state\balls) + " - balls", RGB(1, 0, 0)) 
  DrawText(0, 100, Str(*state\bricks) + " - bricks", RGB(1, 0, 0))  ;
  StopDrawing()
EndProcedure
Procedure init(*state.State)
  LoadFont(#Font1, "Arial", 32, #PB_Font_Bold)
  remakeProfileSprite(*state);
  remakeHUDSprite(*state)
  remakeGameOverSprite(*state, "Click to Start!")
  LoadSprite(#Sheet1, "E:/assets/brickbreaker.png", #PB_Sprite_AlphaBlending);
 
  CopySprite(#Sheet1, #Paddle1, #PB_Sprite_AlphaBlending) ; left
  ClipSprite(#Paddle1, 0, 0, 16, 16)
  CopySprite(#Sheet1, #Paddle2, #PB_Sprite_AlphaBlending) ; blue thing
  ClipSprite(#Paddle2, 16, 0, 16, 16)
  CopySprite(#Sheet1, #Paddle3, #PB_Sprite_AlphaBlending) ; right
  ClipSprite(#Paddle3, 32, 0, 16, 16) 
 
  CopySprite(#Sheet1, #Ball, #PB_Sprite_AlphaBlending)
  ClipSprite(#Ball, 16 * 3, 0, 16, 16)
 
  CopySprite(#Sheet1, #Brick0, #PB_Sprite_AlphaBlending)
  ClipSprite(#Brick0, 0, 64, 64, 32)

  CreateSprite(#Particle1, 25, 25);
  StartDrawing(SpriteOutput(#Particle1))
  Box(0, 0, 20, 20, RGB(0, 0, 0))       ;
  DrawingMode(#PB_2DDrawing_Default)
  Box(0, 0, 50, 25, RGB(255, 255, 255));
  StopDrawing()
 
  For k = 0 To 3
    CreateSprite(#RGB1 + k, 64, 32, #PB_Sprite_AlphaBlending);
    StartDrawing(SpriteOutput(#RGB1 + k))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    Box(0, 0, 64, 32, $00000000)
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    Box(0, 0, 64, 32, RGBA(Random(255, 0), Random(255, 0), Random(255, 0), 100))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    Box(0, 0, 2, 2, $00000000)
    Box(62, 0, 2, 2, $00000000)
    Box(0, 30, 2, 2, $00000000)
    Box(62, 30, 2, 2, $00000000)
    StopDrawing()
  Next
 
  CopySprite(#Sheet1, #Pickup0, #PB_Sprite_AlphaBlending)
  ClipSprite(#Pickup0, 64, 0, 16, 16)
  ZoomSprite(#Pickup0, 32, 32)
  For k = 0 To 10
    CopySprite(#Sheet1, #Pickup1 + k, #PB_Sprite_AlphaBlending)
    ClipSprite(#Pickup1 + k, (16 * k), 16, 16, 16)
    ZoomSprite(#Pickup1 + k, 32, 32)
  Next
   
  Paddle(*state, paddleObj.Object, *state\width / 2 - 50, *state\height - 100)
  addObject(*state, paddleObj)
 
  Ball(*state, ballObj.Object, 0, 0)
  addObject(*state, ballObj)
 
  For x = 0 To 59;99
    tx = 50 + ((x % 10) * 70)
    ty = 100 + ((x / 10) * 50)
    Brick(*state, brickObj.Object, tx, ty, 1+(ty%3))
    addObject(*state, brickObj)
  Next
EndProcedure
Procedure isOffScreen(*this.Object, *state.State)
  If (*this\x < 0) : ProcedureReturn 1 ; left
  ElseIf (*this\x + *this\w > *state\width) : ProcedureReturn 2 ; right
  ElseIf (*this\y < 0) : ProcedureReturn 3 ; top
  ElseIf (*this\y + *this\h > *state\height) : ProcedureReturn 4 : EndIf ; bottom
  ProcedureReturn 0
EndProcedure
Procedure intersect2d(*o.Object, *b.Object)
  ax.d = *o\x
  ay.d = *o\y
  au.d = *o\x + *o\w
  av.d = *o\y + *o\h
   
  bx.d = *b\x
  by.d = *b\y
  bu.d = *b\x + *b\w
  bv.d = *b\y + *b\h
 
  If (ax <= bu And au >= bx And ay <= bv And av >= by)
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure
Procedure flush(*state.State)
  size = ArraySize(*state\objectsList())
  For i = 0 To size - 1 ; clean up objects that were marked for removal
    If (*state\objectsList(i)\removed = 1 Or (*state\objectsList(i)\expires > 0 And *state\dt > *state\objectsList(i)\expires))
      *addr = *state\objectsList(i)\fnDestructor;
      CallFunctionFast(*addr, *state\objectsList(i));
      ; i dont know how to properly resize arrays in purebasic so hopefully this won't be too slow :(
      *state\objectsList(i) = *state\objectsList(size - 1) ; swap removing object with one at end
      ReDim *state\objectsList.Object(size - 1)
      size = size -1; have to modify loop or bad things will happen
      i = i - 1; the object that was swapped must also be checked for removal
    EndIf
  Next

  n = ArraySize(*state\objectsQueue());
  If (n > 0) ; merge queued objects into the object list
    o = ArraySize(*state\objectsList());
    If (o >= 1000) ; this is just an arbitrary size limit for the number of objects to allow
      ReDim *state\objectsQueue(0);
      ProcedureReturn
    EndIf

    ReDim *state\objectsList(o+n);
    For k = 0 To n ; the merge part
      *state\uuidAccumulator = *state\uuidAccumulator + 1
      *state\objectsList(o+k) = *state\objectsQueue(k);
      *state\objectsList(o+k)\id = *state\uuidAccumulator
      If (*state\objectsList(o+k)\fnConstructor = @Paddle())
        *state\paddle = *state\objectsList(o+k)
      EndIf
    Next
    ReDim *state\objectsQueue(0)
  EndIf
EndProcedure

With game.State
  \width = 800
  \height = 800
  \running = 1
  \score = 0
  \continues = 1
  \now = ElapsedMilliseconds()
  \dt = ElapsedMilliseconds();
  Dim \objectsQueue.Object(0)
  Dim \objectsList.Object(0)
EndWith

InitSprite()
InitMouse()
UsePNGImageDecoder()

OpenWindow(0, 0, 0, game\width, game\height, "Nostalgia", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget);
OpenWindowedScreen(WindowID(0), 0, 0, game\width, game\height, #False, #Null, #Null, #PB_Screen_NoSynchronization ); 

init(game)
;-Loop
While game\running = 1
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      game\running = 0
  EndSelect

  ExamineMouse()

  If (game\dt > game\drawing\tpst + 1000)
    game\drawing\tps = game\drawing\tpsc;
    game\drawing\tpsc = 0  ;
    game\logic\tps = game\drawing\tpsc;
    game\logic\tpsc = 0  ;
    game\drawing\tpst = game\dt ;
    game\logic\count1 = ArraySize(game\objectsList())
    game\logic\count2 = ArraySize(game\objectsQueue())
    remakeProfileSprite(game);
  EndIf
 
  flush(game)
 
  game\now = ElapsedMilliseconds();
  If (game\now - game\dt > 1000) ; skip some logic steps because previous one took too long
    game\dt = game\now;
  EndIf
 
  CopyStructure(@game, @prev.State, State) ; a copy of state to compare with after updating
  While game\dt < game\now ; step game objects at a constant rate
    game\dt = game\dt + 1000/30;
    update(game);
    game\logic\tpsc=game\logic\tpsc+1;
  Wend
 
  If (prev\score <> game\score Or prev\continues <> game\continues)
    remakeHUDSprite(game)
  EndIf
  If (prev\gameover <> game\gameover Or prev\active <> game\active)
    msg.s = "Click to start!"
    If (game\gameover = 2)
      msg.s = "Game over!!"
    ElseIf (game\gameover = 1)
      msg.s = "You win!"
    EndIf
    remakeGameOverSprite(game, msg)
  EndIf
   
  render(game)
  game\drawing\tpsc=game\drawing\tpsc+1; 
Wend
▓▓▓▓▓▒▒▒▒▒░░░░░
Rinzwind
Enthusiast
Enthusiast
Posts: 636
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: (PB Demo safe) Yet another brickbreaker clone

Post by Rinzwind »

A little object support through extending the structure type by PB (a bit like GameMaker's GML did recently) would make this code much easier to look at ;)
Wishful thinking. Couldn't help it.
Nituvious
Addict
Addict
Posts: 999
Joined: Sat Jul 11, 2009 4:57 am
Location: United States

Re: (PB Demo safe) Yet another brickbreaker clone

Post by Nituvious »

I have no idea what any of that means but I saw mention of objects.. Tell me the secrets!
▓▓▓▓▓▒▒▒▒▒░░░░░
Rinzwind
Enthusiast
Enthusiast
Posts: 636
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: (PB Demo safe) Yet another brickbreaker clone

Post by Rinzwind »

Dont get excited, its missing from PB and missed by a lot of users.

Im talking about adding the minimum language syntax to support function pointers in structures and a self reference and while were at it default values (which it already has, but not customizable in any way). A basic way of (optional) encapsulation of data and behavior and hiding of complexity and keeping things together. Object inspired programming in PB always ends in a mess because only half of essentials were added while it would be easy to fix by just implementing the final few missing pieces. It would also be a better replacement of the current modules if anything.

GLBasic has it: http://www.glbasic.com/forum/index.php?topic=8579.0
GML 2.3 has it after demands for ways to create better structured/organized code: https://www.yoyogames.com/blog/549/game ... l-features (Structs) . GML is not even afraid of making breaking changes when needed.
Blitzmax all the way tutorial: https://blitzmax.org/docs/en/tutorials/oop_tutorial/
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: (PB Demo safe) Yet another brickbreaker clone

Post by Mistrel »

This was really fun. I love breakout-like games!

Thanks for sharing. :)
ricardo_sdl
Enthusiast
Enthusiast
Posts: 109
Joined: Sat Sep 21, 2019 4:24 pm

Re: (PB Demo safe) Yet another brickbreaker clone

Post by ricardo_sdl »

Rinzwind wrote:Dont get excited, its missing from PB and missed by a lot of users.

Im talking about adding the minimum language syntax to support function pointers in structures...
It is possible to have function pointers inside structures:

Code: Select all

Prototype UpdateSpriteProc(Elapsed.f, *Sprite) : Prototype DrawSpriteProc(SpriteType.a, Width.a, Height.a, ZoomLevel.f, x.f, y.f, Transparency.a, *Sprite = #Null)
Structure TSprite
  x.f : y.f : LastX.f : LastY.f : Width.a : Height.a : Lives.b : XVelocity.f : YVelocity.f : SpriteType.a
  ZoomLevel.f : Mass.f : Transparency.a : Map References.i() : UpdateSprite.UpdateSpriteProc : DrawSprite.DrawSpriteProc
  Timer1.f : Timer2.f : State1.a : State2.a : Deleted.a
EndStructure
In the example above is possible to assign UpdateSprite and DrawSprite to different procedures that have the same signature as the prototypes but different behaviors.
You can check my games at:
https://ricardo-sdl.itch.io/
Ural4320
New User
New User
Posts: 7
Joined: Fri Oct 30, 2020 6:59 pm

Re: (PB Demo safe) Yet another brickbreaker clone

Post by Ural4320 »

Works perfectly for me as well, thanks, and it really helps me, as a beginner, to see a simple yet complete program laying "naked" in front of me. Starting from something more complicated is intimidating, and so is standing in front of an empty text box with just memories of the tutorials you just watched...
Post Reply