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.
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)
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.
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)
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)