diff --git a/star-dash/star-dash.xcodeproj/project.pbxproj b/star-dash/star-dash.xcodeproj/project.pbxproj index 36f1b3fc..e9fc720d 100644 --- a/star-dash/star-dash.xcodeproj/project.pbxproj +++ b/star-dash/star-dash.xcodeproj/project.pbxproj @@ -41,6 +41,9 @@ 461148962BA1D53D0073E7E1 /* PositionSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461148952BA1D53D0073E7E1 /* PositionSystem.swift */; }; 461148982BA1E41F0073E7E1 /* PhysicsSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461148972BA1E41F0073E7E1 /* PhysicsSystem.swift */; }; 46B8C09A2BA328D900498705 /* GameEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46B8C0992BA328D900498705 /* GameEngine.swift */; }; + 46C3B1C02BB45C2700F10574 /* EntityFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B1BF2BB45C2700F10574 /* EntityFactory.swift */; }; + 46C3B1C22BB467BB00F10574 /* EntityManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B1C12BB467BB00F10574 /* EntityManagerInterface.swift */; }; + 46C3B1C42BB5055F00F10574 /* EntityBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B1C32BB5055F00F10574 /* EntityBuilder.swift */; }; 46D418112BA5C88B0091A38B /* Wall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418102BA5C88B0091A38B /* Wall.swift */; }; 46D418132BA5C9930091A38B /* Floor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418122BA5C9930091A38B /* Floor.swift */; }; 46D418162BA5CBD60091A38B /* Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418152BA5CBD60091A38B /* Collidable.swift */; }; @@ -188,6 +191,9 @@ 461148952BA1D53D0073E7E1 /* PositionSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionSystem.swift; sourceTree = ""; }; 461148972BA1E41F0073E7E1 /* PhysicsSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhysicsSystem.swift; sourceTree = ""; }; 46B8C0992BA328D900498705 /* GameEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameEngine.swift; sourceTree = ""; }; + 46C3B1BF2BB45C2700F10574 /* EntityFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityFactory.swift; sourceTree = ""; }; + 46C3B1C12BB467BB00F10574 /* EntityManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityManagerInterface.swift; sourceTree = ""; }; + 46C3B1C32BB5055F00F10574 /* EntityBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityBuilder.swift; sourceTree = ""; }; 46D418102BA5C88B0091A38B /* Wall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wall.swift; sourceTree = ""; }; 46D418122BA5C9930091A38B /* Floor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Floor.swift; sourceTree = ""; }; 46D418152BA5CBD60091A38B /* Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collidable.swift; sourceTree = ""; }; @@ -548,6 +554,9 @@ 4E8660602BA0961F0035530D /* GameEntities */, 4E630F262B9F7E770008F887 /* Entity.swift */, 4E630F382B9F943B0008F887 /* EntityManager.swift */, + 46C3B1BF2BB45C2700F10574 /* EntityFactory.swift */, + 46C3B1C12BB467BB00F10574 /* EntityManagerInterface.swift */, + 46C3B1C32BB5055F00F10574 /* EntityBuilder.swift */, ); path = Entities; sourceTree = ""; @@ -840,6 +849,7 @@ 1471B0A62BA6AC2D00878B14 /* PlayerAttackMonsterEvent.swift in Sources */, 4E8660642BA096600035530D /* Tool.swift in Sources */, 4E630F392B9F943B0008F887 /* EntityManager.swift in Sources */, + 46C3B1C02BB45C2700F10574 /* EntityFactory.swift in Sources */, 4E630F302B9F83DE0008F887 /* SpriteComponent.swift in Sources */, 46D4181A2BA5CDBD0091A38B /* CollisionHandler.swift in Sources */, 4E86605F2BA095E30035530D /* Collectible.swift in Sources */, @@ -874,6 +884,7 @@ 14E247952BA2CB480071FFC0 /* EventManager.swift in Sources */, 4EC561E32BB1E98400166DDC /* PlayerObstacleContactEvent.swift in Sources */, 4E630EF82B9F7E070008F887 /* SceneDelegate.swift in Sources */, + 46C3B1C22BB467BB00F10574 /* EntityManagerInterface.swift in Sources */, E69FDDE02BAD3DAD0089D5F3 /* PointsComponent.swift in Sources */, 14970F542BA8163300CC1E8A /* ScoreSystem.swift in Sources */, E64361112BA4C2CD003850FD /* SyncModule.swift in Sources */, @@ -885,6 +896,7 @@ 143AA3972BA4E0D9009C28E7 /* PickupCollectibleEvent.swift in Sources */, 46D418242BA5D5280091A38B /* Tool+Collidable.swift in Sources */, E64361142BA4C2CD003850FD /* CreationModule.swift in Sources */, + 46C3B1C42BB5055F00F10574 /* EntityBuilder.swift in Sources */, 4E59E2612BAB42FD007B3FA7 /* LevelPersistable.swift in Sources */, 460A200B2BB1D04F002597B8 /* AttackSystem.swift in Sources */, E6A745162BA057040080C1BE /* MTKRenderer.swift in Sources */, diff --git a/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift b/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift index 9493067d..7d71a696 100644 --- a/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift +++ b/star-dash/star-dash/Events/CommonEvents/JumpEvent.swift @@ -10,13 +10,11 @@ import Foundation class JumpEvent: Event { let timestamp: Date let entityId: EntityId - let jumpImpulse: CGVector init(on entityId: EntityId, by jumpImpulse: CGVector) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId self.jumpImpulse = jumpImpulse } - } diff --git a/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift b/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift index 39bdbe95..3366a821 100644 --- a/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift +++ b/star-dash/star-dash/Events/CommonEvents/MoveEvent.swift @@ -10,13 +10,11 @@ import Foundation class MoveEvent: Event { let timestamp: Date let entityId: EntityId - let toLeft: Bool init(on entityId: EntityId, toLeft: Bool) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId self.toLeft = toLeft } - } diff --git a/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift b/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift index 54c66eef..f1d79bee 100644 --- a/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift +++ b/star-dash/star-dash/Events/CommonEvents/RemoveEvent.swift @@ -12,7 +12,7 @@ class RemoveEvent: Event { let entityId: EntityId init(on entityId: EntityId) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId } } diff --git a/star-dash/star-dash/Events/CommonEvents/StopMovingEvent.swift b/star-dash/star-dash/Events/CommonEvents/StopMovingEvent.swift index 80ae13b1..550cce97 100644 --- a/star-dash/star-dash/Events/CommonEvents/StopMovingEvent.swift +++ b/star-dash/star-dash/Events/CommonEvents/StopMovingEvent.swift @@ -5,8 +5,7 @@ class StopMovingEvent: Event { let entityId: EntityId init(on entityId: EntityId) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId } - } diff --git a/star-dash/star-dash/Events/CommonEvents/TeleportEvent.swift b/star-dash/star-dash/Events/CommonEvents/TeleportEvent.swift index 304ab54b..32460c27 100644 --- a/star-dash/star-dash/Events/CommonEvents/TeleportEvent.swift +++ b/star-dash/star-dash/Events/CommonEvents/TeleportEvent.swift @@ -3,11 +3,10 @@ import Foundation class TeleportEvent: Event { let timestamp: Date let entityId: EntityId - let destination: CGPoint init(on entityId: EntityId, to destination: CGPoint) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId self.destination = destination } diff --git a/star-dash/star-dash/Events/ContactEvents/PlayerFloorContactEvent.swift b/star-dash/star-dash/Events/ContactEvents/PlayerFloorContactEvent.swift index 68b851dc..4fd82aa3 100644 --- a/star-dash/star-dash/Events/ContactEvents/PlayerFloorContactEvent.swift +++ b/star-dash/star-dash/Events/ContactEvents/PlayerFloorContactEvent.swift @@ -3,12 +3,12 @@ import CoreGraphics class PlayerFloorContactEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId let contactPoint: CGPoint - init(from playerEntityId: EntityId, at contactPoint: CGPoint) { + init(from playerId: EntityId, at contactPoint: CGPoint) { self.timestamp = Date.now - self.entityId = playerEntityId + self.playerId = playerId self.contactPoint = contactPoint } } diff --git a/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift b/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift index a4c51c68..a426aa68 100644 --- a/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift +++ b/star-dash/star-dash/Events/ContactEvents/PlayerMonsterContactEvent.swift @@ -8,14 +8,13 @@ import Foundation class PlayerMonsterContactEvent: Event { - var entityId: EntityId - let timestamp: Date + let playerId: EntityId let monsterId: EntityId init(from playerId: EntityId, on monsterId: EntityId) { self.timestamp = Date.now - self.entityId = playerId + self.playerId = playerId self.monsterId = monsterId } } diff --git a/star-dash/star-dash/Events/ContactEvents/PlayerObstacleContactEvent.swift b/star-dash/star-dash/Events/ContactEvents/PlayerObstacleContactEvent.swift index ed9bf629..9240be70 100644 --- a/star-dash/star-dash/Events/ContactEvents/PlayerObstacleContactEvent.swift +++ b/star-dash/star-dash/Events/ContactEvents/PlayerObstacleContactEvent.swift @@ -8,12 +8,12 @@ import Foundation class PlayerObstacleContactEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId let obstacleId: EntityId - init(from playerEntityId: EntityId, on obstacleEntityId: EntityId) { + init(from playerId: EntityId, on obstacleEntityId: EntityId) { self.timestamp = Date.now - self.entityId = playerEntityId + self.playerId = playerId self.obstacleId = obstacleEntityId } } diff --git a/star-dash/star-dash/Events/ContactEvents/PlayerToolContactEvent.swift b/star-dash/star-dash/Events/ContactEvents/PlayerToolContactEvent.swift index dc7bf8ed..a17b9aaa 100644 --- a/star-dash/star-dash/Events/ContactEvents/PlayerToolContactEvent.swift +++ b/star-dash/star-dash/Events/ContactEvents/PlayerToolContactEvent.swift @@ -8,12 +8,12 @@ import Foundation class PlayerToolContactEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId let toolId: EntityId - init(from playerEntityId: EntityId, on toolEntityId: EntityId) { + init(from playerId: EntityId, on toolEntityId: EntityId) { self.timestamp = Date.now - self.entityId = playerEntityId + self.playerId = playerId self.toolId = toolEntityId } } diff --git a/star-dash/star-dash/Events/Event.swift b/star-dash/star-dash/Events/Event.swift index e759c04d..e865d1b7 100644 --- a/star-dash/star-dash/Events/Event.swift +++ b/star-dash/star-dash/Events/Event.swift @@ -9,5 +9,4 @@ import Foundation protocol Event { var timestamp: Date { get } - var entityId: EntityId { get } } diff --git a/star-dash/star-dash/Events/EventManager.swift b/star-dash/star-dash/Events/EventManager.swift index d9cbe6de..1ecfa95c 100644 --- a/star-dash/star-dash/Events/EventManager.swift +++ b/star-dash/star-dash/Events/EventManager.swift @@ -37,7 +37,7 @@ class EventManager { listeners[key]?.append(listener) } - func emit(event: Event) { + private func emit(event: Event) { let key = ObjectIdentifier(type(of: event)) if let eventListeners = listeners[key] { for listener in eventListeners { diff --git a/star-dash/star-dash/Events/EventModifiable.swift b/star-dash/star-dash/Events/EventModifiable.swift index 4c92ad13..fc367db2 100644 --- a/star-dash/star-dash/Events/EventModifiable.swift +++ b/star-dash/star-dash/Events/EventModifiable.swift @@ -9,11 +9,8 @@ import Foundation /// EventModifiable represents instances with entities that can be modified by events through systems. protocol EventModifiable { - func component(ofType type: T.Type, ofEntity entityId: EntityId) -> T? - func entity(with entityId: EntityId) -> Entity? func system(ofType type: T.Type) -> T? - func add(entity: Entity) - func remove(entity: Entity) func add(event: Event) + func add(entity: Entity) func registerListener(for eventType: T.Type, listener: EventListener) } diff --git a/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift b/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift index c7291021..0901337d 100644 --- a/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift +++ b/star-dash/star-dash/Events/MonsterEvents/MonsterAttackPlayerEvent.swift @@ -9,12 +9,12 @@ import Foundation class MonsterAttackPlayerEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId let monsterId: EntityId - init(from monsterId: EntityId, on entityId: EntityId) { - timestamp = Date.now - self.entityId = entityId + init(from monsterId: EntityId, on playerId: EntityId) { + self.timestamp = Date.now + self.playerId = playerId self.monsterId = monsterId } } diff --git a/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift b/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift index 97782495..c6fd8171 100644 --- a/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift +++ b/star-dash/star-dash/Events/MonsterEvents/MonsterDeathEvent.swift @@ -9,10 +9,10 @@ import Foundation class MonsterDeathEvent: Event { let timestamp: Date - let entityId: EntityId + let monsterId: EntityId - init(on entityId: EntityId) { + init(on monsterId: EntityId) { self.timestamp = Date.now - self.entityId = entityId + self.monsterId = monsterId } } diff --git a/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift b/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift index 97d28c57..ec51db2a 100644 --- a/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift +++ b/star-dash/star-dash/Events/MonsterEvents/PlayerAttackMonsterEvent.swift @@ -9,10 +9,12 @@ import Foundation class PlayerAttackMonsterEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId + let monsterId: EntityId - init(on entityId: EntityId) { - timestamp = Date.now - self.entityId = entityId + init(from playerId: EntityId, on monsterId: EntityId) { + self.timestamp = Date.now + self.playerId = playerId + self.monsterId = monsterId } } diff --git a/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift b/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift index 53e822b9..bcae6eef 100644 --- a/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift +++ b/star-dash/star-dash/Events/PlayerEvents/PickupCollectibleEvent.swift @@ -9,13 +9,12 @@ import Foundation class PickupCollectibleEvent: Event { let timestamp: Date - let entityId: EntityId - + let playerId: EntityId let collectibleEntityId: EntityId - init(by entityId: EntityId, collectibleEntityId: EntityId) { - timestamp = Date.now - self.entityId = entityId + init(by playerId: EntityId, collectibleEntityId: EntityId) { + self.timestamp = Date.now + self.playerId = playerId self.collectibleEntityId = collectibleEntityId } } diff --git a/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift b/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift index 2d5a8084..46cb4a1a 100644 --- a/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift +++ b/star-dash/star-dash/Events/PlayerEvents/PlayerDeathEvent.swift @@ -9,10 +9,10 @@ import Foundation class PlayerDeathEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId - init(on entityId: EntityId) { - timestamp = Date.now - self.entityId = entityId + init(on playerId: EntityId) { + self.timestamp = Date.now + self.playerId = playerId } } diff --git a/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift b/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift index fbf5fd9f..0333fde6 100644 --- a/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift +++ b/star-dash/star-dash/Events/PlayerEvents/RespawnEvent.swift @@ -10,11 +10,10 @@ import Foundation class RespawnEvent: Event { let timestamp: Date let entityId: EntityId - let newPosition: CGPoint init(on entityId: EntityId, to newPosition: CGPoint) { - timestamp = Date.now + self.timestamp = Date.now self.entityId = entityId self.newPosition = newPosition } diff --git a/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift b/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift index cc6dea1c..92ea8a63 100644 --- a/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift +++ b/star-dash/star-dash/Events/ToolEvents/UseGrappleHookEvent.swift @@ -9,10 +9,10 @@ import Foundation class UseGrappleHookEvent: Event { let timestamp: Date - let entityId: EntityId + let playerId: EntityId - init(from playerEntityId: EntityId) { + init(from playerId: EntityId) { self.timestamp = Date.now - entityId = playerEntityId + self.playerId = playerId } } diff --git a/star-dash/star-dash/GameEngine/Components/PlayerComponent.swift b/star-dash/star-dash/GameEngine/Components/PlayerComponent.swift index 7dd6fef4..f889525e 100644 --- a/star-dash/star-dash/GameEngine/Components/PlayerComponent.swift +++ b/star-dash/star-dash/GameEngine/Components/PlayerComponent.swift @@ -1,7 +1,6 @@ import Foundation class PlayerComponent: Component { - let playerIndex: Int var canJump = true var canMove = true diff --git a/star-dash/star-dash/GameEngine/Entities/Entity.swift b/star-dash/star-dash/GameEngine/Entities/Entity.swift index 2124ea02..c2247ea4 100644 --- a/star-dash/star-dash/GameEngine/Entities/Entity.swift +++ b/star-dash/star-dash/GameEngine/Entities/Entity.swift @@ -11,6 +11,4 @@ typealias EntityId = UUID protocol Entity { var id: EntityId { get } - - func setUpAndAdd(to: EntityManager) } diff --git a/star-dash/star-dash/GameEngine/Entities/EntityBuilder.swift b/star-dash/star-dash/GameEngine/Entities/EntityBuilder.swift new file mode 100644 index 00000000..9ef09f77 --- /dev/null +++ b/star-dash/star-dash/GameEngine/Entities/EntityBuilder.swift @@ -0,0 +1,153 @@ +// +// EntityBuilder.swift +// star-dash +// +// Created by Ho Jun Hao on 28/3/24. +// + +import Foundation + +class EntityBuilder { + private var components: [ObjectIdentifier: Component] = [:] + private let entity: Entity + private let entityId: EntityId + private let entityManager: EntityManagerInterface + + init(entity: Entity, entityManager: EntityManagerInterface) { + self.entityManager = entityManager + self.entity = entity + self.entityId = entity.id + } + + func withPosition(at position: CGPoint) -> Self { + let componentType = ObjectIdentifier(PositionComponent.self) + let component = PositionComponent(entityId: entityId, position: position, rotation: .zero) + + self.components[componentType] = component + return self + } + + func withPlayer(playerIndex: Int) -> Self { + let componentType = ObjectIdentifier(PlayerComponent.self) + let component = PlayerComponent(entityId: entityId, playerIndex: playerIndex) + + self.components[componentType] = component + return self + } + + func withHealth(health: Int) -> Self { + let componentType = ObjectIdentifier(HealthComponent.self) + let component = HealthComponent(entityId: entityId, health: health) + + self.components[componentType] = component + return self + } + + func withSprite(image: String, textureSet: TextureSet?, textureAtlas: String?, size: CGSize?) -> Self { + let componentType = ObjectIdentifier(SpriteComponent.self) + let component = SpriteComponent(entityId: entityId, + image: image, + textureSet: textureSet, + textureAtlas: textureAtlas, + size: size) + + self.components[componentType] = component + return self + } + + func withScore(score: Int) -> Self { + let componentType = ObjectIdentifier(ScoreComponent.self) + let component = ScoreComponent(entityId: entityId, score: score) + + self.components[componentType] = component + return self + } + + func withPoint(points: Int) -> Self { + let componentType = ObjectIdentifier(PointsComponent.self) + let component = PointsComponent(entityId: entityId, points: points) + + self.components[componentType] = component + return self + } + + func withPhysics(size: CGSize) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + let component = PhysicsComponent(entityId: entityId, size: size) + + self.components[componentType] = component + return self + } + + func configureCategoryBitMask(_ categoryBitMask: UInt32) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.categoryBitMask = categoryBitMask + return self + } + + func configureContactTestMask(_ contactTestMask: UInt32) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.contactTestMask = contactTestMask + return self + } + + func configureCollisionBitMask(_ collisionBitMask: UInt32) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.collisionBitMask = collisionBitMask + return self + } + + func configureAffectedByGravity(_ affectedByGravity: Bool) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.affectedByGravity = affectedByGravity + return self + } + + func configureRestitution(_ restitution: CGFloat) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.restitution = restitution + return self + } + + func configureIsDynamic(_ isDynamic: Bool) -> Self { + let componentType = ObjectIdentifier(PhysicsComponent.self) + + guard let physicsComponent = self.components[componentType] as? PhysicsComponent else { + return self + } + + physicsComponent.isDynamic = isDynamic + return self + } + + func addToGame() { + self.entityManager.add(entity: self.entity) + + self.components.values.forEach { self.entityManager.add(component: $0) } + } +} diff --git a/star-dash/star-dash/GameEngine/Entities/EntityFactory.swift b/star-dash/star-dash/GameEngine/Entities/EntityFactory.swift new file mode 100644 index 00000000..3899cebe --- /dev/null +++ b/star-dash/star-dash/GameEngine/Entities/EntityFactory.swift @@ -0,0 +1,136 @@ +// +// EntityFactory.swift +// star-dash +// +// Created by Ho Jun Hao on 27/3/24. +// + +import Foundation + +struct EntityFactory { + static func createAndAddPlayer(to entityManager: EntityManagerInterface, + playerIndex: Int, + position: CGPoint, + sprite: PlayerSprite) { + let playerBuilder = EntityBuilder(entity: Player(id: UUID()), entityManager: entityManager) + + playerBuilder + .withPlayer(playerIndex: playerIndex) + .withPosition(at: position) + .withHealth(health: GameConstants.InitialHealth.player) + .withSprite(image: SpriteConstants.PlayerRedNose, + textureSet: SpriteConstants.PlayerRedNoseTexture, + textureAtlas: nil, + size: PhysicsConstants.Dimensions.player) + .withScore(score: 0) + .withPhysics(size: PhysicsConstants.Dimensions.player) + .configureCategoryBitMask(PhysicsConstants.CollisionCategory.player) + .configureContactTestMask(PhysicsConstants.ContactMask.player) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.player) + .configureAffectedByGravity(true) + .configureRestitution(0.0) + .addToGame() + } + + static func createAndAddMonster(to entityManager: EntityManagerInterface, + position: CGPoint, + health: Int, + sprite: String, + size: CGSize) { + let monsterBuilder = EntityBuilder(entity: Monster(id: UUID()), entityManager: entityManager) + + monsterBuilder + .withPosition(at: position) + .withHealth(health: health) + .withSprite(image: sprite, + textureSet: nil, + textureAtlas: nil, + size: size) + .withPhysics(size: PhysicsConstants.Dimensions.monster) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.monster) + .addToGame() + } + + static func createAndAddCollectible(to entityManager: EntityManagerInterface, + position: CGPoint, + sprite: String, + points: Int, + size: CGSize) { + let collectibleBuilder = EntityBuilder(entity: Collectible(id: UUID()), entityManager: entityManager) + + collectibleBuilder + .withPosition(at: position) + .withSprite(image: sprite, + textureSet: nil, + textureAtlas: nil, + size: size) + .withPoint(points: points) + .withPhysics(size: size) + .configureCategoryBitMask(PhysicsConstants.CollisionCategory.collectible) + .configureContactTestMask(PhysicsConstants.ContactMask.collectible) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.collectible) + .configureAffectedByGravity(false) + .configureIsDynamic(false) + .addToGame() + } + + static func createAndAddObstacle(to entityManager: EntityManagerInterface, + position: CGPoint, + sprite: String, + size: CGSize) { + let obstacleBuilder = EntityBuilder(entity: Obstacle(id: UUID()), entityManager: entityManager) + + obstacleBuilder + .withPosition(at: position) + .withSprite(image: sprite, + textureSet: nil, + textureAtlas: nil, + size: size) + .withPhysics(size: size) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.obstacle) + .configureIsDynamic(false) + .addToGame() + } + + static func createAndAddTool(to entityManager: EntityManagerInterface, + position: CGPoint, + sprite: String, + size: CGSize) { + let toolBuilder = EntityBuilder(entity: Tool(id: UUID()), entityManager: entityManager) + + toolBuilder + .withPosition(at: position) + .withSprite(image: sprite, + textureSet: nil, + textureAtlas: nil, + size: size) + .withPhysics(size: size) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.tool) + .configureIsDynamic(false) + .addToGame() + } + + static func createAndAddWall(to entityManager: EntityManagerInterface, position: CGPoint, size: CGSize) { + let wallBuilder = EntityBuilder(entity: Wall(id: UUID()), entityManager: entityManager) + + wallBuilder + .withPosition(at: position) + .withPhysics(size: size) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.wall) + .addToGame() + } + + static func createAndAddFloor(to entityManager: EntityManagerInterface, position: CGPoint, size: CGSize) { + let floorBuilder = EntityBuilder(entity: Floor(id: UUID()), entityManager: entityManager) + + floorBuilder + .withPosition(at: position) + .withPhysics(size: size) + .configureCategoryBitMask(PhysicsConstants.CollisionCategory.floor) + .configureContactTestMask(PhysicsConstants.ContactMask.floor) + .configureCollisionBitMask(PhysicsConstants.CollisionMask.floor) + .configureIsDynamic(false) + .configureRestitution(0.0) + .addToGame() + } +} diff --git a/star-dash/star-dash/GameEngine/Entities/EntityManager.swift b/star-dash/star-dash/GameEngine/Entities/EntityManager.swift index eb45f234..61b61839 100644 --- a/star-dash/star-dash/GameEngine/Entities/EntityManager.swift +++ b/star-dash/star-dash/GameEngine/Entities/EntityManager.swift @@ -13,9 +13,9 @@ typealias EntityMap = [EntityId: Entity] typealias EntityComponentMap = [EntityId: ComponentSet] class EntityManager { - var componentMap: ComponentMap - var entityMap: EntityMap - var entityComponentMap: EntityComponentMap + private var componentMap: ComponentMap + private var entityMap: EntityMap + private var entityComponentMap: EntityComponentMap init(componentMap: ComponentMap, entityMap: EntityMap, entityComponentMap: EntityComponentMap) { self.componentMap = componentMap @@ -27,6 +27,10 @@ class EntityManager { self.init(componentMap: .init(), entityMap: .init(), entityComponentMap: .init()) } + var entities: [Entity] { + Array(entityMap.values) + } + func add(component: Component) { guard self.componentMap[component.id] == nil else { return @@ -45,15 +49,26 @@ class EntityManager { func remove(entity: Entity) { entityMap[entity.id] = nil + guard let componentIds = entityComponentMap[entity.id] else { return } + for componentId in componentIds { componentMap[componentId] = nil } + entityComponentMap[entity.id] = nil } + func remove(entityId: EntityId) { + guard let entity = entity(with: entityId) else { + return + } + + remove(entity: entity) + } + func entity(with entityId: EntityId) -> Entity? { entityMap[entityId] } @@ -78,4 +93,8 @@ class EntityManager { return componentMap[componentId] as? T } + + func components(ofType type: T.Type) -> [T] { + componentMap.values.compactMap({ $0 as? T }) + } } diff --git a/star-dash/star-dash/GameEngine/Entities/EntityManagerInterface.swift b/star-dash/star-dash/GameEngine/Entities/EntityManagerInterface.swift new file mode 100644 index 00000000..16d24e25 --- /dev/null +++ b/star-dash/star-dash/GameEngine/Entities/EntityManagerInterface.swift @@ -0,0 +1,11 @@ +// +// EntityManagerInterface.swift +// star-dash +// +// Created by Ho Jun Hao on 27/3/24. +// + +protocol EntityManagerInterface { + func add(component: Component) + func add(entity: Entity) +} diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Collectible.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Collectible.swift index b7544059..6367eb52 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Collectible.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Collectible.swift @@ -9,53 +9,8 @@ import Foundation class Collectible: Entity { let id: EntityId - private let position: CGPoint - private let sprite: String - private let points: Int - private let size: CGSize - init(id: EntityId, position: CGPoint, sprite: String, points: Int, size: CGSize) { + init(id: EntityId) { self.id = id - self.position = position - self.sprite = sprite - self.points = points - self.size = size - } - - convenience init(position: CGPoint, sprite: String, points: Int, size: CGSize) { - self.init(id: UUID(), position: position, sprite: sprite, points: points, size: size) - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let physicsComponent = PhysicsComponent(entityId: self.id, size: self.size) - physicsComponent.affectedByGravity = false - physicsComponent.isDynamic = false - physicsComponent.categoryBitMask = PhysicsConstants.CollisionCategory.collectible - physicsComponent.contactTestMask = PhysicsConstants.ContactMask.collectible - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.collectible - let spriteComponent = SpriteComponent( - entityId: self.id, - image: sprite, - textureSet: nil, - textureAtlas: nil, - size: size - ) - let pointsComponent = PointsComponent(entityId: self.id, points: points) - - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: physicsComponent) - to.add(component: spriteComponent) - to.add(component: pointsComponent) - } - - static func createStarCollectible(position: CGPoint) -> Collectible { - Collectible( - position: position, - sprite: SpriteConstants.star, - points: EntityConstants.StarCollectible.points, - size: EntityConstants.StarCollectible.size - ) } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Floor.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Floor.swift index 1d4f00a8..4c5ef651 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Floor.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Floor.swift @@ -9,29 +9,8 @@ import Foundation class Floor: Entity { let id: EntityId - private let position: CGPoint - init(id: EntityId, position: CGPoint) { + init(id: EntityId) { self.id = id - self.position = position - } - - init(position: CGPoint) { - self.id = UUID() - self.position = position - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let physicsComponent = PhysicsComponent(entityId: self.id, size: CGSize(width: 8_000, height: 10)) - physicsComponent.restitution = 0.0 - physicsComponent.isDynamic = false - physicsComponent.categoryBitMask = PhysicsConstants.CollisionCategory.floor - physicsComponent.contactTestMask = PhysicsConstants.ContactMask.floor - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.floor - - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: physicsComponent) } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Monster.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Monster.swift index 315c59af..a2ff92e7 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Monster.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Monster.swift @@ -9,37 +9,8 @@ import Foundation class Monster: Entity { let id: EntityId - private let position: CGPoint - private let health: Int - private let sprite: String - private let size: CGSize - init(id: EntityId, position: CGPoint, health: Int, sprite: String, size: CGSize) { - self.id = id - self.position = position - self.health = health - self.sprite = sprite - self.size = size - } - - convenience init(position: CGPoint, health: Int, sprite: String, size: CGSize) { - self.init(id: UUID(), position: position, health: health, sprite: sprite, size: size) - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let healthComponent = HealthComponent(entityId: self.id, health: self.health) - let physicsComponent = PhysicsComponent(entityId: self.id, size: PhysicsConstants.Dimensions.monster) - let spriteComponent = SpriteComponent(entityId: self.id, - image: self.sprite, - textureSet: nil, - textureAtlas: nil, - size: self.size) - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.monster - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: healthComponent) - to.add(component: physicsComponent) - to.add(component: spriteComponent) + init(id: EntityId) { + self.id = id } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Obstacle.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Obstacle.swift index 69135deb..323b466d 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Obstacle.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Obstacle.swift @@ -9,33 +9,8 @@ import Foundation class Obstacle: Entity { let id: EntityId - private let position: CGPoint - private let sprite: String - private let size: CGSize - init(id: EntityId, position: CGPoint, sprite: String, size: CGSize) { - self.id = id - self.position = position - self.sprite = sprite - self.size = size - } - convenience init(position: CGPoint, sprite: String, size: CGSize) { - self.init(id: UUID(), position: position, sprite: sprite, size: size) - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let physicsComponent = PhysicsComponent(entityId: self.id, size: self.size) - let spriteComponent = SpriteComponent(entityId: self.id, - image: self.sprite, - textureSet: nil, - textureAtlas: nil, - size: self.size) - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.obstacle - physicsComponent.isDynamic = false - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: physicsComponent) - to.add(component: spriteComponent) + init(id: EntityId) { + self.id = id } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Player.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Player.swift index 2a352603..ffbc8df5 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Player.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Player.swift @@ -9,46 +9,8 @@ import Foundation class Player: Entity { let id: EntityId - private let playerIndex: Int - private let position: CGPoint - private let playerSprite: PlayerSprite - init(id: EntityId, playerIndex: Int, position: CGPoint, playerSprite: PlayerSprite) { + init(id: EntityId) { self.id = id - self.playerIndex = playerIndex - self.position = position - self.playerSprite = playerSprite - } - - convenience init(playerIndex: Int, position: CGPoint, playerSprite: PlayerSprite) { - self.init(id: UUID(), playerIndex: playerIndex, position: position, playerSprite: playerSprite) - } - - func setUpAndAdd(to: EntityManager) { - let playerComponent = PlayerComponent(entityId: self.id, playerIndex: playerIndex) - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let healthComponent = HealthComponent(entityId: self.id, health: GameConstants.InitialHealth.player) - let physicsComponent = PhysicsComponent(entityId: self.id, size: PhysicsConstants.Dimensions.player) - physicsComponent.categoryBitMask = PhysicsConstants.CollisionCategory.player - physicsComponent.contactTestMask = PhysicsConstants.ContactMask.player - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.player - physicsComponent.affectedByGravity = true - physicsComponent.restitution = 0.0 - let spriteComponent = SpriteComponent( - entityId: self.id, - image: SpriteConstants.PlayerRedNose, - textureSet: SpriteConstants.PlayerRedNoseTexture, - textureAtlas: nil, - size: PhysicsConstants.Dimensions.player - ) - let scoreComponent = ScoreComponent(entityId: self.id, score: 0) - - to.add(entity: self) - to.add(component: playerComponent) - to.add(component: positionComponent) - to.add(component: healthComponent) - to.add(component: physicsComponent) - to.add(component: spriteComponent) - to.add(component: scoreComponent) } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Tool.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Tool.swift index 2662953a..19d91d35 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Tool.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Tool.swift @@ -9,34 +9,8 @@ import Foundation class Tool: Entity { let id: EntityId - private let position: CGPoint - private let sprite: String - private let size: CGSize - init(id: EntityId, position: CGPoint, sprite: String, size: CGSize) { - self.id = id - self.position = position - self.sprite = sprite - self.size = size - } - - convenience init(position: CGPoint, sprite: String, size: CGSize) { - self.init(id: UUID(), position: position, sprite: sprite, size: size) - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let physicsComponent = PhysicsComponent(entityId: self.id, size: PhysicsConstants.Dimensions.tool) - let spriteComponent = SpriteComponent(entityId: self.id, - image: self.sprite, - textureSet: nil, - textureAtlas: nil, - size: self.size) - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.tool - physicsComponent.isDynamic = false - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: physicsComponent) - to.add(component: spriteComponent) + init(id: EntityId) { + self.id = id } } diff --git a/star-dash/star-dash/GameEngine/Entities/GameEntities/Wall.swift b/star-dash/star-dash/GameEngine/Entities/GameEntities/Wall.swift index c5fe61a4..e517b247 100644 --- a/star-dash/star-dash/GameEngine/Entities/GameEntities/Wall.swift +++ b/star-dash/star-dash/GameEngine/Entities/GameEntities/Wall.swift @@ -9,24 +9,8 @@ import Foundation class Wall: Entity { let id: EntityId - private let position: CGPoint - init(id: EntityId, position: CGPoint) { + init(id: EntityId) { self.id = id - self.position = position - } - - convenience init(position: CGPoint) { - self.init(id: UUID(), position: position) - } - - func setUpAndAdd(to: EntityManager) { - let positionComponent = PositionComponent(entityId: self.id, position: self.position, rotation: .zero) - let physicsComponent = PhysicsComponent(entityId: self.id, size: PhysicsConstants.Dimensions.wall) - physicsComponent.collisionBitMask = PhysicsConstants.CollisionMask.wall - - to.add(entity: self) - to.add(component: positionComponent) - to.add(component: physicsComponent) } } diff --git a/star-dash/star-dash/GameEngine/GameEngine.swift b/star-dash/star-dash/GameEngine/GameEngine.swift index 5acf3caa..a235d659 100644 --- a/star-dash/star-dash/GameEngine/GameEngine.swift +++ b/star-dash/star-dash/GameEngine/GameEngine.swift @@ -9,7 +9,7 @@ import Foundation class GameEngine { private let systemManager: SystemManager - let entityManager: EntityManager // TODO: Set to private + private let entityManager: EntityManager private let eventManager: EventManager init() { @@ -95,39 +95,22 @@ class GameEngine { } extension GameEngine: EventModifiable { - func entity(with entityId: EntityId) -> Entity? { - entityManager.entity(with: entityId) - } - func system(ofType type: T.Type) -> T? { systemManager.system(ofType: type) } - func component(ofType type: T.Type, ofEntity entityId: EntityId) -> T? { - entityManager.component(ofType: type, of: entityId) - } - - func add(entity: Entity) { - entityManager.add(entity: entity) - } - func add(event: Event) { eventManager.add(event: event) } - func remove(entity: Entity) { - entityManager.remove(entity: entity) - } - func registerListener(for eventType: T.Type, listener: EventListener) { eventManager.registerListener(for: eventType, listener: listener) } } extension GameEngine: EntitySyncInterface { - var entities: [Entity] { - Array(entityManager.entityMap.values) + entityManager.entities } func component(ofType type: T.Type, of entityId: EntityId) -> T? { @@ -135,6 +118,16 @@ extension GameEngine: EntitySyncInterface { } func entity(of entityId: EntityId) -> Entity? { - entityManager.entityMap[entityId] + entityManager.entity(with: entityId) + } +} + +extension GameEngine: EntityManagerInterface { + func add(entity: Entity) { + entityManager.add(entity: entity) + } + + func add(component: Component) { + entityManager.add(component: component) } } diff --git a/star-dash/star-dash/GameEngine/Systems/AttackSystem.swift b/star-dash/star-dash/GameEngine/Systems/AttackSystem.swift index 60220e64..91f2a86f 100644 --- a/star-dash/star-dash/GameEngine/Systems/AttackSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/AttackSystem.swift @@ -44,21 +44,21 @@ class AttackSystem: System { } guard let monsterPosition = positionSystem.getPosition(of: event.monsterId), - let playerPosition = positionSystem.getPosition(of: event.entityId) else { + let playerPosition = positionSystem.getPosition(of: event.playerId) else { return } - healthSystem.applyHealthChange(to: event.entityId, healthChange: GameConstants.HealthChange.attackedByMonster) + healthSystem.applyHealthChange(to: event.playerId, healthChange: GameConstants.HealthChange.attackedByMonster) let isMonsterToRight = monsterPosition.x > playerPosition.x let impulse = isMonsterToRight ? GameConstants.DamageImpulse.attackedByMonster * -1 : GameConstants.DamageImpulse.attackedByMonster - physicsSystem.applyImpulse(to: event.entityId, impulse: impulse) + physicsSystem.applyImpulse(to: event.playerId, impulse: impulse) - if !healthSystem.hasHealth(for: event.entityId) { - dispatcher?.add(event: PlayerDeathEvent(on: event.entityId)) + if !healthSystem.hasHealth(for: event.playerId) { + dispatcher?.add(event: PlayerDeathEvent(on: event.playerId)) } } @@ -68,11 +68,11 @@ class AttackSystem: System { return } - healthSystem.applyHealthChange(to: event.entityId, healthChange: GameConstants.HealthChange.attackedByPlayer) - physicSystem.applyImpulse(to: event.entityId, impulse: GameConstants.AttackImpulse.attackedByPlayer) + healthSystem.applyHealthChange(to: event.monsterId, healthChange: GameConstants.HealthChange.attackedByPlayer) + physicSystem.applyImpulse(to: event.monsterId, impulse: GameConstants.AttackImpulse.attackedByPlayer) - if !healthSystem.hasHealth(for: event.entityId) { - dispatcher?.add(event: MonsterDeathEvent(on: event.entityId)) + if !healthSystem.hasHealth(for: event.monsterId) { + dispatcher?.add(event: MonsterDeathEvent(on: event.monsterId)) } } } diff --git a/star-dash/star-dash/GameEngine/Systems/CollisionSystem.swift b/star-dash/star-dash/GameEngine/Systems/CollisionSystem.swift index f214ce63..51a1864d 100644 --- a/star-dash/star-dash/GameEngine/Systems/CollisionSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/CollisionSystem.swift @@ -55,15 +55,12 @@ class CollisionSystem: System { } private func handleRemoveEvent(event: RemoveEvent) { - guard let entity = dispatcher?.entity(with: event.entityId) else { - return - } - dispatcher?.remove(entity: entity) + entityManager.remove(entityId: event.entityId) } private func handlePlayerFloorContactEvent(event: PlayerFloorContactEvent) { - guard let positionComponent = entityManager.component(ofType: PositionComponent.self, of: event.entityId), - let playerComponent = entityManager.component(ofType: PlayerComponent.self, of: event.entityId), + guard let positionComponent = entityManager.component(ofType: PositionComponent.self, of: event.playerId), + let playerComponent = entityManager.component(ofType: PlayerComponent.self, of: event.playerId), positionComponent.position.y > event.contactPoint.y else { return } @@ -78,12 +75,12 @@ class CollisionSystem: System { return } - guard let playerPosition = positionSystem.getPosition(of: event.entityId), + guard let playerPosition = positionSystem.getPosition(of: event.playerId), let monsterPosition = positionSystem.getPosition(of: event.monsterId) else { return } - guard let playerSize = physicsSystem.getSize(of: event.entityId), + guard let playerSize = physicsSystem.getSize(of: event.playerId), let monsterSize = physicsSystem.getSize(of: event.monsterId) else { return } @@ -91,17 +88,17 @@ class CollisionSystem: System { let isPlayerAbove = playerPosition.y - (playerSize.height / 2) >= monsterPosition.y + (monsterSize.height / 2) if isPlayerAbove { - dispatcher?.add(event: PlayerAttackMonsterEvent(on: event.monsterId)) + dispatcher?.add(event: PlayerAttackMonsterEvent(from: event.playerId, on: event.monsterId)) } else { - dispatcher?.add(event: MonsterAttackPlayerEvent(from: event.monsterId, on: event.entityId)) + dispatcher?.add(event: MonsterAttackPlayerEvent(from: event.monsterId, on: event.playerId)) } } private func handlePlayerObstacleContactEvent(event: PlayerObstacleContactEvent) { - guard let playerPositionComponent = entityManager.component(ofType: PositionComponent.self, of: event.entityId), + guard let playerPositionComponent = entityManager.component(ofType: PositionComponent.self, of: event.playerId), let obstaclePositionComponent = entityManager.component(ofType: PositionComponent.self, of: event.obstacleId), - let playerComponent = entityManager.component(ofType: PlayerComponent.self, of: event.entityId), + let playerComponent = entityManager.component(ofType: PlayerComponent.self, of: event.playerId), playerPositionComponent.position.y > obstaclePositionComponent.position.y else { return } diff --git a/star-dash/star-dash/GameEngine/Systems/MonsterSystem.swift b/star-dash/star-dash/GameEngine/Systems/MonsterSystem.swift index 3747503b..168bede5 100644 --- a/star-dash/star-dash/GameEngine/Systems/MonsterSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/MonsterSystem.swift @@ -31,6 +31,6 @@ class MonsterSystem: System { } private func handleMonsterDeathEvent(event: MonsterDeathEvent) { - dispatcher?.add(event: RemoveEvent(on: event.entityId)) + dispatcher?.add(event: RemoveEvent(on: event.monsterId)) } } diff --git a/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift b/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift index cd828a87..3711f81f 100644 --- a/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift @@ -21,7 +21,7 @@ class PhysicsSystem: System { } func update(by deltaTime: TimeInterval) { - let physicsComponents = entityManager.componentMap.values.compactMap({ $0 as? PhysicsComponent }) + let physicsComponents = entityManager.components(ofType: PhysicsComponent.self) for physicsComponent in physicsComponents { physicsComponent.force = .zero @@ -36,14 +36,6 @@ class PhysicsSystem: System { return physicsComponent.velocity != .zero } - func applyForce(to entityId: EntityId, newForce: CGVector) { - guard let physicsComponent = getPhysicsComponent(of: entityId) else { - return - } - - physicsComponent.force += newForce - } - func applyImpulse(to entityId: EntityId, impulse: CGVector) { guard let physicsComponent = getPhysicsComponent(of: entityId) else { return diff --git a/star-dash/star-dash/GameEngine/Systems/ScoreSystem.swift b/star-dash/star-dash/GameEngine/Systems/ScoreSystem.swift index 3b8769ab..27b86f9f 100644 --- a/star-dash/star-dash/GameEngine/Systems/ScoreSystem.swift +++ b/star-dash/star-dash/GameEngine/Systems/ScoreSystem.swift @@ -52,7 +52,7 @@ class ScoreSystem: System { return } - applyScoreChange(to: event.entityId, scoreChange: pointsComponent.points) + applyScoreChange(to: event.playerId, scoreChange: pointsComponent.points) dispatcher?.add(event: RemoveEvent(on: event.collectibleEntityId)) } diff --git a/star-dash/star-dash/Model/Level.swift b/star-dash/star-dash/Model/Level.swift index db8e47de..4c862f0c 100644 --- a/star-dash/star-dash/Model/Level.swift +++ b/star-dash/star-dash/Model/Level.swift @@ -6,16 +6,18 @@ // import Foundation + struct Level { var name: String var entities: [Entity] - init(name: String, entities: [Entity]) { - self.name = name - self.entities = entities - } - init(levelPersistable: LevelPersistable, entityPersistables: [EntityPersistable]) { - let entities = entityPersistables.compactMap { $0.toEntity() } - self.init(name: levelPersistable.name, entities: entities) - } +// init(name: String, entities: [Entity]) { +// self.name = name +// self.entities = entities +// } + +// init(levelPersistable: LevelPersistable, entityPersistables: [EntityPersistable]) { +// let entities = entityPersistables.compactMap { $0.toEntity() } +// self.init(name: levelPersistable.name, entities: entities) +// } } diff --git a/star-dash/star-dash/Persistence/CollectibleEntityPersistable.swift b/star-dash/star-dash/Persistence/CollectibleEntityPersistable.swift index f5e4d63b..fead7347 100644 --- a/star-dash/star-dash/Persistence/CollectibleEntityPersistable.swift +++ b/star-dash/star-dash/Persistence/CollectibleEntityPersistable.swift @@ -6,14 +6,19 @@ // import Foundation -struct CollectibleEntityPersistable: Codable, EntityPersistable { +struct CollectibleEntityPersistable: Codable, EntityPersistable { var levelId: Int64 var position: CGPoint var sprite: String var points: Int var size: CGSize - func toEntity() -> Entity { - Collectible(position: self.position, sprite: self.sprite, points: self.points, size: self.size) + + func addTo(_ entityManager: EntityManagerInterface) { + EntityFactory.createAndAddCollectible(to: entityManager, + position: self.position, + sprite: self.sprite, + points: self.points, + size: self.size) } } diff --git a/star-dash/star-dash/Persistence/EntityPersistable.swift b/star-dash/star-dash/Persistence/EntityPersistable.swift index 2d564b3f..efde7eaf 100644 --- a/star-dash/star-dash/Persistence/EntityPersistable.swift +++ b/star-dash/star-dash/Persistence/EntityPersistable.swift @@ -8,5 +8,5 @@ import Foundation protocol EntityPersistable: Codable { - func toEntity() -> Entity + func addTo(_ entityManager: EntityManagerInterface) } diff --git a/star-dash/star-dash/Persistence/LevelPersistable.swift b/star-dash/star-dash/Persistence/LevelPersistable.swift index ff629912..bf119f84 100644 --- a/star-dash/star-dash/Persistence/LevelPersistable.swift +++ b/star-dash/star-dash/Persistence/LevelPersistable.swift @@ -6,9 +6,9 @@ // import Foundation + struct LevelPersistable: Codable { var id: Int64 var name: String var size: CGSize - } diff --git a/star-dash/star-dash/Persistence/MonsterEntityPersistable.swift b/star-dash/star-dash/Persistence/MonsterEntityPersistable.swift index 7eb012ab..d8a96b20 100644 --- a/star-dash/star-dash/Persistence/MonsterEntityPersistable.swift +++ b/star-dash/star-dash/Persistence/MonsterEntityPersistable.swift @@ -8,14 +8,17 @@ import Foundation struct MonsterEntityPersistable: Codable, EntityPersistable { - var levelId: Int64 var position: CGPoint var sprite: String var health: Int var size: CGSize - func toEntity() -> Entity { - Monster(position: self.position, health: self.health, sprite: self.sprite, size: self.size) + func addTo(_ entityManager: EntityManagerInterface) { + EntityFactory.createAndAddMonster(to: entityManager, + position: self.position, + health: self.health, + sprite: self.sprite, + size: self.size) } } diff --git a/star-dash/star-dash/Persistence/ObstacleEntityPersistable.swift b/star-dash/star-dash/Persistence/ObstacleEntityPersistable.swift index e2e0d723..fbfd5228 100644 --- a/star-dash/star-dash/Persistence/ObstacleEntityPersistable.swift +++ b/star-dash/star-dash/Persistence/ObstacleEntityPersistable.swift @@ -6,13 +6,17 @@ // import Foundation -struct ObstacleEntityPersistable: Codable, EntityPersistable { +struct ObstacleEntityPersistable: Codable, EntityPersistable { var levelId: Int64 var position: CGPoint var sprite: String var size: CGSize - func toEntity() -> Entity { - Obstacle(position: self.position, sprite: self.sprite, size: self.size) + + func addTo(_ entityManager: EntityManagerInterface) { + EntityFactory.createAndAddObstacle(to: entityManager, + position: self.position, + sprite: self.sprite, + size: self.size) } } diff --git a/star-dash/star-dash/Persistence/StorageManager.swift b/star-dash/star-dash/Persistence/StorageManager.swift index f60b5e24..1686d014 100644 --- a/star-dash/star-dash/Persistence/StorageManager.swift +++ b/star-dash/star-dash/Persistence/StorageManager.swift @@ -14,11 +14,9 @@ class StorageManager { database = Database() } - func getLevel(id: Int64) -> Level? { - if let levelPersistable = self.database.getLevelPersistable(id: id) { - let entityPersistables = self.database.getAllEntities(levelId: id) - return Level(levelPersistable: levelPersistable, entityPersistables: entityPersistables) - } - return nil + func loadLevel(id: Int64, into entityManager: EntityManagerInterface) { + let entityPersistables = self.database.getAllEntities(levelId: id) + + entityPersistables.forEach { $0.addTo(entityManager) } } } diff --git a/star-dash/star-dash/Persistence/ToolEntityPersistable.swift b/star-dash/star-dash/Persistence/ToolEntityPersistable.swift index 5b41bccb..7d0ef6ca 100644 --- a/star-dash/star-dash/Persistence/ToolEntityPersistable.swift +++ b/star-dash/star-dash/Persistence/ToolEntityPersistable.swift @@ -6,14 +6,17 @@ // import Foundation -struct ToolEntityPersistable: Codable, EntityPersistable { +struct ToolEntityPersistable: Codable, EntityPersistable { var levelId: Int64 var position: CGPoint var sprite: String var size: CGSize - func toEntity() -> Entity { - Tool(position: self.position, sprite: self.sprite, size: self.size) + func addTo(_ entityManager: EntityManagerInterface) { + EntityFactory.createAndAddTool(to: entityManager, + position: self.position, + sprite: self.sprite, + size: self.size) } } diff --git a/star-dash/star-dash/ViewController.swift b/star-dash/star-dash/ViewController.swift index 8289ac94..b15f3a93 100644 --- a/star-dash/star-dash/ViewController.swift +++ b/star-dash/star-dash/ViewController.swift @@ -9,12 +9,12 @@ import UIKit import SDPhysicsEngine class ViewController: UIViewController { - var scene: SDScene? var renderer: Renderer? var gameBridge: GameBridge? var gameEngine: GameEngine? var storageManager: StorageManager? + override func viewDidLoad() { super.viewDidLoad() @@ -39,7 +39,7 @@ class ViewController: UIViewController { func setupGameEntities() { guard let scene = self.scene, - let entityManager = gameEngine?.entityManager else { + let gameEngine = self.gameEngine else { return } @@ -52,28 +52,23 @@ class ViewController: UIViewController { background.zPosition = -1 scene.addObject(background) - let player = Player( - playerIndex: 0, - position: CGPoint(x: 100, y: scene.size.height / 2 + 200), - playerSprite: PlayerSprite.RedNose - ) - player.setUpAndAdd(to: entityManager) + EntityFactory.createAndAddPlayer(to: gameEngine, + playerIndex: 0, + position: CGPoint(x: 100, y: scene.size.height / 2 + 200), + sprite: PlayerSprite.RedNose) - let floor = Floor(position: CGPoint(x: scene.size.width / 2, y: scene.size.height / 2 - 400)) - floor.setUpAndAdd(to: entityManager) + EntityFactory.createAndAddFloor(to: gameEngine, + position: CGPoint(x: scene.size.width / 2, y: scene.size.height / 2 - 400), + size: CGSize(width: 8_000, height: 10)) - if let level = self.storageManager?.getLevel(id: 0) { - for entity in level.entities { - entity.setUpAndAdd(to: entityManager) - } - } else { - print("level not found") - } + EntityFactory.createAndAddCollectible(to: gameEngine, + position: CGPoint(x: scene.size.width / 2 + 30, + y: scene.size.height / 2 - 100), + sprite: SpriteConstants.star, + points: EntityConstants.StarCollectible.points, + size: EntityConstants.StarCollectible.size) - let collectible = Collectible.createStarCollectible( - position: CGPoint(x: scene.size.width / 2 + 30, y: scene.size.height / 2 - 100) - ) - collectible.setUpAndAdd(to: entityManager) + self.storageManager?.loadLevel(id: 0, into: gameEngine) } }