Re: What are you working on?
Posted: Sun Jun 07, 2020 8:10 pm
				
				these days i rarely post myself, but i still enjoy browsing the forums to see what people are up to. personally, i've found a lot of happiness by growing plants hehe. it's peaceful and my hard work and patience rewards me with tasty little treats like strawberries even in the dead of winterKuron wrote:I appreciate your comments. Post-TBI, life is a struggle (complicated by my Asperger's) and in all honesty, if I had life insurance, I would have already "accidentally" stepped in front of a bus so my wife could have a decent life. Not well-liked here (people with Asperger's often rub folks the wrong way without meaning to) so rarely post anymore, and given moving to get married back in 2015, my friends were left behind, and being at the age where I am now retired, I do not have a chance to make any new friends, so dealing with my TBI recovery on my own. Haven't given up, yet... *shrugs*
as for this talk about making games.. i decided to give it a go in purebasic!! i worked on this mostly yesterday morning and a few hours today for cleanup and botched particles.. after coming back to the language from so many years it's difficult to tell which way is the right way to do things so i just guessed at it all haha. i have about 250 lines left to use in the demo though
warning!! it has been a long time since I played with purebasic so if this gives anyone nightmares.. well that's on you!
A Breakout/Arkanoid style prototype:
Code: Select all
Enumeration 0
  #Ball
  #Brick
  #Text1
  #Text2
  #Paddle
  #Particle1
EndEnumeration
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
  
  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
EndStructure
Structure PaddleObject
  x2.d
EndStructure
Structure BallObject
  lastParticleCreation.l 
EndStructure
Structure ParticleObject
EndStructure
Structure Profile
  fps.l
  fpsc.l
  fpst.l; frame rate timer
  sps.l
  spsc.l
  c.l
  q.l
EndStructure
Structure State
  width.l ; window dimensions
  height.l
 
  dt.l 
  now.l
  running.b
  
  score.l
  continues.l
  
  profiler.Profile
  
  uuidAccumulator.l ; used to create unique id's for objects
  
  Array objectsList.Object(1)
  Array objectsQueue.Object(1)
EndStructure
; object declarations
Declare Paddle(*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(*ref.Object, x.l, y.l)
Declare ballDestructor(*ref.Object)
Declare ballDraw(*ref.Object, dt.l)
Declare ballStep(*ref.Object, *state.State)
Declare Brick(*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 Particle(*ref.Object, x.l, y.l, expires.l = 50, *parent.Object=#Null)
Declare particleDestructor(*ref.Object)
Declare particleDraw(*ref.Object, dt.l)
Declare particleStep(*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)
;-Paddle definition
Procedure Paddle(*ref.Object, x.l, y.l)
  *ptr.PaddleObject = AllocateStructure(PaddleObject)
  *ptr\x2 = x
  With *ref
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0
    \w = 100
    \h = 25
    \fnRender = @paddleDraw()
    \fnStep = @paddleStep()
    \fnConstructor = @Paddle()
    \fnDestructor = @paddleDestructor()
    \type = #Paddle
  EndWith
EndProcedure
Procedure paddleDestructor(*ref.Object)
  FreeMemory(*crash);FreeStructure(*ref\extends)
EndProcedure
Procedure paddleStep(*this.Object, *state.State);
  *self.PaddleObject = *this\extends
  *self\x2 = *self\x2 + MouseDeltaX()
  *this\x = *this\x + ((*self\x2 - *this\x) / 5) ; make the paddle accumulate towards a position instead of instant movement
EndProcedure
Procedure paddleDraw(*this.Object, dt.l)
  ZoomSprite(#Paddle, *this\w, *this\h)
  DisplayTransparentSprite(#Paddle, *this\x, *this\y)
EndProcedure
;-Ball definition
Procedure Ball(*ref.Object, x.l, y.l)
  *ptr.BallObject = AllocateStructure(BallObject)
  *ptr\lastParticleCreation = 0
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x;width / 2
    \y = y;height - 50 ;
    \removed = 0        ;
    \speed = 5
    \w = 20 ;
    \h = 20
    \dir[0] = 0.0
    \dir[1] = -1.0
    \fnRender = @ballDraw()
    \fnStep = @ballStep()
    \fnConstructor = @Ball()
    \fnDestructor = @ballDestructor()
    \type = #Ball
  EndWith
EndProcedure
Procedure ballDestructor(*ref.Object)
  FreeStructure(*ref\extends)
EndProcedure
Procedure ballStep(*this.Object, *state.State);
  *self.BallObject = *this\extends
  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);
    If (*this\id <> that\id And intersect2d(tmp, that) = 1)
      If (that\type = #Brick)
        *ptr.BrickObject = that\extends
        *ptr\hp = *ptr\hp - 1
        *this\dir[0] = -(*this\dir[0])
        *this\dir[1] = -(*this\dir[1])
        *state\score + 1
      ElseIf (that\type = #Paddle)
        rads = (((that\x - *this\x) + ((that\w - *this\w )/2) )) * #PI
        *this\dir[0] = -Sin(rads / 180)
        *this\dir[1] = -Cos(rads / 180)
      EndIf
            
    EndIf
  Next
  If (isOffScreen(tmp, *state))
    *this\dir[0] = -(*this\dir[0])
    *this\dir[1] = -(*this\dir[1])
  EndIf
  
  With *this 
    \x = \x + (\dir[0] * \speed)
    \y = \y + (\dir[1] * \speed)
  EndWith
  
  If (*state\dt > *self\lastParticleCreation + 10)
    *self\lastParticleCreation = *state\dt
    For i = 0 To 15
      Particle(particleObj.Object, 0, 0, *state\dt + Random(300, 50), *this)
      addObject(*state, particleObj)
    Next
  EndIf
   
EndProcedure
Procedure ballDraw(*this.Object, dt.l)
  ZoomSprite(#Ball, *this\w, *this\h)
  DisplayTransparentSprite(#Ball, *this\x, *this\y);
EndProcedure
;-Particle definition
Procedure Particle(*ref.Object, x.l, y.l, expires.l = 50, *parent.Object=#Null)
  *ptr.ParticleObject = AllocateStructure(ParticleObject)
  s = Random(5, 1)
  With *ref
    \extends = *ptr
    \parent = *parent
    \x = x;width / 2
    \y = y;height - 50 ;
    \w = s ;
    \h = s
    \expires = expires
    \dir[0] = 0.0
    \dir[1] = -1.0
    \fnRender = @particleDraw()
    \fnStep = @particleStep()
    \fnConstructor = @Particle()
    \fnDestructor = @particleDestructor()
    \type = #Particle1
  EndWith
  If (*parent <> #Null)
    *ref\dir[0] = -(*parent\dir[0])
    *ref\dir[1] = -(*parent\dir[1])
    *ref\speed = Random(5, 1);
    *ref\x = *parent\x + (s*2);
    *ref\y = *parent\y + (s*2);
  EndIf
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)
  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(*ref.Object, x.l, y.l, hp.l)
  *ptr.BrickObject = AllocateStructure(BrickObject)
  *ptr\hp = hp
  With *ref
    \type = #Brick
    \extends = *ptr
    \x = x
    \y = y
    \removed = 0;
    \w = 50;
    \h = 25;
    \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)
    *this\removed = 1
  EndIf
    
EndProcedure
Procedure brickDraw(*this.Object, dt.l)
  ZoomSprite(#Brick, *this\w, *this\h)
  DisplayTransparentSprite(#Brick, *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
EndProcedure
Procedure render(*state.State)
  
  ClearScreen(RGB(50,100,140));
  For k = 0 To ArraySize(*state\objectsList()) - 1
    If (*state\objectsList(k)\removed = 1)
      Continue
    EndIf
    
    *addr = *state\objectsList(k)\fnRender;
    CallFunctionFast(*addr, *state\objectsList(k), *state);
  Next  
 
  DisplayTransparentSprite(#Text1, *state\width - 150, 10); 
  DisplayTransparentSprite(#Text2, 10, 10)
  
  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 remakeProfileSprite(*prof.Profile)  
  If (IsSprite(#Text1))
    FreeSprite(#Text1)
  EndIf
  
  CreateSprite(#Text1, 200, 75);
  StartDrawing(SpriteOutput(#Text1))
  DrawingMode(#PB_2DDrawing_Transparent)
  DrawText(0, 0, Str(*prof\fps) + " - frames per sec", RGB(1, 0, 0));
  DrawText(0, 20, Str(*prof\sps) + " - steps per sec", RGB(1, 0, 0));
  DrawText(0, 40, Str(*prof\c) + " - object count", RGB(1, 0, 0)) ;
  DrawText(0, 60, Str(*prof\q) + " - queue count", RGB(1, 0, 0))  ;
  StopDrawing()
EndProcedure
Procedure init(*state.State)
  remakeProfileSprite(*state\profiler);
  remakeHUDSprite(*state)
  
  CreateSprite(#Ball, 20, 20);
  StartDrawing(SpriteOutput(#Ball)) 
  Box(0, 0, 20, 20, RGB(0, 0, 0))       ;
  DrawingMode(#PB_2DDrawing_Default)
  Circle(10, 10, 5, RGB(255, 255, 255));
  StopDrawing()
  
  CreateSprite(#Brick, 50, 25);
  StartDrawing(SpriteOutput(#Brick)) 
  Box(0, 0, 20, 20, RGB(0, 0, 0))       ;
  DrawingMode(#PB_2DDrawing_Default)
  Box(0, 0, 50, 25, RGB(255, 255, 255));
  StopDrawing()  
  
  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()
  
  CreateSprite(#Paddle, 100, 50);
  StartDrawing(SpriteOutput(#Paddle)) 
  Box(0, 0, 20, 20, RGB(0, 0, 0))       ;
  DrawingMode(#PB_2DDrawing_Default)
  Box(0, 0, 100, 50, RGB(255, 255, 255));
  StopDrawing()
  
  Paddle(paddleObj.Object, *state\width / 2 - 50, *state\height - 100)
  addObject(*state, paddleObj)
  
  Ball(ballObj.Object, *state\width / 2, *state\height - 150)
  addObject(*state, ballObj)
  
  For x = 0 To 99
    tx = 50 + ((x % 10) * 70)
    ty = 100 + ((x / 10) * 50)
    Brick(brickObj.Object, tx, ty, 1)
    addObject(*state, brickObj)
  Next
EndProcedure
Procedure isOffScreen(*this.Object, *state.State) 
  If (*this\x < 0) Or (*this\x > *state\width) Or (*this\y < 0) Or (*this\y > *state\height)
    ProcedureReturn 1
  EndIf
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
    Next
    ReDim *state\objectsQueue(0)
  EndIf
EndProcedure
With game.State
  \width = 800
  \height = 800
  \running = 1
  \score = 0
  \continues = 0
  \now = ElapsedMilliseconds()
  \dt = ElapsedMilliseconds();
  \profiler\fps = 0;
  \profiler\fpsc = 0;
  \profiler\fpst = 0;
  \profiler\sps = 0;
  \profiler\spsc = 0;
  \profiler\c = 0   ;
  \profiler\q = 0   ;
  Dim \objectsQueue.Object(0)
  Dim \objectsList.Object(0)
EndWith
InitSprite()
InitMouse()
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);  
init(game)
While game\running = 1
  Select WindowEvent()
    Case #PB_Event_CloseWindow
      game\running = 0
  EndSelect
  ExamineMouse()
  flush(game)
  If (game\dt > game\profiler\fpst + 1000)
    game\profiler\fps = game\profiler\fpsc;
    game\profiler\fpsc = 0  ;
    game\profiler\sps = game\profiler\spsc;
    game\profiler\spsc = 0  ;
    game\profiler\fpst = game\dt ;
    game\profiler\c = ArraySize(game\objectsList())
    game\profiler\q = ArraySize(game\objectsQueue())
    remakeProfileSprite(game\profiler);
  EndIf 
  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)
  While game\dt < game\now ; step game objects at a constant rate
    game\dt = game\dt + 1000/30;
    update(game);
    game\profiler\spsc=game\profiler\spsc+1;
  Wend
  
  If (prev\score <> game\score Or prev\continues <> game\continues)
    remakeHUDSprite(game)
  EndIf
  
  render(game)
  game\profiler\fpsc=game\profiler\fpsc+1;  
Wend



