Несколько реализаций инвентаря. Работа с типами и массивами.
Материал из Blitz Et Cetera
Omnia mea mecum porto. (лат. Все свое ношу с собой.)
Цицерон
Кто пытался делать инвентарь, тот знает, что основная проблема - это соединение клеток на экране и кода программы. Весьма сложно также не допустить наложения объектов друг на друга. Сейчас я попробую объяснить, как сделать инвентарь. Приступим.
Создадим глобальные переменные, которые будут отвечать за количество клеток в поле и их размер. Это поможет нашей программе быть универсальной и впоследствии можно будет динамично изменять вместимость инвентаря.
Global ImH= 30 ;<= высота одной клетки
Global ImW=30 ;<= ширина одной клетки
Global nachx=100 ;<= начальное положение поля по x
Global nachy=100 ;<= начальное положение поля по y
Global vertic=4 ;<= клеток вниз
Global goriz= 8 ;<= клеток вправо
Теперь зададим графический режим и создадим камеру (предполагается, что будет использоваться в 3D игре).
Graphics3D 500, 400, 32, 2
SetBuffer BackBuffer()
camera=CreateCamera()
CameraClsColor camera, 100, 100, 100
Вот и самое главное, создаем массив картинок и массив соответствующий ему, но хранящий цифры. Это и будет основа нашего инвентаря. Заполняем все элементы массива, как пустые и загружаем изображение пустой клетки в цикле.
Dim image (goriz*vertic +1)
Dim imageyn(goriz*vertic+1)
i=1
While i < goriz*vertic+1
image(i)=LoadImage("Inventory1-SmallItem0.png")
imageyn(i)=0
i=i+1
Wend
Вам понадобится картинка пустого отсека, впрочем, вы бы, наверняка и сами смогли ее нарисовать. Создадим еще пару предметов для нашего инвентаря.
image (3)= LoadImage("Inventory1-SmallItem1.png")
imageyn(3)=1
image(11)=LoadImage("Inventory1-SmallItem2.png")
imageyn (11)= 1
Я специально выбрал произвольные элементы массива. Нам понадобятся изображения предметов. Они могут быть любые, главное, чтобы они соответствовали по ширине и высоте нашим клеткам. Так же надо, чтобы у них был черный фон для его прозрачности, но это не обязательно. Нарисуйте их сами или воспользуйтесь моими.
Что ж, давайте, отобразим наше поле. Сначала главный цикл:
While Not exitprogram= True
If KeyHit(1)=True Then exitprogram=True
UpdateWorld()
RenderWorld()
draw()
Flip
Wend
End
Для отображения поля на экране создадим функцию draw():
Function draw ()
k=1
i=1
j=1
While i < vertic+1
j=1
While j < goriz+1
DrawImage image(k),nachx+j*ImW,nachy+i*ImH
j=j+1
k=k+1
Wend
i=i+1
Wend
End Function
Эта функция отображает каждое из изображений массива image() в определенном месте экрана. Для этого используется двойной цикл. В нем: переменные i, j - это переменные циклов, по которым рассчитывается позиция изображения. Переменная k - это счетчик элементов массива, он перебирает все элементы.
Все, можно проверять!
Выглядит неплохо. Но хотелось бы иметь возможность динамичного добавления вещей в инвентарь. Давайте создадим функцию, которая добавляет предмет в инвентарь.
Function addim (imstr$ )
i=1
l=0
While i < goriz*vertic+1
If imageyn(i)=0 Then
imageyn(i)=1
image(i)=LoadImage(imstr$)
i=goriz*vertic+1
l=1
Else
i=i+1
l=0
EndIf
Wend
If l=0 Then RuntimeError "нет места!"
End Function
Что, думаете, что она слишком большая для простого добавления предмета? А, вот и нет. Тут не так все просто, как кажется. Нужно учесть то, что клетка, куда мы собираемся "положить" наш предмет может оказаться не пустой, а так же то, что в инвентаре может вообще не быть места. Вот тут-то нам и понадобиться второй массив. Его элементы могут принимать два значения: 1 и 0. Т. е. занято, пусто. В цикле проверяем все клетки и кладем предмет в первую пустую клетку, после чего выходим из цикла. Если пустых клеток нет, то пишем, что в инвентаре нет места и выходим. В принципе выходить не обязательно, поэтому можете заменить RuntimeError на text, например.
В главный цикл добавляем вызов этой функции по нажатию клавиши пробел:
If KeyHit(57) Then addim("Inventory1-SmallItem1.png")
Ну, вот уже на что-то похоже. Неплохо бы еще иметь возможность самому менять положение предметов, например мышкой. Это очень занимательное занятие. Создадим еще одну функцию refresh().
В ней будет оцениваться, есть ли в вашей виртуальной руке предмет или нет, поэтому перед главным циклом указываем глобальную переменную:
Global vlog=0
Теперь о функции. Она должна перемещать предметы по полю и менять их местами при наложении одного предмета на другой. Нам понадобиться три временных изображения. Одно возле мыши и два для копирования, поэтому дописываем в программу:
Global tempimage1= CreateImage(ImW,ImH )
Global tempimage2=CreateImage(ImW,ImH)
Global tempimage3= CreateImage(ImW,ImH )
Так как они будут использоваться в функциях, то должны быть глобальны.
Добавляем в функцию отображения следующий код:
If vlog=1 Then DrawImage tempimage1,MouseX()-ImW/2,MouseY()-ImH/2
Это необходимо для того, чтобы картинка следовала за мышью при перемещении.
Теперь сама функция refresh():
Function refresh ()
If MouseX() > nachx+ImW And MouseX() < nachx+ImW*(goriz+1) And MouseY() > nachy+ImH And MouseY() < nachy+ImH*(vertic+1) Then
If MouseHit(1) Then
If vlog=0 Then
nom=(((MouseY()-nachy)/ImH)*goriz)-(goriz-((MouseX()-nachx)/ImW))
tempimage1=CopyImage(image(nom))
image(nom)=LoadImage("Inventory1-SmallItem0.png")
vlog=1
imageyn(nom)=0
Else
nom=(((MouseY()-nachy)/ImH)*goriz)-(goriz-((MouseX()-nachx)/ImW))
If imageyn(nom)=0 Then
tempimage2=CopyImage(image(nom))
image(nom)=CopyImage(tempimage1)
vlog=0
imageyn(nom)=1
Else
tempimage3=CopyImage(image(nom))
image(nom)=CopyImage(tempimage1)
tempimage1=CopyImage(tempimage3)
vlog=1
EndIf
EndIf
EndIf
EndIf
End Function
Вначале стоит условие проверяющее находится ли мышь в зоне поля. Далее если был сделан клик левой кнопкой мыши, то в зависимости от того, в каком состоянии находится переменная vlog, выполняется одно из следующих действий. В случае когда предмет взят возможно два варианта: положить его в пустую клетку, поменять его с предметом лежащим в клетке. Думаю, что теперь все понятно. Реализовать удаление предметов достаточно просто, а их связь с предметами выбранными игроком можно реализовать копированием. Это я оставляю вам.
Вот весь код программы:
;(c) Agnislav Lipcan
; инвентарь
;avelnet@yandex.ru
Global ImH=30 ;<= высота одной клетки
Global ImW=30 ;<= ширина одной клетки
Global nachx=100 ;<= начальное положение поля по x
Global nachy=100 ;<= начальное положение поля по y
Global vertic=4 ;<= клеток вниз
Global goriz=8 ;<= клеток вправо
Graphics3D 500,400,32,2
SetBuffer BackBuffer()
camera=CreateCamera()
CameraClsColor camera,100,100,100
Dim image(goriz*vertic+1)
Dim imageyn(goriz*vertic+1)
i=1
While i < goriz*vertic+1
image(i)=LoadImage("Inventory1-SmallItem0.png")
imageyn(i)=0
i=i+1
Wend
Global vlog=0
image(3)=LoadImage("Inventory1-SmallItem1.png")
imageyn(3)=1
image(11)=LoadImage("Inventory1-SmallItem2.png")
imageyn(11)=1
Global tempimage1=CreateImage(ImW,ImH)
Global tempimage2=CreateImage(ImW,ImH)
Global tempimage3=CreateImage(ImW,ImH)
;main cikles
While Not exitprogram=True
If KeyHit(1)=True Then exitprogram=True
refresh()
If KeyHit(57) Then addim("Inventory1-SmallItem1.png")
UpdateWorld()
RenderWorld()
draw()
Flip
Wend
End
;functions
Function draw()
k=1
i=1
j=1
While i < vertic+1
j=1
While j < goriz+1
DrawImage image(k),nachx+j*ImW,nachy+i*ImH
j=j+1
k=k+1
Wend
i=i+1
Wend
If vlog=1 Then DrawImage tempimage1,MouseX()-ImW/2,MouseY()-ImH/2
End Function
Function refresh()
If MouseX() > nachx+ImW And MouseX() < nachx+ImW*(goriz+1) And MouseY() > nachy+ImH And MouseY() < nachy+ImH*(vertic+1) Then
If MouseHit(1) Then
If vlog=0 Then
nom=(((MouseY()-nachy)/ImH)*goriz)-(goriz-((MouseX()-nachx)/ImW))
tempimage1=CopyImage(image(nom))
image(nom)=LoadImage("Inventory1-SmallItem0.png")
vlog=1
imageyn(nom)=0
Else
nom=(((MouseY()-nachy)/ImH)*goriz)-(goriz-((MouseX()-nachx)/ImW))
If imageyn(nom)=0 Then
tempimage2=CopyImage(image(nom))
image(nom)=CopyImage(tempimage1)
vlog=0
imageyn(nom)=1
Else
tempimage3=CopyImage(image(nom))
image(nom)=CopyImage(tempimage1)
tempimage1=CopyImage(tempimage3)
vlog=1
EndIf
EndIf
EndIf
EndIf
End Function
Function addim(imstr$)
i=1
l=0
While i < goriz*vertic+1
If imageyn(i)=0 Then
imageyn(i)=1
image(i)=LoadImage(imstr$)
i=goriz*vertic+1
l=1
Else
i=i+1
l=0
EndIf
Wend
If l=0 Then RuntimeError "нет места!"
End Function
Теперь реализуем кое-что посложнее, а заодно поработаем с типами. В инвентаре кроме картинки надо хранить еще и другую информацию: цену, название, 3Dмодель, описание. Можно для всего этого завести отдельные массивы или несколько многомерных массивов, но лучше воспользоваться типами - это очень удобно. Так же хотелось бы что бы предметы могли занимать больше одной клетки. Поэтому мы изменим логику программы. Теперь у нас будет не поле клеток, а некий набор предметов, которые могут находиться в инвентаре, вне его, быть выбраны игроком в качестве оружия. Для простоты не будем учитывать тип предмета, а так же примем ограничение, что предмет занимает четное количество клеток, т. е. как минимум 2.
Приступим. Начало примерно такое же:
Global ImH= 30 ;<= высота одной клетки
Global ImW=30 ;<= ширина одной клетки
Global nachx=70 ;<= начальное положение поля по x минус ImW
Global nachy=70 ;<= начальное положение поля по y минус ImH
Global vertic=6 ;<= клеток вниз
Global goriz=8 ;<= клеток вправо
Global kolvovesh=2 ;общее количество вещей
Global mouse ;мыша
Global orugnom= 0 ;первое выбранное оружие
Создаем экран и камеру.
Graphics3D 800, 700, 32, 2
SetBuffer BackBuffer()
AppTitle "inventar"
font=LoadFont(courier ,20)
SetFont font
camera= CreateCamera()
Надеюсь, что пояснять данные строки не надо.
Создаем поле клеток. Оно у нас все-таки будет, но теперь оно не главное.
Dim pole(goriz+1,vertic+1)
Загружаем изображение пустой клетки.
Global noneim=LoadImage("Inventory1-SmallItem0.png")
Теперь создаем новый тип с параметрами: кол-во занимаемых клеток, позиция в поле по x и y, картинка. Можно добавить и другие параметры, но это уже усовершенствование:
Type predmet
Field kolvokletok,posx,posy
Field gde ;0-не активно; 1-в инвентаре; 2-выбрано
Field image ;картинка
;Field cena, sila, lovkost ;параметры
;Field name$ ;название
;field model ;3d модель
End Type
Теперь создаем массив вещей нашего типа.
Dim pr.predmet(kolvovesh+1)
Нам, как и в прошлом случае понадобится временная картинка для мыши и еще две временных переменных для определения того, где в данный момент находится предмет.
Global tempimage
Global temp1=0
Global temp2= 0
Главный цикл не сильно изменился:
Repeat
UpdateWorld()
RenderWorld()
refresh()
draw()
Flip
Until KeyHit(1)
End
Теперь о функции создания. Не будем проверять свободное место в инвентаре, это вы уже умеете делать. Просто создадим вещь и положим в определенную позицию поля.
Function newvesh (x,y,kl,im$ )
pr(num) = New predmet
pr(num)\posx=x
pr(num)\posy=y
pr(num)\kolvokletok=kl
pr(num)\image=LoadImage(im$)
pr(num)\gde=1
num=num+1
End Function
Глобальная переменная num отвечает за номер элемента массива вещей.
Вот так выглядит вызов функции:
Global num= 1
newvesh(1,1,6,"Inventory1-Item1.png")
newvesh (4, 2, 8, "Inventory1-Item2.png")
Вам понадобятся изображения предметов. Не забывайте о соответствии ширины и высоты.
Теперь нарисуем все это на экране. Принцип тот же.
Function draw ()
For i=1 To vertic
For j=1 To goriz
DrawImage noneim,nachx+j*ImW,nachy+i*ImH
;Text nachx+j*ImW+5,nachy+i*ImH+5,pole(j,i)
Next
Next
For i=0 To 3
For j=0 To 1
DrawImage noneim,507+j*ImW,368+i*ImH
Next
Next
For i=1 To kolvovesh
If pr(i)\gde=1 Then
DrawImage pr(i)\image,nachx+pr(i)\posx*ImW,nachx+pr(i)\posy*ImH
EndIf
If pr(i)\gde=2 Then
DrawImage pr(i)\image,505,367
EndIf
Next
If mouse=1 Then DrawImage tempimage,MouseX(),MouseY()
End Function
Тут мы сначала в цикле рисуем поле из пустых клеток. Закомментированная строка будет показывать номер вещи, которая находится в клетке. Это полезно на начальных этапах, чтобы лучше понять, как это работает. Если в клетке ничего нет, то ноль. Потом опять в цикле рисуем место, куда можно будет положить оружие при его выборе. Далее перебираем в цикле все вещи и в зависимости от того, чему равен параметр gde, либо отображаем их в инвентаре, либо в качестве выбранного оружия, либо не отображаем совсем. В конце рисуем возле курсора картинку, если переменная mouse=1 т. е. взят предмет. Можно проверять. Теперь о функции refresh(). Тут все совсем поменялось.
Function refresh ()
For i=0 To vertic
For j=0 To goriz
pole(j,i)=0
Next
Next
For i=1 To kolvovesh
If pr(i)\gde=1 Then
For k=0 To pr(i)\kolvokletok*0.5-1
For m=0 To 1
pole(pr(i)\posx+m,pr(i)\posy+k)=i
Next
Next
EndIf
Next
If MouseX() > nachx+ImW And MouseX() < nachx+ImW*(goriz) And MouseY() > nachy+ImH And MouseY() < nachy+ImH*(vertic) Then
If MouseHit(1) Then
If mouse=0 Then
If pole((MouseX()-nachx)/ImW,(MouseY()-nachy)/ImH) <> 0 Then
mouse=1
i=pole((MouseX()-nachx)/ImW,(MouseY()-nachy)/ImH)
pr(i)\gde=0
temp1=i
tempimage=pr(i)\image
EndIf
Else
temp2=0
For k=0 To pr(temp1)\kolvokletok*0.5-1
For m=0 To 1
If pole((MouseX()-nachx)/ImW+m,(MouseY()-nachy)/ImH+k) <> 0 Then temp2=pole((MouseX()-nachx)/ImW+m,(MouseY()-nachy)/ImH+k)
Next
Next
If temp2=0 Then
pr(temp1)\posx=(MouseX()-nachx)/ImW
pr(temp1)\posy=(MouseY()-nachy)/ImH
pr(temp1)\gde=1
temp1=0
mouse=0
Else
If pr(temp2)\kolvokletok >= pr(temp1)\kolvokletok Then
pr(temp1)\posx=pr(temp2)\posx
pr(temp1)\posy=pr(temp2)\posy
pr(temp1)\gde=1
pr(temp2)\gde=0
tempimage=pr(temp2)\image
temp1=temp2
EndIf
EndIf
EndIf
EndIf
EndIf
If MouseX() > 505 And MouseX() < 565 And MouseY() > 367 And MouseY() < 427 Then
If MouseHit(1) Then
If mouse=1 Then
If orugnom <> 0 Then
pr(orugnom)\gde=0
pr(temp1)\gde=2
temp=temp1
temp1=orugnom
tempimage=pr(orugnom)\image
orugnom=temp
Else
pr(temp1)\gde=2
orugnom=temp1
temp1=0
mouse=0
EndIf
Else
If orugnom <> 0 Then
mouse=1
pr(orugnom)\gde=0
temp1=orugnom
tempimage=pr(orugnom)\image
orugnom=0
EndIf
EndIf
EndIf
EndIf
End Function
Описываю по порядку.
Заполняем в двойном цикле все элементы поля как нулевые.
Перебираем все вещи и если они находятся в инвентаре, то в двойном цикле заполняем все клетки, которые он занимает его номером.
Далее идут операции с мышью. Они работают по тому же принципу, что и раньше. Сначала проверяется, не нажата ли мышь в поле инвентаря, а затем в поле выбранного оружия. Если мышь пуста, то ей присваивается номер клетки, над которой она находится. Если не пуста, то проверяется, поместиться ли предмет туда, куда вы его кладете. Если да, то кладете его, если нет, то ничего не происходит. Если под вами предмет, то возможно два варианта. Если предмет взятый занимает меньше либо столько же места, сколько тот, что под ним, то они меняются. Иначе ничего не происходит. Все это проверяется с помощью итерационных циклов и условий. Надеюсь, что вы разберетесь. Если же нет, то пишите мне на мыло, постараюсь ответить. Напоследок, привожу полный код:
;(c) Agnislav Lipcan
; inventar
;avelnet@yandex.ru
Graphics3D 800,700,32,2
SetBuffer BackBuffer()
AppTitle "inventar"
font=LoadFont(courier ,20)
SetFont font
camera=CreateCamera()
Global ImH=30 ;<= высота одной клетки
Global ImW=30 ;<= ширина одной клетки
Global nachx=70 ;<= начальное положение поля по x минус ImW
Global nachy=70 ;<= начальное положение поля по y минус ImH
Global vertic=6 ;<= клеток вниз
Global goriz=8 ;<= клеток вправо
Global kolvovesh=2 ;общее количество вещей
Global mouse ;мыша
Global orugnom=0 ;первое выбранное оружие
;создаем массив клеток инвентаря
Dim pole(goriz+1,vertic+1)
;картинка пустоты
Global noneim=LoadImage("Inventory1-SmallItem0.png")
Type predmet
Field kolvokletok,posx,posy
Field gde ;0-не активно; 1-в инвентаре; 2-выбрано
Field image ;картинка
;Field cena, sila, lovkost ;параметры
;Field name$ ;название
;field model ;3d модель
End Type
Global tempimage
Global temp1=0
Global temp2=0
;создаем массив предметов
Dim pr.predmet(kolvovesh+1)
;temp
Global num=1
newvesh(1,1,6,"Inventory1-Item1.png")
newvesh(4,2,8,"Inventory1-Item2.png")
;************************* main cikles ****************************
Repeat
UpdateWorld()
RenderWorld()
refresh()
draw()
Flip
Until KeyHit(1)
End
;************************* end ***************************
;************************* functions ***************************
Function draw()
For i=1 To vertic
For j=1 To goriz
DrawImage noneim,nachx+j*ImW,nachy+i*ImH
;Text nachx+j*ImW+5,nachy+i*ImH+5,pole(j,i)
Next
Next
For i=0 To 3
For j=0 To 1
DrawImage noneim,507+j*ImW,368+i*ImH
Next
Next
For i=1 To kolvovesh
If pr(i)\gde=1 Then
DrawImage pr(i)\image,nachx+pr(i)\posx*ImW,nachx+pr(i)\posy*ImH
EndIf
If pr(i)\gde=2 Then
DrawImage pr(i)\image,505,367
EndIf
Next
If mouse=1 Then DrawImage tempimage,MouseX(),MouseY()
End Function
Function refresh()
For i=0 To vertic
For j=0 To goriz
pole(j,i)=0
Next
Next
For i=1 To kolvovesh
If pr(i)\gde=1 Then
For k=0 To pr(i)\kolvokletok*0.5-1
For m=0 To 1
pole(pr(i)\posx+m,pr(i)\posy+k)=i
Next
Next
EndIf
Next
If MouseX() > nachx+ImW And MouseX() < nachx+ImW*(goriz) And MouseY() > nachy+ImH And MouseY() < nachy+ImH*(vertic) Then
If MouseHit(1) Then
If mouse=0 Then
If pole((MouseX()-nachx)/ImW,(MouseY()-nachy)/ImH) <> 0 Then
mouse=1
i=pole((MouseX()-nachx)/ImW,(MouseY()-nachy)/ImH)
pr(i)\gde=0
temp1=i
tempimage=pr(i)\image
EndIf
Else
temp2=0
For k=0 To pr(temp1)\kolvokletok*0.5-1
For m=0 To 1
If pole((MouseX()-nachx)/ImW+m,(MouseY()-nachy)/ImH+k) <> 0 Then temp2=pole((MouseX()-nachx)/ImW+m,(MouseY()-nachy)/ImH+k)
Next
Next
If temp2=0 Then
pr(temp1)\posx=(MouseX()-nachx)/ImW
pr(temp1)\posy=(MouseY()-nachy)/ImH
pr(temp1)\gde=1
temp1=0
mouse=0
Else
If pr(temp2)\kolvokletok >= pr(temp1)\kolvokletok Then
pr(temp1)\posx=pr(temp2)\posx
pr(temp1)\posy=pr(temp2)\posy
pr(temp1)\gde=1
pr(temp2)\gde=0
tempimage=pr(temp2)\image
temp1=temp2
EndIf
EndIf
EndIf
EndIf
EndIf
If MouseX() > 505 And MouseX() < 565 And MouseY() > 367 And MouseY() < 427 Then
If MouseHit(1) Then
If mouse=1 Then
If orugnom <> 0 Then
pr(orugnom)\gde=0
pr(temp1)\gde=2
temp=temp1
temp1=orugnom
tempimage=pr(orugnom)\image
orugnom=temp
Else
pr(temp1)\gde=2
orugnom=temp1
temp1=0
mouse=0
EndIf
Else
If orugnom <> 0 Then
mouse=1
pr(orugnom)\gde=0
temp1=orugnom
tempimage=pr(orugnom)\image
orugnom=0
EndIf
EndIf
EndIf
EndIf
End Function
Function newvesh(x,y,kl,im$)
pr(num) = New predmet
pr(num)\posx=x
pr(num)\posy=y
pr(num)\kolvokletok=kl
pr(num)\image=LoadImage(im$)
pr(num)\gde=1
num=num+1
End Function
Автор: Агнислав Липкан (E-mail: avelnet@yandex.ru)
|