# Izgara Tabanlı Envanter Sistemi

- **İngilizce aslı:** https://wiki.facepunch.com/gmod/Grid-Based_Inventory_System
- **Çeviri tarihi:** 2025-09-08

### Ö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](https://gmod.tr/wiki/Object_Oriented_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.

```lua
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.

```lua
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:

```lua
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:

```lua
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.

```lua
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.

```lua
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`.

```lua
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.

```lua
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.

```lua
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

```lua
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ı

![](https://gmod.tr/api/uploads/wiki/dba74d530b3ce41590dd.jpg)

- [GMod Wiki ana sayfası](https://gmod.tr/wiki)

---

[Tüm sunucular](https://gmod.tr/) · [GmodTR Launcher](https://gmod.tr/launcher) · [Rehber](https://gmod.tr/rehber) · [GMod Wiki](https://gmod.tr/wiki) · [GmodTR nedir?](https://gmod.tr/hakkinda) · [Sunucu ekle](https://gmod.tr/sunucu-ekle) · [Discord](https://discord.gg/gmodtr)

Kaynak: https://gmod.tr/wiki/Grid-Based_Inventory_System
