ViewportFrame or .Parent issue?

1. What do you want to achieve?

I’d like the model to be displayed in the viewport constantly (i.e., not needing the mouse to hover over the item) and I’d like the correct model to be displayed in the viewportframe 100% of the time.

2. What is the issue?
Here’s a video that shows the functionality and the issues:

The model displayed in the viewportframe does either of the following:

  1. It doesn’t even appear within the viewportframe (happens occasionally)
  2. The wrong model is displayed in the viewportframe (aka, it doesn’t correspond to the model within the ObjectDisplay part)

3. What solutions have you tried so far?

I tried adding a part in the GUI ClientScripts that checks the Num.Value in a SpecialDisplay model to make sure the item corresponds with the ObjectDisplay part within the SpecialDisplay mode; however, this seems to not be successful…


Workspace setup: ShopGUIs
*I created two separate GUIs for each SpecialDisplay model since I wasn’t sure how I’d go about only using one, I’m a new scripter so my knowledge is very limited; hence, I used two.

Screen Shot 2023-04-04 at 2.58.14 PM

ShopGUI ClientScript
*Only going to put one of them (ie. “SpecialShopItem1GUI”) into this post since they’re basically the same

local function playSound()
	script.AddSound:Play()
end

-- Get the ItemDesc object from the parent of the script
local Info = script.Parent.ItemDesc

local objDisplay
for _, specialDisplay in ipairs(game.Workspace:GetDescendants()) do
	if specialDisplay.Name == "SpecialDisplay" then --specialDisplay:IsA("Folder") and
		local numValue = specialDisplay:FindFirstChild("Num")
		if numValue and numValue:IsA("NumberValue") and numValue.Value == 1 then
			objDisplay = specialDisplay:FindFirstChild("ObjectDisplay")
			print(objDisplay)
			break
		end
	end
end

-- Loop through each slot in the grid
for i, slot in ipairs(script.Parent.Slots:GetChildren()) do
	-- Check if the slot is a Frame object
	if slot:IsA("Frame") then
		-- Add a MouseEnter event listener to the slot
		slot.MouseEnter:Connect(function()
			
			-- Clone the first child and set its parent to the ViewportFrame.Model property of the specific slot
			local firstChild = objDisplay:GetChildren()[1]
			print(firstChild) -- check the value of firstChild
		
			-- Get the first child of the ObjectDisplay part and set the ItemName object in the Info variable to its Name property
			Info.ItemName.Text = firstChild.Name
			Info.ItemDescription.Text = firstChild:FindFirstChild("Desc").Value
			script.Parent.Slots.Cost.Text = "Coins: ".. firstChild:FindFirstChild("Cost").Value --added

			-- Clone the first child and set its parent to the ViewportFrame.Model property of the specific slot
			local clonedChild = objDisplay:GetChildren()[1]:Clone()
			clonedChild.Parent = slot.ViewportFrame.Model
			print(clonedChild)
			-- Set the Visible property of the Info object to true
			Info.Visible = true
		end)
		
		
		-- Add a MouseLeave event listener to the slot
		slot.MouseLeave:Connect(function()
			-- Set the Visible property of the Info object to false
			Info.Visible = false
		end)
		local slotframe = script.Parent.Slots

		local function update(specific)

			-- Disable and re-enable the RotateVis script under the specific slot's parent
			local rotateVis = slotframe:FindFirstChild(specific).RotateVis
			rotateVis.Disabled = true
			rotateVis.Disabled = false
		end

		
		local slots = script.Parent.Slots
		--// glowing selection reset
		slots.Primary.ImageButton.Transparency =1
		
		local equipped = true
		
		for i, slot in ipairs(script.Parent.Slots:GetChildren()) do

			-- Check if the slot is a Frame object
			if slot:IsA("Frame") then
				
	
				-- Add a MouseClick event listener to the slot
				slot.ImageButton.MouseButton1Click:Connect(function(player)
					-- Get the first child of the ViewportFrame.Model and set the itemName variable to its Name property
					local itemModel = slot.ViewportFrame.Model:GetChildren()[1]
					local itemName = itemModel.Name
					print(itemModel)
					
					local player = game.Players.LocalPlayer
					print(player) -- This will print the value of player in the console
					print(player.leaderstats)

					-- Check if the slot has an item equipped
					local shopitem = objDisplay:GetChildren()[1]
					local itemcost = shopitem:FindFirstChild("Cost").Value
					print(shopitem)

					if equipped == true and itemcost > player.leaderstats:FindFirstChild("Coins").Value then

						player.PlayerGui.GameInventory.DeleteOneMsg.Visible = true
						player.PlayerGui.GameInventory.DeleteOneMsg.Msg.Text = "You don't have enough coins."
						--player.PlayerGui.GameInventory.Frame.Visible = true
						wait(2)
						player.PlayerGui.GameInventory.DeleteOneMsg.Visible = false

					else

						if equipped == true and itemcost <= player.leaderstats:FindFirstChild("Coins").Value then

							local event = game.ReplicatedStorage.InvEvents.PickUp
							local purchaseitem = game.ReplicatedStorage.ShopEvents.Purchase	
							local item = itemName
							local firstChild = itemModel
							local itemdescription = firstChild:FindFirstChild("Desc").Value
							event:FireServer(item, itemdescription)
							purchaseitem:FireServer(itemcost)	

							-- Set the Equipped value of the item to false and hide the item in the slot
							equipped = false
							slot.Visible = false


							-- Clear the model of the slot to remove the item from the slot
							slot.ViewportFrame.Model:ClearAllChildren()

							-- Update the value in the settings for the slot
							if slot == slotframe.Primary then
								script.Parent.Settings.Primary.Value = ''
							end
						end
						-- Play a sound
						playSound()

						script.Parent.Parent.Parent:Destroy()
					end
				end)
			end
		end
	end
end

Workspace setup: GUI/ViewportFrame
*Again, the other GUI has the same setup
*The model displayed in the viewportframe is parented to the “Model”

Screen Shot 2023-04-04 at 3.07.52 PM

Screen Shot 2023-04-04 at 3.09.07 PM

Workspace setup: SpecialDisplay Object/Model
*All SpecialDisplay Objects/Models have the same setup, just different Num.Value

*The Num.Value = 1
Screen Shot 2023-04-04 at 3.10.49 PM

SpecialDisplay ProximityPrompt Script

local cooldown = 1800
local canUse = true

script.Parent.Triggered:Connect(function(player)
	if canUse then
	local card_stat = player.Data_Folder.MAX_CARD_STORAGE
	local current_card = player.Data_Folder.CurrentCardStorage
	
	local art_stat = player.Data_Folder.MAX_ART_STORAGE
	local current_art = player.Data_Folder.CurrentArtStorage


	if current_card.Value >= card_stat.Value or current_art.Value >= art_stat.Value then
		-- Show the delete message for a few seconds
			player.PlayerGui.GameInventory.DeleteOneMsg.Visible = true
			player.PlayerGui.GameInventory.DeleteOneMsg.Msg.Text = "You must remove ONE item from your inventory."
		
		wait(2)
		player.PlayerGui.GameInventory.DeleteOneMsg.Visible = false
		
	else
		if current_card.Value < card_stat.Value or current_art.Value < art_stat.Value then
			-- Hide the delete message if it's still visible
			player.PlayerGui.GameInventory.DeleteOneMsg.Visible = false
		

			-- Open the card selection GUI
			local gui = game.ReplicatedStorage.GUIs.SpecialShopItem2GUI:Clone()
			gui.Parent = player.PlayerGui
			
			end
		end

		canUse = false
		script.Parent.Enabled = false
		wait(cooldown)
		canUse = true
		script.Parent.Enabled = true
	end
	end)

ObjectDisplayScript in ServerScriptService
*Basically tells the game. what to put into the ObjectDisplayPart of a SpecialDisplay Model/Object

local replicatedStorage = game:GetService("ReplicatedStorage")
local shopFolder = replicatedStorage:WaitForChild("Shop")
local workspace = game:GetService("Workspace")

-- Define the items and their rarities
--I'll change rarities so that they all add up to 100 once all the items are finished
local Items = {
	Crate = 30, --l
	Emerald = 30, --c
	Diamond = 30, --r
	Sapphire = 30, --uc
	Rock = 30, --c
	Wood = 30, --uc
	Leaf = 30, --r
	Strawberry = 30, --l
	Leg = 30, --c
	Arm = 30, --uc
	Ribs = 30, --r
	Skull = 30, --l
	["Beast Sense I"] = 15,
	["Resistance I"] = 15,
	["Soul Seeker I"] = 15,
	["Loot Finder I"] = 20,
	["Stealth I"] = 20,
	["Beast Sense II"] = 10,
	["Resistance II"] = 10,
	["Soul Seeker II"] = 10,
	["Loot Finder II"] = 10,
	["Stealth II"] = 10,
	["Soul Seeker III"] = 5,
	["Resistance III"] = 5,
	["Loot Finder III"] = 5,
	["Beast Sense III"] = 5,
	["Stealth III"] = 5,
}

local Seed = Random.new(tick())

local function PickItem(RarityList: {[string]: number}): string
	local Num = Seed:NextNumber(0, 100)
	local Int = 0
	for Item, Chance in pairs(Items) do
		Int += Chance
		if Num <= Int then
			return  Item
		end
	end
end

local ChosenItem = PickItem(Items)
print(ChosenItem) -- 66% chance to be Crate, 33% chance to be Stealth, 1% chance to be Loot Finder

-- Find all the SpecialDisplay1 objects in the workspace
local specialDisplays = workspace:GetDescendants()
for _, obj in ipairs(specialDisplays) do
	if obj.Name == "SpecialDisplay" then
		-- Find all the ObjectDisplay children of each SpecialDisplay1 object
		local objectDisplays = obj:GetDescendants()
		for _, child in ipairs(objectDisplays) do
			if child.Name == "ObjectDisplay" then
				-- Spawn a random model for each ObjectDisplay child
				local function spawnRandomModel()
					-- Select a random rarity level first
					local ItemName = PickItem(Items)
					local ItemModel = shopFolder[ItemName]:Clone()

					local randomModel = shopFolder:FindFirstChild(ItemName)
					if randomModel == nil then
						print("Model not found for item "..ItemName..".")
						return
							--end
							else
					
					local clonedModel = randomModel:Clone()

					-- Find the root part of the cloned model
					local rootPart = clonedModel.PrimaryPart.CFrame.Position

					-- Calculate the offset between the root part and the ObjectDisplay child
					local offset = child.Position - rootPart

					-- Parent the cloned model to the ObjectDisplay child and move it to the correct position
					clonedModel.Parent = child
					clonedModel:SetPrimaryPartCFrame(CFrame.new(child.Position)) --+ offset))
				end
				end --added
				-- Spawn a random model immediately for each ObjectDisplay child
				spawnRandomModel()

				-- Spawn a new random model for each ObjectDisplay child every 30 minutes
				--while true do
				--      wait(30 * 60)
				--      spawnRandomModel()
					--  end
			--	end --added
			end
		end
	end
end

Let me know if you need more information!

For the first issue, are you sure you are positioning the object correctly in the viewportframe? If the item doesn’t appear, it could also be related to the script running before it gets the required data to show the item, which means you can use a WaitForChild or find other ways to make sure the item is loaded into the viewport frame and positioned.

For the wrong model displayed, could you maybe insert a string value, apply the item you want to load to that value, and then make sure the item matches the string value, because then you have a name, which could be useful in this scenario.

1 Like

I’m sure the position is correct since when the item does appear, it’s in the proper position. Additionally, the object appears sometimes, and other times it doesn’t… but alas, not sure where I’d put the WaitForChild:() in this code that .Parent’s the object:Clone() to the ViewportFrame.Model.

Not sure where you’re indicating I insert a Stringvalue — in the cloned model? the ObjectDisplay?