About Project:
This project explores how the concept of Steering Behaviors can simulate social interaction patterns in public spaces. The goal is to understand and interpret the behaviors of visual objects as social patterns and integrate these into geometric compositions. Essentially, this is a visual experiment using the principles of social behavior.
Overview:
Steering Behaviors are a type of simple artificial intelligence that governs the movement of autonomous agents. They allow agents to navigate their environment in a lifelike manner by calculating and applying forces that influence their direction and speed.
Focusing on visual and aesthetic development, I found it interesting to apply human behaviors as a foundation for exploring visual composition. Steering behavior provides classes that can be directly applied to this purpose. Therefore, I dedicated my weekend sketches and nighttime studies (while waiting for the Euro 2024 match) to learning and creating this prototype.
User Guide:
- Behavior Selection — Click the buttons to select the behavior mode, or use the keyboard shortcuts on a desktop:
key 1 = Strangers Mode
key 2 = Cult Mode
key 3 = Queue Mode
key 4 = Socialize Mode
- Amount of Objects — Adjust the number of objects using the Amount slider.
- Size of Objects — Adjust the size of the objects using the Size slider.
This work is licensed under CC BY-NC-SA 4.0
Image courtesy of Saranont Limpananont
Image courtesy of Saranont Limpananont
Image courtesy of Saranont Limpananont
งานสเก็ตช์/โปรโตไทป์ชิ้นนี้เป็นการสร้างเครื่องมือเพื่อหาความเป็นไปได้ของการจัดวางองค์ประกอบจากเรขาคณิตพื้นฐาน (สามเหลี่ยม สี่เหลี่ยม วงกลม) ผ่านการใช้แพทเทิร์นการเคลื่อนที่ในรูปแบบต่าง ๆ ในพื้นที่สาธารณะของฝูงชนครับ โดยนำหลักการ Steering Behaviors ซึ่งเป็น framework การจำลองการเคลื่อนที่ของวัตถุมาใช้สร้างแบบจำลองปฏิสัมพันธ์ระหว่างมนุษย์กับมนุษย์ในพื้นที่สาธารณะ โดยผมทดลองทำมาสี่แบบคร่าว ๆ คือ
Strangers (คนแปลกหน้า): จะเป็นการเคลื่อนที่แบบต่างคนต่างไป แต่ก็อาจจะทักทายกันบ้าง ถ้าอยู่ใกล้กันมาก ๆ เข้าก็อาจจะเกาะกลุ่มไปด้วยกันได้
Cult (ลัทธิ): จะเป็นการเคลื่อนที่แบบฝูงชนต่างพากันเข้าไปหาท่านผู้นำ ส่วนท่านผู้นำก็พาไปไหนต่อไหนก็ไม่รู้เหมือนกัน ฝูงชนก็ตามกันไป
Queue (แถว): จะเป็นการเคลื่อนที่แบบมีระเบียบ คือจะค่อย ๆ พากันไปต่อแถวเพื่อไปตามตามกัน คล้าย ๆ การเดินของมด แต่หัวแถวจะพาไปไหนก็ไม่ทราบได้เช่นกัน
Socialize (เข้าสังคม): จะเป็นการจำลองการเข้าสังคม ต่างฝ่ายต่างเกาะกลุ่มเล็ก ๆ ในระยะที่พอไปหาได้ คล้าย ๆ พฤติกรรมในงานเลี้ยงสังสรรค์
เป้าหมายของการทำงานชิ้นนี้ คือสัปดาห์ที่ผ่านมาระหว่างที่รอดูฟุตบอลยูโร 2024 ผมก็อยากลองหาอะไรที่มีประโยชน์ทำ เลยนึกถึงการสร้างเครื่องมือที่หาความเป็นไปได้ของการจัดองค์ประกอบรูปร่างเรขาคณิตที่มีกติกาอะไรสักอย่างขึ้นมา คิดว่าพฤติกรรมการเคลื่อนที่ของมนุษย์ในพื้นที่สาธารณะก็น่าสนใจ เลยลองค้นคว้าศึกษาหาวิธีสร้างโปรโตไทป์นี้ขึ้นมาครับ
ขอบคุณครับ
2024.07.16
*This program was made using p5.js, a JavaScript library for creative coding.
Appendix A: Social Behavior Modes
This project uses the Steering Behaviors framework to simulate different social interaction patterns: Strangers, Cult, Queue, and Socialize.
Strangers Mode:
This mode shows how strangers interact in public spaces. They might gather, avoid each other, or just pass by without much interaction. It uses:
Wandering Behaviors: Objects move randomly, creating a sense of individual exploration.
Separation Behaviors: Objects maintain a certain distance from each other to avoid collisions, simulating personal space.
if (currentBehavior === 'Strangers') {
let wanderForce = mover.wander();
let separationForce = mover.separate(movers);
wanderForce.mult(0.5);
separationForce.mult(0.75);
mover.applyForce(wanderForce);
mover.applyForce(separationForce);
}
Cult Mode:
This mode represents a cult-like social pattern where most members follow a leader. It combines:
Leader-Follower Behaviors: One object acts as the leader, moving randomly. Other objects follow the leader while maintaining a distance from each other.
Wandering Leader: The leader object moves randomly, guiding the group's movement.
if (currentBehavior === 'Cult') {
let leader = movers[0];
if (i === 0) {
let wanderForce = leader.wander();
wanderForce.mult(1.0);
leader.applyForce(wanderForce);
} else {
let followForce = mover.followLeader(leader, movers);
mover.applyForce(followForce);
}
}
Queue Mode:
This mode simulates the orderly behavior seen in queues, where each object follows the one in front of it, maintaining a certain distance.
Queue Behaviors: Objects align and follow each other, simulating the formation of a queue.
if (currentBehavior === 'queue') {
mover.applyForce(mover.queueBehavior(movers, i));
}
Socialize Mode:
This mode simulates social interactions in small groups, where objects form clusters and interact with each other.
Group Behaviors: Objects form small groups, exhibiting cohesive movement and maintaining group integrity.
if (currentBehavior === 'Socialize') {
let groupForce = mover.formGroup(movers);
mover.applyForce(groupForce);
}
Appendix B: Optimizing Multi-Object and Role Dynamic Scenarios for Smooth Transitions
Overall, this project features an algorithm that enables smooth transitions for multiple objects and roles within a simulated environment. For example, in the Cult mode, the algorithm creates natural movements by adjusting the interactions between a leader and its followers using Perlin noise. This approach smoothly integrates leader, evangelist, and follower roles, incorporating separation and dynamically weighted forces, allowing for realistic and fluid transitions between different roles and behaviors.
The project also uses Perlin noise to generate natural variations in movement, ensuring seamless transitions between behaviors. This method ensures that changes between different states and roles appear smoothly, avoiding abrupt shifts that could disrupt immersion. The subtle approach to managing multi-object and multi-role behaviors demonstrates a bit more attention to detail of control and flexibility in applying steering behaviors that are both aesthetic and functionally optimized.
Sample Usage:
// Abstracted logic for leader following with Perlin noise
// Function to generate smooth, natural variations
function getPerlinNoiseValue(x, y) {
return noise(x * 0.01, y * 0.01);
}
// Function to calculate the desired distance based on Perlin noise
function calculateDesiredDistance(noiseValue) {
return map(noiseValue, 0, 1, 55, 125); // Desired distance range
// Main algorithm for leader-following behavior
function followLeader(mover, leader, isEvangelist) {
// Calculate the force to move towards the leader
let followForce = seek(mover.position, leader.position);
// Calculate separation force to avoid crowding
let separationForce = separate(mover, allMovers);
// Adjust separation force based on distance from the leader
let distanceToLeader = dist(mover.position, leader.position);
let noiseValue = getPerlinNoiseValue(mover.position.x, mover.position.y);
let desiredDistance = calculateDesiredDistance(noiseValue);
if (distanceToLeader < desiredDistance) {
let difference = p5.Vector.sub(mover.position, leader.position);
difference.normalize();
difference.div(distanceToLeader); // Weight by distance
separationForce.add(difference);
}
// Apply different weights for evangelists and regular followers
followForce.mult(isEvangelist ? 3.0 : 0.55);
// Modulate separation force with Perlin noise
separationForce.mult(1.5 * noiseValue);
// Combine forces to get the final steering force
let finalForce = p5.Vector.add(followForce, separationForce);
return finalForce;
}
// Function to seek a target position
function seek(currentPosition, targetPosition) {
let desired = p5.Vector.sub(targetPosition, currentPosition);
desired.setMag(maxSpeed); // Set the magnitude to the maximum speed
let steer = p5.Vector.sub(desired, currentVelocity);
steer.limit(maxForce); // Limit to the maximum force
return steer;
}
// Function to calculate separation force
function separate(mover, movers) {
let desiredSeparation = mover.size * 1.0;
let steer = createVector(0, 0);
let count = 0;
for (let other of movers) {
let d = p5.Vector.dist(mover.position, other.position);
if (d > 0 && d < desiredSeparation) {
let diff = p5.Vector.sub(mover.position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++;
}
}
if (count > 0) {
steer.div(count); // Average steering force
}
if (steer.mag() > 0) {
steer.setMag(maxSpeed);
steer.sub(mover.velocity);
steer.limit(maxForce); // Limit to the maximum steering force
}
return steer;
}
Function Parameters:
- The
getPerlinNoiseValue(x, y)
function generates smooth variations by returning a noise value based on the x and y coordinates. - The
calculateDesiredDistance(noiseValue)
function maps this noise value to a range (e.g., 55 to 125) to determine the desired distance dynamically. - In the
followLeader
function, the noise value influences how closely followers stay to the leader, introducing natural variation.