xxxxxxxxxx
230
let siteInfinite = false; // Determine if the site can infinitely produce food
let drawObjectPath = false;
let pathHistoryLimit = 50; // Limit for how many path points to keep
// Array to hold all instances in the simulation
let objectArray = [];
let foodArray = [];
let siteArray = [];
// Initial parameters for the simulation
let initialObjectNum = 20;
let initialFoodNum = 20;
let initialSiteNum = 2;
let initialSpeed = 5;
let initialAge = 10;
let initialSize = 30;
let initialHunger = 0.5;
let initialStatus = 'doodle';
let setAgingFactor = 2;
let setSizingFactor = 2;
let setHungerFactor = 1;
let setEntropyFactor = 1; // The randomness in many process
let setMaxAge = 100;
let setMaxUtility = 3; // An integer, the number of foods a site could generate, and the maximum hunger level a food could recover
let setMaxBirth = 3; // An integer, the number of birth an object could give
let setWorkThreshold = 2; // The ratio below which objects start to work
let workNeeded = 5; // The amount of work needed for a new food to be generated
let simStart = false; // Flag indicating whether the simulation has started
let startFrame = null; // Frame count at which the simulation starts
function setup() {
createCanvas(windowWidth, windowHeight);
frameRate(30);
// Initialize food objects
for (i = 0; i < initialFoodNum; i ++) {
foodArray.push(new Foods(random(windowWidth), random(windowHeight), setMaxUtility));
}
// Initialize site objects
for (i = 0; i < initialSiteNum; i ++) {
siteArray.push(new Sites(random(windowWidth), random(windowHeight), setMaxUtility));
}
}
function draw() {
background('#b9d59f');
// Call behavior function for each object
for (j = 0; j < objectArray.length; j ++) {
objectsBehave(objectArray[j]);
}
// Call behavior function for each food item
for (i = 0; i < foodArray.length; i ++) {
foodsBehave(foodArray[i]);
}
// Call behavior function for each site
for (i = 0; i < siteArray.length; i ++) {
siteBehave(siteArray[i]);
}
// Display the start panel if the simulation hasn't started
if (simStart === false) {
startPanel();
startFrame = frameCount;
} else if (simStart === true) {
push();
fill(255);
noStroke();
textAlign(RIGHT, CENTER);
textSize(20);
text('Sim Point: ' + (frameCount - startFrame), windowWidth - 10, 20);
text('Object Alive: ' + objectArray.length, windowWidth - 10, 50);
textAlign(LEFT, CENTER);
text('S/O/F + MouseClike to spawn new instance', 10, 20);
pop();
}
}
function mouseClicked() {
// Spawn new instances at the mouse location when clicked with different keys pressed
if (keyCode === 79) { // If the last pressed button is 'O'
initiateObject(mouseX, mouseY);
} else if (keyCode === 70) { // If the last pressed button is 'F'
foodArray.push(new Foods(mouseX, mouseY, setMaxUtility));
} else if (keyCode === 83) { // If the last pressed button is 'S'
siteArray.push(new Sites(mouseX, mouseY, setMaxUtility));
} else {
// If the simulation hasn't started, initiate it and create initial objects
if (simStart === false) {
simStart = true; // Set the simulation start flag to true
for (i = 0; i < initialObjectNum / 2; i ++) {
// Spawn initial objects off-screen
initiateObject(random([0 - initialSize / 2, windowWidth + initialSize / 2]), random(windowHeight));
initiateObject(random(windowWidth), random([0 - initialSize / 2, windowHeight + initialSize / 2]));
}
}
}
}
function initiateObject(x = random(windowWidth), y = random(windowHeight)) {
// Create and add a new object to the objectArray with parameters set initially
objectArray.push(new Objects(x , y,
tempAge = initialAge,
tempSize = initialSize,
tempSpeed = initialSpeed,
tempHunger = initialHunger,
tempStatus = initialStatus,
tempMaxAge = setMaxAge,
agingFactor = setAgingFactor,
sizingFactor = setSizingFactor,
hungerFactor = setHungerFactor,
entropyFactor = setEntropyFactor,
maxBirth = setMaxBirth,
workThreshold = setWorkThreshold));
}
function foodsBehave(food) {
// Manage behaviors for food items
if (food.status === 'reached') {
foodArray.splice(i, 1); // Remove the food if it has been reached
} else {
food.display();
}
}
function siteBehave(site) {
// Manage behaviors for sites
if ((site.workDone != 0) && (site.workDone % workNeeded === 0)) {
// Generate new food based on work done
for (i = 0; i < workNeeded; i ++) {
foodArray.push(new Foods(random(windowWidth), random(windowHeight), setMaxUtility));
}
site.workDone = 0; // Reset work done after generating food
if (siteInfinite === false) {
site.utility -= 1; // Decrease site utility if not infinite
}
}
// Remove the site if its utility is zero
if (site.utility === 0) {
siteArray.splice(i, 1);
} else {
site.display();
}
}
function objectsBehave(object) {
// Manage behaviors for objects
object.statusUpdate(); // Update the object's status
// Handle object behavior based on its current status
if (object.status === 'doodle') {
object.doodle();
} else if (object.status === 'mate') {
object.move(objectArray); // Move towards other objects
} else if (object.status === 'work') {
if (siteArray[0]) {
object.move(siteArray); // Move towards sites
} else {
object.status = 'doodle'; // Change status if no sites are available
}
} else if (object.status === 'eat') {
if (foodArray[0]) {
object.move(foodArray);
} else {
object.status = 'work';
}
} else if (object.status === 'dead') {
objectArray.splice(j, 1); // Remove the object if it has reached max age
}
// Periodically update aging, hunger, and direction every 60 frames
if (frameCount % 60 === 0) {
object.aging();
object.hungering();
object.directing();
}
// Display the object regardless of its status
object.display();
}
function startPanel() {
// Create and display the start panel for the simulation
filter(BLUR); // Apply blur effect to the background
sp = createGraphics(windowWidth * 0.75, windowHeight * 0.5); // Create Graphics object for the start panel
sp.background('#a49fd5');
sp.stroke('white');
sp.strokeWeight(3);
sp.fill('white');
sp.textAlign(CENTER, CENTER);
sp.textSize(50);
sp.textFont('Courier New');
sp.text('Object Life Sim', sp.width / 2, sp.height / 2);
// Draw the start panel on the canvas
image(sp, (windowWidth - windowWidth * 0.75) / 2,
(windowHeight - windowHeight * 0.5) / 2);
}
// Adjust canvas size when the window is resized
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
// The rest two functions comes from https://editor.p5js.org/mangtronix/full/t4G0erH1B
function keyTyped() {
// $$$ For some reason on Chrome/Mac you may have to press f twice to toggle. Works correctly on Firefox/Mac
if (key === 'f') {
toggleFullscreen();
}
// uncomment to prevent any default behavior
// return false;
}
// Toggle fullscreen state. Must be called in response
// to a user event (i.e. keyboard, mouse click)
function toggleFullscreen() {
let fs = fullscreen(); // Get the current state
fullscreen(!fs); // Flip it!
}