Osher Shechter s5301744@bournemouth.ac.uk
This project implements an interactive 3D flocking simulation inspired by Craig Reynolds’ Boids model (SIGGRAPH, 1987). The system simulates a group of autonomous agents (“boids”) that exhibit natural collective behaviour through three simple local rules: separation, alignment, and cohesion.
The final artefact allows the user to interact with the flock in real time through a graphical interface and direct manipulation in the scene. An attractor can be created and dragged with the mouse to guide the flock’s movement, while static obstacles can be placed and removed to influence the flock’s path and formation.
The primary focus of the project is on modular system design, real-time interaction, and performance-aware simulation rather than purely visual fidelity.
flock_preview.1.mp4
- Real-time flocking simulation based on Reynolds’ three-rule model:
- Separation - avoid crowding nearby boids
- Alignment - match the average heading of neighbours
- Cohesion - steer towards the local group centre
- Interactive user interface:
- Adjustable sliders for flocking rule weights
- Boid size control
- Obstacle strength control
- Boid count adjustment
- Direct scene interaction:
- Left mouse button - create and drag an attractor point
- Right mouse button - place or remove static obstacles
- Button to clear all obstacles at once
- World bounds force to keep the flock within the visible simulation area
- Performance optimisation using a spatial grid for neighbour lookup
- Unit testing with pytest for behaviour and system validation
- Language: Python
- UI Framework: PySide6 (Qt for Python)
- Rendering: OpenGL via PyNGL (NCCA Graphics Library)
- Math & Vectors: ncca.ngl.vec3.Vec3
- Testing: Pytest
The project follows a modular, object-oriented structure:
Boid
Represents a single agent in the simulation.
Stores:
- Position, velocity, and acceleration vectors
- Maximum speed and steering force
- Rule weights for separation, alignment, and cohesion
- Orientation for smooth visual heading
Responsibilities:
- Compute steering forces
- Apply flocking behaviours
- Update motion and orientation
Flock
Manages the collection of boids.
Responsibilities:
- Add and remove boids dynamically
- Maintain a spatial grid for efficient neighbour lookup
- Apply global parameters such as obstacle strength
- Update all boids per frame
Attractor
Represents a user-controlled target point that boids steer towards when active.
Obstacle
Represents a static repulsion object with a position and radius that generates avoidance forces in nearby boids.
Simulation Manager (MainWindow)
Handles:
- OpenGL rendering
- UI signal connections
- Mouse input
- World projection and coordinate conversion
- Main update loop
classDiagram
class Boid {
+Vec3 position
+Vec3 velocity
+Vec3 acceleration
+float sep_weight
+float ali_weight
+float cohesion_weight
+update()
+apply_behaviours()
+separation()
+alignment()
+cohesion()
+avoid_obstacles()
+world_bounds_force()
}
class Flock {
+List<Boid> boids
+float obstacle_strength
+float cell_size
+Map grid
+update()
+add_boid()
+get_neighbours()
+build_grid()
}
class MainWindow {
+Flock flock
+List<Obstacle> obstacles
+Attractor attractor
+update_boid_count()
+update_separation()
+update_alignment()
+update_cohesion()
+update_obstacle_strength()
+screen_to_world()
}
class Attractor {
+Vec3 pos
}
class Obstacle {
+Vec3 position
+float radius
+contains_point()
}
MainWindow "1" --> "1" Flock
Flock "1" --> "*" Boid
MainWindow "1" --> "0..1" Attractor
MainWindow "1" --> "0..*" Obstacle
The simulation is based on Craig Reynolds’ Boids algorithm, where each boid computes a new steering force at every frame based on nearby flockmates.
For each boid:
- Neighbour Detection
Nearby boids are identified within a fixed radius using a spatial grid. This reduces the computational cost compared to checking every boid in the flock.
- Force Calculation Three steering vectors are computed:
- Separation vector
- Alignment vector
- Cohesion vector
- Weighting and Combination Each vector is scaled by a user-controlled weight and combined into a final acceleration force.
- Additional Forces
- Attractor steering force (if active)
- Obstacle avoidance force
- World bounds force to keep boids within the visible region
- Motion Update The resulting acceleration is applied to velocity, clamped to a maximum speed, and then applied to position.
To avoid the O(n²) cost of checking every boid against every other boid, the system uses a spatial hashing grid. The simulation space is divided into square cells, and each boid is assigned to a cell based on its position. When searching for neighbours, only boids in the current cell and the eight surrounding cells are considered.
This reduces neighbour queries to approximately O(n) per frame and allows the simulation to scale to several hundred boids while maintaining real-time performance.
Additional rendering optimisations include:
- Reusing a single boid mesh for all instances
- Dynamic level-of-detail adjustments for boid geometry
- Skipping heading updates on alternating frames to reduce per-frame trigonometric cost
Despite these optimisations, Python loop overhead remains the main performance bottleneck for flock sizes above several hundred boids.
The project began with a basic implementation of the three flocking rules using vector math and a simple update loop. Early versions exhibited correct behaviour but suffered from visual instability and boids leaving the simulation area.
A world bounds force was added to gently steer boids back into the visible region instead of abruptly reversing their direction. Orientation smoothing was introduced to prevent visual “flipping” when boids changed direction sharply.
The interface was built using a .ui file designed in Qt Designer and loaded dynamically. Sliders were connected to simulation parameters through PySide6 signal-slot connections, allowing real-time adjustment of flocking behaviour.
Because Qt sliders only support integer values, all inputs are mapped to floating-point ranges internally using scaling factors.
Static obstacles were added as interactive objects placed with the right mouse button. These generate repulsion forces in nearby boids, allowing the user to shape the flock’s movement path dynamically.
A spatial grid was implemented to reduce neighbour lookup cost. This significantly improved performance at higher boid counts and allowed the simulation to remain responsive beyond 300-400 agents.
The project follows a test-driven Development (TDD) approach using pytest.
Tests validate:
- Neighbour detection logic
- Behaviour force calculations
- Class interactions between Boid and Flock
- Stability of steering outputs under controlled conditions
This ensures changes to optimisation or behaviour weighting do not silently break simulation logic.
- Performance ceiling: Python loop overhead limits scalability beyond approximately 500–700 boids in real time. A future improvement would be to align spatial cell size with interaction radius to eliminate most per-frame distance checks entirely.
- Visual fidelity: The scene uses unlit, flat-shaded geometry. A lighting model and textured meshes would significantly improve realism.
- Predator system: Adding a secondary agent type that the flock avoids could introduce more complex emergent behaviour.
- GPU acceleration: Offloading neighbour and force calculations to a compute shader or NumPy-based vectorisation would greatly improve performance at scale.
- Left Mouse Button (Hold and Drag) - Create and move an attractor
- Right Mouse Button - Place or remove obstacles
- Clear Obstacles Button - Remove all obstacles from the scene
- Sliders - Adjust flocking rule weights and visual parameters in real time
Reynolds, C. W. (1987). Flocks, herds and schools: A distributed behavioral model. Computer Graphics, 21(4), 25–34. https://www.red3d.com/cwr/boids/
Qt Company. (2023). QSlider class (Qt Widgets 6.6). https://doc.qt.io/qt-6/qslider.html
Teschner, M., Heidelberger, B., Müller, M., Pomeranets, D., & Gross, M. (2003). Optimized spatial hashing for collision detection of deformable objects. Proceedings of Vision, Modeling, and Visualization. https://matthias-research.github.io/pages/publications/tetraederCollision.pdf
Reynolds, C. W. (1999). Steering behaviors for autonomous characters. https://www.red3d.com/cwr/steer/
Treuille, A., Cooper, S., & Popović, Z. (2006). Continuum crowds. ACM SIGGRAPH. https://grail.cs.washington.edu/projects/crowd-flows/
SimonDev. (2020, November 30). Spatial Hash Grids & Tales from Game Development. YouTube. https://www.youtube.com/watch?v=sx4IIQL0x7c