xxxxxxxxxx
408
// ====================
// Custom RectButton Class
// ====================
class RectButton {
constructor(x, y, w, h, label, callback) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.label = label;
this.callback = callback;
// Default aesthetic properties
this.bgColor = color(200, 100, 150);
this.hoverColor = color(220, 120, 170);
this.textColor = color(255);
this.borderRadius = 10;
this.visible = true;
this.shadowOffset = 4;
this.shadowColor = color(0, 0, 0, 100);
this.textSize = 16; // Default text size
}
draw() {
if (!this.visible) return;
// Draw shadow
noStroke();
fill(this.shadowColor);
rect(this.x + this.shadowOffset, this.y + this.shadowOffset, this.w, this.h, this.borderRadius);
// Choose fill based on hover state
if (this.isMouseOver()) {
fill(this.hoverColor);
} else {
fill(this.bgColor);
}
noStroke();
rect(this.x, this.y, this.w, this.h, this.borderRadius);
// Draw label text using the instance's textSize
fill(this.textColor);
textSize(this.textSize);
textAlign(CENTER, CENTER);
text(this.label, this.x + this.w / 2, this.y + this.h / 2);
}
isMouseOver() {
return (mouseX >= this.x &&
mouseX <= this.x + this.w &&
mouseY >= this.y &&
mouseY <= this.y + this.h);
}
handleMousePressed() {
if (this.isMouseOver() && this.callback) {
this.callback();
}
}
hide() {
this.visible = false;
}
show() {
this.visible = true;
}
}
// ====================
// Global variables for UI, objects, and audio
// ====================
let promptInput, createButton, startButton, nextButton, restartButton, audioButton;
let dreamObjects = []; // Only one dream object is shown at a time
let gameStarted = false;
// Main background image (static)
let bgMain;
// Array of board images (b1.png - b10.png)
let boards = [];
let currentBoardIndex = 0;
let staticBoard;
let chalkFont;
// Audio variables
let music;
let audioOn = true;
function preload() {
chalkFont = loadFont('PermanentMarker-Regular.ttf');
bgMain = loadImage("background.jpg");
staticBoard = loadImage("paintboardd.png");
music = loadSound("music.mp3");
// Load board images b1.png to b10.png.
for (let i = 1; i <= 10; i++) {
boards.push(loadImage("b" + i + ".png"));
}
}
function setup() {
createCanvas(900, 600);
textFont(chalkFont);
textStyle(BOLD);
// Start music if audio is on
if (audioOn && music) {
music.loop();
}
// Create the instructions screen start button (position will be set in drawInstructions)
startButton = new RectButton(width / 2 - 50, height / 2 + 40, 100, 40, "Start", startGame);
// Create the text input as a multi-line textarea.
// (We know the desired position: x=350, y=100, and size: 300×60)
promptInput = createElement('textarea', "Provide Object Description");
promptInput.position(300, 100);
promptInput.size(300, 60);
promptInput.hide();
// Create the "Create your painting!" button.
// Hard-coded to be positioned just below the input.
createButton = new RectButton(350 + 10, 100 + 60 + 10, 180, 40, "Create your painting!", () => {
let userText = promptInput.value();
let geminiPrompt = `
You are an AI code generator working within an online p5.js editor environment. In this environment, the following conditions apply:
- The p5.js library is loaded along with its DOM addon (p5.dom), so functions like createCanvas, createButton, and createInput are available.
- A canvas of 800x600 pixels is created as part of a "dream room" simulation.
- The dream room maintains an array of dream objects. Each dream object must be defined as a JavaScript object with the following properties:
- x: a numeric value representing the horizontal coordinate (default value: 100)
- y: a numeric value representing the vertical coordinate (default value: 100)
- size: a numeric value representing the object’s size (default value: 50)
- draw: a function that uses p5.js drawing commands (for example, ellipse) to render the object at (x, y) using its size. Ensure you combine multiple shapes for a rich rendering.
- move: a function that accepts two parameters, dx and dy, and updates the x and y coordinates respectively
Your task is to generate a valid p5.js code snippet that creates a new dream object (named \dreamObj\) with these properties. The object must be defined using "let".
Output Requirements:
- Your output must be a valid JSON object with exactly two keys: "code" and "description".
- The "code" key’s value must be a string containing the p5.js code that defines the dream object as described.
- The "description" key’s value must be a concise explanation of what the code does.
- Do not include any additional keys or text; output only the JSON.
Now, generate the object:
`;
let prompt = geminiPrompt + userText;
generateDreamObject(prompt);
});
createButton.textSize = 14;
createButton.hide();
// Create the "Next Board" button on bottom right.
nextButton = new RectButton(750, height - 80, 100, 40, "Next Board", nextScreen);
// Set nextButton's colors to dark blue.
nextButton.bgColor = color(0, 0, 139);
nextButton.hoverColor = color(30, 30, 170);
nextButton.hide();
// Create the "Restart" button on bottom left.
restartButton = new RectButton(30, height - 80, 80, 40, "Restart", restartGame);
// Set restartButton's colors to dark blue.
restartButton.bgColor = color(0, 0, 139);
restartButton.hoverColor = color(30, 30, 170);
restartButton.hide();
// Create the Audio Toggle button on the top right.
audioButton = new RectButton(width - 150, 20, 120, 40, "Audio: On", toggleAudio);
// Set audio button's colors to black.
audioButton.bgColor = color(0);
audioButton.hoverColor = color(50);
}
function draw() {
if (!gameStarted) {
drawInstructions();
} else {
// Draw the static main background image.
if (bgMain) {
image(bgMain, 0, 0, width, height);
} else {
background(30, 30, 50);
}
// Draw the board image on the left side.
if (boards.length > 0 && boards[currentBoardIndex]) {
image(boards[currentBoardIndex], 50, 150, 420, 420);
}
// Draw the static board image.
image(staticBoard, 420, 150, 400, 400);
// Draw the current dream object (if any).
for (let obj of dreamObjects) {
if (obj.draw) {
obj.draw();
}
}
// Update nextButton label based on board index.
nextButton.label = (currentBoardIndex === boards.length - 1) ? "End Game" : "Next Board";
// Draw custom buttons.
createButton.draw();
nextButton.draw();
restartButton.draw();
}
// Always draw the audio toggle button.
audioButton.draw();
}
function drawInstructions() {
// Use the same background image as game screens.
if (bgMain) {
image(bgMain, 0, 0, width, height);
} else {
background(30, 30, 50);
}
// Define rectangle for instructions.
let rectX = width / 2 - 300;
let rectY = height / 2 - 150;
let rectW = 600;
let rectH = 300;
// Draw a semi-transparent rectangle for instructions.
fill(0, 0, 0, 150);
rect(rectX, rectY, rectW, rectH, 20);
// Draw the heading: center-aligned.
fill(255);
textSize(24);
textAlign(CENTER, TOP);
text("Welcome to The Magic Studio!", rectX + rectW/2, rectY + 20);
// Draw the instructions text left-aligned inside the rectangle.
textSize(16);
textAlign(LEFT, TOP);
let instructionsText = "Instructions:\n" +
"1. Click 'Start' to begin.\n" +
"2. Enter a description and click 'Create your painting!' to generate an object\n" +
"3. Click the button again to regenerate!\n" +
"4. Only one object is shown at a time.\n" +
"5. Click 'Next Board' (bottom right) to go to the board.\n" +
"6. Click 'Restart' (bottom left) to return to the instructions screen.\n" +
"7. Use the 'Audio: On/Off' toggle (top right) to turn the music on or off.";
text(instructionsText, rectX + 20, rectY + 60, rectW - 40, rectH - 80);
// Position the start button below the instructions rectangle.
startButton.x = width / 2 - 50;
startButton.y = rectY + rectH + 20;
startButton.draw();
}
// Called when the Start button is pressed.
function startGame() {
gameStarted = true;
startButton.hide();
promptInput.show();
createButton.show();
nextButton.show();
restartButton.show();
}
// Called when the Next Board button is pressed.
function nextScreen() {
if (currentBoardIndex === boards.length - 1) {
// On last board, end the game.
endGame();
} else {
currentBoardIndex = (currentBoardIndex + 1) % boards.length;
// Clear any generated dream object so only one is displayed.
dreamObjects = [];
}
}
// Called when the Restart button is pressed.
function restartGame() {
endGame();
}
// End game: return to the instructions screen and show the start button.
function endGame() {
gameStarted = false;
promptInput.hide();
createButton.hide();
nextButton.hide();
restartButton.hide();
dreamObjects = [];
currentBoardIndex = 0;
startButton.show();
}
// Handle mousePressed for our custom buttons.
function mousePressed() {
if (!gameStarted) {
if (startButton && startButton.isMouseOver()) {
startButton.handleMousePressed();
}
} else {
if (createButton.isMouseOver()) {
createButton.handleMousePressed();
}
if (nextButton.isMouseOver()) {
nextButton.handleMousePressed();
}
if (restartButton.isMouseOver()) {
restartButton.handleMousePressed();
}
}
// Always check the audio button.
if (audioButton && audioButton.isMouseOver()) {
audioButton.handleMousePressed();
}
}
// --- Audio Toggle ---
// Toggle music on/off.
function toggleAudio() {
audioOn = !audioOn;
if (audioOn) {
audioButton.label = "Audio: On";
if (music && !music.isPlaying()) {
music.loop();
}
} else {
audioButton.label = "Audio: Off";
if (music && music.isPlaying()) {
music.stop();
}
}
}
// --- Gemini LLM integration ---
async function callGeminiLM(prompt) {
const url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-pro-exp-02-05:generateContent";
const apiKey = "AIzaSyDqdz0OgjmhewE43CqmqQ9o4J1jZcDZnMg"; // Replace with your actual API key
const responseSchema = {
type: "object",
properties: {
code: {
type: "string",
description: "p5.js code defining the object"
},
description: {
type: "string",
description: "Explanation of what the code does"
}
},
required: ["code", "description"]
};
const requestBody = {
contents: [{
parts: [{ text: prompt }]
}],
generationConfig: {
response_mime_type: "application/json",
response_schema: responseSchema
}
};
try {
const response = await fetch(url + "?key=" + apiKey, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(requestBody)
});
const data = await response.json();
if (data.candidates && data.candidates.length > 0) {
let structuredResponse = data.candidates[0].content.parts[0].text;
console.log(structuredResponse);
return JSON.parse(structuredResponse);
} else {
console.error("No structured output received from Gemini:", data);
return null;
}
} catch (error) {
console.error("Error calling Gemini LLM:", error);
return null;
}
}
// Pipeline: call Gemini API, then execute the returned code.
// Append "return dreamObj;" to capture the generated object, even if defined with "let".
async function generateDreamObject(prompt) {
let response = await callGeminiLM(prompt);
if (response && response.code) {
try {
// Clear any previous dream object so only one is displayed at a time.
dreamObjects = [];
let codeWithReturn = response.code + "\nreturn dreamObj;";
let globalFn = new Function(codeWithReturn);
let generatedDreamObj = globalFn();
if (generatedDreamObj) {
// Position the generated dream object on the right side.
generatedDreamObj.x = 620;
generatedDreamObj.y = 290;
dreamObjects.push(generatedDreamObj);
} else {
console.error("Generated code did not return a valid 'dreamObj'.");
}
console.log("Gemini's description:", response.description);
} catch (err) {
console.error("Error executing generated p5.js code:", err);
}
}
}