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

Touch up collision events and add inventory #13

Merged
merged 7 commits into from Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 35 additions & 1 deletion SDPhysicsEngine/Sources/SDPhysicsEngine/GameScene.swift
@@ -1,11 +1,19 @@
import SpriteKit

public class GameScene: SKScene, SDScene {
public class GameScene: SKScene {

public var sceneDelegate: SDSceneDelegate?

private var lastUpdateTime: TimeInterval?

private var objectMap: [SKNode: SDObject] = [:]

override public func sceneDidLoad() {
super.sceneDidLoad()

physicsWorld.contactDelegate = self
}

override public func update(_ currentTime: TimeInterval) {
super.update(currentTime)

Expand All @@ -19,8 +27,34 @@ public class GameScene: SKScene, SDScene {

sceneDelegate?.update(self, deltaTime: deltaTime)
}
}

extension GameScene: SDScene {
public func addObject(_ object: SDObject) {
guard objectMap[object.node] == nil else {
return
}

objectMap[object.node] = object
addChild(object.node)
}
}

extension GameScene: SKPhysicsContactDelegate {
public func didBegin(_ contact: SKPhysicsContact) {
guard let skNodeA = contact.bodyA.node,
let skNodeB = contact.bodyB.node else {
return
}

guard let objectA = objectMap[skNodeA],
let objectB = objectMap[skNodeB] else {
fatalError("Unknown node in game scene")
}

sceneDelegate?.contactOccured(
objectA: objectA,
objectB: objectB
)
}
}
5 changes: 5 additions & 0 deletions SDPhysicsEngine/Sources/SDPhysicsEngine/Object/SDObject.swift
@@ -1,15 +1,20 @@
import SpriteKit

public typealias SDObjectId = UUID

public class SDObject {
public let id: SDObjectId
let node: SKNode

var innerRotation: CGFloat = 0

public init() {
id = UUID()
node = SKNode()
}

init(node: SKNode) {
self.id = UUID()
self.node = node
}

Expand Down
15 changes: 15 additions & 0 deletions SDPhysicsEngine/Sources/SDPhysicsEngine/Object/SDPhysicsBody.swift
Expand Up @@ -36,4 +36,19 @@
get { body.isDynamic }
set { body.isDynamic = newValue }
}

Check failure on line 39 in SDPhysicsEngine/Sources/SDPhysicsEngine/Object/SDPhysicsBody.swift

View workflow job for this annotation

GitHub Actions / build

Lines should not have trailing whitespace. (trailing_whitespace)
public var categoryBitMask: UInt32 {
get { body.categoryBitMask }
set { body.categoryBitMask = newValue }
}

Check failure on line 44 in SDPhysicsEngine/Sources/SDPhysicsEngine/Object/SDPhysicsBody.swift

View workflow job for this annotation

GitHub Actions / build

Lines should not have trailing whitespace. (trailing_whitespace)
public var contactTestMask: UInt32 {
get { body.contactTestBitMask }
set { body.contactTestBitMask = newValue }
}

Check failure on line 49 in SDPhysicsEngine/Sources/SDPhysicsEngine/Object/SDPhysicsBody.swift

View workflow job for this annotation

GitHub Actions / build

Lines should not have trailing whitespace. (trailing_whitespace)
public var collisionBitMask: UInt32 {
get { body.collisionBitMask }
set { body.collisionBitMask = newValue }
}
}
@@ -1,4 +1,5 @@
public protocol SDSceneDelegate: AnyObject {

func update(_ scene: SDScene, deltaTime: Double)
func contactOccured(objectA: SDObject, objectB: SDObject)
}
12 changes: 8 additions & 4 deletions star-dash/star-dash.xcodeproj/project.pbxproj
Expand Up @@ -29,6 +29,8 @@
14E2478E2BA22FCE0071FFC0 /* MoveEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */; };
14E247932BA2CA920071FFC0 /* DequeModule in Frameworks */ = {isa = PBXBuildFile; productRef = 14E247922BA2CA920071FFC0 /* DequeModule */; };
14E247952BA2CB480071FFC0 /* EventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14E247942BA2CB480071FFC0 /* EventManager.swift */; };
4604BBD52BA819C70078B84C /* InventoryComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4604BBD42BA819C70078B84C /* InventoryComponent.swift */; };
4604BBD92BA81C940078B84C /* InventorySystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4604BBD82BA81C940078B84C /* InventorySystem.swift */; };
461148912BA1CDBF0073E7E1 /* SystemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461148902BA1CDBF0073E7E1 /* SystemManager.swift */; };
461148932BA1D04B0073E7E1 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461148922BA1D04B0073E7E1 /* System.swift */; };
461148962BA1D53D0073E7E1 /* PositionSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 461148952BA1D53D0073E7E1 /* PositionSystem.swift */; };
Expand All @@ -39,7 +41,6 @@
46D418162BA5CBD60091A38B /* Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418152BA5CBD60091A38B /* Collidable.swift */; };
46D418182BA5CD840091A38B /* Player+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418172BA5CD840091A38B /* Player+Collidable.swift */; };
46D4181A2BA5CDBD0091A38B /* CollisionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418192BA5CDBD0091A38B /* CollisionHandler.swift */; };
46D4181C2BA5CDC70091A38B /* SeparationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D4181B2BA5CDC70091A38B /* SeparationHandler.swift */; };
46D4181E2BA5D2620091A38B /* Monster+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D4181D2BA5D2620091A38B /* Monster+Collidable.swift */; };
46D418202BA5D3420091A38B /* Collectible+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D4181F2BA5D3420091A38B /* Collectible+Collidable.swift */; };
46D418222BA5D4E60091A38B /* Obstacle+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418212BA5D4E60091A38B /* Obstacle+Collidable.swift */; };
Expand Down Expand Up @@ -144,6 +145,8 @@
14D14B722BA5A3CD00386C3B /* RemoveEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveEvent.swift; sourceTree = "<group>"; };
14E2478D2BA22FCE0071FFC0 /* MoveEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveEvent.swift; sourceTree = "<group>"; };
14E247942BA2CB480071FFC0 /* EventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventManager.swift; sourceTree = "<group>"; };
4604BBD42BA819C70078B84C /* InventoryComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InventoryComponent.swift; sourceTree = "<group>"; };
4604BBD82BA81C940078B84C /* InventorySystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InventorySystem.swift; sourceTree = "<group>"; };
461148902BA1CDBF0073E7E1 /* SystemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemManager.swift; sourceTree = "<group>"; };
461148922BA1D04B0073E7E1 /* System.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = "<group>"; };
461148952BA1D53D0073E7E1 /* PositionSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionSystem.swift; sourceTree = "<group>"; };
Expand All @@ -154,7 +157,6 @@
46D418152BA5CBD60091A38B /* Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collidable.swift; sourceTree = "<group>"; };
46D418172BA5CD840091A38B /* Player+Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Player+Collidable.swift"; sourceTree = "<group>"; };
46D418192BA5CDBD0091A38B /* CollisionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollisionHandler.swift; sourceTree = "<group>"; };
46D4181B2BA5CDC70091A38B /* SeparationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparationHandler.swift; sourceTree = "<group>"; };
46D4181D2BA5D2620091A38B /* Monster+Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Monster+Collidable.swift"; sourceTree = "<group>"; };
46D4181F2BA5D3420091A38B /* Collectible+Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collectible+Collidable.swift"; sourceTree = "<group>"; };
46D418212BA5D4E60091A38B /* Obstacle+Collidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Obstacle+Collidable.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -315,6 +317,7 @@
14970F532BA8163300CC1E8A /* ScoreSystem.swift */,
461148952BA1D53D0073E7E1 /* PositionSystem.swift */,
461148972BA1E41F0073E7E1 /* PhysicsSystem.swift */,
4604BBD82BA81C940078B84C /* InventorySystem.swift */,
);
path = Systems;
sourceTree = "<group>";
Expand All @@ -337,7 +340,6 @@
46D418152BA5CBD60091A38B /* Collidable.swift */,
46D418172BA5CD840091A38B /* Player+Collidable.swift */,
46D418192BA5CDBD0091A38B /* CollisionHandler.swift */,
46D4181B2BA5CDC70091A38B /* SeparationHandler.swift */,
46D4181D2BA5D2620091A38B /* Monster+Collidable.swift */,
46D4181F2BA5D3420091A38B /* Collectible+Collidable.swift */,
46D418212BA5D4E60091A38B /* Obstacle+Collidable.swift */,
Expand Down Expand Up @@ -451,6 +453,7 @@
4E630F2D2B9F81850008F887 /* HealthComponent.swift */,
4E630F2F2B9F83DE0008F887 /* SpriteComponent.swift */,
4E630F312B9F887C0008F887 /* PhysicsComponent.swift */,
4604BBD42BA819C70078B84C /* InventoryComponent.swift */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -734,6 +737,7 @@
4E630F2A2B9F7EF60008F887 /* PositionComponent.swift in Sources */,
46D418132BA5C9930091A38B /* Floor.swift in Sources */,
E64361152BA4C2CD003850FD /* GameBridge.swift in Sources */,
4604BBD92BA81C940078B84C /* InventorySystem.swift in Sources */,
14E247952BA2CB480071FFC0 /* EventManager.swift in Sources */,
4E630EF82B9F7E070008F887 /* SceneDelegate.swift in Sources */,
14970F542BA8163300CC1E8A /* ScoreSystem.swift in Sources */,
Expand All @@ -760,9 +764,9 @@
14970F502BA814D500CC1E8A /* ScoreComponent.swift in Sources */,
143AA3912BA4D7AC009C28E7 /* MonsterDeathEvent.swift in Sources */,
E64361102BA4C2CD003850FD /* SpriteModule.swift in Sources */,
4604BBD52BA819C70078B84C /* InventoryComponent.swift in Sources */,
46B8C09A2BA328D900498705 /* GameEngine.swift in Sources */,
461148982BA1E41F0073E7E1 /* PhysicsSystem.swift in Sources */,
46D4181C2BA5CDC70091A38B /* SeparationHandler.swift in Sources */,
4E630F2E2B9F81850008F887 /* HealthComponent.swift in Sources */,
4E86605C2BA095460035530D /* Monster.swift in Sources */,
145F2C802BA203B400457549 /* Event.swift in Sources */,
Expand Down
8 changes: 4 additions & 4 deletions star-dash/star-dash/Constants/PhysicsConstants.swift
Expand Up @@ -24,10 +24,10 @@ struct PhysicsConstants {
static let player = CollisionCategory.max ^ CollisionCategory.player
static let monster = CollisionCategory.player | CollisionCategory.tool
static let collectible = CollisionCategory.player
static let obstacle = CollisionCategory.player
static let tool = CollisionCategory.player | CollisionCategory.monster
static let wall = CollisionCategory.player | CollisionCategory.monster
static let floor = CollisionCategory.player | CollisionCategory.monster
static let obstacle = CollisionCategory.player | CollisionCategory.monster | CollisionMask.tool
static let tool = CollisionCategory.max ^ CollisionCategory.collectible ^ CollisionCategory.tool
static let wall = CollisionCategory.player | CollisionCategory.monster | CollisionCategory.tool
static let floor = CollisionCategory.player | CollisionCategory.monster | CollisionCategory.tool
}

struct Dimensions {
Expand Down
Expand Up @@ -8,19 +8,39 @@
import Foundation

class PlayerMonsterContactEvent: Event {
let timestamp: Date
let entityId: EntityId
var entityId: EntityId

let monsterEntityId: EntityId
let timestamp: Date
let monsterId: EntityId

init(from playerEntityId: EntityId, on monsterEntityId: EntityId) {
init(from playerId: EntityId, on monsterId: EntityId) {
self.timestamp = Date.now
entityId = playerEntityId
self.monsterEntityId = monsterEntityId
self.entityId = playerId
self.monsterId = monsterId
}

func execute(on target: EventModifiable) {
// TODO: Determine if player is directly on top of monster.
// Trigger PlayerAttackMonsterEvent and MonsterAttackPlayerEvent accordingly
guard let positionSystem = target.system(ofType: PositionSystem.self),
let physicsSystem = target.system(ofType: PhysicsSystem.self) else {
return
}

guard let playerPosition = positionSystem.getPosition(of: entityId),
let monsterPosition = positionSystem.getPosition(of: monsterId) else {
return
}

guard let playerSize = physicsSystem.getSize(of: entityId),
let monsterSize = physicsSystem.getSize(of: monsterId) else {
return
}

let isPlayerAbove = playerPosition.y - (playerSize.height / 2) >= monsterPosition.y + (monsterSize.height / 2)

if isPlayerAbove {
target.add(event: PlayerAttackMonsterEvent(on: monsterId))
} else {
target.add(event: MonsterAttackPlayerEvent(from: monsterId, on: entityId))
}
}
}
Expand Up @@ -8,20 +8,39 @@
import Foundation

class MonsterAttackPlayerEvent: Event {
private static let damageImpulse = CGVector(dx: 500, dy: 0)

let timestamp: Date
let entityId: EntityId
let monsterId: EntityId

init(on entityId: EntityId) {
init(from monsterId: EntityId, on entityId: EntityId) {
timestamp = Date.now
self.entityId = entityId
self.monsterId = monsterId
}

func execute(on target: EventModifiable) {
guard let healthSystem = target.system(ofType: HealthSystem.self) else {
guard let healthSystem = target.system(ofType: HealthSystem.self),
let positionSystem = target.system(ofType: PositionSystem.self),
let physicsSystem = target.system(ofType: PhysicsSystem.self) else {
return
}

guard let monsterPosition = positionSystem.getPosition(of: monsterId),
let playerPosition = positionSystem.getPosition(of: entityId) else {
return
}

healthSystem.applyHealthChange(to: entityId, healthChange: GameConstants.HealthChange.attackedByMonster)

let isMonsterToRight = monsterPosition.x > playerPosition.x
let impulse = isMonsterToRight
? MonsterAttackPlayerEvent.damageImpulse * -1
: MonsterAttackPlayerEvent.damageImpulse

physicsSystem.applyImpulse(to: entityId, impulse: impulse)

if !healthSystem.hasHealth(for: entityId) {
target.add(event: PlayerDeathEvent(on: entityId))
}
Expand Down
Expand Up @@ -8,6 +8,8 @@
import Foundation

class PlayerAttackMonsterEvent: Event {
private static let attackImpulse = CGVector(dx: 0, dy: 400)

let timestamp: Date
let entityId: EntityId

Expand All @@ -17,10 +19,13 @@ class PlayerAttackMonsterEvent: Event {
}

func execute(on target: EventModifiable) {
guard let healthSystem = target.system(ofType: HealthSystem.self) else {
guard let healthSystem = target.system(ofType: HealthSystem.self),
let physicSystem = target.system(ofType: PhysicsSystem.self) else {
return
}

healthSystem.applyHealthChange(to: entityId, healthChange: GameConstants.HealthChange.attackedByPlayer)
physicSystem.applyImpulse(to: entityId, impulse: PlayerAttackMonsterEvent.attackImpulse)

if !healthSystem.hasHealth(for: entityId) {
target.add(event: MonsterDeathEvent(on: entityId))
Expand Down
7 changes: 7 additions & 0 deletions star-dash/star-dash/GameBridge/GameBridge.swift
Expand Up @@ -6,6 +6,7 @@ class GameBridge {
var scene: SDScene

var entitiesMap: [EntityId: SDObject]
var objectMap: [SDObjectId: EntityId]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe can rename to objectsMap? to be consistent with the above variable naming convention

var modules: [SyncModule]
var creationModule: CreationModule?

Expand All @@ -14,6 +15,7 @@ class GameBridge {
self.scene = scene
modules = []
entitiesMap = [:]
objectMap = [:]

registerModules()
}
Expand Down Expand Up @@ -43,6 +45,10 @@ class GameBridge {
}
}

func entityId(of objectId: SDObjectId) -> EntityId? {
objectMap[objectId]
}

private func registerModule(_ module: SyncModule) {
modules.append(module)
}
Expand Down Expand Up @@ -73,6 +79,7 @@ class GameBridge {
return
}
entitiesMap[entity.id] = newObject
objectMap[newObject.id] = entity.id

modules.forEach {
$0.create(for: newObject, from: entity)
Expand Down
6 changes: 6 additions & 0 deletions star-dash/star-dash/GameBridge/SyncModule/PhysicsModule.swift
Expand Up @@ -18,6 +18,9 @@ class PhysicsModule: SyncModule {
physicsComponent.velocity = body.velocity
// physicsComponent.force = body.force
physicsComponent.affectedByGravity = body.affectedByGravity
physicsComponent.categoryBitMask = body.categoryBitMask
physicsComponent.contactTestMask = body.contactTestMask
physicsComponent.collisionBitMask = body.collisionBitMask
}

func sync(object: SDObject, from entity: Entity) {
Expand All @@ -30,6 +33,9 @@ class PhysicsModule: SyncModule {
body.velocity = physicsComponent.velocity
// body.force = physicsComponent.force
body.affectedByGravity = physicsComponent.affectedByGravity
body.categoryBitMask = body.categoryBitMask
body.contactTestMask = body.contactTestMask
body.collisionBitMask = body.collisionBitMask
}

func create(for object: SDObject, from entity: Entity) {
Expand Down