Hello, Everyone. I am new to Unity and am having trouble getting my head around the scripting (not conceptually, just syntax and set-up). I’ve spent about a year forcing myself to think in Actionscript. The only other programming experience I’ve had was Basic way back in high school. I’m determined to learn, though, and I’ve set up a basic project to get myself going. It’s a dice game, and I thought I’d set up a thread and ask for help as I go along…
So far, I can instantiate and “roll” two dice with random starting rotation so the roll is different every time. My strategy for solving the outcome is to set up a prefab die with a null just outside of each of the six sides. I then want to compare the y values of each of the nulls and see which is the highest. This would be the number rolled for the die. My first question, then, is how do I read the world coordinates of a child object (the nulls)?
The other concept I’m having trouble with is the Update. It seems on the surface that this is the same as Actionscript’s onEnterFrame - meaning is fires constantly. How do I test for “rigidbody.IsSleeping()” just once? Meaning I want to do my null position test just once when the dice come to rest.
// drag your 6 blank objects one into each slot in sides in the inspector.
// requires rigidbody.
var randomSpin = 1.00;
var randomVelocity = 1.00;
var sides = new Transform[6];
private var wasSleeping = false;
function Start ()
{
transform.rotation = Quaternion.LookRotation(Random.onUnitSphere);
rigidbody.angularVelocity = Random.insideUnitSphere * randomSpin;
rigidbody.velocity = Random.insideUnitSphere * randomVelocity;
}
function Update ()
{
if(!wasSleeping rigidbody.IsSleeping())
{
var high = 0.00;
var i = 0;
var highIndex = 0;
for(side in sides)
{
i ++;
if(side.position.y > high) highIndex = i;
}
print("Die landed on " + highIndex);
}
wasSleeping = rigidbody.IsSleeping();
}
Interesting question. I don’t have any answers for you, but I have some ideas (sort of stream of consciousness stuff here)
For determining which side is up some possible ways to do that are:
You could cast a ray upward from your die using Camera.main.ScreenToWorldPoint(Vector3.up) as the direction.
If the surface the dice are on is level, you could simply check with “null” point was above a given y value since your dice have a fixed height (I assume)
You can have your “nulls” be thin box colliders that extend a tiny bit past the edge of each die. Then your up side would be the only side not “colliding” with the ground.
For the checking for sleep and Update().
4. You can just have a flag that is set to false. Then in update check the rigidBody.isSleeping() if it is false. When you determine the rigidbody is sleeping you could set the value to true.
5. You may also be able to use collisions by having each “null” tell the die when it collides with the ground. You then know that the up side is the opposite side of the last side to collide with the ground. The “nulls” might also have to also the “die” when they exit the ground.
6. You could also probably just check when your die stops which “null” is currently colliding wiht the ground and you up side will be the opposite side.
Camera.main.ScreenToWorldPoint(Vector3.up) does something completely different. It projects a point from screen space into world space as viewed by the camera.
You don’t have to add game objects to detect which side is up. You could simply calculate the direction vectors on the fly:
// The 3 low-end expressed in local space vectors.
// We don't need the last 3 as their direction
// vectors are simply the negative of the
// opposite side.
// (You will have to change the vectors if
// you have orientated the dice differently.)
var sides : Vector3[] =
[Vector3.up, // 1(+) or 6(-)
Vector3.right, // 2(+) or 5(-)
Vector3.forward]; // 3(+) or 4(-)
function WhichIsUp() {
var maxY = float.NegativeInfinity;
var result = -1;
for(var i = 0; i < 3; i++) {
// Transform the vector to world-space:
var worldSpace = transform.TransformDirection(sides[i]);
if(worldSpace.y > maxY) {
result = i+1; // index 0 is 1
maxY=worldSpace.y;
}
if (-worldSpace.y > maxY) { // also check opposite side
result = 6-i; // sum of opposite sides = 7
maxY=-worldSpace.y;
}
}
return result;
}
And for checking if the dice just began sleeping you could do something like this:
private var was_sleeping_last_time = false;
function BeganSleeping() {
var is_sleeping_now = rigidbody.IsSleeping();
var result = (is_sleeping_now !was_sleeping_last_time);
was_sleeping_last_time = is_sleeping_now;
return result;
}
I am a noob trying with dices. I have two dices rolling and then stoping, so I guess next I need is the same that c.rocks was asking here.
As far as I can understand, the sleeping code is for defining the moment we read the points, and the direction vector code is for getting the final punctuation.
But how would I use them? I asume I need to build a behaviour for the dices where I will look first for the dices to be sleeping, and then detect which side is up. But I cannot find any reference to the “return result” of the last line of both scripts. I guess it is a very basic scripting question, but how do I get those results aplied?
Don’t really understand the question. The “return result;” simply returns the dice result collected when sleeping occurs. You need to poll the algorithm at set intervals from somewhere, at least that’s what I do as I don’t think there’s a call sent when sleep occurs.
BTW, another way to detect dice sides (which is useful for dice with more than 6 sides) is to raycast against the top of the dice and do a lookup in a polygon reference table. Works great.
You can use this script to determine which face of a cube is facing upwards:-
var face: int;
function Update () {
if (rigidbody.IsSleeping()) {
face = DiceFace();
}
}
function DiceFace() {
var upDir = Vector3.up;
var x = Vector3.Dot(transform.right, upDir);
var y = Vector3.Dot(transform.up, upDir);
var z = Vector3.Dot(transform.forward, upDir);
var xAbs = Mathf.Abs(x);
var yAbs = Mathf.Abs(y);
var zAbs = Mathf.Abs(z);
if (xAbs > yAbs) {
if (xAbs > zAbs) {
return x > 0 ? 1 : 6;
} else {
return z > 0 ? 2 : 5;
}
} else if (yAbs > zAbs) {
return y > 0 ? 3 : 4;
} else {
return z > 0 ? 2 : 5;
}
}
If you take +x to mean the positive X axis is facing upward and -x to mean it is facing downward, etc, the faces are interpreted as:-
+x = 1
-x = 6
+y = 3
-y = 4
+z = 2
-z = 5
The script fills in the value of the die roll when the rigidbody goes to sleep (indicating it has stopped moving) but you may prefer to test for this in other ways. And of course you can change the numbers to suit whatever texture you have on your dice.
My method was roughly as follow below. It may not be a super-elegant approach as I note the verticle index of each face and use that in a switch to get back the correct result. It works well however and does so for all kinds of dice regardless of the number of faces.
function GetResult() {
var ray = new Ray ((transform.position+Vector3(0,15,0)),-Vector3.up);
var hit : RaycastHit;
if (Physics.Raycast (ray, hit)) {
if (hit.transform.tag!="die") ReThrow();
//either just use hit.triangleIndex if the die is modelled correctly, or:
switch (hit.triangleIndex) {
//"map" each triangle to the correct result:
case 88:
return(0);
//and so on for each face
//if the result is not a proper face, I elected to rethrow the die (my dice could in rare cases come to rest against objects or on an edge).
default:
ReThrow();
return(-1);
}
}
}
If you are using a physics based solution for the roll there are functions to measure and add movement to physics objects. How about doing some actual research before posting…?
I’m attempting a dice game as well. Those are awesome scripts. Most of those are making sense to me but what do I do with them? How does that apply to a points system or an output to tell the system what is indeed facing up when the dice are rolled? The code themselves make sense but what to do with them is making very little sense.
I figured out a way to tell the system which side is which via Raycasting. And even tell which side is up. It was surprisingly easy actually. I could post the code I used too if you’d like?
Now to find a way how to add the values together. This dice game I was so graciously thrown upon is no longer my priority but my curiosity is getting the better of me. haha
I’d personally love to see the code. I came up with my own dice solution, similar to one described above- I used named colliders placed outside the die mesh, made them triggers and turned off mesh rendering. I have a detector below the table that announces their value, then destroys the collider. I’m not much of a coder (Art side), so this worked well enough for me. My only issue is that once in a while if a die is rolling it will trigger the detector more than once. Careful detector placement makes it rare, but still.
I’m going to reread the thread tomorrow to figure out the proper usage of *sleeping, but yeah, in the meantime, I’d love to see that code. The idea about looking for the collider with the highest/lowest elevation via script without actual collision sounds great. I might change my implementation if there’s an easier/more foolproof way to get polyhedrals to work. I need up to 20 sided dice to work, but I have a d24 and d30 mesh just in case I can make them work.
I just made it so when the die comes to a stop (the IsSleeping command) then allow it to perform the Raycasting actions. I set it up so that when the Ray hits the floor it’ll register the side that is down but since that’s the case I just wrote out in the Console the side opposite of that and added the points to the GUI. (You can perform all the other actions you want in there though.)
This method works for a six sided die. Since I’m no master at this, doing a 24 and/or 30 sided die like you’re looking for, I am not sure how to accomplish that.
static var startPoints : int = 0;
function Update()
{
var upDown = transform.TransformDirection(Vector3.up);
var leftRight = transform.TransformDirection(Vector3.right);
var frontBack = transform.TransformDirection(Vector3.forward);
var hit : RaycastHit;
Debug.DrawRay(transform.position, -frontBack * 1, Color.blue);
Debug.DrawRay(transform.position, -leftRight * 1, Color.red);
Debug.DrawRay(transform.position, -upDown * 1, Color.green);
if (rigidbody.IsSleeping())
{
if (Physics.Raycast(transform.position, -upDown, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 2");
startPoints = 2;
}
}
if (Physics.Raycast(transform.position, upDown, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 6");
startPoints = 6;
}
}
if (Physics.Raycast(transform.position, -leftRight, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 1");
startPoints = 1;
}
}
if (Physics.Raycast(transform.position, leftRight, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 3");
startPoints = 3;
}
}
if (Physics.Raycast(transform.position, -frontBack, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 5");
startPoints = 5;
}
}
if (Physics.Raycast(transform.position, frontBack, hit, 1))
{
if (hit.collider.gameObject.tag == "Floor")
{
Debug.Log("Face 4");
startPoints = 4;
}
}
}
}