This is a demo of my creature, Magnemite. Its design originates from the magnet, which I've stylized and adapted to give it the form and feel of a living organism.
This demo shows the basic movement of Magnemite. You are in control of one Magnemite, which follows your commands, while the other creatures in the area roam around freely on their own.
Magnemite comes in three distinct variants: Neutral, Positive, and Negative. Each type is visually distinguished by its color: green, red, and blue, respectively.
Explain when/why you used translate(), push() and pop() in your prototype. What did you learn and/or what did you struggle with?
In this prototype, I used push(), translate(), and pop() together within the drawCreature() function to solve a fundamental problem: how to draw the same complex object at different locations and with different rotations without recalculating every coordinate manually.
I designed the creature's drawing functions (drawbody, draweye, drawpupil) as if the creature were always at the origin point (0, 0). This makes the drawing logic extremely simple. The eye is at (0, 0), the body is a circle at (0, 0), etc.
The biggest lesson was the power of modularity and managing state. By creating a single, reusable drawCreature() function that always draws relative to (0, 0), my main draw() loop remains clean and easy to read. The concept of "state" became very clear; push() and pop() are the essential tools for managing that state so that different elements on the canvas don't interfere with each other.
How did you used User-Defined Functions? What parameters did you added to your functions? Explain why you chose those parameters.
The Main Drawing Function: drawCreature()
This function acts as the "master controller" for drawing a single Magnemite. It's responsible for positioning, rotating, and assembling all the parts.
x and y: These are the most fundamental parameters. They tell the function where on the canvas to draw the creature's center. This allows me to place multiple creatures anywhere I want.
bodycolor: I chose this parameter to make the function flexible. Instead of hard-coding a color inside, I can pass a color in when I call the function. This allows me to use the exact same drawCreature function to create the green (Neutral), red (Positive), and blue (Negative) variants.
rotateSpeed: This controls the self-rotation of the creature. The player-controlled Neutral Magnemite doesn't spin, so I pass in 0. The other two spin at different speeds, which is easily achieved by passing in different values for this parameter.
pupilOffsetX and pupilOffsetY: These control the position of the pupil relative to the center of the eye. I made them optional parameters with a default value of 0.
The Component Functions: drawbody(), draweye(), drawpupil()
These are simple, single-purpose functions that just draw one part of the creature. drawCreature calls them to assemble the final product.
In the component functions I used multiple Size parameters to make the parts scalable and easy to adjust.
How do you think using translate(), push() and pop() and User-Defined Functions improved your creature?
Using translate(), push(), pop(), and user-defined functions fundamentally improved my creature prototype by making the code efficient, scalable, and easy to read. They moved the project from being a static drawing to a dynamic and manageable system.
Before, I could only draw a static creature at a fixed spot. To make it move, I would have had to manually add x and y offsets to every single shape, and calculating rotation would have involved complex math for every point.
translate() allowed me to treat the creature as a single unit. To make it follow the mouse, I just had to move the entire coordinate system, and all its parts came along for the movement.
The push() and pop() functions created a "sandbox" for each creature. This enabled me to have the Positive and Negative variants spinning on their own axes without interfering with each other. Each creature's state (its position and rotation) is completely independent.
User-defined functions acted like LEGO bricks, improving the creature by making the design modular. Instead of writing the drawing code for the body, eye, and pupil three separate times for each variant, I wrote it just once inside functions like drawbody() and draweye(). The main drawCreature() function acts as an instruction manual for assembling these bricks. Though it took considerations in deciding the variables, the User-defined functions are reusable, easy to Maintain and scalable with simple changes in different parameters.
The code is divided into 1 function calculatePupilOffset for calculating how far the pupil should move based on the mouse cursor, and multiple drawing functions responsible for drawing different parts of the creature.
let bodyX, bodyY;
function setup() {
createCanvas(800, 500);
bodyX = 0;
bodyY = 0;
noCursor();
}
function draw() {
background(220);
// NEUTRAL
let neutralColor = color(173, 216, 200);
bodyX = lerp(bodyX, mouseX, 0.05);
bodyY = lerp(bodyY, mouseY, 0.05);
let pupilPos = calculatePupilOffset(bodyX, bodyY, mouseX, mouseY, 12.5);
drawCreature(bodyX, bodyY, neutralColor, 0, pupilPos.x, pupilPos.y);
// POSITIVE
let positiveColor = color(255, 0, 0);
drawCreature(width / 2 - 200, height / 2, positiveColor, radians(frameCount));
// NEGATIVE
let negativeColor = color(0, 0, 255);
drawCreature(width / 2 + 200, height / 2, negativeColor, -radians(frameCount) * 2);
}
function calculatePupilOffset(centerX, centerY, targetX, targetY, maxOffset) {
let dx = targetX - centerX;
let dy = targetY - centerY;
let distance = sqrt(dx * dx + dy * dy);
let offsetX = dx;
let offsetY = dy;
if (distance > maxOffset) {
offsetX = (dx / distance) * maxOffset;
offsetY = (dy / distance) * maxOffset;
}
return { x: offsetX, y: offsetY };
}
function drawCreature(x, y, bodycolor, rotateSpeed, pupilOffsetX = 0, pupilOffsetY = 0) {
push();
translate(x, y);
rotate(rotateSpeed);
drawbody(100, bodycolor);
draweye(50);
drawpupil(25, pupilOffsetX, pupilOffsetY);
pop();
}
function drawbody(bodySize, bodycolor) {
fill(bodycolor);
stroke(0);
strokeWeight(2);
circle(0, 0, bodySize);
fill(100);
stroke(0);
strokeWeight(2);
rect(0, -bodySize / 2, bodySize / 2, bodySize);
}
function draweye(eyeSize) {
fill(255);
stroke(0);
strokeWeight(2);
circle(0, 0, eyeSize);
}
function drawpupil(pupilSize, x, y) {
fill(0);
noStroke();
circle(x, y, pupilSize);
}