fullscreen
Collision.pdeEntityManager.pdeFECS.pdeRender.pdeSystem.pdeVelocity.pde
class CollisionSystem extends System <Float> {
// This is a simple collision system that -only- checks for collision with the screen edge.
// Find out on how to include other objects here:
// http://processing.org/learning/topics/circlecollision.html
void process(int entityID) {
Float radius = (Float)objects.get(entityID);
PVector pos = position.getDataFor(entityID);
PVector vel = velocity.getDataFor(entityID);
if (radius != null && pos != null && vel != null) {
// Check for collision with the screen edge (left and right)
boolean collision = false;
if (pos.x + radius >= width) {
collision = true;
pos.x = width - radius;
}
else {
if (pos.x - radius < 0) {
collision = true;
pos.x = radius;
}
}
// Update position and velocity if there was a collision
if (collision) {
position.updateDataFor(entityID, pos);
vel.x *= -1;
velocity.updateDataFor(entityID, vel);
}
// Check for collision with the screen edge (top and bottom)
collision = false;
if (pos.y + radius > height) {
collision = true;
pos.y = height - radius;
}
else {
if (pos.y - radius < 0) {
collision = true;
pos.y = radius;
}
}
// Update position and velocity if there was a collision
if (collision) {
position.updateDataFor(entityID, pos);
vel.y *= -1;
velocity.updateDataFor(entityID, vel);
}
}
}
}
class EntityManager {
Vector activeEntityIDs;
EntityManager() {
activeEntityIDs = new Vector();
}
int getNextAvailableID() {
// See if there is an unused id in the activeEntityIDs list
for (int i = 0; i < activeEntityIDs.size(); i++) {
// If so, set that id to "true" (meaning it's in use now) and return it
if (!(Boolean)activeEntityIDs.get(i)) {
activeEntityIDs.set(i, true);
return i;
}
}
// Otherwise, add a new entity id to the list and return its index
activeEntityIDs.add(true);
return (activeEntityIDs.size() - 1);
}
void removeEntity (int entityID) {
if (entityID < activeEntityIDs.size()) {
position.removeDataFor(entityID);
render.removeDataFor(entityID);
velocity.removeDataFor(entityID);
collision.removeDataFor(entityID);
activeEntityIDs.set(entityID, false);
}
}
}
// Fast Entity Component System
// Ted Brown, Jan. 21, 2011
// roguedreamerdev.blogspot.com
// This is a "Fast" system that uses direct lookups via integer id's, instead of paging through arrays at each request.
// Please note, there is no "entity" class, per se, just an integer. How you manage a list of numbers is up to you.
// This has no tradeoffs for this ultra-simple application of the concept.
// But as more components are added, and entities become collections of different components, the number of "null" values in each component system will grow.
// Good things you can expand upon:
// - Create a "diameter" system so that render and collision are locked in sync.
// - Create a flyweight "motion" class that stores velocity, drag, etc. and externalize PVector methods
// - Create "group" classes that manage their own list of entity ID's, while using entity manager to keep id's unique.
// Assuming members of a group have the same components, this would ensure component systems only process entity id's that are in use.
// Uploaded to:
// - Open Processing: http://www.openprocessing.org/visuals/?visualID=18023
// - Entity Systems Wiki: http://entity-systems.wikidot.com/fast-entity-component-system
// Further reading on Entity Component models:
// - http://gamasutra.com/blogs/MeganFox/20101208/6590/Game_Engines_101_The_EntityComponent_Model.php
// - http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
// - http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/
EntityManager em = new EntityManager();
PositionSystem position = new PositionSystem();
RenderSystem render = new RenderSystem();
VelocitySystem velocity = new VelocitySystem();
CollisionSystem collision = new CollisionSystem();
PFont font;
boolean show_help = true;
void setup () {
/////////////////////////////////////////
size(200, 200);
imageMode(CENTER);
fill(128);
stroke(0);
font = createFont("Verdana", 12);
textFont(font);
textAlign(CENTER);
addEntity(32f);
}
void draw () {
/////////////////////////////////////////
velocity.process();
collision.process();
background(255);
render.process();
if (show_help) {
text("Hit SPACEBAR to add an entity", width/2, height - 20);
}
}
void addEntity (float diameter) {
/////////////////////////////////////////
int newEntityID = em.getNextAvailableID();
float posX = random(diameter, width - diameter);
float posY = random(diameter, height - diameter);
float theta = random(360);
position.addDataFor(newEntityID, new PVector(posX, posY, 0));
velocity.addDataFor(newEntityID, radians(theta), 3);
collision.addDataFor(newEntityID, diameter * 0.5);
render.addDataFor(newEntityID, diameter);
}
void keyReleased() {
/////////////////////////////////////////
if (key == ' ') {
addEntity(random(1, 64));
show_help = false;
}
}
// Ultra-basic system to store position as a vector
class PositionSystem extends System <PVector> {
}
class RenderSystem extends System <Float> {
// This system currently stores a Float to dynamically generate a sphere.
// This could naturally store an image reference as well.
void process(int entityID) {
Float diameter = (Float)objects.get(entityID);
PVector pos = position.getDataFor(entityID);
if (diameter != null && pos != null) {
ellipse(pos.x, pos.y, diameter, diameter);
}
}
}
class System <Type> {
// Instantiate this system with <Type> to determine which class to handle
// http://en.wikipedia.org/wiki/Generics_in_Java
Vector objects;
System() {
objects = new Vector();
}
// This method allows for the engine to access data for every entity ID, without knowing if it exists.
void process () {
for (int i = 0; i < objects.size(); i++) {
if ((Type)objects.get(i) != null) {
process(i);
}
}
}
// Use this method if you are managing entity ID's in outside arrays, such as "enemies" or "projectiles"
void process (int entityID) {}
void addDataFor (int entityID) {
addDataFor(entityID, null);
}
void addDataFor (int entityID, Type object) {
// If the id is out of bounds of the objects array, expand it to meet the requirements of the entity.
if (entityID >= objects.size()) {
objects.setSize(entityID);
objects.add(entityID, object);
}
else {
// If the id is within the bounds of the current array, simply update it with the new object
if (entityID < objects.size()) {
objects.set(entityID, object);
}
}
}
void updateDataFor (int entityID, Type object) {
objects.set(entityID, object);
}
void removeDataFor (int entityID) {
if (entityID < objects.size()) {
objects.set(entityID, null);
}
}
Type getDataFor (int entityID) {
if (entityID < objects.size()) return (Type)objects.elementAt(entityID);
else return null;
}
}
class VelocitySystem extends System <PVector> {
// For future versions, it would make sense to have a custom class with inertia, drag, etc.
void process (int entityID) {
PVector vel = (PVector)objects.get(entityID);
PVector pos = position.getDataFor(entityID);
if (vel != null && pos != null) {
pos.add(vel);
position.updateDataFor(entityID, pos);
}
}
void addDataFor (int entityID, float theta, float speed) {
addDataFor(entityID, new PVector(cos(theta) * speed, sin(theta) * speed));
}
void updateThetaFor (int entityID, float theta) {
PVector velocity = getDataFor(entityID);
float speed = velocity.mag();
velocity.x = cos(theta) * speed;
velocity.y = sin(theta) * speed;
updateDataFor(entityID, velocity);
}
void updateSpeedFor (int entityID, float speed) {
PVector velocity = getDataFor(entityID);
float theta = atan2(velocity.y, velocity.x);
velocity.x = cos(theta) * speed;
velocity.y = sin(theta) * speed;
updateDataFor(entityID, velocity);
}
}