Das System ist nachrichtenbasiert. Alles, was ein Objekt tut ist die Antwort auf eine Nachricht. Will man ein Objekt in der Spielwelt bewegen, so sendet man ihm einfach eine Nachricht. Auf diese Weise kann man die einzelnen Bestandteile des Spiel leicht separieren, sodass sie keine allzu großen Abhängigkeiten entwickeln. Außerdem ist der Code viel leichter erweiterbar.
Der Objektmanager:
Code: Alles auswählen
;{- Standard messages
Enumeration
;Class messages
#EM_clsInit
#EM_clsFree
#EM_clsName
;Entity messages
#EM_Create
#EM_Destroy
;User messages
#EM_User
EndEnumeration
;}-
;{- Structures
Structure Class
clsName.b[64]
clsProc.l
*clsNext.Class
EndStructure
Structure Entity
entClass.b[64]
entProc.l
entData.l
*entParent.Entity
*entNextSibling.Entity
*entPrevSibling.Entity
*entChild.Entity
entDestroy.b
EndStructure
;}-
;{- Global
Global *clsList_FirstItem.Class, *clsList_LastItem.Class, *clsList_CurrItem.Class
Global entCleanup.l
;}-
;{- Procedures
;{ Class procedures
Procedure EntNewClass(ClassProc.l)
*Class.Class =AllocateMemory(SizeOf(Class))
*Class\clsProc =ClassProc
If *clsList_FirstItem=0
*clsList_FirstItem =*Class
Else
*clsList_LastItem\clsNext=*Class
EndIf
*clsList_CurrItem =*Class
*clsList_LastItem =*Class
CallFunctionFast(ClassProc, 0, #EM_clsName, @*Class\clsName, 63)
CallFunctionFast(ClassProc, 0, #EM_clsInit, 0, 0)
EndProcedure
Procedure EntDestroyClass(Class$)
*clsList_CurrItem=*clsList_FirstItem
Repeat
If PeekS(@*clsList_CurrItem\clsName)=Class$ And *clsList_CurrItem=*clsList_FirstItem
CallFunctionFast(*clsList_CurrItem\clsProc, 0, #EM_clsFree, 0, 0)
*clsList_FirstItem=*clsList_CurrItem\clsNext
FreeMemory(*clsList_CurrItem)
*clsList_CurrItem =0
Break
ElseIf *clsList_CurrItem\clsNext And PeekS(@*clsList_CurrItem\clsNext\clsName)=Class$
CallFunctionFast(*clsList_CurrItem\clsNext\clsProc, 0, #EM_clsFree, 0, 0)
*clsNext.Class =*clsList_CurrItem\clsNext\clsNext
FreeMemory(*clsList_CurrItem\clsNext)
*clsList_CurrItem\clsNext=*clsNext
If *clsNext=0
*clsList_LastItem =*clsList_CurrItem
EndIf
Break
EndIf
Until *clsList_CurrItem=0
EndProcedure
Procedure EntDestroyAllClasses()
*clsNext.Class
*clsList_CurrItem=*clsList_FirstItem
While *clsList_CurrItem
CallFunctionFast(*clsList_CurrItem\clsProc, 0, #EM_clsFree, 0, 0)
*clsNext=*clsList_CurrItem\clsNext
FreeMemory(*clsList_CurrItem)
*clsList_CurrItem=*clsNext
Wend
EndProcedure
;}
;{ Message procedures
Procedure.l EntSendMessage(*Entity.Entity, Msg, param1, param2)
ProcedureReturn CallFunctionFast(*Entity\entProc, *Entity, Msg, param1, param2)
EndProcedure
Procedure EntSendMessagePre(*Entity.Entity, Msg, param1, param2)
*Child.Entity
If *Entity\entDestroy=#False
EntSendMessage(*Entity, Msg, param1, param2)
EndIf
*Child=*Entity\entChild
While *Child
EntSendMessagePre(*Child, Msg, param1, param2)
*Child=*Child\entNextSibling
Wend
EndProcedure
;}
;{ Entity procedures
Procedure.l EntNewEntity(Class$, *Parent.Entity, param1, param2)
*clsList_CurrItem=*clsList_FirstItem
Repeat
If PeekS(@*clsList_CurrItem\clsName)=Class$
*Entity.Entity =AllocateMemory(SizeOf(Entity))
*Entity\entProc =*clsList_CurrItem\clsProc
*Entity\entParent =*Parent
If *Parent
If *Parent\entChild
*Entity\entNextSibling =*Parent\entChild
*Parent\entChild\entPrevSibling =*Entity
EndIf
*Parent\entChild =*Entity
EndIf
CopyMemory(@*clsList_CurrItem\clsName, @*Entity\entClass, 64)
EntSendMessage(*Entity, #EM_Create, param1, param2)
ProcedureReturn *Entity
EndIf
*clsList_CurrItem=*clsList_CurrItem\clsNext
Until *clsList_CurrItem=0
EndProcedure
Procedure EntDestroyEntity(*Entity.Entity)
*Entity\entDestroy=#True
entCleanup+1
EndProcedure
Procedure EntUnhookEntity(*Entity.Entity)
If *Entity\entParent<>0
If *Entity\entPrevSibling=0
If *Entity\entNextSibling<>0
*Entity\entNextSibling\entPrevSibling=0
EndIf
*Entity\entParent\entChild=*Entity\entNextSibling
Else
*Entity\entPrevSibling\entNextSibling=*Entity\entNextSibling
If *Entity\entNextSibling<>0
*Entity\entNextSibling\entPrevSibling=*Entity\entPrevSibling
EndIf
EndIf
EndIf
EndProcedure
Procedure EntCleanup(*Entity.Entity)
DefType.Entity *Child, *NextSibling
If entCleanup<>0
*Child=*Entity\entChild
While *Child
*NextSibling=*Child\entNextSibling
EntCleanup(*Child)
*Child =*NextSibling
Wend
If *Entity\entDestroy=#True
EntUnhookEntity(*Entity)
FreeMemory(*Entity)
entCleanup-1
EndIf
EndIf
EndProcedure
;}
;}-
Code: Alles auswählen
IncludeFile "Entity Manager.pb"
Global Quit.b
Global Field.l, Ball.l, Board.l, WLCon.l
;{- Entity messages
Enumeration #EM_User
#EM_Draw
#EM_Update
#EM_SetPosition
#EM_GetPositionY
#EM_SetDirection
#EM_ChangeDirection
#EM_SetSpeed
#EM_SetColor
#EM_UserInput
#EM_HitTest
#EM_BlockDestroyed
EndEnumeration
;}-
;{- Classes
Structure Block
x.l
y.l
Color.l
EndStructure
Procedure Class_Block(*Entity.Entity, Msg, param1, param2)
If *Entity : *entData.Block=*Entity\entData : EndIf
Select Msg
Case #EM_clsName
PokeS(param1, "Block", param2)
Case #EM_Create
*Entity\entData=AllocateMemory(SizeOf(Block))
Case #EM_Destroy
FreeMemory(*entData)
EntDestroyEntity(*Entity)
Case #EM_SetPosition
*entData\x=param1
*entData\y=param2
Case #EM_SetColor
*entData\Color=param1
Case #EM_Draw
StartDrawing(ScreenOutput())
Box(*entData\x+2, *entData\y+2, 50, 20, 0)
Box(*entData\x, *entData\y, 50, 20, *entData\Color)
StopDrawing()
Case #EM_HitTest
If param1>=*entData\x-10 And param1<=*entData\x+60 And param2>=*entData\y-10 And param2<=*entData\y+30
If Abs(param1-(*entData\x+25))>25
EntSendMessage(Ball, #EM_ChangeDirection, 1, 0)
Else
EntSendMessage(Ball, #EM_ChangeDirection, 0, 1)
EndIf
EntSendMessage(WLCon , #EM_BlockDestroyed, 0, 0)
EntSendMessage(*Entity, #EM_Destroy, 0, 0)
EndIf
EndSelect
EndProcedure
Procedure Class_Field(*Entity.Entity, Msg, param1, param2)
Select Msg
Case #EM_clsInit
InitSprite()
Case #EM_clsName
PokeS(param1, "Field", param2)
Case #EM_Create
OpenScreen(800, 600, 32, "EM Test")
Case #EM_Destroy
CloseScreen()
EntDestroyEntity(*Entity)
Case #EM_Draw
ClearScreen(191, 191, 255)
Case #EM_HitTest
If param1<=10 Or param1>=790
EntSendMessage(Ball, #EM_ChangeDirection, 1, 0)
EndIf
If param2<=10
EntSendMessage(Ball, #EM_ChangeDirection, 0, 1)
EndIf
EndSelect
EndProcedure
Structure Ball
x.l
y.l
ax.l
ay.l
speed.l
EndStructure
Procedure Class_Ball(*Entity.Entity, Msg, param1, param2)
If *Entity : *entData.Ball=*Entity\entData : EndIf
Select Msg
Case #EM_clsName
PokeS(param1, "Ball", param2)
Case #EM_Create
*Entity\entData=AllocateMemory(SizeOf(Ball))
Case #EM_Destroy
FreeMemory(*entData)
EntDestroyEntity(*Entity)
Case #EM_SetPosition
*entData\x=param1
*entData\y=param2
Case #EM_SetDirection
If param1<>0 : *entData\ax=param1 : EndIf
If param2<>0 : *entData\ay=param2 : EndIf
Case #EM_ChangeDirection
If param1=1
*entData\ax=-*entData\ax
ElseIf param2=1
*entData\ay=-*entData\ay
EndIf
Case #EM_SetSpeed
*entData\speed=param1
Case #EM_Update
EntSendMessagePre(Field, #EM_HitTest, *entData\x, *entData\y)
*entData\x+*entData\ax**entData\speed
*entData\y+*entData\ay**entData\speed
Case #EM_Draw
StartDrawing(ScreenOutput())
Circle(*entData\x, *entData\y, 10, 0)
Circle(*entData\x, *entData\y, 8, $FF0000)
StopDrawing()
Case #EM_GetPositionY
ProcedureReturn *entData\y
EndSelect
EndProcedure
Structure Board
x.l
y.l
EndStructure
Procedure Class_Board(*Entity.Entity, Msg, param1, param2)
If *Entity : *entData.Board=*Entity\entData : EndIf
Select Msg
Case #EM_clsName
PokeS(param1, "Board", param2)
Case #EM_Create
*Entity\entData=AllocateMemory(SizeOf(Board))
Case #EM_Destroy
FreeMemory(*entData)
EntDestroyEntity(*Entity)
Case #EM_SetPosition
*entData\x=param1
*entData\y=param2
Case #EM_Draw
StartDrawing(ScreenOutput())
Box(*entData\x-50, *entData\y, 100, 10, $00AAFF)
StopDrawing()
Case #EM_UserInput
*entData\x+param1*3
If *entData\x<50 : *entData\x=50 : ElseIf *entData\x>750 : *entData\x=750 : EndIf
Case #EM_HitTest
If param1>=*entData\x-50 And param1<=*entData\x+50 And param2>=*entData\y-10 And param2<=*entData\y
EntSendMessage(Ball, #EM_SetDirection, 0, -1)
EndIf
EndSelect
EndProcedure
Procedure Class_WLCon(*Entity.Entity, Msg, param1, param2)
Select Msg
Case #EM_clsName
PokeS(param1, "WLCon", param2)
Case #EM_Create
*Entity\entData=60
Case #EM_Destroy
EntDestroyEntity(*Entity)
Case #EM_Update
If EntSendMessage(Ball, #EM_GetPositionY, 0, 0)>600 Or *Entity\entData=0
Quit=1
EndIf
Case #EM_BlockDestroyed
*Entity\entData-1
EndSelect
EndProcedure
Procedure Class_Input(*Entity.Entity, Msg, param1, param2)
Select Msg
Case #EM_clsInit
InitKeyboard()
Case #EM_clsName
PokeS(param1, "Input", param2)
Case #EM_Destroy
EntDestroyEntity(*Entity)
Case #EM_Update
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Escape)
Quit=#True
EndIf
If KeyboardPushed(#PB_Key_Right)
EntSendMessage(Board, #EM_UserInput, 1, 0)
ElseIf KeyboardPushed(#PB_Key_Left)
EntSendMessage(Board, #EM_UserInput, -1, 0)
EndIf
EndSelect
EndProcedure
;}-
EntNewClass(@Class_Block())
EntNewClass(@Class_Field())
EntNewClass(@Class_Ball ())
EntNewClass(@Class_Board())
EntNewClass(@Class_WLCon())
EntNewClass(@Class_Input())
Field=EntNewEntity("Field", 0, 0, 0)
Ball =EntNewEntity("Ball" , Field, 0, 0)
EntSendMessage(Ball, #EM_SetPosition , 100, 300)
EntSendMessage(Ball, #EM_SetSpeed , 3, 0)
EntSendMessage(Ball, #EM_SetDirection, 1, 1)
Board=EntNewEntity("Board", Field, 0, 0)
EntSendMessage(Board, #EM_SetPosition, 400, 570)
WLCon=EntNewEntity("WLCon", Field, 0, 0)
EntNewEntity("Input", Field, 0, 0)
For y=0 To 4
For x=0 To 11
Block=EntNewEntity("Block", Field, 0, 0)
EntSendMessage(Block, #EM_SetPosition, 60*x+45, 30*y+45)
EntSendMessage(Block, #EM_SetColor , RGB(255, 50*y+50, 0), 0)
Next
Next
;- Main loop
Repeat
EntCleanup(Field)
EntSendMessagePre(Field, #EM_Update, 0, 0)
EntSendMessagePre(Field, #EM_Draw , 0, 0)
FlipBuffers()
Until Quit=#True
EntSendMessagePre(Field, #EM_Destroy, 0, 0)
EntCleanup(Field)
End