Журнал о программированнии на языках Blitz3D, BlitzPlus, BlitzMax

Связанные списки в Blitz3D

Материал из Blitz Et Cetera

Перейти к: навигация, поиск

Содержание

Введение

Первое что встает на пути новичков, судя по вопросам на форумах, это типы. Итак, для начала, что же это такое? Представьте себе базу данных, где каждая запись представляет собой несколько полей (имя, фамилия итд). Создать шаблон записи можно так:

Type TestType

 Field name$
 Field age

End Type

Что бы создать запись и присвоить переменной адрес этой записи, делаем так:

Test.TestType=New TestType

Запись помещается в конец связанного списка. Для каждого типа создается свой список. Теперь мы можем заполнять поля записи

Test\name$="Артур"
Test\Age=20

Итд. Для чего мы можем использовать типы? Представьте, что вы делаете систему частиц. Для каждой частицы необходимо указать скорость движения, координаты, текстуру итд. Ну, тут без типов не обойтись.

Массивы в типах

Для того чтоб хранить векторы в типе удобно использовать специальный массив. Он не динамический и одномерный. От обычного массива отличается квадратными скобками. Например - Vector[3]. Пример использования:

Type Player

 Field X%[10],Y%[10]
 Field Bullet%[5]
End Type

p.Player=New Player
p\x[1]=320
p\y[1]=240

p\bullet[4]=32

Массив типов создается точно так же как и обычный, естественно он также является частью связанного списка. Пример:

Type Player

 Field Number%
End Type

Dim GoodPlayer.Player(50)
Dim BadPlayer.Player(100)

For i=0 To 50
 GoodPlayer(i)=New Player
 GoodPlayer(i)\number=i
Next

For i=0 To 100
 BadPlayer(i)=New Player
 BadPlayer(i)\number=i

Next

Типы внутри типов

Зачастую вам нужно будет создавать поля в новом типе какого-нибудь другого типа. Вот небольшой пример для иллюстрации:

Type Parent

 Field Name$
 Field kid.child
 Field spouse.parent
End Type
 
Type Child
 Field Name$
 Field mom.parent,dad.parent
End Type
 
papa.parent=New parent
mama.parent=New parent
lamer.child=New child
 
papa\name="papa"
papa\spouse.parent=mama.parent
papa\kid.child=lamer.child
 
mama\name="mama"
mama\spouse.parent=papa.parent
mama\kid.child=lamer.child
 
lamer\name="lamer"
lamer\mom.parent=mama.parent
lamer\dad.parent=papa.parent
 
For c.child=Each child
 DebugLog c\name+"'s dad is "+c\dad\name
 DebugLog c\name+"'s mom is "+c\mom\name
Next
For p.parent=Each parent
 DebugLog p\name+" is married to "+p\spouse\name+" ("+p\kid\name+" is there child)"

Next

Теперь смотрите в дебуге: Все должно быть предельно понятно за исключением слова Each. С его помощью мы пробегаем по всему связанному списку записей одного типа. Так же существуют команды Last, First, After и Before. Т.е. если вы хотите получить первую запись из списка вам необходимо сделать так:

FirstParentRecord.parent = First parent

Следующую запись:

SecondParentRecord=After(FirstParentRecord)

Итд, думаю с этим все понятно... Ах да, чуть не забыл про команду Insert:

Insert SecondParentRecord Before First parent

Перемещает нашу вторую запись в начало списка. Все просто. Для удаления записи используйте Delete SecondParentRecord, а для очистки всего списка Delete Each parent.

Команды Object() и Handle()

Команды эти не документированы, но всем давно известны, и широко используются.

Handle(Type.something)

Возвращает адрес в памяти указанного типа

Object.Something(Handle%)

Возвращает тип по указанному адресу в памяти. Если тип не существует, то вы получите ошибку при попытке обратиться к нему. Пример:

Type Player

 Field X%,Y%
End Type
p1.player=New player
 p1\x=128
 p1\y=64
p2.player=New player
 p1\x=256
 p1\y=128

address1%=Handle(p1.player)
address2%=Handle(p2.player)
copy1.player=Object.Player(address1)
copy2.player=Object.Player(address2)

Delete copy1; удаляется оригинал, так как это просто указатель

Что первое приходит в голову при виде этих команд? Конечно же быстрый доступ к отдельным записям, читайте игровым объектам.

Graphics3D 640,480,0,2

Type info
 Field name$,a,b,c,stuff,mass#,physics,etc
End Type
;Создаем шарик
a.info=New info
a\name$="bowling ball"
a\mass#=0.9
ball=CreateSphere()
NameEntity ball,Handle(a) ; Записываем адрес в имя объекта
;Теперь, когда вы к примеру получили объект используя  LinePicks, коллизии итд
ent = EntityCollided(bat,collisiontype_ball)
If ent
 a.info = Object.info(EntityName(ent)) ; получаем нужный нам тип
 Print a\name$ ; и читаем все параметры J
 Print a\mass#

End If

Таким образом, мы избавляемся от перебора всех записей в поисках нужной через Each.

Создание своих собственных связанных списков

Итак мы наконец подошли к связанным спискам! При создании записей они, как я уже упоминал, помещаются в список. А что если нам нужно несколько списков? Скажем у нас есть куча юнитов. Некоторые из них активны, некоторые нет: Можно задать поле Active и смотреть активный обьект или нет, если активный то обновлять, но в таком случае нам придется перебирать все время всех юнитов. А можно создать отдельный список и помещать туда только тех юнитов которые в данный момент активны и работать уже с этим списком. Для этого создайте файл BList.bb. Заинклудим его в проект и можем использовать списки:

Type Ball

Field id
End Type
list.BList = New BList

b.Ball = New Ball
AddFront(list,Handle(b))
ptr% = FirstItem(list)

b = Object.Ball(ptr)

Чуть короче:

Type Ball

Field id
End Type
list.BList = New BList

AddFront(list,Handle(New Ball))

b.Ball = Object.Ball(FirstItem(list))

Полный список команд для работы со списком:

userTypePtr% = AddFront%( list.Blist, userTypePtr% )

userTypePtr% = AddBack%( list.Blist, userTypePtr% )
trueFalse% = ResetList%( list.BList )
userTypePtr% = NextItem%( list.BList )
userTypePtr% = FirstItem%( list.BList )
userTypePtr% = LastItem%( list.BList )
userTypePtr% = CurrentItem%( list.BList )
userTypePtr% = MoveItem%( fromList.BList, toList.BList )
userTypePtr% = KillItem%( list.BList, node.BNode )
node.BNode = CurrentNode.BNode( list.Blist )

totalItems% = TotalItems%( list.BList )

Небольшой пример:

Include "BList.bb"

Type Ball
 Field id
 Field x#
 Field y#
End Type
 
; создаем 2 списка
list1.BList = New BList
list2.BList = New BList
 
;Создаем 6 объектов в первом списке
For i = 0 To 5
 b.Ball = Object.Ball(AddFront(list1,Handle(New Ball)))
 b\id = i
Next
 
Print "Список 1:" : printlist(list1)
 
;Обрабатываем первый список
ResetList(list1)
While NextItem(list1)
 b.Ball = Object.Ball(CurrentItem(list1))
 ;Перемещаем четвертую запись во второй список
 If b\id = 4 Then MoveItem(list1,list2)
 ; удаляем 3-ю запись, только позже, после обработки списка
 If b\id = 3 Then tmp.BNode = CurrentNode(list1)
 ; удаляем вторую запись
 If b\id = 2 Then Delete Object.Ball(KillItem(list1,Null))
Wend
Delete Object.Ball(KillItem(list1,tmp))
 
Print "Список 1: (удалены - 2,3, перемещена- 4)" : printlist(list1)
Print "Список 2:" : printlist(list2)
 
;очищаем весь первый список
While FirstItem(list1)
 Delete Object.Ball(KillItem(list1,Null))
Wend
Print "Список 1:" : printlist(list1)
 
WaitKey
 
Function printlist(list.BList)
 ResetList(list)
 While NextItem(list)
  b.Ball = Object.Ball(CurrentItem(list))
  Print b\id
 Wend

End Function

Ну вот и все. Удачи!


Автор: Остапенко "@rtur" Артур (e-mail: vd_arthur_sobaka_mail.ru, ICQ: 143476320)

Другие

Друзья