Spielobjektmanager

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Spirit
Beiträge: 174
Registriert: 13.04.2005 19:09

Spielobjektmanager

Beitrag von Spirit »

Besonders wenn man sehr komplexe Spiele entwickelt, kann es vorkommen, dass der Code sehr unübersichtlich wird, sodass es fast unmöglich ist weiterzuarbeiten. Hier kann ein Spielobjektmanager abhilfe schaffen.
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

;}

;}-
...und ein kleines Beispiel:

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
Benutzeravatar
MVXA
Beiträge: 3823
Registriert: 11.09.2004 00:45
Wohnort: Bremen, Deutschland
Kontaktdaten:

Beitrag von MVXA »

10 von 10 Punkten :allright:.
Bild
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

:allright:
Super Idee!
Benutzeravatar
Green Snake
Beiträge: 1394
Registriert: 22.02.2005 19:08

Beitrag von Green Snake »

KUHL :mrgreen:

gute idee :D
-.-"
Benutzeravatar
Spirit
Beiträge: 174
Registriert: 13.04.2005 19:09

Beitrag von Spirit »

Die Idee ist leider nicht von mir, nur die Umsetzung (Windows z.B. benutzt ein ähnliches System). Die Idee solch ein System in einem Spiel zu benutzen habe ich aus einem Buch über Spieleprogrammierung (Gems 4).
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

^^auf jeden fall ist die Idee wirklich cool ... und nobel von dir, den lob nicht einfach ein zu stecken :D

Die Umsetzung ist aber das was ich auf die schnelle gesehen hab auch nicht von schlechten eltern <)

Das System ist auf jeden fall eine überlegung wert :allright: für meine Projekte :wink: , würd natürlich nen eigenes machen -.- scho damit keine probleme später mittem copyright gibt /:->

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
Spirit
Beiträge: 174
Registriert: 13.04.2005 19:09

Beitrag von Spirit »

PMV hat geschrieben: Das System ist auf jeden fall eine überlegung wert :allright: für meine Projekte :wink: , würd natürlich nen eigenes machen -.- scho damit keine probleme später mittem copyright gibt /:->
Du kannst meinen Code in deinen Projekten verwenden, wenn du möchtest (ich würde mich sogar darüber freuen :wink: ).
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Vielen dank für die Einladung :D ... aber ich könnte deinen Code selbst wenn ich wollte garnicht so einsetzten :wink: .

^^Mal davon abgesehen das ich mich dann erst ganz genau da reinlesen müsste um es wirklich optimal zu nutzen muss der Gedanke ja an meine vorhaben angeglichen werden. So ist das selbe Coden unumweichlich /:->

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Antworten