Trying to get swords to rotate properly in ViewportFrame

Here is where I am. My swords are mostly rotating sideways.

I have a catalog of swords. I have pulled meshes and textures from the Roblox library. So I did not control their orientation from authoring. I would like to have the swords in a catalog using Viewportframes. I have already done this with bombs and they work great. Where I am going bonkers is orienting my swords in the viewportframe.

I apply my Cframe corrections to the sword orientation, but then when I go to rotate the sword it has issues.

Can I make my Cframe corrections the new zero points for the part?
Is there a way to reorient a mesh part? I can’t get orientation to work in ViewportFrame.

local runService = game:GetService(“RunService”) – get runservice before the loop
local startingTick = tick()

local vf = Instance.new(“ViewportFrame”, script.Parent)
vf.Size = UDim2.new(1, 0, 1, 0)
vf.Position = UDim2.new(0, 0, 0, 0)
vf.BackgroundColor3 = Color3.new(1, 1, 1)
vf.BackgroundTransparency = (1)
vf.ZIndex = 20

local part = Instance.new(“Part”, vf)
part.Position = Vector3.new(0, 0, 0)
part.Anchored = false

local specialMesh = Instance.new(“SpecialMesh”)
specialMesh.MeshId = “http://www.roblox.com/asset/?id=22769578
specialMesh.TextureId = “http://www.roblox.com/asset/?id=22769571
specialMesh.Scale = Vector3.new(.2,.2,.2)
specialMesh.MeshType = Enum.MeshType.FileMesh
specialMesh.Parent = part

–local specialMesh = script.Parent.Mesh
–specialMesh.Parent = part

local camera = Instance.new(“Camera”, vf)
vf.CurrentCamera = camera

local cameraPosition = Vector3.new(3, 0, 2)
camera.CFrame = CFrame.new(cameraPosition, part.Position)

part.CFrame = CFrame.Angles(math.rad(270), 0, math.rad(270))

function makeBrickMove()
– rotating mesh
–[[
while true do – never ending loop
local diff = tick() - startingTick – time since loop started
–part.CFrame = CFrame.Angles(0, math.rad(diff), 0) – rotate equal to amount of time passed
part.CFrame = CFrame.Angles(math.rad(270),40*math.rad(diff), math.rad(270)) – two times as fast

	-- wait a frame
	runService.RenderStepped:Wait()
end
]]

end

makeBrickMove()

It’d probably be easier to set the swords CFrames, then instead of rotating them you rotate the camera in each frame around them instead.

Rotating the sword so that it faces vertically in a ViewportFrame is fine, but don’t rotate the actual sword in the ViewportFrame. Apply rotations towards the camera instead.

Whenever you update the contents of a ViewportFrame, it forces it to re-render. Changing the camera is less performance heavy than changing the rotation of parts. This is not only a recommendation by the Developer Hub but it also lines up with the Gui engine improvements where Guis are cached until they’re changed in any way, improving performance.

That’s to say: feel free to continue to rotate swords so they face vertically, but don’t change the rotation of the swords. Focus on making the swords stand vertically and the camera rotating around the center point where the sword is located.

There are some other recommendations that can be offered here. I notice that you use the parent argument while instancing a new ViewportFrame: don’t. Next is that I think you’re using both while loops and RenderStepped incorrectly: consider RunService.BindToRenderStep. Camera updates are fine in RenderStepped but just be wary about the fact that running something in RenderStepped can block frames from rendering and code from running, content depending.

2 Likes

I appreciate your input. I’m happy to have the attention of someone who understands what I am trying to do. Here is my Bomb tab code for the Alien Teddy Bomb. How would you recommend I change it. I will apply the changes to the oh so many other objects I have using the ViewportFrames. Any performance gain is greatly appreciated. People have said that I should turn off the rotations when the frame is not being displayed. Is that true or does Roblox take care of that for me.

local runService = game:GetService("RunService") -- get runservice before the loop
local startingTick = tick()

--Alien Teddy

local vf = Instance.new("ViewportFrame", script.Parent)
vf.Size = UDim2.new(1, 0, 1, 0)
vf.Position = UDim2.new(0, 0, 0, 0)
vf.BackgroundColor3 = Color3.new(1, 1, 1)
vf.BackgroundTransparency = (1)
vf.ZIndex = 20

local part = Instance.new("Part", vf)
part.Position = Vector3.new(0, 0, 0)
part.Anchored = false

local specialMesh = Instance.new("SpecialMesh")
specialMesh.MeshId = "rbxassetid://346563222"
specialMesh.TextureId = "rbxassetid://346563291"
specialMesh.Scale = Vector3.new(4,4,4)
specialMesh.MeshType = Enum.MeshType.FileMesh
specialMesh.Parent = part


local camera = Instance.new("Camera", vf)
vf.CurrentCamera = camera

local cameraPosition = Vector3.new(3, 0, 2)
camera.CFrame = CFrame.new(cameraPosition, part.Position)

function makeBrickMove()

-- rotating mesh
	while true do -- never ending loop
   	 	local diff = tick() - startingTick -- time since loop started
	part.CFrame = CFrame.Angles(0, 40*math.rad(diff), 0)
   
   		-- wait a frame
   		runService.RenderStepped:Wait()
	end
end

makeBrickMove()

The people who offered you those recommendations gave a good one. Turning off rotations when a frame isn’t visible leaves your Gui static and ensures it doesn’t run operations when it can’t be seen.

As far as the code you’ve provided goes, they fall under relatively the same recommendations as before, specifically regarding parenting. I personally, in addition to not using the parent argument, instance children first (e.g. the SpecialMesh in the part) before parenting the actual part.

To boot, the loop code should be changed. I find that using both RunService and a while loop are unnecessary. You should just stick to one. As you are updating cameras in a ViewportFrame (and thus rendering), I recommend moving to BindToRenderStep. A beauty is that you can bind and unbind at will, making things very easy to set up.

You can probably get away with doing the ViewportFrame updates in RenderStepped.

-- Rotate camera instead!
function makeBrickMove()
    part.CFrame = part.CFrame * CFrame.Angles(0, math.rad(10), 0)
end

-- Can also use Heartbeat
RunService.RenderStepped:Connect(makeBrickMove)

I didn’t use BindToRenderStep in the above example since that’d require a bit more investigation from you, such as generating a new name to be used for the first argument. Multiple functions being bound with the same name can cause problems later.

1 Like

Making progress. This code is in a local script sitting in a frame. - For anyone who wants to try it. I like for my progress to benefit others. I hate it when people say “it works, thanks”, but never actually post the code that now works.

OK, so I was looking at the Roblox sample for camera control. I’ve taken that and used it to adjust my code. Now the camera rotates around the part. Yeah!!! I noticed that the Roblox code has renderstepping in it. I don’t fully understand, but think this is doing what you were suggesting. The code works and I will try it on some other objects and see if there are any sync issues.

local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

--Alien Teddy

local vf = Instance.new("ViewportFrame", script.Parent)
vf.Size = UDim2.new(1, 0, 1, 0)
vf.Position = UDim2.new(0, 0, 0, 0)
vf.BackgroundColor3 = Color3.new(1, 1, 1)
vf.BackgroundTransparency = (1)
vf.ZIndex = 20

local part = Instance.new("Part", vf)
part.Position = Vector3.new(0, 0, 0)
part.Anchored = false
local target = part  -- The object to rotate around

local specialMesh = Instance.new("SpecialMesh")
specialMesh.MeshId = "rbxassetid://346563222"
specialMesh.TextureId = "rbxassetid://346563291"
specialMesh.Scale = Vector3.new(4,4,4)
specialMesh.MeshType = Enum.MeshType.FileMesh
specialMesh.Parent = part


	
	local camera = Instance.new("Camera", vf)
	vf.CurrentCamera = camera
    camera.CameraType = Enum.CameraType.Scriptable
    camera.Focus = target.CFrame
    local rotationAngle = Instance.new("NumberValue")
    local tweenComplete = false
     
    local cameraOffset = Vector3.new(3,0,2)
    local rotationTime = 10  -- Time in seconds
    local rotationDegrees = 360
    local rotationRepeatCount = -1  -- Use -1 for infinite repeats
    local lookAtTarget = true  -- Whether the camera tilts to point directly at the target
     
    local function updateCamera()
    	local rotatedCFrame = CFrame.Angles(0, -math.rad(rotationAngle.Value), 0)
    	camera.CFrame = rotatedCFrame:ToWorldSpace(CFrame.new(cameraOffset))
    	if lookAtTarget == true then
    		camera.CFrame = CFrame.new(camera.CFrame.Position, target.Position)
    	end
    end
     
    -- Set up and start rotation tween
    local tweenInfo = TweenInfo.new(rotationTime, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, rotationRepeatCount)
    local tween = TweenService:Create(rotationAngle, tweenInfo, {Value=rotationDegrees})
    tween.Completed:Connect(function()
    	tweenComplete = true
    end)
    tween:Play()
     
    -- Update camera position while tween runs
    RunService.RenderStepped:Connect(function()
    	if tweenComplete == false then
    		updateCamera()
    	end
    end)
1 Like