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