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

Несколько реализаций инвентаря. Работа с типами и массивами.

Материал из 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

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

Изображение:Inventory1-SmallItem0.png Изображение:Inventory1-SmallItem1.png Изображение:Inventory1-SmallItem2.png

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")

Вам понадобятся изображения предметов. Не забывайте о соответствии ширины и высоты.

Изображение:Inventory1-Item1.png Изображение: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)

Другие

Друзья