Skip to content

Commit

Permalink
Merge pull request #4 from cs3217-2324/systems
Browse files Browse the repository at this point in the history
feat: Add Systems
  • Loading branch information
HoJunHao2000 committed Mar 15, 2024
2 parents e4e7825 + 06cb9dc commit 86d9cf6
Show file tree
Hide file tree
Showing 23 changed files with 436 additions and 45 deletions.
177 changes: 132 additions & 45 deletions star-dash/star-dash.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

@@ -0,0 +1,14 @@
{
"pins" : [
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
"version" : "1.1.0"
}
}
],
"version" : 2
}
15 changes: 15 additions & 0 deletions star-dash/star-dash/Events/Event.swift
@@ -0,0 +1,15 @@
//
// Event.swift
// star-dash
//
// Created by Jason Qiu on 13/3/24.
//

import Foundation

protocol Event {
var timestamp: Date { get }
var entityId: EntityId { get }

func execute(on target: EventModifiable)
}
29 changes: 29 additions & 0 deletions star-dash/star-dash/Events/EventManager.swift
@@ -0,0 +1,29 @@
//
// EventManager.swift
// star-dash
//
// Created by Jason Qiu on 14/3/24.
//

import DequeModule
import Foundation

typealias EventQueue = Deque<Event>

class EventManager {
private var events: EventQueue

init() {
events = EventQueue()
}

func add(event: Event) {
events.append(event)
}

func executeAll(on target: EventModifiable) {
while let event = events.popFirst() {
event.execute(on: target)
}
}
}
11 changes: 11 additions & 0 deletions star-dash/star-dash/Events/EventModifiable.swift
@@ -0,0 +1,11 @@
//
// EventModifiable.swift
// star-dash
//
// Created by Jason Qiu on 14/3/24.
//

import Foundation

/// EventModifiable represents objects that can be modified by events.
protocol EventModifiable { }
25 changes: 25 additions & 0 deletions star-dash/star-dash/Events/MoveEvent.swift
@@ -0,0 +1,25 @@
//
// MoveEvent.swift
// star-dash
//
// Created by Jason Qiu on 14/3/24.
//

import Foundation

class MoveEvent: Event {
var entityId: EntityId
var timestamp: Date

var displacement: CGVector

init(entityId: EntityId, displacement: CGVector, timestamp: Date = Date.now) {
self.entityId = entityId
self.timestamp = timestamp
self.displacement = displacement
}

func execute(on target: EventModifiable) {
// TODO: Use PositionSystem from target to modify entity of entityId
}
}
File renamed without changes.
Expand Up @@ -41,4 +41,16 @@ class EntityManager {
}
self.entityMap[entity.id] = entity
}

func component<T: Component>(ofType type: T.Type, of entityId: EntityId) -> T? {
guard let components = entityComponentMap[entityId] else {
return nil
}

guard let componentId = components.first(where: { componentMap[$0] is T }) else {
return nil
}

return componentMap[componentId] as? T
}
}
37 changes: 37 additions & 0 deletions star-dash/star-dash/GameEngine/GameEngine.swift
@@ -0,0 +1,37 @@
//
// GameEngine.swift
// star-dash
//
// Created by Ho Jun Hao on 14/3/24.
//

import Foundation

class GameEngine {
private let systemManager: SystemManager
private let entityManager: EntityManager
private let eventManager: EventManager

init(scene: GameScene) {
self.systemManager = SystemManager()
self.entityManager = EntityManager()
self.eventManager = EventManager()
// TODO: link game engine to renderer

setUpSystems()
}

func update(by deltaTime: TimeInterval) {
systemManager.update(by: deltaTime)
eventManager.executeAll(on: self)
}

private func setUpSystems() {
systemManager.add(PositionSystem(entityManager, dispatcher: self))
systemManager.add(PhysicsSystem(entityManager, dispatcher: self))
}
}

extension GameEngine: EventModifiable {
// TODO: functions of event modifiable
}
69 changes: 69 additions & 0 deletions star-dash/star-dash/GameEngine/Systems/PhysicsSystem.swift
@@ -0,0 +1,69 @@
//
// PhysicsSystem.swift
// star-dash
//
// Created by Ho Jun Hao on 13/3/24.
//

import Foundation

class PhysicsSystem: System {
var isActive: Bool
var dispatcher: EventModifiable?
var entityManager: EntityManager

init(_ entityManager: EntityManager, dispatcher: EventModifiable? = nil) {
self.isActive = true
self.dispatcher = dispatcher
self.entityManager = entityManager
}

func update(by deltaTime: TimeInterval) {
let physicsComponents = entityManager.componentMap.values.compactMap({ $0 as? PhysicsComponent })

for physicsComponent in physicsComponents {
physicsComponent.force = .zero
}
}

func isMoving(_ entityId: EntityId) -> Bool {
guard let physicsComponent = getPhysicsComponent(of: entityId) else {
return false
}

return physicsComponent.velocity != .zero
}

func isJumping(_ entityId: EntityId) -> Bool {
guard let physicsComponent = getPhysicsComponent(of: entityId) else {
return false
}

return physicsComponent.velocity.dy != .zero
}

func applyForce(to entityId: EntityId, newForce: CGVector) {
guard let physicsComponent = getPhysicsComponent(of: entityId) else {
return
}

let newForce = CGVector(dx: physicsComponent.force.dx + newForce.dx,
dy: physicsComponent.force.dy + newForce.dy)

physicsComponent.force = newForce
}

func sync(entityVelocityMap: [EntityId: CGVector]) {
for (entityId, newVelocity) in entityVelocityMap {
guard let physicsComponent = getPhysicsComponent(of: entityId) else {
continue
}

physicsComponent.velocity = newVelocity
}
}

private func getPhysicsComponent(of entityId: EntityId) -> PhysicsComponent? {
entityManager.component(ofType: PhysicsComponent.self, of: entityId)
}
}
50 changes: 50 additions & 0 deletions star-dash/star-dash/GameEngine/Systems/PositionSystem.swift
@@ -0,0 +1,50 @@
//
// PositionSystem.swift
// star-dash
//
// Created by Ho Jun Hao on 13/3/24.
//

import Foundation

class PositionSystem: System {
var isActive: Bool
var dispatcher: EventModifiable?
var entityManager: EntityManager

init(_ entityManager: EntityManager, dispatcher: EventModifiable? = nil) {
self.isActive = true
self.entityManager = entityManager
self.dispatcher = dispatcher
}

func move(entityId: EntityId, to newPosition: CGPoint) {
guard let positionComponent = getPositionComponent(of: entityId) else {
return
}

positionComponent.setPosition(position: newPosition)
}

func rotate(entityId: EntityId, to newRotation: Float) {
guard let positionComponent = getPositionComponent(of: entityId) else {
return
}

positionComponent.setRotation(rotation: newRotation)
}

func sync(entityPositionMap: [EntityId: CGPoint], entityRotationMap: [EntityId: Float]) {
for (entityId, newPosition) in entityPositionMap {
move(entityId: entityId, to: newPosition)
}

for (entityId, newRotation) in entityRotationMap {
rotate(entityId: entityId, to: newRotation)
}
}

private func getPositionComponent(of entityId: EntityId) -> PositionComponent? {
entityManager.component(ofType: PositionComponent.self, of: entityId)
}
}
20 changes: 20 additions & 0 deletions star-dash/star-dash/GameEngine/Systems/System.swift
@@ -0,0 +1,20 @@
//
// System.swift
// star-dash
//
// Created by Ho Jun Hao on 13/3/24.
//

import Foundation

protocol System {
var isActive: Bool { get set }
var dispatcher: EventModifiable? { get set }
var entityManager: EntityManager { get set }

func update(by deltaTime: TimeInterval)
}

extension System {
func update(by deltaTime: TimeInterval) {}
}
22 changes: 22 additions & 0 deletions star-dash/star-dash/GameEngine/Systems/SystemManager.swift
@@ -0,0 +1,22 @@
//
// SystemManager.swift
// star-dash
//
// Created by Ho Jun Hao on 13/3/24.
//

import Foundation

class SystemManager {
private var systems: [System] = []

func add(_ system: System) {
systems.append(system)
}

func update(by deltaTime: TimeInterval) {
for system in systems where system.isActive {
system.update(by: deltaTime)
}
}
}

0 comments on commit 86d9cf6

Please sign in to comment.