3
\$\begingroup\$

I'm making a 2-D dice rolling game in BASIC (think mechanics similar to Devil Dice on the Playstation, but top-down 2D graphics). I'm trying to accurately represent the transitions of a die when rolling it from one face to another along the cardinal directions. I can look up the current value of a die at a given position but I'm not maintaining actual object representations corresponding to a physical die, so modeling the 3D dice in game is out.

I can think of two ways to do this, but can't quite implement either correctly.

  1. Make a lookup table given a die face, orientation, and direction of roll that returns the new correctly oriented die face.
  2. Something about bitwise arithmetic I don't understand from answer #3 on this page. Seems like it could be harder to implement but possibly smaller, more elegant code. I don't, however, need anything to do with spinning or edges or animation, just fetching the next face up.

I'm trying not to prematurely optimize, but eventually the code should be able to be made as compact as possible. BASIC, so I'm not using any complicated 3D libraries or game engines, I want to keep this close to the bare metal as far as logical physics simulation goes.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

I think a lookup table indexing each of the 24 orientations is likely to be the most compact you can get. Then your current orientation is a byte and you can look up the values of the faces as quickly as single read.

But if you'd like to use code logic instead, here's one strategy you can use to exploit the symmetric structure and work on a compact representation:

  • Store the value shown on the side of the die facing the x+ direction, and y+, and z+
  • Rotating about a given axis...

    • keeps the side facing along that axis the same
    • interchanges the faces on the other two axes, and
    • replaces one of those two interchanged sides with its opposite (ie. side --> (7 - side))
      (which one gets flipped depends on whether you're rotating +90 or -90)


    ...so you could either write these as 4 operations, or a single one that treats the axis like an index to select which array elements / bits to swap around.

You can store this list of sides very compactly. One side of a die (1-6) fits in 3 bits, so you could fit two in a single byte if you wanted to, and compute the third side on demand:

// These just make what follows a bit more legible
static int Opposite(int side) { return 7 - side; }

static int Axis(int side) { return side <= 3 ? side : Opposite(side); }

static int ComputeFacingX(int facingY, int facingZ) {
    int axis = 6 - Axis(facingY) - Axis(facingZ);
    if ((facingY < facingZ) ^ (facingY > Opposite(facingZ)) ^ (((facingY + facingZ) & 1) == 0))
        return Opposite(axis);
    return axis;
}

Or since 6*6*6 = 216 < 256, you could even store all 3 sides in a single byte, though you'd need some integer division rather than a fast bitmask to decode them from that packed form.

\$\endgroup\$
0
\$\begingroup\$

I would create a lookup for each of the die faces with their neighbors in each direction. So the center is the faceup:

       n
     +---+
     | 4 |
 +---+---+---+
w| 5 | 1 | 2 |e
 +---+---+---+
     | 3 |
     +---+
       s

When rolling, one need to keep in mind how this orientation changes what N-E-S-W is. Basically the die face can rotate 90 degrees either direction because of the rolling motion.

So I model a DieFace, where you can keep track what is the current north orientation is, and facing values for each direction and (counter)clockwise rotate operation:

public class DieFace
{
    private int[] adjecentNumbers = new int[4];
    private int north = 0;

    public DieFace(int north, int east, int south, int west)
    {
        adjecentNumbers = new int[] { north, east, south, west };
    }

    public int North
    {
        get { return adjecentNumbers[north]; }
    }

    public int East
    {
        get { return adjecentNumbers[(north + 1) % 4]; }
    }

    public int South
    {
        get { return adjecentNumbers[(north + 2) % 4]; }
    }

    public int West
    {
        get { return adjecentNumbers[(north + 3) % 4]; }
    }

    public void TurnCW()
    {
        north = (north + 1) % 4;
    }

    public void TurnCCW()
    {
        north--;
        if (north < 0)
            north = 3;
    }

}

Next the difficult bit. Modelling the die itself:

        Die = new DieFace[7]; // 1-indexed so the index correspond to the face up value.
        Die[1] = new DieFace(4, 2, 3, 5);
        Die[2] = new DieFace(4, 6, 3, 1);
        // this takes some figuring out what the correct initial values are I guess.

        currentFaceUp = 1;

For example, when you roll North, a couple of things happen:

  • the side facing south becomes the new face up value
  • the side East rotates clockwise
  • the side West rotates counterclockwise

      switch(rolldirection)
      {
          case RollDirections.North:
                 int east = Die[currentFaceUp].East;
                 int west = Die[currentFaceUp].West;
    
                 Die[east].TurnCW();
                 Die[west].TurnCCW();
    
                 currentFaceUp = Die[currentFaceUp].South;
                 break;
    
           //and so on...
       }
    

Code could be optimized- but this should illustrate the basic idea.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .