final int MENU_WIDTH = 420;
final int MENU_HEIGHT = 380;
final int ACCOUNT_BUTTON_WIDTH = 120;
final int ACCOUNT_BUTTON_HEIGHT = 42;
final int ACCOUNT_BUTTON_MARGIN = 25;
final int ACCOUNT_BUTTON_GAP = 15;
final color BACKGROUND_COLOR = #121623;
final color BORDER_COLOR = #5A648C;
final color TITLE_COLOR = #FFD250;
final color TEXT_COLOR = #EBEBEB;
final color SELECTED_COLOR = #FFD250;
final color INSTRUCTION_COLOR = #C8C8C8;
final color BUTTON_COLOR = #262C41;
final color BUTTON_HOVER_COLOR = #5A648C;
final color BUTTON_BORDER_COLOR = #FFD250;
final String[] MENU_OPTIONS = { "New Game", "Options", "Exit" };
final String MENU = "menu";
final String NAME_ENTRY = "name_entry";
final String GUEST = "guest";
PFont titleFont;
PFont optionFont;
PFont accountButtonFont;
PFont instructionFont;
PFont arrowFont;
int menuX, menuY;
int selectedOption = 0;
String currentScreen = MENU;
String playerName = "";
void settings() {
size(900, 650);
}
void setup() {
frameRate(60);
textAlign(CENTER, CENTER);
titleFont = loadGameFont("AromaVintage-Regular.otf", 56);
optionFont = loadGameFont("Mooligat-Demo-BF689966f7263ea.otf", 32);
accountButtonFont = loadGameFont("Mooligat-Demo-BF689966f7263ea.otf", 22);
instructionFont = createFont("Serif", 18);
arrowFont = createFont("Consolas Bold", 42);
menuX = (width - MENU_WIDTH) / 2;
menuY = (height - MENU_HEIGHT) / 2;
}
void draw() {
background(BACKGROUND_COLOR);
if (currentScreen.equals(MENU)) {
drawMenu();
drawAccountButton("Login", loginButtonX(), ACCOUNT_BUTTON_MARGIN);
drawAccountButton("Register", registerButtonX(), ACCOUNT_BUTTON_MARGIN);
} else if (currentScreen.equals(NAME_ENTRY)) {
drawNameEntry();
} else if (currentScreen.equals(GUEST)) {
drawGuestPage();
}
}
PFont loadGameFont(String filename, float fontSize) {
String[] candidates = {
sketchPath("data/assets/fonts/" + filename),
sketchPath("assets/fonts/" + filename)
};
for (String path : candidates) {
if (new File(path).isFile()) {
return createFont(path, fontSize);
}
}
return createFont("Serif", fontSize);
}
void drawCenteredText(PFont font, String message, color textColor, float x, float y) {
textFont(font);
fill(textColor);
textAlign(CENTER, CENTER);
text(message, x, y);
}
void drawMenu() {
drawMenuBorder();
drawCenteredText(titleFont, "Crazy Words", TITLE_COLOR, width / 2, menuY + 65);
int firstOptionY = menuY + 160;
for (int index = 0; index < MENU_OPTIONS.length; index++) {
float optionY = firstOptionY + index * 65;
color optionColor = (index == selectedOption) ? SELECTED_COLOR : TEXT_COLOR;
drawCenteredText(optionFont, MENU_OPTIONS[index], optionColor, width / 2, optionY);
if (index == selectedOption) {
textFont(arrowFont);
float halfTextWidth = textWidth(MENU_OPTIONS[index]) / 2.0;
drawCenteredText(arrowFont, ">", SELECTED_COLOR, width / 2 - halfTextWidth - 40, optionY);
drawCenteredText(arrowFont, "<", SELECTED_COLOR, width / 2 + halfTextWidth + 40, optionY);
}
}
}
void drawMenuBorder() {
noFill();
stroke(BORDER_COLOR);
strokeWeight(3);
rect(menuX, menuY, MENU_WIDTH, MENU_HEIGHT, 20);
strokeWeight(1);
}
void drawAccountButton(String label, int x, int y) {
boolean hovered = pointInRect(mouseX, mouseY, x, y, ACCOUNT_BUTTON_WIDTH, ACCOUNT_BUTTON_HEIGHT);
fill(hovered ? BUTTON_HOVER_COLOR : BUTTON_COLOR);
stroke(BUTTON_BORDER_COLOR);
strokeWeight(2);
rect(x, y, ACCOUNT_BUTTON_WIDTH, ACCOUNT_BUTTON_HEIGHT, 10);
strokeWeight(1);
drawCenteredText(accountButtonFont, label, TEXT_COLOR,
x + ACCOUNT_BUTTON_WIDTH / 2.0, y + ACCOUNT_BUTTON_HEIGHT / 2.0);
}
void drawNameEntry() {
drawMenuBorder();
drawCenteredText(optionFont, "Enter your name", TITLE_COLOR, width / 2, menuY + 80);
int inputWidth = 320;
int inputHeight = 60;
int inputX = width / 2 - inputWidth / 2;
int inputY = menuY + 165 - inputHeight / 2;
noFill();
stroke(BORDER_COLOR);
strokeWeight(2);
rect(inputX, inputY, inputWidth, inputHeight, 8);
strokeWeight(1);
String cursor = ((millis() / 500) % 2 == 0) ? "_" : "";
String displayedName = playerName.length() > 0 ? playerName + cursor : "Guest name" + cursor;
color displayedColor = playerName.length() > 0 ? TEXT_COLOR : INSTRUCTION_COLOR;
drawCenteredText(optionFont, displayedName, displayedColor, width / 2, menuY + 165);
drawCenteredText(instructionFont, "Press Enter to continue", INSTRUCTION_COLOR, width / 2, menuY + 245);
drawCenteredText(instructionFont, "Press Esc to return to the menu", INSTRUCTION_COLOR, width / 2, menuY + 280);
}
void drawGuestPage() {
drawCenteredText(titleFont, "Welcome, " + playerName + "!", TITLE_COLOR, width / 2, height / 2 - 30);
drawCenteredText(optionFont, "You are playing as a guest.", TEXT_COLOR, width / 2, height / 2 + 40);
}
void keyPressed() {
if (currentScreen.equals(MENU)) {
handleMenuKeyPressed();
} else if (currentScreen.equals(NAME_ENTRY)) {
handleNameEntryKeyPressed();
} else if (currentScreen.equals(GUEST) && key == ESC) {
key = 0;
currentScreen = MENU;
}
}
void handleMenuKeyPressed() {
if (key == CODED && keyCode == UP) {
selectedOption = (selectedOption - 1 + MENU_OPTIONS.length) % MENU_OPTIONS.length;
} else if (key == CODED && keyCode == DOWN) {
selectedOption = (selectedOption + 1) % MENU_OPTIONS.length;
} else if (key == ENTER || key == RETURN) {
executeMenuAction(selectedOption);
}
}
void handleNameEntryKeyPressed() {
if (key == ESC) {
key = 0;
currentScreen = MENU;
} else if (key == BACKSPACE) {
if (playerName.length() > 0) {
playerName = playerName.substring(0, playerName.length() - 1);
}
} else if (key == ENTER || key == RETURN) {
String trimmedName = trim(playerName);
if (trimmedName.length() > 0) {
playerName = trimmedName;
currentScreen = GUEST;
}
} else if (key != CODED && !Character.isISOControl(key) && playerName.length() < 16) {
playerName += key;
}
}
void mouseMoved() {
if (!currentScreen.equals(MENU)) {
return;
}
for (int index = 0; index < MENU_OPTIONS.length; index++) {
if (pointInOptionRect(mouseX, mouseY, index)) {
selectedOption = index;
return;
}
}
}
void mousePressed() {
if (!currentScreen.equals(MENU) || mouseButton != LEFT) {
return;
}
if (pointInRect(mouseX, mouseY, loginButtonX(), ACCOUNT_BUTTON_MARGIN,
ACCOUNT_BUTTON_WIDTH, ACCOUNT_BUTTON_HEIGHT)) {
println("Login button clicked");
} else if (pointInRect(mouseX, mouseY, registerButtonX(), ACCOUNT_BUTTON_MARGIN,
ACCOUNT_BUTTON_WIDTH, ACCOUNT_BUTTON_HEIGHT)) {
println("Register button clicked");
} else {
for (int index = 0; index < MENU_OPTIONS.length; index++) {
if (pointInOptionRect(mouseX, mouseY, index)) {
selectedOption = index;
executeMenuAction(selectedOption);
return;
}
}
}
}
void executeMenuAction(int option) {
if (option == 0) {
playerName = "";
currentScreen = NAME_ENTRY;
} else if (option == 1) {
println("Options selected");
} else if (option == 2) {
exit();
}
}
boolean pointInOptionRect(float px, float py, int optionIndex) {
int optionWidth = 250;
int optionHeight = 50;
int optionX = width / 2 - optionWidth / 2;
int optionY = menuY + 160 + optionIndex * 65 - optionHeight / 2;
return pointInRect(px, py, optionX, optionY, optionWidth, optionHeight);
}
boolean pointInRect(float px, float py, float x, float y, float w, float h) {
return px >= x && px <= x + w && py >= y && py <= y + h;
}
int loginButtonX() {
return width - ACCOUNT_BUTTON_WIDTH - ACCOUNT_BUTTON_MARGIN;
}
int registerButtonX() {
return loginButtonX() - ACCOUNT_BUTTON_WIDTH - ACCOUNT_BUTTON_GAP;
}
Hello everyone,
I am building a word game in Processing. I have already created the starting menu, and I now want to implement the gameplay.
My idea is:
- Random cubes fall downward on the Y axis.
- Each cube contains one letter.
- The player selects or arranges the falling cubes to form a word.
- If the formed word matches the required/correct word, the cubes should explode or disappear.
- After that, the score should increase, or the game should advance to the next level.
I would appreciate guidance on how to structure this. In particular, I need help with:
- Creating and storing multiple falling cubes.
- Giving each cube a random letter and downward speed.
- Detecting when a cube reaches the bottom of the screen.
- Letting the player select cubes and build a word from their letters.
- Checking whether the created word is correct.
- Creating a simple explosion/disappearing animation.
- Updating the score and changing levels.
I am still learning Processing, so examples or suggestions for the correct class structure would be very helpful. Should I create a Cube class, a WordManager class, and a Game class?
Thank you in advance,
Best Regards,
Alex