0
\$\begingroup\$

This question is about the proper way to architect an implementation of Craig Reynolds Boids algorithm.

I have an Agent object like this:

struct Agent: Equatable
{
    let identifier = NSProcessInfo.processInfo().globallyUniqueString

    var position: CGPoint
    var velocity: CGVector

    init(position: CGPoint = .zero, velocity: CGVector = .zero)
    {
        self.position = position
        self.velocity = velocity
    }
}

And a Simulation object that holds an array of agents. Each iteration of the simulation the Simulation object updates the position and velocity of each Agent in its array of Agents. Something like this:

class Simulation
{
    private var agents: [Agent] = []

    init(agents: [Agent])
    {
        self.agents = agents
    }

    func update()
    {
        for agent in self.agents
        {
            // Update agent's position and velocity
        }
    }

This is ok. I can run the simulation and update the Agent models appropriately. However, I also want to draw the models on screen.

The question is whether I should create a new AgentView class as the view representation of an Agent model object. Or whether (1) maintaining a relationship between each Agent model and a corresponding AgentView is too cumbersome and (2) I should just replace the Agent model with AgentView entirely and let AgentView be the sole representation of an agent (sort of blending model and view state).

Thanks in advance for your help. If you would like additional clarification just let me know.

\$\endgroup\$
  • \$\begingroup\$ Perhaps this may be of interest... I've been trying to convert Apple's boids flocking demo from WWDC 2014 into Swift with limited success. But maybe something there might be of assistance. stackoverflow.com/q/44235801/4376309 \$\endgroup\$ – peacetype Jul 12 '17 at 10:06
1
\$\begingroup\$

Coming from a game dev perspective, I'd suggest you pursue a entity component based system (ECS) to build up your simulation.

An entity is a container for components. Components represent state (and/or) behaviour of an entity.

In pseudocode, it translates to something like:

class Component { }

class Entity { 
    var components: [Component]
    // ... code to add / get components
}

class PositionComponent : Component {
    var position: CGPoint
}

class VelocityComponent : Component { 
    var velocity: CGPoint
}

class SpriteComponent : Component {
    var node: SKSpriteNode
}

Using this notion of components, you can build up an agent as follows:

let agent = Entity()
agent.add(PositionComponent(x: 0, y: 0))
agent.add(VeolcityComponent(x: 0, y: 0))
agent.add(SpriteComponent(texture: "boid.png"))

Given a set of agents, we need to perform actions on them. We can split this functionality across what is usually termed a "System". We could have a system that updates all agents that have a position and velocity component. This would be the core boids algorithm. We could have a display system that syncs the position component of an entity with it's sprite component.

class BoidSimulation {

    func update(dt, entities) {
        for agent in entities {

            // update position
            let p: PositionComponent = agent.get()
            let v: VelocityComponent = agent.get()
            p.position = p.position + v.velocity * dt

        }
    }
}

class BoidDisplay {
    func update(dt, entities) {
        for agent in entities {

            // update position
            let p: PositionComponent = agent.get()
            let sprite: SpriteComponent = agent.get()

            sprite.node.position = p.position

        }
    }   
}

With a setup in place like that, the core update loop would composed of calling updates on these systems.

func update(dt) {
    simulation.update(dt, agents)
    dislay.update(dt, agents)
}

By splitting up an object into components, and having systems work on components rather than an entity directly, you have will have much more flexibility over your architecture - GameplayKit in iOS has an ECS model built in. You can refer to the starting guide here for more information about why an ECS pattern is better suited here.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.