Week 3 -Object Oriented Programming Megan

Concept:

The concept of this project comes from cacho, a traditional Bolivian dice game. The game is played by putting five dice inside a small leather cup, and you try to roll combinations like a poker hand, for example House, Full, etc. This game caught my attention because all the dice are identical, so I thought I could use them as objects for this project in object-oriented programming.

click it!

Process:

For the process, I first started with a photo of the cubilete, the cup, and the dice inside to be able to draw it in p5.js. I used my iPad to make a sketch highlighting the geometric shapes I needed and how I had to rotate them at different angles. At first, I used translate to set the origin at zero, zero, but later I switched to WEBGL because the dice are 3D geometric shapes.

For the rotations, I set the canvas to use Degrees so I could control exactly how much each shape rotated. That is how I made the base drawing. Then came the dice, which was the most difficult part. I started with 2D using rect, and I wanted them to rotate, so I created a move function inside the Dice class and used rotate so that every time a dice completes a full rotation, it moves forward.

Then I realized 2D did not really give the impression of a real dice, so I switched to 3D and used box from the p5.js 3D examples. For some reason, the box was deforming, its sides would stretch or cut off, so I had to apply rectMode(CENTER), which I was already using for 2D. After trying several methods, this was the only one that kept the cubes from deforming, probably because a box is made of rectangles, and if they are centered, they do not distort.

Next, I wanted to put numbers on the dice, but that turned out to be very complicated. When I tried, the dice faces blinked and did not display properly. The solution I found was to color the different faces with various shades of white and gray to make them look more realistic. I also made sure each dice had a random starting position so they did not all start at the same place.

Another very important part of the process was setting boundaries for the dice. At first, when I got a square to move, it went off the canvas, so I looked at examples from p5.js on how to use the command constrain on balls and adapted that idea. I set random limits so each dice would stop at different places on the table, giving the scene more realism like when you through dice. A key detail was making sure the dice stop rotating once they reach their limits because when I first tried it the dices would keep going on circles once they stopped moving forward.

Full code:

let dice1;
let dice2;
let dice3;
let dice4;
let dice5;

function setup() {
  createCanvas(600, 600, WEBGL);
  colorMode(HSB);
  //Random starting points
  dice1 = new Dice(random(-50, 30), random(-200, 30));
  dice2 = new Dice(random(-50, 30), random(-200, 30));
  dice3 = new Dice(random(-50, 30), random(-200, 30));
  dice4 = new Dice(random(-50, 30), random(-200, 30));
  dice5 = new Dice(random(-50, 30), random(-200, 30));
}

function draw() {
  background("#deb887");
  angleMode(DEGREES);
  
//Table
  push();
  rotate(48);
  fill("black");
  rect(-320, -250, 800, 800);
  pop();
  
  push();
  rotate(20);
  fill("black");
  rect(-210, -310, 500, 200);
  pop();
  
// Cubilete
   //Outside
  fill(30, 85, 30);
  noStroke();
  quad(-193,-286, -250, -130, -100, 40, 40, -230);
  push();
  rotate(14);
  fill(30, 85, 30);
  ellipse(-253, -145, 102, 172);
  pop();
  //Inside
  push();
  rotate(19);
  fill(120, 100, 15);
  stroke(30, 85, 38);
  strokeWeight(15);
  ellipse(-50, -77, 220, 292);
  pop();
  
  //Dice
  dice1.move()
  dice1.show()
  dice2.move()
  dice2.show()
  dice3.move()
  dice3.show()
  dice4.move()
  dice4.show()
  dice5.move()
  dice5.show()
}

// Function to restart dice when clicking
function mousePressed(){
  dice1 = new Dice(random(-50, 30), random(-200, 30));
  dice2 = new Dice(random(-50, 30), random(-200, 30));
  dice3 = new Dice(random(-50, 30), random(-200, 30));
  dice4 = new Dice(random(-50, 30), random(-200, 30));
  dice5 = new Dice(random(-50, 30), random(-200, 30));
}
class Dice{
  constructor(_x, _y){
    this.x= _x;
    this.y= _y;
    
    //So that dice can rotate
    this.angle=0
    
    //Random limit to make dice stop going forward at a certain place in the table 
    this.maxX = random(-10, 260);
    this.maxY = random(-10, 260);
    
    this.colors=["#FFFFFF", "#808080", "#D3D3D3", "#FFFDD0", "#F5F5F5", "#FAEBD7"]
    
    //To stop dice rotation
    this.stopped = false;
    
  }
  move(){
    //To make dice stop rotating when limit reached
    if (!this.stopped){
    
      //Movement forward
    this.x += 3
    this.y += 3
    
    //Limits for dice to not go past the table
    this.x = constrain(this.x, -10, this.maxX);
    this.y = constrain(this.y, -50, this.maxY);

    //If limit reached, stop rotation
    if (this.x >= this.maxX && this.y >= this.maxY) {
        this.stopped = true;
      }
    
    
    //To make them roll
    this.angle += 15; 
    this.angle %= 360;
     
    }
  }
  
 show(){
  push();
  translate(this.x, this.y);
  rotate(this.angle);
  rectMode(CENTER);

  noFill(); // Base box color
  box(65);   // 3D dice

  // Color each face with planes 
  let s = 65/2; // Half the size of the dice, used to position the planes at each face

  // Front face
  push();
  translate(0, 0, s); 
  fill("#FFFFFF");         
  plane(65, 65);       
  pop();

  // Back face
  push();
  translate(0, 0, -s); 
  rotateY(180);         
  fill("#808080");        
  plane(65, 65);        
  pop();

  // Right face
  push();
  translate(s, 0, 0);  
  rotateY(90);          
  fill("#D3D3D3");         
  plane(65, 65);
  pop();

  // Left face
  push();
  translate(-s, 0, 0); 
  rotateY(-90);         
  fill("#FFFDD0");       
  plane(65, 65);
  pop();

  // Top face
  push();
  translate(0, -s, 0); 
  rotateX(90);          
  fill("#F5F5F5");       
  plane(65, 65);
  pop();

  // Bottom face
  push();
  translate(0, s, 0);  
  rotateX(-90);         
  fill("#FAEBD7");       
  plane(65, 65);
  pop();

  pop(); 
  }
}

Code I am proud of:

I am especially proud of the code I wrote to set the boundaries for the dice. As I mentioned, it was really challenging to figure out how to make the dice stop moving and rotating once they reached their limit. Also, making sure the dice did not deform while rolling was tricky, and using rectMode(CENTER) solved that problem perfectly.

//Random limit to make dice stop going forward at a certain place in the table 
    this.maxX = random(-10, 260);
    this.maxY = random(-10, 260);
    
    
    //To stop dice rotation
    this.stopped = false;
    
  }
  move(){
    //To make dice stop rotating when limit reached
    if (!this.stopped){
    
      //Movement forward
    this.x += 3
    this.y += 3
    
    //Limits for dice to not go past the table
    this.x = constrain(this.x, -10, this.maxX);
    this.y = constrain(this.y, -50, this.maxY);

    //If limit reached, stop rotation
    if (this.x >= this.maxX && this.y >= this.maxY) {
        this.stopped = true;
      }
show(){
  push();
  translate(this.x, this.y);
  rotate(this.angle);
  rectMode(CENTER);

  noFill(); // Base box color
  box(65);   // 3D dice

Overall reflection:

The hardest part was definitely trying to put numbers on the dice, which I did not fully achieve. Maybe with more research on 3D objects, I could eventually make it exactly how I intended. But I really liked the final result, especially because it looks a lot like the cacho I play in real life. I also enjoyed working with objects because it made it easy to create multiple dice without repeating code, giving each one different positions and starting points that I could edit freely.

Overall, this project taught me a lot about working with 3D shapes and object-oriented programming. I learned that even small details, like the starting position, rotation, and boundaries, make a huge difference in making the animation feel realistic. I am happy with how I managed to combine my reference from real-life cacho, geometric thinking, and programming, and I feel more confident creating multiple interactive objects in p5.js for future projects.

Leave a Reply