Skip to content

Commit

Permalink
Update ContactSystem to not rely on view and move RemoveEvent generat…
Browse files Browse the repository at this point in the history
…ion to HealthSystem
  • Loading branch information
zheng-ze committed Mar 26, 2024
1 parent 4ac9a3d commit 3160d39
Show file tree
Hide file tree
Showing 27 changed files with 165 additions and 193 deletions.
4 changes: 4 additions & 0 deletions TowerForge/TowerForge.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
3C0B608D2BB2B84000FFECB4 /* ContactComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C0B608C2BB2B84000FFECB4 /* ContactComponent.swift */; };
3C769A722BA58DE700F454F9 /* MovementSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C769A712BA58DE700F454F9 /* MovementSystem.swift */; };
3C769A742BA591BD00F454F9 /* SpawnSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C769A732BA591BD00F454F9 /* SpawnSystem.swift */; };
3C9955A12BA47DA500D33FA5 /* BaseTower.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C9955A02BA47DA500D33FA5 /* BaseTower.swift */; };
Expand Down Expand Up @@ -116,6 +117,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
3C0B608C2BB2B84000FFECB4 /* ContactComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactComponent.swift; sourceTree = "<group>"; };
3C769A712BA58DE700F454F9 /* MovementSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovementSystem.swift; sourceTree = "<group>"; };
3C769A732BA591BD00F454F9 /* SpawnSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpawnSystem.swift; sourceTree = "<group>"; };
3C9955A02BA47DA500D33FA5 /* BaseTower.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTower.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -307,6 +309,7 @@
isa = PBXGroup;
children = (
52578B862BA6209700B4D76C /* DamageComponent.swift */,
3C0B608C2BB2B84000FFECB4 /* ContactComponent.swift */,
52DF5FFE2BA3656500135367 /* ShootingComponent.swift */,
52DF5FFA2BA3601400135367 /* HealthComponent.swift */,
);
Expand Down Expand Up @@ -919,6 +922,7 @@
527E3A242BA613F000FE1628 /* PlayerComponent.swift in Sources */,
3C9955C52BA585DD00D33FA5 /* HealthSystem.swift in Sources */,
520062562BA8E026000DBA30 /* PlayerSpawnable.swift in Sources */,
3C0B608D2BB2B84000FFECB4 /* ContactComponent.swift in Sources */,
3CE951652BAE0A04008B2785 /* HomeSystem.swift in Sources */,
52DF5FFB2BA3601400135367 /* HealthComponent.swift in Sources */,
3C9955BC2BA563A800D33FA5 /* TFEvent.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,22 @@ import CoreGraphics

class MovableComponent: TFComponent {
var velocity: CGVector
var position: CGPoint
var isColliding = false
var shouldMove = true

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

func updatePosition(to position: CGPoint) {
self.position = position
}

func updatePosition(with vector: CGVector) {
self.position.x += vector.dx
self.position.y += vector.dy
}
func updatePosition(with vector: CGVector) {
guard let positionComponent = entity?.component(ofType: PositionComponent.self) else {
return
}
positionComponent.move(by: vector)
}

override func update(deltaTime: TimeInterval) {
guard !isColliding,
let entity = entity,
guard shouldMove, let entity = entity,
let positionComponent = entity.component(ofType: PositionComponent.self),
let playerComponent = entity.component(ofType: PlayerComponent.self) else {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
import CoreGraphics

class PositionComponent: TFComponent {
var position: CGPoint
private(set) var position: CGPoint
var anchorPoint: CGPoint

init(position: CGPoint, anchorPoint: CGPoint) {
Expand All @@ -25,4 +25,9 @@ class PositionComponent: TFComponent {
func changeTo(to position: CGPoint) {
self.position = position
}

func move(by displacement: CGVector) {
position.x += displacement.dx
position.y += displacement.dy
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// ContactComponent.swift
// TowerForge
//
// Created by Zheng Ze on 26/3/24.
//

import Foundation

class ContactComponent: TFComponent {
let hitboxSize: CGSize

init(hitboxSize: CGSize) {
self.hitboxSize = hitboxSize
}

func hitbox(position: CGPoint) -> CGRect {
CGRect(origin: position, size: hitboxSize)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class DamageComponent: TFComponent {
}

func damage(_ healthComponent: HealthComponent) -> TFEvent? {
guard canDamage, let enemyId = healthComponent.entity?.id, let id = entity?.id else {
guard canDamage, let enemyId = healthComponent.entity?.id, let entityId = entity?.id else {
return nil
}

Expand All @@ -39,7 +39,7 @@ class DamageComponent: TFComponent {
let event = DamageEvent(on: enemyId, at: lastAttackTime, with: attackPower)

if temporary {
return event.concurrentlyWith(RemoveEvent(on: id, at: lastAttackTime))
return event.concurrentlyWith(RemoveEvent(on: entityId, at: lastAttackTime))
}
return event
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import QuartzCore
class ShootingComponent: TFComponent {
var fireRate: TimeInterval // Delay between shots
var range: CGFloat
private var lastShotTime = TimeInterval(0)
private(set) var lastShotTime = TimeInterval(0)
let attackPower: CGFloat

init(fireRate: TimeInterval, range: CGFloat, attackPower: CGFloat) {
Expand All @@ -24,30 +24,7 @@ class ShootingComponent: TFComponent {
CACurrentMediaTime() - lastShotTime >= fireRate
}

func shoot(_ healthComponent: HealthComponent) -> SpawnEvent? {
guard canShoot, let entityA = self.entity, let entityB = healthComponent.entity else {
return nil
}

guard let playerA = entityA.component(ofType: PlayerComponent.self)?.player,
let playerB = entityB.component(ofType: PlayerComponent.self)?.player,
playerA != playerB else {
return nil
}

guard let positionA = entityA.component(ofType: PositionComponent.self)?.position,
let positionB = entityB.component(ofType: PositionComponent.self)?.position else {
return nil
}

let direction = playerA == .ownPlayer ? -1.0 : 1.0
let distance = (positionA.x - positionB.x) * direction

guard distance > 0, distance <= range, abs(positionA.y - positionB.y) <= 50 else {
return nil
}

func shoot() {
lastShotTime = CACurrentMediaTime()
return SpawnEvent(ofType: Bullet.self, timestamp: lastShotTime, position: positionA, player: playerA)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ class BaseProjectile: TFEntity {
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(PositionComponent(position: position))
self.addComponent(MovableComponent(position: position, velocity: velocity))
self.addComponent(MovableComponent(velocity: velocity))

// Game Components
self.addComponent(PlayerComponent(player: player))
self.addComponent(ContactComponent(hitboxSize: size))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ class BaseTower: TFEntity {
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(PositionComponent(position: position))

// Game Components
self.addComponent(HealthComponent(maxHealth: maxHealth))
self.addComponent(PlayerComponent(player: player))
self.addComponent(ContactComponent(hitboxSize: size))
}

override func collide(with other: any Collidable) -> TFEvent? {
Expand Down Expand Up @@ -51,7 +52,7 @@ class BaseTower: TFEntity {
return nil
}

movableComponent.isColliding = true
movableComponent.shouldMove = true
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ class BaseUnit: TFEntity {
// Core Components
self.addComponent(SpriteComponent(textureNames: textureNames, size: size, animatableKey: key))
self.addComponent(PositionComponent(position: position))
self.addComponent(MovableComponent(position: position, velocity: velocity))
self.addComponent(MovableComponent(velocity: velocity))

// Game Components
self.addComponent(HealthComponent(maxHealth: maxHealth))
self.addComponent(PlayerComponent(player: player))
self.addComponent(ContactComponent(hitboxSize: size))
}

override func collide(with other: any Collidable) -> TFEvent? {
Expand All @@ -39,6 +40,13 @@ class BaseUnit: TFEntity {
return superEvent?.concurrentlyWith(event) ?? event
}

override func onSeparate() {
guard let movableComponent = self.component(ofType: MovableComponent.self) else {
return
}
movableComponent.shouldMove = true
}

override func collide(with damageComponent: DamageComponent) -> TFEvent? {
guard let healthComponent = self.component(ofType: HealthComponent.self) else {
return nil
Expand All @@ -54,11 +62,10 @@ class BaseUnit: TFEntity {
return nil
}

movableComponent.isColliding = true
if let component = self.component(ofType: MovableComponent.self) {
component.isColliding = true
movableComponent.shouldMove = false
if let ownMovableComponent = self.component(ofType: MovableComponent.self) {
ownMovableComponent.shouldMove = false
}

return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import Foundation

class Life: TFEntity {
static let position = CGPoint(x: 300, y: 100)
static let size = CGSize(width: 100, height: 100)
init(initialLife: Int) {
super.init()
self.addComponent(SpriteComponent(textureNames: ["Life"],
height: 100,
width: 100,
position: Life.position,
animatableKey: "life"))
self.addComponent(SpriteComponent(textureNames: ["Life"], size: Life.size, animatableKey: "life"))
self.addComponent(HomeComponent(initialLifeCount: Team.lifeCount, pointInterval: Team.pointsInterval))
self.addComponent(LabelComponent(text: String(initialLife), name: "life"))
self.addComponent(PositionComponent(position: Life.position))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import Foundation

class Point: TFEntity {
static let position = CGPoint(x: 100, y: 100)
static let size = CGSize(width: 100, height: 100)
init(initialPoint: Int) {
super.init()
self.addComponent(SpriteComponent(textureNames: ["Coin"],
height: 100,
width: 100,
position: Point.position, animatableKey: "point"))
self.addComponent(SpriteComponent(textureNames: ["Coin"], size: Point.size, animatableKey: "point"))
self.addComponent(HomeComponent(initialLifeCount: Team.lifeCount, pointInterval: Team.pointsInterval))
self.addComponent(LabelComponent(text: String(initialPoint), name: "point"))
self.addComponent(PositionComponent(position: Point.position))
Expand Down
2 changes: 2 additions & 0 deletions TowerForge/TowerForge/GameModule/Entities/TFEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class TFEntity: Collidable {
nil
}

func onSeparate() {}

func collide(with damageComponent: DamageComponent) -> TFEvent? {
/// assert(checkRepresentation())
nil
Expand Down
13 changes: 4 additions & 9 deletions TowerForge/TowerForge/GameModule/Events/ConcurrentEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ struct ConcurrentEvent: TFEvent {
}

func execute(in target: any EventTarget) -> EventOutput? {
guard var eventOutput = event1.execute(in: target) else {
return nil
}
var eventOutput1 = event1.execute(in: target)
let eventOutput2 = event2.execute(in: target)

guard let eventOutput2 = event2.execute(in: target) else {
return eventOutput
}

eventOutput.combine(with: eventOutput2)
return eventOutput
eventOutput1?.combine(with: eventOutput2)
return eventOutput1 != nil ? eventOutput1 : eventOutput2
}
}
4 changes: 2 additions & 2 deletions TowerForge/TowerForge/GameModule/Events/EventOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct EventOutput {
}

extension EventOutput {
mutating func combine(with otherEventOuput: EventOutput) {
self.events.append(contentsOf: otherEventOuput.events)
mutating func combine(with otherEventOuput: EventOutput?) {
self.events.append(contentsOf: otherEventOuput?.events ?? [])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@ struct DamageEvent: TFEvent {
guard let healthSystem = target.system(ofType: HealthSystem.self) else {
return nil
}
let healthIsZero = healthSystem.modifyHealth(for: entityId, with: -damage)
var eventOutput = EventOutput()
if healthIsZero {
eventOutput.add(RemoveEvent(on: entityId, at: timestamp))
}

return eventOutput
healthSystem.modifyHealth(for: entityId, with: -damage)
return nil
}
}
13 changes: 1 addition & 12 deletions TowerForge/TowerForge/GameModule/GameEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ protocol AbstractGameEngine: EventTarget {
func updateGame(deltaTime: TimeInterval)
func setUpSystems(with grid: Grid)

func contactDidBegin(between idA: UUID, and idB: UUID)
func contactDidEnd(between idA: UUID, and idB: UUID)

func addEntity(_ entity: TFEntity)
func addEvent(_ event: TFEvent)

Expand Down Expand Up @@ -80,7 +77,7 @@ class GameEngine: AbstractGameEngine {

func setUpSystems(with grid: Grid) {
systemManager.add(system: HealthSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: MovementSystem(entityManager: entityManager))
systemManager.add(system: MovementSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: RemoveSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: SpawnSystem(entityManager: entityManager, eventManager: eventManager))
systemManager.add(system: ShootingSystem(entityManager: entityManager, eventManager: eventManager))
Expand All @@ -93,14 +90,6 @@ class GameEngine: AbstractGameEngine {
systemManager.system(ofType: AiSystem.self)?.aiPlayers.append(.oppositePlayer)
}

func contactDidBegin(between idA: UUID, and idB: UUID) {
systemManager.system(ofType: ContactSystem.self)?.insert(contact: TFContact(entityIdA: idA, entityIdB: idB))
}

func contactDidEnd(between idA: UUID, and idB: UUID) {
systemManager.system(ofType: ContactSystem.self)?.remove(contact: TFContact(entityIdA: idA, entityIdB: idB))
}

func addEntity(_ entity: TFEntity) {
entityManager.add(entity)
}
Expand Down
15 changes: 1 addition & 14 deletions TowerForge/TowerForge/GameModule/GameWorld.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,8 @@ class GameWorld {
selectionNode.unitNodeDidSpawn(location)
}

func contactDidBegin(between idA: UUID, and idB: UUID) {
gameEngine.contactDidEnd(between: idA, and: idB)
}

func contactDidEnd(between idA: UUID, and idB: UUID) {
gameEngine.contactDidEnd(between: idA, and: idB)
}

private func setUpSelectionNode() {
selectionNode.delegate = self
// scene?.addChild(selectionNode)
// // Position unit selection node on the left side of the screen
// selectionNode.position = CGPoint(x: 500, y: selectionNode.height / 2)
//
// Calculate vertical spacing between unit nodes
var horizontalX = 10.0
// Position unit nodes vertically aligned
Expand All @@ -65,8 +53,7 @@ class GameWorld {
}

extension GameWorld: Renderable {
func entitiesToRender() -> [TFEntity] {

var entitiesToRender: [TFEntity] {
gameEngine.entities
}
}
Expand Down
Loading

0 comments on commit 3160d39

Please sign in to comment.