{ SPX Library Version 2.0 Copyright 1993 Scott D. Ramsay } SPX_OBJ is the object handling unit. It allows a easy way to keep track of multiple objects. Each object is a descendent of TOBJS. All objects are added to a double linked list for the dynamic creation of numerous objects. When removing objects from the list, we add the node to a 'kill' list which we can quickly deallocate multiple objects. Plist: Is our double linked pointer type which its data points to a TOBJS Type Pobjs = ^Tobjs; Plist = ^Tlist; Tlist = record item : pobjs; prev,next : plist; end; Here is the basic structure of the linked lists. Brackets [] specifies actual records or objects. var head,tail : Plist; { Head/Tail pointers to our object list } kill : Pkill; { Objects that need to be removed from the list } add : Padd; { Objects that need to be added to the list } |- Tail \|/ Head --> [Node1] <--> [Node2] <--> [Node3] <--> [Node4] --> NIL | | | | [Tobj] [Tobj] [Tobj] [Tobj] Kill --> [Knode1] --> [Knode2] --> NIL | | \|/ \|/ [Node2] [Node4] Add --> [Anode1] --> [Anode2] --> NIL | | \|/ \|/ [NodeA] [NodeB] In the above example we have four objects in our list. With the second and third node in the kill list (ready to be deleted). The add list contains two new nodes (NodeA and NodeB) that are read to be added to the linked list. That's basically the structure. To do calcuations on each object, we just traverse the double linked list. Lets say that the object at Node2 was a space alien and was shot. Ok its dead, so we add it to the kill list. After one iteration, usually the Kill and Add list is traversed deallocating and allocating nodes from and to the double linked list. pkill = ^tkill; tkill = record tk : plist; { TK - plist pointer To kill } next : pkill; end; padd = ^tadd; tadd = record ta : plist; { TA - plist pointer to add } front : boolean; { TRUE - add to top of list, else bottom } next : padd; end; EXAMPLE GAME STRUCTURE: repeat doAllitems(head,dErase); { erase objects from work page } doAllitems(head,dCalc); { move, create, kill objects } doAllitems(head,dDraw); { draw objects on work page } doAllitems(head,dUpdate); { copy objects drawn area on work to visual } cleanadd_list(add,head,tail); { add new objects to list } cleankill_list(kill,head,tail); { delete killed objects from list } { frame wait } (* see SPX_TIM.DOC *) until gameover; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Type TObjs = object; Generic object type. TObjs is the base for all objects that need to be handled. VARIABLES: powner:plist A pointer to the object's node in the linked list; killed:boolean TRUE if the object was added to a kill list; cankill:boolean Set to TRUE if allowing the object to be added to the kill list. (See checkhit for better descript); overshow:boolean In my games I traverse the list twice. The first time is to display the objects that will be behind my character, the second time is for the objects that are above my character. Set OVERSHOW to TRUE for the object to be an over character. (In the game RSQUID, the cloud objects are set to TRUE "Always in front", all other objects are set to FALSE. "Behind my main character"; id:integer For each object type I assign a unique number. This is incase I need to find certain objects while traversing the list METHODS: --------------------------------------------------- constructor Tobjs.init; Sets up the object DEFAULTS: KILLED set to FALSE ID set to 0 OVERSHOW set to TRUE OVERRIDE: often Here is how you add an object to the linked list; var p : plist; begin new(p); { Create a new node } p^.item := new(Pobjs,init);{ Create the object } p^.item^.powner := p; { IMPORTANT! have the object point to its node } addp(head,tail,p); { Add the node to the list } end; To delay the new node to be added to the list use add2add_list instead of "addp". See the procedure Add2Kill_list for reason for " p^.item^.powner := p;" --------------------------------------------------- procedure TObjs.drawitemobject; Displays the item. OVERRIDE: often Tobjs.drawitemobject is an empty function. In your drawitemobject function it will usually draw a sprite or object. --------------------------------------------------- procedure TObjs.calcitemobject; Does any calcuations to the object such as changing the object's position. OVERRIDE: often --------------------------------------------------- procedure TObjs.eraseitemobject; In your eraseitemobject function it will usually erase a sprite or object. OVERRIDE: often --------------------------------------------------- procedure TObjs.updateitemobject; In your updateitemobject function it will usually copy the sprite or object to the visual screen. Used often when working with an off screen work page. OVERRIDE: often --------------------------------------------------- function TObjs.checkhit(hx,hy:integer;item:pobjs):boolean; Return TRUE if hx,hy is in range of an object. HX,HY: Location to check; ITEM: Tobjs that it is in conflict with OVERRIDE: often For example: function TMyObject.checkhit(hx,hy:integer;item:pobjs):boolean; begin checkhit := (abs(hx-MyObjectX)<10) and (abs(hy-MyObjectY)<10); end; This is basically a collision detection function. For example, Lets assume we have a Tbullet object (desendant of Tobjs) that wants to check if it hit anyother objects in the list. In the procedure Tbullet.calcitemobject we would traverse the linked list calling the checkhit function for each object in the list. If the function returns TRUE, then the object collided with the object. procedure Tbullet.calcitemobject; var p : plist; begin p := head; while p<>nil do begin { Don't check for colliding bullets or dead objects } if (p^.item^.id<>id_bullet) and not p^.item^.killed then if p^.item^.checkhit(bullet_x,bullet_y,@self) then { we hit the object! } p := p^.next; end; end; --------------------------------------------------- destructor TObjs.done; Deallocates the object; OVERRIDE: sometimes ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure doallitems(head:plist;m:objmode); Traverse the linked list calling the an itemobject function in each node. HEAD: The head pointer of the linked list M: method to call in each node. M is of type objmode: where type objmode = (dNothing,dDraw,dErase,dUpdate,dCalc); dNothing - calls nothing. (just traverses the list) dDraw - calls the drawitemobject procedures dErase - calls the eraseitemobject procedures dUpdate - calls the updateitemobject procedures dCalc - calls the calcitemobject procedures Example: doallitems(Head,dCalc); { calls the calcitemobject procedure in each node } ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure addbeg(var nkbeg,nkend,p:plist); Add a node to the BEGINNING of the linked list NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer; P: Node to add to the list ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure addend(var nkbeg,nkend,p:plist); Add a node to the END of the linked list NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer; P: Node to add to the list ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure addp(var nkbeg,nkend,p:plist); Add a node to the END of the linked list. Same as ADDEND, here for compatibility from SPX v1.0. NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer; P: Node to add to the list ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure deletep(var nkbeg,nkend,p:plist); Delete a note from the list and deallocate the object connected to it NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer; P: Node to delete to the list ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure calcitems(var nkbeg:plist); Traverses a linked list calling the node's object's calcitemobject method NKBEG: Linked list Head pointer NOTE: Still calls method even if the KILLED flag it true. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure drawitems(var nkbeg:plist;over:boolean); Traverses a linked list calling the node's object's drawitem object method NKBEG: Linked list Head pointer OVER: Set to TRUE to call only objects with OVERSHOW set to TRUE Set to FALSE to call only objects with OVERSHOW set to FALSE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure add2add_list(var add:padd;i:plist;front:boolean); Adds a node to the add list. ADD: Add list to add the node P: Note to be added FRONT: Set to TRUE if you want the node to be added to the top of the list, else it will be added the the end. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure cleanadd_list(var add:padd;var nkbeg,nkend:plist); Traverses the ADD list and adds each node to the linked list. ADD: Add list to cleanout NKBEG: Linked list Head pointer NKEND: Linked list Tail pointer On return ADD=NIL ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure add2kill_list(var kill:pkill;var i:plist); Adds a node to the kill list and sets the object's KILLED flag to TRUE. KILL: Kill list to add the node; I: Node to add to the kill list This procedure is usually called with in a object's calcitemobject or checkhit methods. Since this is the case, you CAN'T deallocate a object while still in the object's method. So by adding the node/object to a kill list. One can remove the object later. Each object has a pointer to its node (powner) that is passed to the kill list. Example: function TMyObject.checkhit(hx,hy:integer;item:pobjs):boolean; begin if (abs(hx-MyObjectX)<10) and (abs(hy-MyObjectY)<10) then begin checkhit := true; add2kill_list(kill,powner); end else checkhit := false; end; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure cleankill_list(var kill:pkill;var nkbeg,nkend:plist); Traverses the KILL list deallocating node that is in the kill and linked list. KILL: Kill list to cleanout; NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer On return KILL=NIL ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ procedure clean_plist(var nkbeg,nkend:plist); Traverse the LINKED list deallocating every node. NKBEG: Linked list Head pointer; NKEND: Linked list Tail pointer On return nkbeg=NIL and nkend=NIL ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ