1
votes

I am trying to design a simple game engine for fun and educational purposes. I have a Game protocol that represents my game and Entity protocol that represents an entity (like the player or opponent). Finally, I have an EntityComponent protocol which implementations update an Entity. It looks like this in Swift:

protocol Game {
    var entities: [Entity] { get }
}

protocol Entity {
    var components: [EntityComponent] { get }
}

protocol EntityComponent {
    func update(_ entity: Entity, deltaTime seconds: TimeInterval)
}

I want my entity components to be generalized using entity they update. In Swift, I can use associatedtype:

protocol EntityComponent {
    associatedtype EntityType: Entity
    func update(_ entity: EntityType, deltaTime seconds: TimeInterval)
}

However, this would produce a compilation error for Entity protocol:

protocol Entity {
    var components: [EntityComponent] { get } // ERROR!
}

Protocol 'EntityComponent' can only be used as a generic constraint because it has Self or associated type requirements

This issue could be solved by defining type-erasure for EntityComponent protocol, and updating Entity like this:

protocol Entity {
    var components: [AnyEntityComponent<Self>] { get }
}

final class AnyEntityComponent<EntityType: Entity>: EntityComponent {
    init<T: EntityComponent>(_ component: T) where T.EntityType == EntityType {
        self.update = component.update
    }

    func update(_ entity: EntityType, deltaTime seconds: TimeInterval) {
        update(entity, seconds)
    }

    private let update: (EntityType, TimeInterval) -> Void
}

Unfortunately, that change in the Entity protocol produces another issue. This time in Game protocol:

protocol Game {
    var entities: [Entity] { get } // ERROR!
}

Protocol 'Entity' can only be used as a generic constraint because it has Self or associated type requirements

I am not sure how to fix this one, as I can't do it by defining type-erasure (as in case of EntityComponent).

I will appreciate any hints and ideas!

1

1 Answers

0
votes

It may help to remove the associated type requirement and use generics instead:

protocol EntityComponent {
    func update<T: Entity>(_ entity: T, deltaTime seconds: TimeInterval)
}