How do I make a ViewportFrame GUI that shows a specific camera view of the workspace?

I believe ViewportFrames can be used to achieve this but I’m not sure how to use them or set them up.

What I want to do is set up an invisible camera that’s looking at an area of the workspace and show that camera’s view on the startergui. How do I do this?

4 Likes

Everything that you want the viewport frame to show would have to be cloned into the frame from the view of that camera using something like raycasting.

Can I ask what it’s being used for?

2 Likes

I want to show an NPC talking on the screen and I thought this way would look better than just a static image of its head

2 Likes

Oh, then this is easy.

You would clone the NPC and place it inside the viewportFrame, then add a camera inside the frame infront of the NPC, set the viewportFrame.CurrentCamera to that camera, and then animate the NPC how you want it to talk / move.

The background behind the NPC can be as simple or as complex as you want.

2 Likes

I’d also want the background to be the same as the NPC’s real background in the workspace though, and the npc could move.

I also would want to have this same effect with a player talking in the same gui.

1 Like

It’s doable, but if you want a 1:1 recreation of the NPC & Player’s backgrounds, you’d have to clone them using raycasting and then place them into the viewportFrame.

If you already know what the background scene would be (like trees or a city) then it’s easier to already have a version of that scene in the frame and skip over the raycasting part.

1 Like
function playerTalking()
	local clark = game.StarterGui.TextTicker.Frame.ViewportFrame["Clark the Manager"]
	if clark then
		clark.Parent = game.ServerStorage -- clark still appears
	else
		warn("Clark could not be found")
	end
	local randomPlayer = game.Players:GetPlayers()[math.random(1,#game.Players:GetPlayers())]
	if randomPlayer.Character then
		local randomPlayerClone = randomPlayer.Character:Clone()
		if randomPlayerClone then
			randomPlayerClone.Parent = game.StarterGui.TextTicker.Frame.ViewportFrame
			randomPlayerClone.HumanoidRootPart.CFrame.Position = Vector3.new(-60.598,22.664,16.818)
			randomPlayerClone.HumanoidRootPart.CFrame.Orientation = Vector3.new(0,90,0)
			script.Parent.Label.Text = randomPlayer.DisplayName 
			randomPlayerClone.HumanoidRootPart.Anchored = true
		else
			warn("Random player could not be cloned") -- this happens
		end
	else 
		warn("Random player character not found")
	end
	-- todo: check when finished talking and then put clark back, and delete random player clone
end

I gave up on doing the 1:1 background, more effort than its worth it seems.

I’m trying to put the random player in the viewport frame but two bugs are happening:

  1. Despite seemingly successfully parenting the default NPC into server storage (lack of the warn message in the log) he’s still visible in the viewport frame.
  2. Cloning the random player is failing.
1 Like

Hi there! Is this a server script or a local script? If it’s a local script then the reason clark still appears is because local scripts don’t have access to the ServerStorage service; ServerStorage can only be seen and accessed by the server itself. You can try storing clark in ReplicatedStorage instead, which is accessable by both server and client.

As for the random player not appearing, it has to do with a parameter on player character models called “Archivable” which, by default is set to false. In order to clone the player, you’d have to set archivable to true beforehand, then set it false again after the cloning (for safety).

if randomPlayer.Character then
	randomPlayer.Character.Archivable = true
	local randomPlayerClone = randomPlayer.Character:Clone()
	randomPlayer.Character.Archivable = false

adding those two lines should fix the cloning

1 Like

It’s a server script. Is it better to make it a local script cause it deals with the talking GUI?

script.Parent.Label.Text = randomPlayer.DisplayName

If you’re talking about this line, yeah its just a bug cause it used to be a local script that I decided to move to a server script.

I implemented your cloning fix and now it no longer sends the warning message but I still can’t see that anything has changed in the viewport frame.

I also discovered a new issue: CFrame.Position and CFrame.Orientation are read only. I know how to set the position but how would I do this with orientation?

1 Like

If it’s no longer giving the warning, that means it is cloning the player successfully.
You can set CFrame Orientation and Position using

randomPlayerClone.HumanoidRootPart.CFrame = CFrame.new(0, 0, 0) * CFrame.FromAngles(0, 0, 0)

The first part is the XYZ on the world axis and the second part is the rotation.
Though, I’m not sure setting the HRP position of a cloned player will move the rest of the model with it.

You can try

randomPlayerClone:PivotTo(CFrame.new(X, Y, Z) * CFrame.FromAngles(X, Y, Z))

This works for the entire model, and it’s pivot center is the PrimaryPart of the model, in this case, the HRP.

And to answer your question, yes, making it a local script is better for performance, controlling UI with a server script is a big no-no unless absolutely necessary. This is because the server can’t update as frequently as the client can with a local script.

1 Like

Alright, I’ve made it into a localscript and implemented the teleporting fix and there are no errors, yet I still cannot see the player clone in the viewport frame. I did some reading and perhaps its because I’m parenting it into StarterGUI instead of everyone’s PlayerGUI? If this is the issue, whats the best way to parent the player clone into all players’ playergui?

playerClone.Parent = Players.LocalPlayer.PlayerGui.TextTicker.Frame.ViewportFrame

I did this, and it worked, but I’ve encountered yet another issue. My idle animation doesn’t play in the viewport frame. It seems that it’s just capturing the exact frame of my character in the world and putting it into the viewport frame, because if I walk while it’s running the script what appears in the viewport frame is a freeze frame of me in my run animation.

How do I make the character in the viewport frame animate like the character in the workspace, and how do I set it to do the idle animation even if cloned while the run animation is playing?

Yes, you’re absolutely right, the clone wouldn’t appear unless you parent it to the player’s viewport frame in the PlayerGUI, I didn’t catch that!

As for animating, it’s also doable, after cloning the player, you would run a sequence that 1.) removes any existing scripts within the randomPlayerClone, and 2.) animates the clone using AnimationTrack:Play()

for i, v in randomPlayerClone:GetDescendants() do
	if v:IsA("Script") or v:IsA("LocalScript") then
		v:Destroy()
	end
end
local animation = randomPlayerClone.Humanoid.Animatior:LoadAnimation() -- put a reference to the idle animation between the ( )
animation:Play()

The code would look something like this, I haven’t tested it

If you don’t know how to find the player’s default idle animation, you can find it in your player character under Animate → Idle → Animation1

But wouldn’t Animate → Idle → Animation1 be destroyed?

Yes, it would be necessary to destroy the script itself, otherwise it would conflict with any animations you intend to play.
However, you can either copy over whatever animation you want to play (from something like ReplicatedStorage), copy the “Animation1” instance from the existing player’s character, or copy the “Animation1” instance from the Animate script before deleting it.

In the context of taking the last option, it’d look like this

for i, v in randomPlayerClone:GetDescendants() do
	if v:IsA("Script") or v:IsA("LocalScript") then
		if v.Name == "Animate" then
			local idle = v.Idle.Animation1:Clone()
			idle.Parent == randomPlayerClone
			v:Destroy()
		else
			v:Destroy()
		end
	end
end
local animation = randomPlayerClone.Humanoid.Animatior:LoadAnimation(randomPlayerClone.Animation1)
animation:Play()