# How to check which face of a dice is on top?

Hello there. Title says it all.

I’m currently making dices with letters on top of them, and I wanted the system to know what face is on top when they roll around.

The dices will fall on the floor and will roll a little, then put a face on top. Now, how do I check which face is on top? What exactly should I do?

If you are making a square dice the best way (in my opinion) to determine the top would be with CFrames and RayCasting. Here’s a script I whipped up to demonstrating my method

``````local Dice = -- Your Dice Part
local WidthOfDice = -- the Width of your dice
WidthOfDice = WidthOfDice + 0.1

repeat
wait()
until Dice.Velocity == Vector3.new(0,0,0)

local CastedDirection = "NONE"

for i = 1, 6 do
local rayOr = Dice.Position
local rayDir
if i == 1 then
CastedDirection = "UP"
rayDir = Dice.CFrame.UpVector * WidthOfDice/2
elseif i == 2 then
CastedDirection = "DOWN"
rayDir = Dice.CFrame.UpVector * (-1*WidthOfDice/2)
elseif i == 3 then
CastedDirection = "FRONT"
rayDir = Dice.CFrame.LookVector * WidthOfDice/2
elseif i == 4 then
CastedDirection = "BACK"
rayDir = Dice.CFrame.LookVector * (-1*WidthOfDice/2)
elseif i == 5 then
CastedDirection = "RIGHT"
rayDir = Dice.CFrame.RightVector * WidthOfDice/2
elseif i == 6 then
CastedDirection = "LEFT"
rayDir = Dice.CFrame.RightVector * (-1*WidthOfDice/2)
end

local RayPara = RaycastParams.new()
RayPara.FilterDescendantsInstances = {Dice}
RayPara.FilterType = Enum.RaycastFilterType.Blacklist

local raycast = workspace:Raycast(rayOr,rayDir,RayPara)
if raycast then
local hit = raycast.Instance

if hit.Name == "Top" then -- Change this to whatever the name of your ground is called
if CastedDirection == "BACK" then
-- Front of the Dice
end
if CastedDirection == "RIGHT" then
-- Left of the Dice
end
if CastedDirection == "LEFT" then
-- Right of the Dice
end
if CastedDirection == "FRONT" then
-- Back of the Dice
end
if CastedDirection == "DOWN" then
-- Top of the Dice
end
if CastedDirection == "UP" then
-- Bottom of the Dice
end
end
end
end
``````

There is probably a better method out there but this one works (apologies for poor formatting)

1 Like

Sounds like you might need to use CFrame lookvector. Look vector gets you the forward direction of a part. If the lookvector is pointing downwards, then you know the opposite side is the top. If it is pointing to the right, then you’ll need to take into account the dice orientation and then see which side it is facing. This method would probably take a little while to figure out (Maths)

Alternatively, you could make the dice have 6 invisible parts, representing each side. Then you can get the dice position + a Y offset, then do a raycast downwards towards the dice. Whatever invisible part it hits would be the number on top of the dice. This method is similar to what @ cjhatter is doing

2 Likes

You’ll want to look at the die’s `CFrame.RightVector`, `.UpVector`, and `.LookVector` Assuming it’s a 6-sided die, also check the negative versions of each vector.

This creates 6 vectors for each of the faces of the 6-sided die. Next,
check the y component of each vector. The vector which has the greatest y component is the face pointed up.

Incidentally, for the math geeks, this is taking the dot product of the up vector `Vector3.new(0, 1, 0)` with each of the 6 vectors and seeing which one is projects more onto it.

1 Like

You can use this to get the top face of a 6 sided die:

``````function GetTop(Die)
local upVec = Vector3.new(0, 1, 0)
local maxDotValue, maxDotNormalId
for _, normalId in ipairs(Enum.NormalId:GetEnumItems()) do
local vec = Vector3.FromNormalId(normalId)
local diceVecInWorldSpace = Die.CFrame:VectorToWorldSpace(vec)
local dotValue = upVec:Dot(diceVecInWorldSpace)
if not maxDotValue or dotValue > maxDotValue then
maxDotValue, maxDotNormalId = dotValue, normalId
end
end
return maxDotNormalId.Name
end
``````
3 Likes

The thing here, forgot to mention, is that it’s going to be 13 dices, and it will be, weirdly enough but justified too, 300 dices later on.

@cjhatter Also comes into here. If I’m doing 300 dices do you think this would be a good idea to do for all 300 dices? maybe if the server did all the computing, but it sounds like berserk processing (I would be abusing the servers pretty much).

@gfxblit Well, SSDD, but ok.

I think I kinda have an answer here, let me see if it works.

What am I supposed to put here?

Nothing. The function works as is you just need to give it a part

1 Like

Nice solve. next time I need to bring my laptop while on the go. Lol

Man you take a lot of quality into this, it works.

You should post this in community resources or something. This is one of those functions that you can give people and they will find use upon.

It wasn’t my solution, I’m trying to find the source, but it was posted on the devforum a long time ago. Once I do I’m going to update my post to clear the confusion.

Could you explain the mathematics of how this works? I understand that whatever the biggest dot value is, is the top of the dice. But I don’t understand why the biggest dot value means it’s the top of the dice. I don’t fully understand what :dot does, despite trying to learn about it online.

Yeah, I’ll explain it once I find the original source really quick.

I edited the source in (it took a minute to find because I originally found it 2 years ago). Also, the original source has comments explaining it:

``````local function GetTop()
-- world-space up direction to compare again
local upVec = Vector3.new(0, 1, 0)
-- vars to find maximum
local maxDotValue, maxDotNormalId
-- loop through all possible faces
for _, normalId in ipairs(Enum.NormalId:GetEnumItems()) do
-- get object-space direction for this face
local vec = Vector3.FromNormalId(normalId)
-- get world-space direction for this face
local diceVecInWorldSpace = workspace.Box.Dice.CFrame:VectorToWorldSpace(vec)
-- Dot gets acos(angle) between upVec and diceVecInWorldSpace
-- acos(angle) is a value between 1 (same direction) and -1 (opposite directions)
-- common values you need to know here:
--  1: same direction entirely
--  bigger than 0: same side
--  0: perpendicular directions
--  smaller than 0: opposite sides
--  -1: opposite direction entirely
local dotValue = upVec:Dot(diceVecInWorldSpace)
if not maxDotValue or dotValue > maxDotValue then
maxDotValue, maxDotNormalId = dotValue, normalId
end
end
return maxDotNormalId.Name
end
``````

If you still need help understanding I can provide more assistance, but the comments were done nicely

1 Like

Hey sorry to bother with such a old topic, if your still available.
How can i do this but for a multiple sided die like 12 sides?