🎯 Mål for dette systemet
- Lage en butikk med en katalog av varer.
- Vise varer i et UI med bilder, navn og priser.
- La spillere kjøpe varer med myntene sine.
- Gi varer til spillere (Tools, Accessories, Models).
- Forhindre dobbeltkjøp av varer som kun kan kjøpes én gang.
📦 Komplett oppsett - Oversikt
Dette er strukturen vi skal bygge i dette systemet:
🧱 Del 1: Opprett Shop Items
Først må vi lage varer som kan kjøpes i butikken.
1 Opprett ShopItems Folder
-
Åpne ReplicatedStorage i Explorer.
Høyreklikk ReplicatedStorage → Insert Object → Folder
Navn:
ShopItems
2 Opprett en Tool (Sverd)
-
Legg til en Tool i ShopItems-folderen.
Høyreklikk ShopItems → Insert Object → Tool
Navn:
Sword(dette blir ID for varen) -
Lag en Handle (Part) inne i Sword.
VIKTIG: Den MÅ hete
Handle(med stor H)Anchored:
falseCanCollide:
falseSize:
0.5, 4, 0.5(lang og tynn som et sverd) -
Legg til Attributes til Sword:
Price:
100(heltall)DisplayName:
Sverd(tekst)IconId:
123456(valgfritt, bilde-ID)OneTime:
false (bool, kan kjøpes flere ganger)
3 Opprett en Accessory (Hatt)
-
Legg til en Accessory i ShopItems-folderen.
Høyreklikk ShopItems → Insert Object → Accessory
Navn:
Hat -
Legg til Attributes til Hat:
Price:
50DisplayName:
HattIconId:
789012(valgfritt)OneTime:
true(kan kun kjøpes én gang)
4 Opprett en Model (Stol)
-
Legg til en Model i ShopItems-folderen.
Høyreklikk ShopItems → Insert Object → Model
Navn:
Chair -
Legg til en Part inne i Chair.
Navn:
SeatAnchored:
trueSize:
2, 0.5, 2 -
Sett PrimaryPart til Seat:
Høyreklikk Chair → Set Primary Part → Seat
-
Legg til Attributes til Chair:
Price:
25DisplayName:
StolIconId:
345678(valgfritt)OneTime:
false
🧱 Del 2: Shop Server
Nå skal vi lage server-scriptet som håndterer kjøp og levering av varer.
1 Opprett ShopRemotes Folder
-
Åpne ReplicatedStorage i Explorer.
Høyreklikk ReplicatedStorage → Insert Object → Folder
Navn:
ShopRemotes -
Legg til en RemoteFunction i ShopRemotes.
Høyreklikk ShopRemotes → Insert Object → RemoteFunction
Navn:
GetCatalog -
Legg til en annen RemoteFunction i ShopRemotes.
Høyreklikk ShopRemotes → Insert Object → RemoteFunction
Navn:
Purchase
2 Opprett ShopServer Script
-
Åpne ServerScriptService i Explorer.
Høyreklikk ServerScriptService → Insert Object → Script
Navn:
ShopServer -
Lim inn følgende kode:
-- ServerScriptService/ShopServer.lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Players = game:GetService("Players") -- === FOLDERS & REMOTES === local shopFolder = ReplicatedStorage:FindFirstChild("ShopItems") if not shopFolder then shopFolder = Instance.new("Folder") shopFolder.Name = "ShopItems" shopFolder.Parent = ReplicatedStorage end local remotes = ReplicatedStorage:FindFirstChild("ShopRemotes") if not remotes then remotes = Instance.new("Folder") remotes.Name = "ShopRemotes" remotes.Parent = ReplicatedStorage end local getCatalog = remotes:FindFirstChild("GetCatalog") :: RemoteFunction if not getCatalog then getCatalog = Instance.new("RemoteFunction") getCatalog.Name = "GetCatalog" getCatalog.Parent = remotes end local purchaseFn = remotes:FindFirstChild("Purchase") :: RemoteFunction if not purchaseFn then purchaseFn = Instance.new("RemoteFunction") purchaseFn.Name = "Purchase" purchaseFn.Parent = remotes end -- === UTILS === local function ensureOwnedFolder(player: Player) local owned = player:FindFirstChild("OwnedItems") if not owned then owned = Instance.new("Folder") owned.Name = "OwnedItems" owned.Parent = player end return owned end local function getWalletCoins(player: Player): IntValue? local wallet = player:FindFirstChild("wallet") if not wallet then return nil end local coins = wallet:FindFirstChild("coins") if not coins then return nil end return coins :: IntValue end local function guessItemType(inst: Instance): string if inst:IsA("Tool") then return "Tool" elseif inst:IsA("Accessory") then return "Accessory" elseif inst:IsA("Model") then return "Model" else return "Other" end end local function getIconFromAttributes(item: Instance): string? local iconId = item:GetAttribute("IconId") if typeof(iconId) == "number" and iconId > 0 then return "rbxassetid://" .. iconId end return nil end local function buildCatalog() local list = {} for _, item in ipairs(shopFolder:GetChildren()) do -- Krev unik Name per vare local price = tonumber(item:GetAttribute("Price")) or 0 if price > 0 then table.insert(list, { id = item.Name, -- må være unik displayName = item:GetAttribute("DisplayName") or item.Name, price = price, icon = getIconFromAttributes(item), type = guessItemType(item), oneTime = item:GetAttribute("OneTime") == true, }) end end table.sort(list, function(a,b) return a.price < b.price end) return list end local function alreadyOwned(player: Player, itemId: string): boolean local owned = ensureOwnedFolder(player) return owned:FindFirstChild(itemId) ~= nil end local function markOwned(player: Player, itemId: string) local owned = ensureOwnedFolder(player) if not owned:FindFirstChild(itemId) then local flag = Instance.new("BoolValue") flag.Name = itemId flag.Value = true flag.Parent = owned end end local function giveItem(player: Player, template: Instance, itemType: string) local character = player.Character if itemType == "Tool" then local clone = template:Clone() clone.Parent = player:WaitForChild("Backpack") return true, "Levert til ryggsekken." elseif itemType == "Accessory" then local clone = template:Clone() local char = character or player.CharacterAdded:Wait() local hum = char:WaitForChild("Humanoid") -- Accessory må være Accessory if clone:IsA("Accessory") then hum:AddAccessory(clone) return true, "Tilbehør utstyrt." else clone:Destroy() return false, "Varen er ikke en Accessory." end elseif itemType == "Model" then local clone = template:Clone() local char = character or player.CharacterAdded:Wait() local root = char:WaitForChild("HumanoidRootPart") local putCF = root.CFrame * CFrame.new(0, 0, -4) if clone.PrimaryPart == nil then -- Prøv å sette PrimaryPart automatisk for _, p in ipairs(clone:GetDescendants()) do if p:IsA("BasePart") then clone.PrimaryPart = p break end end end if clone.PrimaryPart then clone:PivotTo(putCF) end clone.Parent = workspace return true, "Plassert foran spilleren." else return false, "Ukjent varetype." end end -- === REMOTES === getCatalog.OnServerInvoke = function(player: Player) -- Kan legge på tilgangskontroll her om ønskelig return buildCatalog() end purchaseFn.OnServerInvoke = function(player: Player, itemId: string) if typeof(itemId) ~= "string" then return { ok = false, message = "Ugyldig forespørsel." } end local template = shopFolder:FindFirstChild(itemId) if not template then return { ok = false, message = "Varen finnes ikke." } end local price = tonumber(template:GetAttribute("Price")) or 0 if price <= 0 then return { ok = false, message = "Varen mangler pris." } end local oneTime = template:GetAttribute("OneTime") == true if oneTime and alreadyOwned(player, itemId) then return { ok = false, message = "Du eier allerede denne varen." } end local coins = getWalletCoins(player) if not coins then return { ok = false, message = "Fant ikke mynter hos spilleren." } end if coins.Value < price then return { ok = false, message = "For lite Coins." } end -- Trekk og gi vare (transaksjon) coins.Value -= price local itemType = guessItemType(template) local ok, msg = giveItem(player, template, itemType) if not ok then -- Rull tilbake coins om utdeling feiler coins.Value += price return { ok = false, message = "Kunne ikke levere: " .. (msg or "Ukjent feil") } end if oneTime then markOwned(player, itemId) end return { ok = true, message = ("Kjøpt %s for %d Coins. %s"):format(template.Name, price, msg or "") } end -- Opprett OwnedItems for alle Players.PlayerAdded:Connect(function(plr) ensureOwnedFolder(plr) end)
🧱 Del 3: Shop UI
Nå skal vi lage et brukergrensesnitt som viser butikken og lar spillere kjøpe varer.
1 Opprett ShopUI
-
Åpne StarterGui i Explorer.
Høyreklikk StarterGui → Insert Object → ScreenGui
Navn:
ShopUI -
Legg til en LocalScript i ShopUI.
Høyreklikk ShopUI → Insert Object → LocalScript
-
Lim inn følgende kode i LocalScript:
-- StarterGui/ShopUI.client.lua (LocalScript) local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local player = Players.LocalPlayer local remotes = ReplicatedStorage:WaitForChild("ShopRemotes") local getCatalog = remotes:WaitForChild("GetCatalog") :: RemoteFunction local purchase = remotes:WaitForChild("Purchase") :: RemoteFunction -- ===== UI BY CODE ===== local screen = Instance.new("ScreenGui") screen.Name = "AutoShopGui" screen.ResetOnSpawn = false screen.Parent = player:WaitForChild("PlayerGui") local openBtn = Instance.new("TextButton") openBtn.Name = "OpenShop" openBtn.Size = UDim2.fromOffset(140, 44) openBtn.Position = UDim2.new(0, 20, 1, -64) openBtn.AnchorPoint = Vector2.new(0,1) openBtn.TextScaled = true openBtn.Text = "🛒 Shop" openBtn.BackgroundColor3 = Color3.fromRGB(50, 50, 50) openBtn.TextColor3 = Color3.fromRGB(255,255,255) openBtn.Parent = screen local frame = Instance.new("Frame") frame.Name = "ShopFrame" frame.Size = UDim2.fromScale(0.38, 0.6) frame.Position = UDim2.new(0.5, 0, 0.5, 0) frame.AnchorPoint = Vector2.new(0.5,0.5) frame.BackgroundColor3 = Color3.fromRGB(22,22,22) frame.Visible = false frame.Parent = screen local uiCorner = Instance.new("UICorner", frame) uiCorner.CornerRadius = UDim.new(0,12) local title = Instance.new("TextLabel") title.Size = UDim2.new(1, -20, 0, 40) title.Position = UDim2.new(0, 10, 0, 8) title.BackgroundTransparency = 1 title.Text = "Shop" title.TextXAlignment = Enum.TextXAlignment.Left title.TextScaled = true title.TextColor3 = Color3.new(1,1,1) title.Parent = frame local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.fromOffset(36, 36) closeBtn.Position = UDim2.new(1, -46, 0, 8) closeBtn.BackgroundColor3 = Color3.fromRGB(60,60,60) closeBtn.Text = "X" closeBtn.TextScaled = true closeBtn.TextColor3 = Color3.new(1,1,1) closeBtn.Parent = frame Instance.new("UICorner", closeBtn).CornerRadius = UDim.new(0,8) local listHolder = Instance.new("Frame") listHolder.Name = "ListHolder" listHolder.Size = UDim2.new(1, -20, 1, -64) listHolder.Position = UDim2.new(0, 10, 0, 54) listHolder.BackgroundTransparency = 1 listHolder.Parent = frame local scroller = Instance.new("ScrollingFrame") scroller.Size = UDim2.fromScale(1,1) scroller.CanvasSize = UDim2.new(0,0,0,0) scroller.ScrollBarThickness = 8 scroller.BackgroundTransparency = 1 scroller.Parent = listHolder local layout = Instance.new("UIGridLayout") layout.CellPadding = UDim2.fromOffset(10,10) layout.CellSize = UDim2.new(0.5, -10, 0, 100) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = scroller local function createItemCard(item) local card = Instance.new("Frame") card.BackgroundColor3 = Color3.fromRGB(35,35,35) card.Parent = scroller Instance.new("UICorner", card).CornerRadius = UDim.new(0,10) local nameLbl = Instance.new("TextLabel") nameLbl.Size = UDim2.new(1, -110, 0, 36) nameLbl.Position = UDim2.new(0, 10, 0, 8) nameLbl.BackgroundTransparency = 1 nameLbl.TextXAlignment = Enum.TextXAlignment.Left nameLbl.TextScaled = true nameLbl.TextColor3 = Color3.new(1,1,1) nameLbl.Text = item.displayName or item.id nameLbl.Parent = card local priceLbl = Instance.new("TextLabel") priceLbl.Size = UDim2.new(1, -110, 0, 24) priceLbl.Position = UDim2.new(0, 10, 0, 50) priceLbl.BackgroundTransparency = 1 priceLbl.TextXAlignment = Enum.TextXAlignment.Left priceLbl.TextScaled = true priceLbl.TextColor3 = Color3.fromRGB(200,200,200) priceLbl.Text = ("Pris: %d"):format(item.price) priceLbl.Parent = card local icon = Instance.new("ImageLabel") icon.Size = UDim2.fromOffset(90, 90) icon.Position = UDim2.new(1, -100, 0, 5) icon.BackgroundTransparency = 1 icon.Parent = card if item.icon then icon.Image = item.icon end local buyBtn = Instance.new("TextButton") buyBtn.Size = UDim2.fromOffset(90, 32) buyBtn.Position = UDim2.new(1, -100, 1, -38) buyBtn.BackgroundColor3 = Color3.fromRGB(80,160,80) buyBtn.Text = "Kjøp" buyBtn.TextScaled = true buyBtn.TextColor3 = Color3.new(1,1,1) buyBtn.Parent = card Instance.new("UICorner", buyBtn).CornerRadius = UDim.new(0,6) buyBtn.MouseButton1Click:Connect(function() buyBtn.Active = false buyBtn.AutoButtonColor = false buyBtn.Text = "..." local result = nil local ok, err = pcall(function() result = purchase:InvokeServer(item.id) end) if not ok then result = { ok = false, message = "Feil: " .. tostring(err) } end buyBtn.Text = "Kjøp" buyBtn.Active = true buyBtn.AutoButtonColor = true -- Liten tilbakemelding local msg = Instance.new("TextLabel") msg.Size = UDim2.new(1, -20, 0, 28) msg.Position = UDim2.new(0, 10, 1, -32) msg.BackgroundColor3 = result.ok and Color3.fromRGB(60,110,60) or Color3.fromRGB(110,60,60) msg.TextColor3 = Color3.new(1,1,1) msg.TextScaled = true msg.Text = result.message or (result.ok and "Kjøp fullført!" or "Kjøp feilet") msg.Parent = frame Instance.new("UICorner", msg).CornerRadius = UDim.new(0,6) task.delay(2, function() msg:Destroy() end) end) end local function populate() for _, c in ipairs(scroller:GetChildren()) do if c:IsA("Frame") then c:Destroy() end end local catalog = {} local ok, err = pcall(function() catalog = getCatalog:InvokeServer() end) if not ok then warn("Kunne ikke hente katalog:", err) return end for _, item in ipairs(catalog) do createItemCard(item) end -- Oppdater scroll-høyde task.defer(function() local contentSize = layout.AbsoluteContentSize scroller.CanvasSize = UDim2.new(0, 0, 0, contentSize.Y + 20) end) end openBtn.MouseButton1Click:Connect(function() frame.Visible = not frame.Visible if frame.Visible then populate() end end) closeBtn.MouseButton1Click:Connect(function() frame.Visible = false end)
🧪 Test systemet
- Sørg for at du har Wallet og Coin System installert.
- Start spillet ved å trykke Play i Roblox Studio.
- Du skal se en "🛒 Shop"-knapp nede til venstre.
- Klikk på knappen for å åpne butikken.
- Du skal se alle varer du har laget med bilder, navn og priser.
- Prøv å kjøpe en vare (du trenger nok mynter!).
- Varen skal leveres til deg (Tool i ryggsekk, Accessory på hodet, eller Model foran deg).
- Prøv å kjøpe en vare med
OneTime=trueto ganger – det skal ikke være mulig.
📚 Konsepter du har lært
- RemoteFunction: To-veis kommunikasjon mellom klient og server.
- Attributes: Egendefinerte data knyttet til objekter (Price, DisplayName, etc.).
- Clone(): Lager en kopi av et objekt.
- Humanoid.AddAccessory: Legger til tilbehør på en karakter.
- UIGridLayout: Arrangerer UI-elementer i et rutenett.
- ScrollingFrame: En container som kan scrolles.
- pcall: Beskytter mot feil ved å kjøre kode trygt.
- task.defer: Utfører kode i neste frame.
🚀 Neste steg
Nå som du har et shop system, kan du:
- Legge til flere varer i butikken med forskjellige typer.
- Lage spesielle tilbud eller rabatter.
- Legge til kategorier for å organisere varer.
- Lagre kjøpshistorikk i DataStore.
- Lage en "My Items"-side som viser hva spilleren eier.