Menu
CoddyTech

사막의 관문

Coddy의 Roblox 게임: 펫 시뮬레이터 코스 레슨. 18개 중 15번째.

뒤쪽의 빨간 벽은 사막으로 가는 관문이며, "잠금 해제" 프롬프트가 있습니다. Settings.GatePrice만큼 지불하면 관문이 열려야 하지만, 지불한 플레이어에게만 열려야 합니다.

이를 구현하려면 양쪽에서 모두 처리해야 합니다. 서버는 코인을 가져가고 플레이어의 속성인 Desert에 잠금 해제 상태를 기록합니다. 그러면 모든 규칙에서 이를 확인할 수 있습니다. 클라이언트는 해당 속성을 감시하고 자기 화면에서 관문을 엽니다. Roblox에서는 각 플레이어의 기기가 자기 캐릭터를 움직이므로, LocalScript가 벽을 비고체 상태로 만들면 그 플레이어는 통과할 수 있지만 다른 모든 플레이어에게는 계속 단단한 벽으로 남습니다.

-- 클라이언트
workspace.Gate.CanCollide = false
workspace.Gate.Transparency = 0.7

같은 방식으로 관문의 프롬프트를 숨기면 두 번째 잠금 해제를 제안하지 않게 됩니다.

challenge icon

챌린지

어려움
  1. PetServer에서 workspace.Gate.ProximityPrompt.Triggered를 연결하세요. 플레이어에게 이미 Desert 속성이 있거나 코인이 Settings.GatePrice보다 적으면 중단하고, 그렇지 않으면 가격만큼 코인을 차감하고 플레이어의 Desert 속성을 true로 설정하세요.
  2. PetClient에서 openGate()를 작성하세요. 플레이어의 Desert 속성이 설정되어 있으면 게이트의 CanCollide를 false로, Transparency를 0.7로, 프롬프트의 Enabled를 false로 설정하세요. 지금 호출하고 속성이 변경될 때도 호출하세요.

플레이를 눌러 100코인을 모으고 Desert를 잠금 해제하세요. 그런 다음 확인을 누르세요.

직접 해보기

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local piles = workspace.Piles
local petParts = workspace.PetParts
local pileSpot = {} -- 각 코인 더미 -> 그 더미가 놓인 자리
local lastHit = {} -- 각 플레이어 -> 마지막으로 타격한 시각

print("A Meadow pile gives " .. Settings.PileValue .. " coins")

-- 이름을 기준으로 정리한 Settings.Pets
local petInfo = {}
for _, info in Settings.Pets do
	petInfo[info.Name] = info
end

-- 펫: 플레이어의 Pets 폴더에 있는 StringValue로, 이름을 값으로 저장하고
-- 희귀도와 위력을 속성으로 저장함
local function givePet(player, name, equipped)
	local info = petInfo[name]
	local pet = Instance.new("StringValue")
	pet.Name = "Pet"
	pet.Value = name
	pet:SetAttribute("Rarity", info.Rarity)
	pet:SetAttribute("Power", info.Power)
	pet:SetAttribute("Equipped", equipped)
	pet.Parent = player.Pets
	return pet
end

-- 플레이어가 꺼내 놓은 펫의 수
local function equippedCount(player)
	local count = 0
	for _, pet in player.Pets:GetChildren() do
		if pet:GetAttribute("Equipped") then
			count += 1
		end
	end
	return count
end

-- 플레이어의 펫이 각 타격에 추가하는 위력
local function petPower(player)
	local power = 0
	for _, pet in player.Pets:GetChildren() do
		if pet:GetAttribute("Equipped") then
			power += pet:GetAttribute("Power")
		end
	end
	return power
end

-- 모든 플레이어의 리더보드에 Coins가 추가됨
Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats

	-- 플레이어가 소유한 펫
	local pets = Instance.new("Folder")
	pets.Name = "Pets"
	pets.Parent = player
end)

-- 자리에 놓인 코인 더미: ServerStorage.CoinPile의 복사본으로, 체력과
-- 가치를 속성으로 가짐
local function spawnPile(spot)
	local pile = ServerStorage.CoinPile:Clone()
	pile.Position = spot.Position
	pile:SetAttribute("MaxHealth", Settings.PileHealth)
	pile:SetAttribute("Health", Settings.PileHealth)
	pile:SetAttribute("Value", Settings.PileValue)
	pile.HealthGui.Label.Text = pile:GetAttribute("Health") .. "/" .. pile:GetAttribute("MaxHealth")
	pileSpot[pile] = spot
	pile.Parent = piles
	return pile
end

-- 한 번 타격할 때마다 코인 더미의 체력이 1에 플레이어 펫의 위력을 더한 만큼 줄어듦;
-- 0이 되면 더미가 부서지고 그 가치만큼 코인을 지급한다
local function hitPile(player, pile)
	local health = pile:GetAttribute("Health") - (1 + petPower(player))
	if health > 0 then
		pile:SetAttribute("Health", health)
		pile.HealthGui.Label.Text = health .. "/" .. pile:GetAttribute("MaxHealth")
		return
	end
	player.leaderstats.Coins.Value += pile:GetAttribute("Value")
	local spot = pileSpot[pile]
	pileSpot[pile] = nil
	pile:Destroy()
	-- 몇 초 후 같은 자리에 새 더미가 생성된다
	task.delay(Settings.RespawnTime, spawnPile, spot)
end

-- Meadow의 모든 위치에 더미 하나씩
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
	spawnPile(spot)
end

-- 클라이언트가 더미를 공격하겠다고 요청한다. 클라이언트를 절대 신뢰하지 말 것: 누구나
-- 어디서든 원하는 만큼 빠르게 어떤 값으로든 이 remote를 호출할 수 있다.
remotes.Hit.OnServerEvent:Connect(function(player, pile)
	-- 실제 더미인지 확인
	if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
		return
	end
	-- 도달할 수 있는 거리인지 확인
	local character = player.Character
	local root = character and character:FindFirstChild("HumanoidRootPart")
	if not root or (root.Position - pile.Position).Magnitude > Settings.Reach then
		return
	end
	-- 너무 빠르게 공격하지 않는지 확인
	local now = time()
	if lastHit[player] and now - lastHit[player] < Settings.HitCooldown then
		return
	end
	lastHit[player] = now
	hitPile(player, pile)
end)

-- 가중치 추첨: 각 pet의 Chance가 전체 100 중 차지하는 비율만큼 당첨 확률을 갖는다
local function rollPet()
	local roll = math.random() * 100
	for _, info in Settings.Pets do
		roll -= info.Chance
		if roll <= 0 then
			return info.Name
		end
	end
	return Settings.Pets[1].Name
end

-- 부화: 알의 프롬프트로 진행하며 코인으로 비용을 지불한다
workspace.Egg.ProximityPrompt.Triggered:Connect(function(player)
	local coins = player.leaderstats.Coins
	if coins.Value < Settings.EggPrice then
		return
	end
	coins.Value -= Settings.EggPrice
	local name = rollPet()
	-- 자리가 있으면 즉시 꺼낸다
	local pet = givePet(player, name, equippedCount(player) < Settings.MaxEquipped)
	print(player.Name .. " hatched a " .. name)
end)

-- 꺼낸 Pets는 주인을 따라다닌다: 각각 월드에 파트를 하나씩 두고,
-- 매 프레임 플레이어 뒤에 줄지어 배치됩니다
local petModels = {} -- 각 펫 -> 월드에서 해당 펫을 나타내는 파트
RunService.Heartbeat:Connect(function()
	for _, player in Players:GetPlayers() do
		local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		local pets = player:FindFirstChild("Pets")
		local slot = 0
		for _, pet in pets and pets:GetChildren() or {} do
			local model = petModels[pet]
			if pet:GetAttribute("Equipped") and root then
				slot += 1
				if not model then
					model = ServerStorage.PetModels[pet.Value]:Clone()
					model.Parent = petParts
					petModels[pet] = model
				end
				model.CFrame = root.CFrame * CFrame.new((slot - 2) * 3, -1.5, 4)
			elseif model then
				model:Destroy()
				petModels[pet] = nil
			end
		end
	end
end)

-- 클라이언트가 펫을 꺼내거나 넣어 달라고 요청합니다. 서버는 해당 펫이
-- 플레이어의 소유인지, 꺼내 놓은 펫이 MaxEquipped를 초과하지 않는지 확인합니다.
remotes.Equip.OnServerEvent:Connect(function(player, pet)
	if typeof(pet) ~= "Instance" or pet.Parent ~= player.Pets then
		return
	end
	if pet:GetAttribute("Equipped") then
		pet:SetAttribute("Equipped", false)
	elseif equippedCount(player) < Settings.MaxEquipped then
		pet:SetAttribute("Equipped", true)
	end
end)
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Roblox 게임: 펫 시뮬레이터의 모든 레슨