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?
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.
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.
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.
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:
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.
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
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?
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.
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?
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
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()