So close!
Short answer: you already have a condition to check if a button has been pressed within a bounding box and a condition to check if the play again menu should be displayed (e.g. if(answerCount > currentQuestion.correct.length)).
Remember what state / menu you're in and use the right condition.
Mimicking you're code, you'd have something like:
if(menu == 1){
if (mouseX < 430 && mouseX > 180) {
if (mouseY < 475 && mouseY > 400) {
// reset answer count
answerCount = 0;
// return to start menu
menu = 0;
}
}
}
Update:
Based on your comments you need to count the number of correct bubbles (one way might be filtering the CorrectBubble instances and counting them (e.g. bubbles.filter(bubble => bubble instanceof CorrectBubble)) and that total number of correct bubbles with the number of correct bubbles that have been clicked (again filtering can help: e.g. correctBubbles.filter(bubble => bubble.col === bubble.clickedColor).length))
clicked() could update an internal boolean property which would make the code more readable (e.g. adding this.isClicked = true; in clicked() would make the cliecked bubbles filter read as correctBubbles.filter(bubble => bubble.isClicked).
There's also a minor collision detection bug, you probably meant something like if (d < this.r * 0.5) (and perhaps renaming this.r to this.diameter to reflect how it's used).
Even better, a reset() method could be added to the Bubble class to reset both the boolean flag and the colour:
reset(){
this.isClicked = false;
this.col = '#000000';
}
Bubbles are nicely encapsulated into classes. You could do the same for buttons which minimise repetition.
Here's a modified version of your main sketch with a basic Button class encapsulating the display and clicking within a bounding rectangle functionality:
let menu = 0 //Variable to show the game main menu.
let bubblepopper; //A variable for the image
let bubblepopperM; //A variable for the menu background image
let bubblepopperG; //A variable for the game background image
const bubbles = []; //Array to store the bubbles.
let currentQuestion = null;
const questions = [
{
question: 'What are the 4 types of access modifiers?',
correct: ['public', 'private', 'protected', 'default'],
incorrect: ['method', 'class', 'protection', 'published', 'nested', 'abstract'],
},
{
question: 'Words that go with encapsulation:',
correct: ['class', 'method', 'variable','access modifiers'],
incorrect: ['initial', 'encapsulate', 'complex', 'automatic', 'primitive', 'interface'],
},
{
question: 'Things you can put in a UML OBJECT diagram:',
correct: ['classes', 'variables', 'components', 'methods'],
incorrect: ['packages', 'Java Project', 'client code', 'no booleans', 'no objects', 'concatenation'],
},
{
question: 'Abstract classes...',
correct: ['declare abstract', 'extend subclass', 'no instantiation', 'static & non-static'],
incorrect: ['interface', 'only static', 'slow', 'only public access', 'default methods', 'multiple inheritance'],
},
{
question: 'OOP important words:',
correct: ['encapsulation', 'inheritance', 'polymorphism', 'abstraction'],
incorrect: ['capsuling', 'aggravation', 'adhesion', 'dissociation', 'array', 'submethod'],
},
{
question: 'Which of the following words go together?',
correct: ['private class', 'getter', 'setter', 'protect data'],
incorrect: ['public class', 'char', 'float', 'array', 'settings', 'extends class'],
},
];
class Button{
constructor(x, y, width, height, label, textSize, fillColor){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.label = label;
this.textSize = textSize;
this.fillColor = fillColor;
}
draw(){
push();
textAlign(CENTER);
fill(this.fillColor);
rect(this.x, this.y, this.width, this.height);
stroke(100);
strokeWeight(3);
textSize(this.textSize);
fill(255);
text(this.label, this.x + (this.width * 0.5), this.y + (this.height * 0.75));
pop();
}
isPressed(){
return ((mouseX >= this.x && mouseX <= this.x + this.width) &&
(mouseY >= this.y && mouseY <= this.y + this.height));
}
}
let playButton;
let exitButton;
let instructionsButton;
function preload() {
bubblepopper = loadImage('Bubble Popper Logo.png');
bubblepopperM = loadImage('Bubble Popper Menu Background.jpg');
bubblepopperG = loadImage('Bubble Popper Game Background.jpg');
}
function setup() {
currentQuestion = questions[Math.floor(Math.random() * questions.length)];
createCanvas(600, 600);
for(let i = 0; i < currentQuestion.correct.length; i++) {
bubbles.push(new CorrectBubble(random(-3, 3), random(-3, 3), currentQuestion.correct[i], random(width), random(height), random(80,100)));
} //The new bubbles will be added to the bubbles array.
for(let i = 0; i < currentQuestion.incorrect.length; i++) {
bubbles.push(new IncorrectBubble(random(-3, 3), random(-3, 3), currentQuestion.incorrect[i], random(width), random(height), random(80,100)));
} //The new bubbles will be added to the bubbles array.
playButton = new Button(50, 100, 200, 75, 'PLAY', 50, color(64,224,208));
exitButton = new Button(50, 400, 200, 75, 'EXIT', 50, color(255, 0, 255));
instructionsButton = new Button(50, 250, 200, 75, 'INSTRUCTIONS', 26, color(64,224,208));
playAgainButton = new Button(180, 400, 250, 75, 'PLAY AGAIN', 26, color(255, 0, 255));
returnToMenuButton = new Button(180, 400, 250, 75, 'RETURN TO MENU', 26, color(255, 0, 255));
}
function mousePressed() {
//For each bubble in the array, click it and allow it to change to it's correct colour.
bubbles.forEach(bubble => bubble.clicked(mouseX, mouseY));
}
function draw() {
//Set up the main menu and the buttons in it.
background(bubblepopperM);
playButton.draw();
exitButton.draw();
instructionsButton.draw();
image(bubblepopper, 275, 150, 300, 300); //Add the Bubble Popper logo.
if (menu == 1) {
background(bubblepopperG)
fill(0)
textSize(25)
text(currentQuestion.question, 25, 300);
//Allow the bubbles to move and be displayed on the screen.
bubbles.forEach(bubble => {
bubble.move();
bubble.display();
})
const correctBubbles = getCorrectBubbles();
if (countClickedBubbles(correctBubbles) === correctBubbles.length){
background(bubblepopperG)
textSize(30)
text('YOU GOT ALL THE RIGHT BUBBLES!', 25, height / 2)
//Add button to return to main menu.
playAgainButton.draw();
}
}
//When clicked this will show the user instructions on how to play the game.
if (menu == 2) {
background(bubblepopperM)
fill(255);
stroke(100);
strokeWeight(3);
textSize(20)
text('1. A question will be displayed to do with object-oriented', 50, 150)
text('programming (OOP) concepts.', 80, 175)
text('2. Click on the bubbles with the correct word associations', 50, 225)
text('to the question. Correct answers will turn green,', 80, 250)
text(' incorrect answers will turn red when clicked on.', 80, 275)
text('3. The round is over when you select all the correct bubbles', 50, 325)
returnToMenuButton.draw();
}
//Will exit out of the program.
if (menu == 3) {
background(bubblepopperM);
fill(255);
textSize(50);
text('THANKS FOR PLAYING!', 25, height / 2);
playAgainButton.draw();
}
}
function getCorrectBubbles(){
return bubbles.filter(bubble => bubble instanceof CorrectBubble);
}
function countClickedBubbles(bubbles){
return bubbles.filter(bubble => bubble.col === bubble.clickedColor).length;
}
//Determine the mouse coordinates so that the user can click on the buttons.
function mouseClicked() {
if (menu == 0) {
if(playButton.isPressed()){
menu = 1;
}
if(instructionsButton.isPressed()){
menu = 2;
}
if(exitButton.isPressed()){
console.log('exit');
menu = 3;
}
}
if(menu == 1){
if (playAgainButton.isPressed()) {
// return to start menu
menu = 0;
}
}
//Allow the user to go back to the main menu if they click instructions.
if (menu == 2) {
if(returnToMenuButton.isPressed()){
menu = 0;
}
}
//Allow the user to go back to the main menu if they click exit.
if (menu == 3) {
if(playAgainButton.isPressed()){
menu = 0;
}
}
}
Additionally, the menu based functionality could also be encapsulated into classes. Here is variant of the above code with a basic state machine illustrating polymorphism:
let bubblepopper; //A variable for the image
let bubblepopperM; //A variable for the menu background image
let bubblepopperG; //A variable for the game background image
const bubbles = []; //Array to store the bubbles.
let currentQuestion = null;
const questions = [
{
question: 'What are the 4 types of access modifiers?',
correct: ['public', 'private', 'protected', 'default'],
incorrect: ['method', 'class', 'protection', 'published', 'nested', 'abstract'],
},
{
question: 'Words that go with encapsulation:',
correct: ['class', 'method', 'variable','access modifiers'],
incorrect: ['initial', 'encapsulate', 'complex', 'automatic', 'primitive', 'interface'],
},
{
question: 'Things you can put in a UML OBJECT diagram:',
correct: ['classes', 'variables', 'components', 'methods'],
incorrect: ['packages', 'Java Project', 'client code', 'no booleans', 'no objects', 'concatenation'],
},
{
question: 'Abstract classes...',
correct: ['declare abstract', 'extend subclass', 'no instantiation', 'static & non-static'],
incorrect: ['interface', 'only static', 'slow', 'only public access', 'default methods', 'multiple inheritance'],
},
{
question: 'OOP important words:',
correct: ['encapsulation', 'inheritance', 'polymorphism', 'abstraction'],
incorrect: ['capsuling', 'aggravation', 'adhesion', 'dissociation', 'array', 'submethod'],
},
{
question: 'Which of the following words go together?',
correct: ['private class', 'getter', 'setter', 'protect data'],
incorrect: ['public class', 'char', 'float', 'array', 'settings', 'extends class'],
},
];
class Button{
constructor(x, y, width, height, label, textSize, fillColor){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.label = label;
this.textSize = textSize;
this.fillColor = fillColor;
}
draw(){
push();
textAlign(CENTER);
fill(this.fillColor);
rect(this.x, this.y, this.width, this.height);
stroke(100);
strokeWeight(3);
textSize(this.textSize);
fill(255);
text(this.label, this.x + (this.width * 0.5), this.y + (this.height * 0.75));
pop();
}
isPressed(){
return ((mouseX >= this.x && mouseX <= this.x + this.width) &&
(mouseY >= this.y && mouseY <= this.y + this.height));
}
}
class State{
constructor(){
}
mouseClicked(){}
draw(){}
}
class MainMenuState extends State{
draw(){
background(bubblepopperM);
playButton.draw();
exitButton.draw();
instructionsButton.draw();
image(bubblepopper, 275, 150, 300, 300); //Add the Bubble Popper logo.
}
mouseClicked(){
if(playButton.isPressed()){
currentState = playState;
}
if(instructionsButton.isPressed()){
currentState = instructionsState;
}
if(exitButton.isPressed()){
currentState = exitState;
}
}
}
class PlayState extends State{
draw(){
background(bubblepopperG)
fill(0)
textSize(25)
text(currentQuestion.question, 25, 300);
//Allow the bubbles to move and be displayed on the screen.
bubbles.forEach(bubble => {
bubble.move();
bubble.display();
})
}
mouseClicked(){
//For each bubble in the array, click it and allow it to change to it's correct colour.
bubbles.forEach(bubble => bubble.clicked(mouseX, mouseY));
// check if all correct bubbles have been clicked
const correctBubbles = getCorrectBubbles();
if (countClickedBubbles(correctBubbles) === correctBubbles.length){
currentState = winState;
}
}
}
class WinState extends State{
draw(){
background(bubblepopperG);
fill(255);
textSize(30);
text('YOU GOT ALL THE RIGHT BUBBLES!', 25, height / 2);
//Add button to return to main menu.
playAgainButton.draw();
}
mouseClicked(){
if(playAgainButton.isPressed()){
// reset clicked answers first
bubbles.forEach(bubble => bubble.reset());
// then change state
currentState = menuState;
}
}
}
class InstructionsState extends State{
draw(){
background(bubblepopperM)
fill(255);
stroke(100);
strokeWeight(3);
textSize(20);
text('1. A question will be displayed to do with object-oriented', 50, 150);
text('programming (OOP) concepts.', 80, 175);
text('2. Click on the bubbles with the correct word associations', 50, 225);
text('to the question. Correct answers will turn green,', 80, 250);
text(' incorrect answers will turn red when clicked on.', 80, 275);
text('3. The round is over when you select all the correct bubbles', 50, 325);
returnToMenuButton.draw();
}
mouseClicked(){
if(returnToMenuButton.isPressed()){
currentState = menuState;
}
}
}
class ExitState extends State{
draw(){
background(bubblepopperM);
fill(255);
textSize(50);
text('THANKS FOR PLAYING!', 25, height / 2);
playAgainButton.draw();
}
mouseClicked(){
if(returnToMenuButton.isPressed()){
currentState = menuState;
}
}
}
let playButton;
let exitButton;
let instructionsButton;
let menuState = new MainMenuState();
let playState = new PlayState();
let winState = new WinState();
let instructionsState = new InstructionsState();
let exitState = new ExitState();
let currentState = menuState;
function preload() {
bubblepopper = loadImage('Bubble Popper Logo.png');
bubblepopperM = loadImage('Bubble Popper Menu Background.jpg');
bubblepopperG = loadImage('Bubble Popper Game Background.jpg');
}
function setup() {
currentQuestion = questions[Math.floor(Math.random() * questions.length)];
createCanvas(600, 600);
for(let i = 0; i < currentQuestion.correct.length; i++) {
bubbles.push(new CorrectBubble(random(-3, 3), random(-3, 3), currentQuestion.correct[i], random(width), random(height), random(80,100)));
} //The new bubbles will be added to the bubbles array.
for(let i = 0; i < currentQuestion.incorrect.length; i++) {
bubbles.push(new IncorrectBubble(random(-3, 3), random(-3, 3), currentQuestion.incorrect[i], random(width), random(height), random(80,100)));
} //The new bubbles will be added to the bubbles array.
playButton = new Button(50, 100, 200, 75, 'PLAY', 50, color(64,224,208));
exitButton = new Button(50, 400, 200, 75, 'EXIT', 50, color(255, 0, 255));
instructionsButton = new Button(50, 250, 200, 75, 'INSTRUCTIONS', 26, color(64,224,208));
playAgainButton = new Button(180, 400, 250, 75, 'PLAY AGAIN', 26, color(255, 0, 255));
returnToMenuButton = new Button(180, 400, 250, 75, 'RETURN TO MENU', 26, color(255, 0, 255));
}
function draw() {
currentState.draw();
}
function mouseClicked() {
currentState.mouseClicked();
}
function getCorrectBubbles(){
return bubbles.filter(bubble => bubble instanceof CorrectBubble);
}
function countClickedBubbles(bubbles){
return bubbles.filter(bubble => bubble.isClicked).length;
}
Of course the Button class could live in button.js and the state clasess in states.js for example: this would greatly reduce the code in the main sketch.