Viewport object looking the mouse position

I want to make a card game, but I want to spruce up the looks a bit.

For the card deck, I want to use viewport frames that give a 3D look to the cards; simply a part inside the frame with a decal of the card texture. I already make the camera for the viewport and making the card face the camera.

However, I want the card to slightly face the mouse when the player hovers over the card. I couldn’t find out how to do it myself so I searched on the Dev Forum and found one post. It works, but I don’t know what any of the numbers mean or how to change them to make it work for me. It flips the card over and isn’t rotated properly.

Here is what it looks like with the current code:

Here is the post where I got that section of code:
https://devforum.roblox.com/t/make-viewportframe-object-face-mouse-on-screen/758090/6

Here is my current local script:

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

local viewport = script.Parent

local player = Players.LocalPlayer
local mouse = player:GetMouse()

local card = viewport:WaitForChild("Card")

local camera = Instance.new("Camera")
camera.CFrame = card.CFrame * CFrame.new(Vector3.new(0, 0.5, -4), card.CFrame.LookVector)

viewport.CurrentCamera = camera

viewport.MouseMoved:Connect(function()
	
	--This part I got from the Dev Forum, but I have to idea how it even works.
	
	local x, y, z = CFrame.new(Vector3.new(9, 0, 0), Vector3.new(mouse.X, -mouse.Y, 1000)):ToOrientation()
	
	TweenService:Create(card,TweenInfo.new(0.4, Enum.EasingStyle.Quint), {Orientation = Vector3.new(x * 25, -(100 - ((y * 125) / 5)), z)}):Play()
	
	--]
end)

1 Like

What you want to do is take the input position, and calculate the relative offset from the center of the viewportframe (center of the screen). Then you normalize both axes, as in most cases the width of the viewport is larger than the height (yet you still want constant tilting, probably). Then you can multiply it by some strength value.
a8f2b088a658fb4c6d487792054dbf7d

I think the most important line here is how to calculate the rotation (targetCFrame). It’s just taking the card’s position, and then rotating it over it’s relative x and axes.

Note: I use a viewportframe which is the size of the entire viewport. Yours is smaller.

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

local player = Players.LocalPlayer
local mouse = player:GetMouse()

local viewport = script.Parent
local card = viewport:WaitForChild("Card")

-- Create and set up camera
local camera = Instance.new("Camera")
camera.Parent = viewport
viewport.CurrentCamera = camera

-- Position the camera so the card is visible
camera.CFrame = CFrame.new(card.Position + Vector3.new(0, 0, 6), card.Position)

-- Get viewport size and center
local viewX, viewY = workspace.CurrentCamera.ViewportSize.X, workspace.CurrentCamera.ViewportSize.Y
local center = Vector2.new(viewX / 2, viewY / 2)

-- Settings
local maxTilt = 10 -- degrees
local tweenSpeed = 0.2

viewport.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local pos = Vector2.new(input.Position.X, input.Position.Y)
		local offset = pos - center

		local normX = offset.X / (viewX / 2)
		local normY = offset.Y / (viewY / 2)

		normX = math.clamp(normX, -1, 1)
		normY = math.clamp(normY, -1, 1)

		local tiltX = normY * maxTilt
		local tiltY = normX * maxTilt

		local targetCFrame = CFrame.new(card.Position) * CFrame.Angles(math.rad(tiltX), math.rad(tiltY), 0)

		TweenService:Create(card, TweenInfo.new(tweenSpeed, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {
			CFrame = targetCFrame
		}):Play()
	end
end)

Thank you so much! It works perfectly.

Sorry to bother you, but I ran into an issue. I’m putting the cards at the bottom of the screen, and the cards aren’t rotating in the same way as when you put it in the middle of the screen. They favor going more down and don’t go up at all, and every card rotates in a weird way. Basically, the don’t all rotate like how your video shows anymore.

Here is what I mean:

Is there a way I can instead of using the workspace’s camera viewport size, I can make it relative to the viewport frame’s size? Therefore, all of the cards rotate equally and rotate like yours does.

Well, yea. Instead of using the center of the screen, use the center of the viewportframe. I also added a function that runs when the mouse leaves the frame, so that it returns to default.

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

local player = Players.LocalPlayer
local mouse = player:GetMouse()

local viewport = script.Parent
local card = viewport:WaitForChild("Card")

-- Create and set up camera
local camera = Instance.new("Camera")
camera.Parent = viewport
viewport.CurrentCamera = camera

-- Position the camera so the card is visible
camera.CFrame = CFrame.new(card.Position + Vector3.new(0, 0, 6), card.Position)

-- Settings
local maxTilt = 10 -- degrees
local tweenSpeed = 0.2

viewport.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local absPos = viewport.AbsolutePosition
		local absSize = viewport.AbsoluteSize

		local relativePos = Vector2.new(
			input.Position.X - absPos.X,
			input.Position.Y - absPos.Y
		)

		local center = absSize / 2


		local offset = relativePos - center

		local normX = math.clamp(offset.X / (absSize.X / 2), -1, 1)
		local normY = math.clamp(offset.Y / (absSize.Y / 2), -1, 1)


		local tiltX = normY * maxTilt
		local tiltY = normX * maxTilt


		local targetCFrame = CFrame.new(card.Position) * CFrame.Angles(math.rad(tiltX), math.rad(tiltY), 0)

		TweenService:Create(card, TweenInfo.new(tweenSpeed, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {
			CFrame = targetCFrame
		}):Play()
	end
end)

viewport.MouseLeave:Connect(function()
	TweenService:Create(card, TweenInfo.new(tweenSpeed, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {
		CFrame = CFrame.new(card.Position)
	}):Play()
end)
1 Like

Great! Thank you so much for this.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.