Izgara Tabanlı Envanter Sistemi

Ön Gereksinimler

  • Lua bilgisi.
  • Bir eşya sistemi. Her eşyanın iki fonksiyonu olmalıdır:
    • ITEM:GetIcon() -- Bir Material() nesnesi döndürmelidir.
    • ITEM:GetSize() -- Hücre cinsinden bir genişlik ve yükseklik döndürmelidir.

Buna benzer bir sistem, Nesne Yönelimli Lua eğitiminde bulunabilir.

Unutmayın ki bu eğitim sadece envanterin istemci tarafı (clientside) yönetimini ele almaktadır. Sunucu tarafı (serverside) ağ iletişimi için fazla tembelim...

Hadi Başlayalım

Yani her eşyanın sırt çantasında bir konumu ve o sırt çantasında bir boyutu vardır. Başlamak için bir tabloya (table) ihtiyacınız olacak. Buna ply.Inv diyeceğiz.

local ply = LocalPlayer() ply.Inv = {}

ply.Inv hem sırt çantasını hem de envanter hakkındaki bilgileri içerecektir.

ply.Inv içinde birkaç alt tablo ve değer oluşturalım.

ply.Inv.Backpack = {} --Asıl sırt çantası. ply.Inv.Equipped = {} --Bunlar, Diablo tarzı olacağı varsayılan, kuşanılmış eşyalardır. ply.Inv.Weight = 0

(Sisteminiz için ihtiyaç duyacağınız her türlü değeri oluşturabilirsiniz. Ağırlık gerekli olmayabilir, vb.)

Sırada, sırt çantası tablosunu 2 boyutlu bir tablo haline getireceğiz; satırlar ilk anahtar olacak ve her satır sütunları içerecek. Bu şuna benzer bir şey olacaktır:

for i=1,8 do --8, sırt çantasının genişliğidir. ply.Inv.Backpack[i] = {} end for k,v in pairs(ply.Inv.Backpack)do for i=1,4 do --4, sırt çantasının yüksekliğidir. ply.Inv.Backpack[k][i] = false --False burada bir yer tutucudur. Bunu daha sonra üzerine yazacağız. end end

Bu, 8 genişliğinde, 4 yüksekliğinde 2 boyutlu bir tablo oluşturacaktır. Yani 2,4 koordinatındaki eşyayı almak için ply.Inv.Backpack[2][4] yapmanız gerekir. Oldukça basit.

Ancak bir kolaylık fonksiyonuyla her zaman daha basittir:

local plymeta = FindMetaTable("Player") function plymeta:GetInvItem(x,y) return self.Inv.Backpack[x][y] end

Tamam, şimdi sistemin temelini açıklayalım. Fikir şu: Yeni bir VGUI elemanı oluşturacağız. Bu eleman, ızgaradaki (grid) tek bir kare olacak. Bu sayede parlamalarını falan sağlayabiliriz. Bu elemanı tasarlamayı bitirdiğimizde, daha önce ayarladığımız false değerlerini değiştirebiliriz. Bir eşyayı ızgarada bir yere koyduğumuzda, eşyanın boyutunu (genişlik ve yükseklik) alırız. Ardından o alandaki tüm karelere, artık üzerlerinde bir eşya olduğunu söyleriz. Eşyayı tüm karelere köklemeyeceğiz; sadece eşyanın sol üstünde bulunan kareye kökleyeceğiz. Diğer tüm karelere haber veren kareye. Bu, eşyanın ebeveyn (parent) karesi olacak. Öyleyse bu ızgara elemanlarını yapalım.

local PANEL = {} AccessorFunc(PANEL, "m_ItemPanel", "ItemPanel") AccessorFunc(PANEL, "m_Color", "Color") function PANEL:Init() self.m_Coords = {x=0,y=0} self:SetSize(30,30) self:SetColor(Color(200,200,200)) self:SetItemPanel(false) self:Receiver("invitem", function(pnl, item, drop, i, x, y) --Sürükle-bırak işlevselliği if drop then item = item[1] local x1,y1 = pnl:GetPos() local x2,y2 = item:GetPos() if math.Dist(x1,y1,x2,y2) <= 30 then --Sol üst yuvayı bul. if not pnl:GetItemPanel() then local itm = item:GetItem() local x,y = pnl:GetCoords() local itmw, itmh = itm:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. local full = false for i1=x, (x+itmw)-1 do if full then break end for i2=y, (y+itmh)-1 do if LocalPlayer():GetInvItem(i1,i2):GetItemPanel() then --alandaki panellerin dolu olup olmadığını kontrol et. full = true break end end end if not full then --Eğer hiçbiri dolu değilse o zaman for i1=x, (x+itmw)-1 do for i2=y, (y+itmh)-1 do LocalPlayer():GetInvItem(i1,i2):SetItemPanel(item) -- Altındaki tüm karelere artık bu eşya ile dolu olduklarını söyle. end end item:SetRoot(pnl) --ebeveyn gibi, ama tam olarak ebeveyn değil. item:SetPos(pnl:GetPos()) --eşyayı taşı. end end end else --Üzerine gelinen yuvaların renklendirilmesiyle ilgili bir şey. end end, {}) end function PANEL:SetCoords(x,y) self.m_Coords.x = x self.m_Coords.y = y end function PANEL:GetCoords() return self.m_Coords.x, self.m_Coords.y end local col function PANEL:Paint(w,h) draw.NoTexture() col = self:GetColor() surface.SetDrawColor(col.r,col.g,col.b,255) surface.DrawRect(0,0,w-2,h-2) --ana kare surface.SetDrawColor(70,70,70,255) surface.DrawRect(w-2,0,h,2) --kenarlıklar surface.DrawRect(0,h-2,2,w) -- ^ end vgui.Register("InvSlot", PANEL, "DPanel")

Bitti. Şimdi tüm işlevselliği ekliyoruz. Eşya sisteminin, her bir eşyayı tanımlamak için sadece rastgele sınıf ID'leri değil, benzersiz eşyalar içerdiğini varsaymalıyız. Bunu yapmanın en iyi yolu, her eşyayı bir entity index'i ile ilişkilendirmektir. Bu, eğer eşyaların hepsi bir noktada entity ise işe yarar. Bir eşyayı aldığınızda, entity versiyonunu kaldırır (entity index'ini kaydederek) ve bıraktığınızda, aynı türde ve aynı bilgilerle yeni bir entity oluşturur. Bunların hepsi sadece bir konsept. Bu eğitimde bir eşya sistemi yazmıyorum.

Yani her envanter yuvası için bir yuva paneli oluşturarak başlayacağız.

for k,v in pairs(ply.Inv.Backpack)do for i=1,4 do --4, sırt çantasının yüksekliğidir. ply.Inv.Backpack[k][i] = vgui.Create("InvSlot") ply.Inv.Backpack[k][i]:SetPos(k*30,i*30) --İkon 30x30 boyutundadır. ply.Inv.Backpack[k][i]:SetCoords(k,i) end end

İkonlar. Her yerde ikonlar var.

Sırada, başka bir VGUI elemanı yapalım. Bu, sırt çantanızdaki bir eşyayı temsil edecek.

local PANEL = {} AccessorFunc(PANEL, "m_Item", "Item") AccessorFunc(PANEL, "m_Root", "Root") function PANEL:Init() self:SetSize(30,30) self:SetItem(false) --false, eşya olmadığı anlamına gelir. self:SetColor(Color(100,100,100)) self:SetDroppable("invitem") end function PANEL:PaintOver(w,h) draw.NoTexture() if self:GetItem() then surface.SetMaterial(self:GetItem():GetIcon()) --Eşyalarınızın bir GetIcon metodu olmalıdır. surface.DrawTexturedRect(0,0,w,h) end end local col function PANEL:Paint(w,h) draw.NoTexture() col = self:GetColor() surface.SetDrawColor(col.r,col.g,col.b,180) surface.DrawRect(0,0,w,h) --arka plan karesi end vgui.Register("InvItem", PANEL, "DPanel")

Tamam, şimdi asıl eşyalara sahibiz. Unutmayın, sadece bir sırt çantası sisteminin temellerini tartışıyoruz. İsterseniz daha sonra daha fazlasını ekleyebilirsiniz. Sırada, belirli bir eşya için sırt çantasında yer olup olmadığını bulan bir fonksiyon oluşturalım. Eğer yer varsa, köklenecek paneli döndürecek. Yoksa, false döndürecek.

function IsRoomFor(item) --not: item, bir InvItem paneli değil, gerçek bir eşya olmalıdır. for k,v in ipairs(LocalPlayer().Inv.Backpack) do for k2, pnl in ipairs(LocalPlayer().Inv.Backpack[k]) do if not pnl:GetItemPanel() then local x,y = pnl:GetCoords() local itmw, itmh = item:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. local full = false for i1=x, (x+itmw)-1 do if full then break end for i2=y, (y+itmh)-1 do if LocalPlayer():GetInvItem(i1,i2):GetItemPanel() then --alandaki panellerin dolu olup olmadığını kontrol et. full = true break end end end if full then return pnl --Eğer yer varsa, boş paneli döndür. end end end end return false --değilse, false döndür. end

Bu kodun çoğu, daha önceki sürükle-bırak işlevselliğinden kopyalanmıştır. Sırada, eşyaların yerden alınmasını sağlayalım.

function PickupItem(item) local place = IsRoomFor(item) if place then local itm = vgui.Create("InvItem") --yeni bir eşya paneli oluştur. itm:SetItem(item) itm:SetRoot(place) itm:SetPos(place:GetPos()) local x,y = place:GetCoords() local itmw, itmh = item:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. for i1=x, (x+itmw)-1 do for i2=y, (y+itmh)-1 do LocalPlayer():GetInvItem(i1,i2):SetItemPanel(itm) -- Altındaki tüm karelere artık bu eşya ile dolu olduklarını söyle. end end return true --eşya başarıyla yerden alındı. else return false --yer yok. end end

Son Ürün

local ply = LocalPlayer() ply.Inv = {} ply.Inv.Backpack = {} --Asıl sırt çantası. ply.Inv.Equipped = {} --Bunlar, Diablo tarzı olacağı varsayılan, kuşanılmış eşyalardır. ply.Inv.Weight = 0 for i=1,8 do --8, sırt çantasının genişliğidir. ply.Inv.Backpack[i] = {} end for k,v in pairs(ply.Inv.Backpack)do for i=1,4 do --4, sırt çantasının yüksekliğidir. ply.Inv.Backpack[k][i] = false --False burada bir yer tutucudur. Bunu daha sonra üzerine yazacağız. end end local plymeta = FindMetaTable("Player") function plymeta:GetInvItem(x,y) return self.Inv.Backpack[x][y] end local PANEL = {} AccessorFunc(PANEL, "m_ItemPanel", "ItemPanel") AccessorFunc(PANEL, "m_Color", "Color") function PANEL:Init() self.m_Coords = {x=0,y=0} self:SetSize(30,30) self:SetColor(Color(200,200,200)) self:SetItemPanel(false) self:Receiver("invitem", function(pnl, item, drop, i, x, y) --Sürükle-bırak if drop then item = item[1] local x1,y1 = pnl:GetPos() local x2,y2 = item:GetPos() if math.Dist(x1,y1,x2,y2) <= 30 then --Sol üst yuvayı bul. if not pnl:GetItemPanel() then local itm = item:GetItem() local x,y = pnl:GetCoords() local itmw, itmh = itm:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. local full = false for i1=x, (x+itmw)-1 do if full then break end for i2=y, (y+itmh)-1 do if LocalPlayer():GetInvItem(i1,i2):GetItemPanel() then --alandaki panellerin dolu olup olmadığını kontrol et. full = true break end end end if not full then --Eğer hiçbiri dolu değilse o zaman for i1=x, (x+itmw)-1 do for i2=y, (y+itmh)-1 do LocalPlayer():GetInvItem(i1,i2):SetItemPanel(item) -- Altındaki tüm karelere artık bu eşya ile dolu olduklarını söyle. end end item:SetRoot(pnl) --ebeveyn gibi, ama tam olarak ebeveyn değil. item:SetPos(pnl:GetPos()) --eşyayı taşı. end end end else --Üzerine gelinen yuvaların renklendirilmesiyle ilgili bir şey. end end, {}) end function PANEL:SetCoords(x,y) self.m_Coords.x = x self.m_Coords.y = y end function PANEL:GetCoords() return self.m_Coords.x, self.m_Coords.y end local col function PANEL:Paint(w,h) draw.NoTexture() col = self:GetColor() surface.SetDrawColor(col.r,col.g,col.b,255) surface.DrawRect(0,0,w-2,h-2) --ana kare surface.SetDrawColor(70,70,70,255) surface.DrawRect(w-2,0,2,h) --kenarlıklar surface.DrawRect(0,h-2,w,2) -- ^ end vgui.Register("InvSlot", PANEL, "DPanel") local dfram = vgui.Create("DFrame") dfram:SetSize(ScrW()/2, ScrH()/2) dfram:Center() dfram:MakePopup() for k,v in pairs(ply.Inv.Backpack)do for i=1,4 do --4, sırt çantasının yüksekliğidir. ply.Inv.Backpack[k][i] = vgui.Create("InvSlot", dfram) ply.Inv.Backpack[k][i]:SetPos((k*30)+100,(i*30)+100) --İkon 30x30 boyutundadır. ply.Inv.Backpack[k][i]:SetCoords(k,i) end end PANEL = {} AccessorFunc(PANEL, "m_Item", "Item") AccessorFunc(PANEL, "m_Root", "Root") function PANEL:Init() self:SetSize(30,30) self:SetItem(false) --false, eşya olmadığı anlamına gelir. self:SetColor(Color(100,100,100)) self:SetDroppable("invitem") end function PANEL:PaintOver(w,h) draw.NoTexture() if self:GetItem() then surface.SetMaterial(self:GetItem():GetIcon()) --Eşyalarınızın bir :GetIcon fonksiyonu olmalıdır. surface.DrawTexturedRect(0,0,w,h) end end local col function PANEL:Paint(w,h) draw.NoTexture() col = self:GetColor() surface.SetDrawColor(col.r,col.g,col.b,180) surface.DrawRect(0,0,w,h) --arka plan karesi end vgui.Register("InvItem", PANEL, "DPanel") function IsRoomFor(item) --not: item, bir InvItem paneli değil, gerçek bir eşya olmalıdır. for k,v in ipairs(LocalPlayer().Inv.Backpack) do for k2, pnl in ipairs(LocalPlayer().Inv.Backpack[k]) do if not pnl:GetItemPanel() then local x,y = pnl:GetCoords() local itmw, itmh = item:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. local full = false for i1=x, (x+itmw)-1 do if full then break end for i2=y, (y+itmh)-1 do if LocalPlayer():GetInvItem(i1,i2):GetItemPanel() then --alandaki panellerin dolu olup olmadığını kontrol et. full = true break end end end if full then return pnl --Eğer yer varsa, boş paneli döndür. end end end end return false --değilse, false döndür. end function PickupItem(item) local place = IsRoomFor(item) if place then local itm = vgui.Create("InvItem") --yeni bir eşya paneli oluştur. itm:SetItem(item) itm:SetRoot(place) itm:SetPos(place:GetPos()) local x,y = place:GetCoords() local itmw, itmh = item:GetSize() --GetSize, eşya sisteminizde bir fonksiyon olmalıdır. for i1=x, (x+itmw)-1 do for i2=y, (y+itmh)-1 do LocalPlayer():GetInvItem(i1,i2):SetItemPanel(itm) -- Altındaki tüm karelere artık bu eşya ile dolu olduklarını söyle. end end return true --eşya başarıyla yerden alındı. else return false --yer yok. end end

Test Sonuçları

Tüm sunucular · GmodTR Launcher · Rehber · GMod Wiki · GmodTR nedir? · Sunucu ekle · Discord