Hello,
So I have been trying to make a sort of change-character system, where the player’s model can change really quickly. A little problem is my solution is kind of held together terribly. Whenever I ‘morph’ my player the camera awkwardly snaps into a straight back-facing camera. There would definitely be better ways to do this, such as maybe only changing the Player’s model.
local oldPlayer = player.Character
local newPlayer = propTransform:Clone()
local oldCFrame = oldPlayer:GetPrimaryPartCFrame()
player.Character = newPlayer
newPlayer.Parent = workspace
newPlayer.Name = player.Name
newPlayer:SetPrimaryPartCFrame(oldCFrame)
oldPlayer:Destroy()
When ‘morph’ function is triggered - first send an event to stop the client’s camera in place and when the change is completed trigger an event to release the camera.
--!strict
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local ClientConstants = require(ReplicatedStorage:WaitForChild("ClientConstants"))
local player = Players.LocalPlayer
local lastCameraCFrame: CFrame? = nil
local function handleMorphingDuringHeartbeat()
local camera = Workspace.CurrentCamera
if not camera then
return
end
if typeof(lastCameraCFrame) == "CFrame" then
camera.CFrame = lastCameraCFrame
end
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
if character and humanoid then
camera.CameraSubject = humanoid
end
end
local function onHeartbeat()
local camera = Workspace.CurrentCamera
if not camera then
return
end
local isMorphing = player:GetAttribute(ClientConstants.IS_MORPHING_ATTRIBUTE)
if isMorphing then
handleMorphingDuringHeartbeat()
return
end
lastCameraCFrame = camera.CFrame
local character = player.Character
if not character then
return
end
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
camera.CameraSubject = humanoid
end
local function initialize()
RunService.Heartbeat:Connect(onHeartbeat)
end
initialize()
--!strict
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")
local Workspace = game:GetService("Workspace")
local ServerConstants = require(ServerScriptService:WaitForChild("ServerConstants"))
local characterTemplates = ServerStorage:WaitForChild("Characters")
local function createDisableIsMorphingAttributeFunction(player: Player)
return function()
player:SetAttribute(ServerConstants.IS_MORPHING_ATTRIBUTE, false)
end
end
local function handleChildDescendantInMorph(descendant: Instance, player: Player)
if not descendant:IsA("BasePart") then
return
end
descendant.Anchored = false
descendant.CanCollide = false
descendant.CanTouch = false
descendant.CanQuery = false
descendant.Massless = true
if descendant:IsDescendantOf(Workspace) and not descendant.Anchored then
descendant:SetNetworkOwner(player)
end
end
local function morphPlayer(player: Player, characterTemplate: Model)
player:SetAttribute(ServerConstants.IS_MORPHING_ATTRIBUTE, true)
local disableIsMorphingAttribute = createDisableIsMorphingAttributeFunction(player)
local character = player.Character
if not character then
disableIsMorphingAttribute()
return
end
local currentPivot = character:GetPivot()
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid then
disableIsMorphingAttribute()
return
end
local rootPart = humanoid.RootPart
if not rootPart then
disableIsMorphingAttribute()
return
end
local newCharacter = characterTemplate:Clone()
newCharacter.Name = player.Name
local newCharacterHumanoid = newCharacter:FindFirstChildOfClass("Humanoid")
if not newCharacterHumanoid then
newCharacter:Destroy()
disableIsMorphingAttribute()
return
end
local newCharacterRootPart = newCharacterHumanoid.RootPart
if not newCharacterRootPart then
newCharacter:Destroy()
disableIsMorphingAttribute()
return
end
newCharacter:SetAttribute(ServerConstants.CURRENT_MORPH_ATTRIBUTE, characterTemplate.Name)
newCharacter.Parent = Workspace
player.Character = newCharacter
task.delay(ServerConstants.DISABLE_IS_MORPHING_DELAY, disableIsMorphingAttribute)
newCharacterRootPart.Anchored = false
newCharacter:PivotTo(currentPivot)
for _, descendant in newCharacter:GetDescendants() do
handleChildDescendantInMorph(descendant, player)
end
end
local function onMorphPartPlayerHeartbeat(morphPart: BasePart, player: Player)
local character = player.Character
if not character then
return
end
local currentMorph = character:GetAttribute(ServerConstants.CURRENT_MORPH_ATTRIBUTE)
if player:GetAttribute(ServerConstants.IS_MORPHING_ATTRIBUTE) or currentMorph == morphPart.Name then
return
end
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
local rootPart = humanoid.RootPart
if not rootPart then
return
end
local rootPartYPlanePosition = Vector2.new(rootPart.Position.X, rootPart.Position.Z)
local morphPartYPlanePosition = Vector2.new(morphPart.Position.X, morphPart.Position.Z)
local distance = (rootPartYPlanePosition - morphPartYPlanePosition).Magnitude
if distance > ServerConstants.MORPH_PART_RADIUS then
return
end
local characterTemplate = characterTemplates:FindFirstChild(morphPart.Name)
if not characterTemplate then
return
end
morphPlayer(player, characterTemplate)
end
local function onMorphPartHeartbeat(morphPart: BasePart)
for _, player in Players:GetPlayers() do
onMorphPartPlayerHeartbeat(morphPart, player)
end
end
local function onHeartbeat()
local morphPartsModel = Workspace:FindFirstChild("MorphParts")
if not morphPartsModel then
return
end
local morphParts: { BasePart } = morphPartsModel:GetChildren()
for _, morphPart in morphParts do
onMorphPartHeartbeat(morphPart)
end
end
local function initialize()
RunService.Heartbeat:Connect(onHeartbeat)
end
initialize()