Main Menu

Lists of UDTs in UDTs

Started by Mahan, September 07, 2010, 03:37:59 AM

Previous topic - Next topic

Mahan

Hi,

What is the standard approach for using Lists of Type-instances inside Type-instances in PlayBasic?

To build hierarchies of data you need this, like this example (pseudo code):

PlayBASIC Code: [Select]
Type TPerson
Name$
EndType

Type TCompany
Name$
Employees As TPerson List
EndType



I've tried to search this forum to find an easy approach but all i could see was references to pointers and "c-like" linked list approaches. Is this the "normal" way of doing it?

Do you have some kind of "template" for doing this easily in your daily PB programming?


Also: How to deal with instance references?


Let's say we got a game with two global lists of enemies and players:
(again pseudo code):
PlayBASIC Code: [Select]
Type TPlayer
name$
hp
EndType


Type TEnemy
name$
hp
isFightingWith As TPlayer Pointer (or reference) List
EndType

Dim players as TPlayer List
Dim enemies as TEnemy List




In this example I'd only like to manage a list of references to TPlayer (that exist in the players-list) in each TEnemy (in the enemies list) that are currently in combat with players.

edit: spelling, addition about refs.

kevin

   
  Arrays In Arrays
  Type Lists as inventory

  Types can't support pointer/dynamic structure fields (arrays/lists), but you can store the data handle/pointers in an integer.  But this make the association a soft one.  So you'll have to clean these fields up manually if required. 





Mahan

#2
Your example with "Type Lists as inventory" was very good and sorts out lots of my questions, thanks a lot for that!

Unfortunately this example eats some memory for me on my machine (1.64M beta 18). Ofc. a little memory eating is to be expected since inventory is added all the time, but I didn't manage to release this memory even though i tried. I modified your program a little and added the function DumpShipInventories() but somehow I don't get it to reclaim the inventory memory from the ships. That is however very likely caused by my lacking knowledge of PB memory handling and commands.  :D




PlayBASIC Code: [Select]
   SetFps 60

type tInventory
itemname$
EndType


// Declare a game object called of SHIP
type tShip
x#,y#
speedx#,speedy#
radius
colour
InventoryArray
EndType


Dim Ship as tship list

// throw some ships randomly into the list so we're someting to look at
for lp =1 to 20
ship = new tship
ship.x=rndrange(100,700)
ship.y=rndrange(100,500)
angle#=rnd#(360)
ship.speedx=cos(angle#)
ship.speedy=sin(angle#)
ship.radius =rndrange(10,20)
ship.colour =rndrgb()

// Store the inventory arrays bank index, for later use
Ship.InventoryArray =CreateShipsInventory()
next



// This the items that characters can pick up.
type tItem
x#,y#
Starttime,DeathTime
Name$ ; name of the pick up item characters can pick up
BoundingBoxShape
endtype

Dim Item as tItem list


// create an array to hold the names of the inventory items that game character can pick up
Dim ItemNames$(0)
s$="Gold Bar, Silver Bar, Cricket Bat,20 cents,Commodore 64"
NumberOfItems=SplitToArray(s$,",",ItemNames$(),0)



; ---------------------------------------------------------
Do // game LOOP
; ---------------------------------------------------------

Cls 0

; ---------------------------------------------------------
; Show Items
; ---------------------------------------------------------
CurrentTime=timer()
if CurrentTime>SpawnNewItem
SpawnNewItem=CurrentTime+rndrange(50,250)

Item = new tItem
Item.x = Rnd(GetScreenWidth())
Item.y = Rnd(GetScreenHeight())
Item.StartTime=CurrentTime
Item.DeathTime=CurrentTime+2500

Item.name =ItemNames$(int(rnd#(NumberOfItems-1)))

width=gettextwidth(item.name)
height=gettextheight(item.name)

x1=-(Width/2)
x2=(Width/2)
y1=0
y2=(Height)
Item.BoundingBoxShape =NewBoundingBoxShape(x1,y1,x2,y2)


endif

for each Item()
timepast#=currenttime-item.StartTime
scaler#=(TimePast#/(item.DeathTime-item.StartTime))
Scaler#=1-cliprange#(scaler#,0,1)
ink rgbfade($ffffff, scaler#*100)
centertext item.x, item.y, Item.name
if CurrentTime>Item.DeathTime
item=Null
endif
next
ink $ffffff

; ---------------------------------------------------------
; Move Ships
; ---------------------------------------------------------
for each Ship()
x#=ship.x#+ship.speedx#
y#=ship.y#+ship.speedy#
if x#<10 then x#=10 : Ship.SpeedX*=-1
if x#>790 then x#=790 : Ship.SpeedX*=-1
if y#<10 then y#=10 : Ship.Speedy*=-1
if y#>590 then y#=590 : Ship.Speedy*=-1

ship.x=x#
ship.y=y#

; chekc if ship hit an inventory item
ShipHitItem(Ship())

circlec x#,y#,ship.radius,true,ship.colour

ShowShipInventory(ship())

next

if KeyState(57) then DumpShipInventories() //on space

Text 10, 10, "fps: " + str$(fps())
text 10, 30, "ships: " + str$(GetListSize(Ship()))
text 10, 50, "items: " + str$(GetListSize(item()))
text 10, 70, "press to dump inventory (reclaim memory)"
Sync
loop



Function CreateShipsInventory()
Dim MyInventory as tInventory list
EndFunction MyInventory.tInventory



Function ShowShipInventory(ThisShip.tship)
Makearray Inventory.tInventory

Inventory() =ThisShip.InventoryArray

x#=thisShip.x#
y#=thisShip.y#
ink $ff0000
text x#, y# + 30, GetListSize(Inventory())
Login required to view complete source code


edit: removed unnecessary code line in the example

kevin

#3
   To kill off inventory and recreate it, all we really need to do is create our Stub array via MakeArray, assign this stub our array handle, then undim it.

  You only need to run through them manually kill something (within the inventory) when a field is holding say a sprite / bank / shape / map (media) index in an integer.   In such a case, you'd have to release the media prior to deleting each type.  Otherwise they'd be leaked.  
 

PlayBASIC Code: [Select]
Function DumpShipInventories()
Makearray Inventory.tInventory

for each Ship()

; check if this object has an inventory
if Ship.InventoryArray

Inventory() = Ship.InventoryArray

for each Inventory()
; here we'd release any associated media such as sprites/images/maps

; now delete the current item in the list.
Inventory = null
next

; release the memory this list contains.
undim Inventory()

; create a new list
Ship.InventoryArray = CreateShipsInventory()

endif

next

EndFunction






Mahan

#4
Hi and thanks again Kevin very good info there.

This is slightly off-topic, but i got a problem when playing with the code posted above, and i couldn't get the example above to stop eating more and more memory over time. (even with your latest code)

I decided to write a much more simple test-program to reduce complexity and pinpoint the problem and I still got the same (what I think at least) memory leaks:


PlayBASIC Code: [Select]
explicit on

Type TITakeLotsOfSpace
OhThatsMuch(1000)
EndType

MakeArray spacer.TITakeLotsOfSpace
spacer = makeSpacerList()

print "Before allocing"
sync
FlushKeys:WaitKey

dim i as integer
for i = 0 to 999
spacer = new TITakeLotsOfSpace
next

print "Allocated spacers"
sync
FlushKeys:WaitKey

for each spacer()
spacer = null
next
undim spacer()

print "After freeing"
sync
FlushKeys:WaitKey

spacer = makeSpacerList()
for i = 0 to 999
spacer = new TITakeLotsOfSpace
next

print "Allocated spacers2"
sync
FlushKeys:WaitKey

for each spacer()
spacer = null
next
undim spacer()

print "After freeing2"
sync
FlushKeys:WaitKey

end


function makeSpacerList()
Dim spacer as TITakeLotsOfSpace List
EndFunction spacer as TITakeLotsOfSpace




After each alloc/free cycle in the program above, the process (when watched in Windows Task manager) uses up more memory, almost 1 Mbyte.