Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimise Renderer and EntityManager #107

Merged
merged 4 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions TowerForge/TowerForge.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
3CD37AA52BBEC10700222D8A /* FirebaseRemoteEventSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD37AA42BBEC10700222D8A /* FirebaseRemoteEventSubscriber.swift */; };
3CD37AA72BBEC5EF00222D8A /* BaseRemoteEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD37AA62BBEC5EF00222D8A /* BaseRemoteEvent.swift */; };
3CE9514B2BAC83FA008B2785 /* SpawnableEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE9514A2BAC83FA008B2785 /* SpawnableEntities.swift */; };
3CE9514F2BAC8936008B2785 /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE9514E2BAC8936008B2785 /* Renderer.swift */; };
3CE9514F2BAC8936008B2785 /* TFRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE9514E2BAC8936008B2785 /* TFRenderer.swift */; };
3CE951512BAC8955008B2785 /* Renderable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951502BAC8955008B2785 /* Renderable.swift */; };
3CE951562BACA0CF008B2785 /* Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951552BACA0CF008B2785 /* Collidable.swift */; };
3CE951582BAD724D008B2785 /* TFContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951572BAD724D008B2785 /* TFContact.swift */; };
Expand All @@ -71,6 +71,7 @@
3CE951652BAE0A04008B2785 /* HomeSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951642BAE0A04008B2785 /* HomeSystem.swift */; };
3CE951672BAEAB0E008B2785 /* ContactSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951662BAEAB0E008B2785 /* ContactSystem.swift */; };
3CE951692BAEB719008B2785 /* RequestSpawnEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CE951682BAEB719008B2785 /* RequestSpawnEvent.swift */; };
3CF273242BC6E51F007E645C /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF273232BC6E51F007E645C /* Renderer.swift */; };
3CFA72E72BC0398E0081337F /* RemoteEventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA72E62BC0398E0081337F /* RemoteEventManager.swift */; };
5200624E2BA8D597000DBA30 /* AiComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5200624D2BA8D597000DBA30 /* AiComponent.swift */; };
520062562BA8E026000DBA30 /* PlayerSpawnable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520062552BA8E026000DBA30 /* PlayerSpawnable.swift */; };
Expand Down Expand Up @@ -282,7 +283,7 @@
3CD37AA42BBEC10700222D8A /* FirebaseRemoteEventSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseRemoteEventSubscriber.swift; sourceTree = "<group>"; };
3CD37AA62BBEC5EF00222D8A /* BaseRemoteEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseRemoteEvent.swift; sourceTree = "<group>"; };
3CE9514A2BAC83FA008B2785 /* SpawnableEntities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpawnableEntities.swift; sourceTree = "<group>"; };
3CE9514E2BAC8936008B2785 /* Renderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
3CE9514E2BAC8936008B2785 /* TFRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFRenderer.swift; sourceTree = "<group>"; };
3CE951502BAC8955008B2785 /* Renderable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderable.swift; sourceTree = "<group>"; };
3CE951552BACA0CF008B2785 /* Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collidable.swift; sourceTree = "<group>"; };
3CE951572BAD724D008B2785 /* TFContact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFContact.swift; sourceTree = "<group>"; };
Expand All @@ -292,6 +293,7 @@
3CE951642BAE0A04008B2785 /* HomeSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSystem.swift; sourceTree = "<group>"; };
3CE951662BAEAB0E008B2785 /* ContactSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSystem.swift; sourceTree = "<group>"; };
3CE951682BAEB719008B2785 /* RequestSpawnEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestSpawnEvent.swift; sourceTree = "<group>"; };
3CF273232BC6E51F007E645C /* Renderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
3CFA72E62BC0398E0081337F /* RemoteEventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteEventManager.swift; sourceTree = "<group>"; };
5200624D2BA8D597000DBA30 /* AiComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AiComponent.swift; sourceTree = "<group>"; };
520062552BA8E026000DBA30 /* PlayerSpawnable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSpawnable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -550,7 +552,8 @@
isa = PBXGroup;
children = (
3CAC4A632BB696D700A5D22E /* RenderStages */,
3CE9514E2BAC8936008B2785 /* Renderer.swift */,
3CE9514E2BAC8936008B2785 /* TFRenderer.swift */,
3CF273232BC6E51F007E645C /* Renderer.swift */,
3CAC4A662BB6975200A5D22E /* RenderStage.swift */,
3CE951502BAC8955008B2785 /* Renderable.swift */,
);
Expand Down Expand Up @@ -1392,6 +1395,7 @@
3CE951632BAE037C008B2785 /* AiSystem.swift in Sources */,
3CE9515F2BADE2C5008B2785 /* ShootingSystem.swift in Sources */,
5240D0AB2BB3340F004F1486 /* CaptureTheFlagMode.swift in Sources */,
3CF273242BC6E51F007E645C /* Renderer.swift in Sources */,
3CD37A9D2BBEBD1600222D8A /* RemoteSpawnable.swift in Sources */,
3C9955CA2BA5888F00D33FA5 /* SpawnEvent.swift in Sources */,
3CE951582BAD724D008B2785 /* TFContact.swift in Sources */,
Expand Down Expand Up @@ -1459,7 +1463,7 @@
527A07862BB411FB00CD9D08 /* GameOverScene.swift in Sources */,
3C9955B12BA4ACA100D33FA5 /* Bullet.swift in Sources */,
3CA829C62BB719A500D8E72A /* ButtonRenderStage.swift in Sources */,
3CE9514F2BAC8936008B2785 /* Renderer.swift in Sources */,
3CE9514F2BAC8936008B2785 /* TFRenderer.swift in Sources */,
527A07802BB3F81C00CD9D08 /* TimerComponent.swift in Sources */,
BAFFB9662BB98ADC00D8301F /* AchievementStorage.swift in Sources */,
3C769A722BA58DE700F454F9 /* MovementSystem.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ class SpriteComponent: TFComponent {
var animatableKey: String
var alpha = 1.0
var staticOnScreen = false
var zPosition: CGFloat

init(textureNames: [String], size: CGSize, animatableKey: String) {
init(textureNames: [String], size: CGSize, animatableKey: String, zPosition: CGFloat = .zero) {
self.textures = TFTextures(textureNames: textureNames, textureAtlasName: "Sprites")
self.size = size
self.animatableKey = animatableKey
self.zPosition = zPosition
super.init()
}
}
14 changes: 14 additions & 0 deletions TowerForge/TowerForge/GameModule/Components/TFComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

import Foundation

struct TFComponentTypeWrapper {
let type: TFComponent.Type
}

extension TFComponentTypeWrapper: Hashable {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.type == rhs.type
}

func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(type))
}
}

class TFComponent: Identifiable {
var id = UUID()
weak var entity: TFEntity?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation

class BaseProjectile: TFEntity, Spawnable {
static let zPosition: CGFloat = 0
required init(position: CGPoint, player: Player) {
// TODO: to fill
}
Expand All @@ -16,7 +17,8 @@ class BaseProjectile: TFEntity, Spawnable {
player: Player, velocity: CGVector = .zero) {
super.init()
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(SpriteComponent(textureNames: textureNames, size: size,
animatableKey: key, zPosition: BaseProjectile.zPosition))
self.addComponent(PositionComponent(position: position))
self.addComponent(MovableComponent(velocity: velocity))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation

class BaseTower: TFEntity {
static let zPosition: CGFloat = 200
init(textureNames: [String],
size: CGSize,
key: String,
Expand All @@ -17,7 +18,8 @@ class BaseTower: TFEntity {
id: UUID = UUID()) {
super.init()
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(SpriteComponent(textureNames: textureNames, size: size,
animatableKey: key, zPosition: BaseTower.zPosition))
self.addComponent(PositionComponent(position: position))

// Game Components
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation

class BaseUnit: TFEntity {
static let zPosition: CGFloat = 100
init(textureNames: [String],
size: CGSize,
key: String,
Expand All @@ -18,7 +19,8 @@ class BaseUnit: TFEntity {
id: UUID = UUID()) {
super.init(id: id)
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(SpriteComponent(textureNames: textureNames, size: size,
animatableKey: key, zPosition: BaseUnit.zPosition))
self.addComponent(PositionComponent(position: position))
self.addComponent(MovableComponent(velocity: velocity))

Expand Down
29 changes: 26 additions & 3 deletions TowerForge/TowerForge/GameModule/Entities/EntityManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import Foundation

class EntityManager {
private var entitiesMap: [UUID: TFEntity]
private var componentsMap: [TFComponentTypeWrapper: [UUID: TFEntity]]

init() {
entitiesMap = Dictionary()
entitiesMap = [:]
componentsMap = [:]
}

var entities: [TFEntity] {
Expand All @@ -24,14 +26,34 @@ class EntityManager {

func add(_ entity: TFEntity) {
entitiesMap[entity.id] = entity
for (key, _) in entity.components {
if componentsMap[key] == nil {
componentsMap[key] = [:]
}
componentsMap[key]?[entity.id] = entity
}
/// assert(checkRepresentation())
}

func removeEntity(with id: UUID) {
entitiesMap.removeValue(forKey: id)
guard let entity = entitiesMap.removeValue(forKey: id) else {
return
}

for (key, _) in entity.components {
componentsMap[key]?.removeValue(forKey: entity.id)
}
/// assert(checkRepresentation())
}

func entities<T: TFComponent>(with componentType: T.Type) -> [TFEntity] {
guard let entityMap = componentsMap[TFComponentTypeWrapper(type: componentType)] else {
return []
}

return Array(entityMap.values)
}

func component<T: TFComponent>(ofType type: T.Type, of entityId: UUID) -> T? {
guard let entity = entity(with: entityId) else {
return nil
Expand All @@ -42,7 +64,8 @@ class EntityManager {

func components<T: TFComponent>(ofType type: T.Type) -> [T] {
/// assert(checkRepresentation())
entities.compactMap { $0.component(ofType: type) }
let typeWrapper = TFComponentTypeWrapper(type: type)
return componentsMap[typeWrapper]?.compactMap({ $0.value.component(ofType: type) }) ?? []
}

/// Ensures that the UUID keys of entries in the dictionary match the UUID id of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ArrowTower: BaseTower, PlayerSpawnable {
static let textureNames = ["LightHouse-1"]
static let size = CGSize(width: 100, height: 100)
static let key = "arrowTower"
static let maxHealth = 200.0
static let maxHealth = 500.0
static let damage = 10.0
static var cost = 10
static let range = 400.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class MeleeUnit: BaseUnit, PlayerSpawnable {
static let textureNames = ["melee-1", "melee-2"]
static let size = CGSize(width: 100, height: 100)
static let key = "melee"
static let maxHealth = 100.0
static let damage = 10.0
static let maxHealth = 150.0
static let damage = 15.0
static var cost = 10
static let attackRate = 1.0
static let velocity = CGVector(dx: 30.0, dy: 0.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class WizardUnit: BaseUnit, PlayerSpawnable {
static let textureNames = ["Wizard-0", "Wizard-1", "Wizard-2"]
static let size = CGSize(width: 100, height: 100)
static let key = "shoot"
static let maxHealth = 100.0
static let maxHealth = 80.0
static let damage = 10.0
static var cost = 5
static var cost = 10
static let attackRate = 1.0
static let velocity = CGVector(dx: 50.0, dy: 0.0)
static let range = 400.0
Expand Down
21 changes: 9 additions & 12 deletions TowerForge/TowerForge/GameModule/Entities/TFEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,16 @@ import Foundation

class TFEntity: Collidable {
let id: UUID
private(set) var components: [UUID: TFComponent]
private(set) var components: [TFComponentTypeWrapper: TFComponent]

init(id: UUID = UUID()) {
self.id = id
components = Dictionary()
}

func component<T: TFComponent>(ofType type: T.Type) -> T? {
for component in components.values {
guard let component = component as? T else {
continue
}
return component
}
return nil
let typeWrapper = TFComponentTypeWrapper(type: type)
return components[typeWrapper] as? T
}

func hasComponent<T: TFComponent>(ofType type: T.Type) -> Bool {
Expand All @@ -36,7 +31,8 @@ class TFEntity: Collidable {
return
}
component.didAddToEntity(self)
components[component.id] = (component)
let typeWrapper = TFComponentTypeWrapper(type: type(of: component))
components[typeWrapper] = component
}

func removeComponent<T: TFComponent>(ofType type: T.Type) {
Expand All @@ -45,7 +41,8 @@ class TFEntity: Collidable {
return
}
componentToBeRemoved.willRemoveFromEntity()
components.removeValue(forKey: componentToBeRemoved.id)
let typeWrapper = TFComponentTypeWrapper(type: type)
components.removeValue(forKey: typeWrapper)
}

// To be overriden by sub classes as needed
Expand Down Expand Up @@ -79,8 +76,8 @@ class TFEntity: Collidable {
/// Ensures that the UUID keys of entries in the dictionary match the UUID id of
/// the associated values
private func checkRepresentation() -> Bool {
for (key, _) in components where key != components[key]?.id {
return false
for (key, _) in components where type(of: components[key]) == key.type {
return false
}
return true
}
Expand Down
5 changes: 5 additions & 0 deletions TowerForge/TowerForge/GameModule/GameEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ protocol AbstractGameEngine: EventTarget {
func addRemoteEvent(_ remoteEvent: TFRemoteEvent)

func system<T: TFSystem>(ofType type: T.Type) -> T?
func entities<T: TFComponent>(with componentType: T.Type) -> [TFEntity]
}

/// A class that encapsulates handling of all Managers.
Expand Down Expand Up @@ -71,6 +72,10 @@ class GameEngine: AbstractGameEngine {
systemManager.system(ofType: type)
}

func entities<T: TFComponent>(with componentType: T.Type) -> [TFEntity] {
entityManager.entities(with: componentType)
}

private func setupTeam() {
let ownTeam = Team(player: .ownPlayer)
let oppositeTeam = Team(player: .oppositePlayer)
Expand Down
6 changes: 5 additions & 1 deletion TowerForge/TowerForge/GameModule/GameWorld.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class GameWorld {
powerUpSelectionNode = PowerUpSelectionNode(eventManager: gameEngine.eventManager)
grid = Grid(screenSize: worldBounds)
popup = StatePopupNode()
renderer = Renderer(target: self, scene: scene)
renderer = TFRenderer(target: self, scene: scene)

setUp()
}
Expand Down Expand Up @@ -116,6 +116,10 @@ extension GameWorld: Renderable {
var entitiesToRender: [TFEntity] {
gameEngine.entities
}

func entities<T: TFComponent>(with componentType: T.Type) -> [TFEntity] {
gameEngine.entities(with: componentType)
}
}

extension GameWorld: UnitSelectionNodeDelegate {
Expand Down
13 changes: 13 additions & 0 deletions TowerForge/TowerForge/Scenes/GameScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,17 @@ extension GameScene: TFScene {
func panCamera(by displacement: CGVector) {
cameraNode?.move(by: displacement)
}

func isStatic(node: TFNode) -> Bool {
cameraNode?.contains(node) ?? false
}

func setNode(_ node: TFNode, isStatic: Bool) {
guard isStatic != self.isStatic(node: node) else {
return
}

remove(node: node)
add(node: node, staticOnScreen: isStatic)
}
}
23 changes: 17 additions & 6 deletions TowerForge/TowerForge/Scenes/Rendering/RenderStage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@

import Foundation

protocol RenderTarget: AnyObject {
var target: Renderable { get }
var renderedNodes: [UUID: TFNode] { get }
func flagNodeUpdated(with id: UUID)
func updateStaticNode(with id: UUID)
}

protocol RenderStage {
func createAndAdd(node: TFNode, for entity: TFEntity)
func transform(node: TFNode, for entity: TFEntity)
func update(node: TFNode, for entity: TFEntity)
func render()
func create(for entity: TFEntity)
func transform(for entity: TFEntity)
func update(for entity: TFEntity)
func removeAndUncache(for id: UUID)
}

extension RenderStage {
func createAndAdd(node: TFNode, for entity: TFEntity) {}
func transform(node: TFNode, for entity: TFEntity) {}
func update(node: TFNode, for entity: TFEntity) {}
func render() {}
func create(for entity: TFEntity) {}
func transform(for entity: TFEntity) {}
func update(for entity: TFEntity) {}
func removeAndUncache(for id: UUID) {}
}
Loading