Skip to content

Commit

Permalink
Merge pull request #33 from cs3217-2324/feat/rendering-multiplayer
Browse files Browse the repository at this point in the history
Feat/rendering multiplayer
  • Loading branch information
Junyi00 committed Mar 28, 2024
2 parents 5aca934 + 208ff9e commit ba6c8ee
Show file tree
Hide file tree
Showing 23 changed files with 424 additions and 250 deletions.
36 changes: 26 additions & 10 deletions SDPhysicsEngine/Sources/SDPhysicsEngine/GameScene.swift
Expand Up @@ -8,6 +8,8 @@ public class GameScene: SKScene {

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

private var cameraPlayerMap: [Int: SDCameraObject] = [:]

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

Expand All @@ -21,15 +23,38 @@ public class GameScene: SKScene {
lastUpdateTime = currentTime
return
}

let deltaTime = currentTime - lastUpdateTime
self.lastUpdateTime = currentTime

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

func updateCameras() {
for camera in cameraPlayerMap.values {
camera.update()
}
}

public func useCamera(of playerIndex: Int, rotatedBy rotation: CGFloat) {
guard let cameraObject = cameraPlayerMap[playerIndex] else {
return
}

cameraObject.zRotation = rotation
self.camera = cameraObject.cameraNode
}
}

extension GameScene: SDScene {
public func addPlayerObject(_ playerObject: SDObject, playerIndex: Int) {
let camera = SDCameraObject(player: playerObject)
cameraPlayerMap[playerIndex] = camera

addObject(camera)
addObject(playerObject)
}

public func addObject(_ object: SDObject) {
guard objectMap[object.node] == nil else {
return
Expand All @@ -43,15 +68,6 @@ extension GameScene: SDScene {
objectMap[object.node] = nil
object.removeFromParent()
}

public func addCameraObject(_ cameraObject: SDCameraObject) {
addObject(cameraObject)
camera = cameraObject.cameraNode
}

public func setCameraObjectXPosition(to x: CGFloat) {
camera?.position.x = x
}
}

extension GameScene: SKPhysicsContactDelegate {
Expand Down
Expand Up @@ -2,9 +2,29 @@ import SpriteKit

public class SDCameraObject: SDObject {
let cameraNode: SKCameraNode
let player: SDObject?

override public init() {
player = nil
cameraNode = SKCameraNode()
super.init(node: cameraNode)
}

init(player: SDObject) {
self.player = player
cameraNode = SKCameraNode()
super.init(node: cameraNode)
}

var zRotation: CGFloat {
get { cameraNode.zRotation }
set { cameraNode.zRotation = newValue }
}

func update() {
guard let player = self.player else {
return
}
self.position = player.position
}
}
3 changes: 1 addition & 2 deletions SDPhysicsEngine/Sources/SDPhysicsEngine/SDScene.swift
Expand Up @@ -4,8 +4,7 @@ public protocol SDScene {

var size: CGSize { get }

func addPlayerObject(_ playerObject: SDObject, playerIndex: Int)
func addObject(_ object: SDObject)
func removeObject(_ object: SDObject)
func addCameraObject(_ cameraObject: SDCameraObject)
func setCameraObjectXPosition(to x: CGFloat)
}
66 changes: 48 additions & 18 deletions star-dash/star-dash.xcodeproj/project.pbxproj
Expand Up @@ -55,7 +55,6 @@
46D418242BA5D5280091A38B /* Tool+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418232BA5D5280091A38B /* Tool+Collidable.swift */; };
46D418262BA5D6500091A38B /* Wall+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418252BA5D6500091A38B /* Wall+Collidable.swift */; };
46D418282BA5D6800091A38B /* Floor+Collidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D418272BA5D6800091A38B /* Floor+Collidable.swift */; };
4E0905FD2BB4A15600DE666B /* MiniMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0905FC2BB4A15600DE666B /* MiniMapView.swift */; };
4E0905FF2BB4A4EB00DE666B /* PlayerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0905FE2BB4A4EB00DE666B /* PlayerInfo.swift */; };
4E3458DF2BA7490B00E817C6 /* EntityManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3458DE2BA7490B00E817C6 /* EntityManagerTests.swift */; };
4E3458E52BA75E6800E817C6 /* PositionComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3458E42BA75E6800E817C6 /* PositionComponentTests.swift */; };
Expand Down Expand Up @@ -108,13 +107,17 @@
E64361132BA4C2CD003850FD /* PhysicsModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E643610D2BA4C2CC003850FD /* PhysicsModule.swift */; };
E64361142BA4C2CD003850FD /* CreationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E643610E2BA4C2CC003850FD /* CreationModule.swift */; };
E64361152BA4C2CD003850FD /* GameBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = E643610F2BA4C2CC003850FD /* GameBridge.swift */; };
E64F31672BB3D14600EC5371 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64F31662BB3D14600EC5371 /* UIView.swift */; };
E660353D2BB5EAEE00397068 /* MiniMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E660353C2BB5EAEE00397068 /* MiniMapView.swift */; };
E69EE9322BAC6CBB00033AB5 /* GameInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69EE9312BAC6CBB00033AB5 /* GameInfo.swift */; };
E69EE9342BAC6CC300033AB5 /* OverlayInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69EE9332BAC6CC300033AB5 /* OverlayInfo.swift */; };
E69FDDE02BAD3DAD0089D5F3 /* PointsComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69FDDDF2BAD3DAD0089D5F3 /* PointsComponent.swift */; };
E69FDDE22BAD3DC40089D5F3 /* EntityConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69FDDE12BAD3DC40089D5F3 /* EntityConstants.swift */; };
E69FDDE42BADC2F10089D5F3 /* SpriteConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69FDDE32BADC2F10089D5F3 /* SpriteConstants.swift */; };
E69FDDE62BADD11B0089D5F3 /* StopMovingEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69FDDE52BADD11B0089D5F3 /* StopMovingEvent.swift */; };
E6A011172BA5F4AD006904D9 /* EntitySyncInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A011162BA5F4AD006904D9 /* EntitySyncInterface.swift */; };
E6A72FC52BB2E1E10015729E /* PlayerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A72FC32BB2E1E10015729E /* PlayerViewLayout.swift */; };
E6A72FC62BB2E1E10015729E /* LayoutUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A72FC42BB2E1E10015729E /* LayoutUtils.swift */; };
E6A745162BA057040080C1BE /* MTKRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A745112BA057040080C1BE /* MTKRenderer.swift */; };
E6A745172BA057040080C1BE /* ControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A745122BA057040080C1BE /* ControlView.swift */; };
E6A745182BA057040080C1BE /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A745132BA057040080C1BE /* Renderer.swift */; };
Expand Down Expand Up @@ -263,13 +266,17 @@
E643610D2BA4C2CC003850FD /* PhysicsModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhysicsModule.swift; sourceTree = "<group>"; };
E643610E2BA4C2CC003850FD /* CreationModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreationModule.swift; sourceTree = "<group>"; };
E643610F2BA4C2CC003850FD /* GameBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameBridge.swift; sourceTree = "<group>"; };
E64F31662BB3D14600EC5371 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
E660353C2BB5EAEE00397068 /* MiniMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiniMapView.swift; sourceTree = "<group>"; };
E69EE9312BAC6CBB00033AB5 /* GameInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameInfo.swift; sourceTree = "<group>"; };
E69EE9332BAC6CC300033AB5 /* OverlayInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlayInfo.swift; sourceTree = "<group>"; };
E69FDDDF2BAD3DAD0089D5F3 /* PointsComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointsComponent.swift; sourceTree = "<group>"; };
E69FDDE12BAD3DC40089D5F3 /* EntityConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityConstants.swift; sourceTree = "<group>"; };
E69FDDE32BADC2F10089D5F3 /* SpriteConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpriteConstants.swift; sourceTree = "<group>"; };
E69FDDE52BADD11B0089D5F3 /* StopMovingEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StopMovingEvent.swift; sourceTree = "<group>"; };
E6A011162BA5F4AD006904D9 /* EntitySyncInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntitySyncInterface.swift; sourceTree = "<group>"; };
E6A72FC32BB2E1E10015729E /* PlayerViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerViewLayout.swift; sourceTree = "<group>"; };
E6A72FC42BB2E1E10015729E /* LayoutUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutUtils.swift; sourceTree = "<group>"; };
E6A745112BA057040080C1BE /* MTKRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MTKRenderer.swift; sourceTree = "<group>"; };
E6A745122BA057040080C1BE /* ControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlView.swift; sourceTree = "<group>"; };
E6A745132BA057040080C1BE /* Renderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -315,6 +322,7 @@
isa = PBXGroup;
children = (
143AA38E2BA4D61A009C28E7 /* CGVector.swift */,
E64F31662BB3D14600EC5371 /* UIView.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -507,6 +515,7 @@
142D9F8E2BA15FBB005FE9E0 /* .swiftlint.yml */,
4E630EF32B9F7E070008F887 /* Products */,
E6B1DC912BA34A5E00473563 /* Frameworks */,
E660353B2BB5EAD300397068 /* Recovered References */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -636,6 +645,27 @@
path = SyncModule;
sourceTree = "<group>";
};
E64F31682BB400CB00EC5371 /* PlayerView */ = {
isa = PBXGroup;
children = (
E660353C2BB5EAEE00397068 /* MiniMapView.swift */,
E6A7451A2BA0C1890080C1BE /* PlayerView.swift */,
E6A7451C2BA0CAD90080C1BE /* JoystickView.swift */,
E6A745122BA057040080C1BE /* ControlView.swift */,
E6B550A02BA15E9C00DC7396 /* OverlayView.swift */,
E6B0AAD22BAAE438009CB939 /* ControlViewDelegate.swift */,
);
path = PlayerView;
sourceTree = "<group>";
};
E660353B2BB5EAD300397068 /* Recovered References */ = {
isa = PBXGroup;
children = (
4E0905FC2BB4A15600DE666B /* MiniMapView.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
};
E6A011152BA5D111006904D9 /* GameBridge */ = {
isa = PBXGroup;
children = (
Expand All @@ -646,14 +676,24 @@
path = GameBridge;
sourceTree = "<group>";
};
E6A72FC22BB2E1E10015729E /* Utils */ = {
isa = PBXGroup;
children = (
E6A72FC32BB2E1E10015729E /* PlayerViewLayout.swift */,
E6A72FC42BB2E1E10015729E /* LayoutUtils.swift */,
);
path = Utils;
sourceTree = "<group>";
};
E6A745102BA057040080C1BE /* Rendering */ = {
isa = PBXGroup;
children = (
E69EE9332BAC6CC300033AB5 /* OverlayInfo.swift */,
E6B5509F2BA15D2000DC7396 /* MTKRenderer */,
E6A72FC22BB2E1E10015729E /* Utils */,
E64F31682BB400CB00EC5371 /* PlayerView */,
E6A745132BA057040080C1BE /* Renderer.swift */,
E6A745112BA057040080C1BE /* MTKRenderer.swift */,
E6B0AAD02BAAE3DC009CB939 /* ViewDelegate.swift */,
E6B0AAD22BAAE438009CB939 /* ControlViewDelegate.swift */,
E69EE9332BAC6CC300033AB5 /* OverlayInfo.swift */,
);
path = Rendering;
sourceTree = "<group>";
Expand All @@ -665,19 +705,6 @@
name = Frameworks;
sourceTree = "<group>";
};
E6B5509F2BA15D2000DC7396 /* MTKRenderer */ = {
isa = PBXGroup;
children = (
E6A745112BA057040080C1BE /* MTKRenderer.swift */,
E6A7451A2BA0C1890080C1BE /* PlayerView.swift */,
E6A7451C2BA0CAD90080C1BE /* JoystickView.swift */,
E6A745122BA057040080C1BE /* ControlView.swift */,
E6B550A02BA15E9C00DC7396 /* OverlayView.swift */,
4E0905FC2BB4A15600DE666B /* MiniMapView.swift */,
);
path = MTKRenderer;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -870,11 +897,11 @@
E64361122BA4C2CD003850FD /* ObjectModule.swift in Sources */,
4E0905FF2BB4A4EB00DE666B /* PlayerInfo.swift in Sources */,
4E59E2532BAB3061007B3FA7 /* Database.swift in Sources */,
E6A72FC52BB2E1E10015729E /* PlayerViewLayout.swift in Sources */,
4E8660662BA097D40035530D /* PhysicsConstants.swift in Sources */,
143AA3952BA4DF1C009C28E7 /* MonsterAttackPlayerEvent.swift in Sources */,
143AA3932BA4DBE7009C28E7 /* PlayerMonsterContactEvent.swift in Sources */,
4E59E2682BADA7B6007B3FA7 /* CollectibleEntityPersistable.swift in Sources */,
4E0905FD2BB4A15600DE666B /* MiniMapView.swift in Sources */,
46D418162BA5CBD60091A38B /* Collidable.swift in Sources */,
461148912BA1CDBF0073E7E1 /* SystemManager.swift in Sources */,
460A20112BB1E6DF002597B8 /* MonsterSystem.swift in Sources */,
Expand All @@ -899,6 +926,7 @@
4604BBD92BA81C940078B84C /* InventorySystem.swift in Sources */,
14E247952BA2CB480071FFC0 /* EventManager.swift in Sources */,
4EC561E32BB1E98400166DDC /* PlayerObstacleContactEvent.swift in Sources */,
E660353D2BB5EAEE00397068 /* MiniMapView.swift in Sources */,
4E630EF82B9F7E070008F887 /* SceneDelegate.swift in Sources */,
46C3B1C22BB467BB00F10574 /* EntityManagerInterface.swift in Sources */,
E69FDDE02BAD3DAD0089D5F3 /* PointsComponent.swift in Sources */,
Expand All @@ -915,6 +943,7 @@
46C3B1C42BB5055F00F10574 /* EntityBuilder.swift in Sources */,
4E59E2612BAB42FD007B3FA7 /* LevelPersistable.swift in Sources */,
460A200B2BB1D04F002597B8 /* AttackSystem.swift in Sources */,
E6A72FC62BB2E1E10015729E /* LayoutUtils.swift in Sources */,
E6A745162BA057040080C1BE /* MTKRenderer.swift in Sources */,
E6A745182BA057040080C1BE /* Renderer.swift in Sources */,
4E59E25F2BAB4134007B3FA7 /* EntityType.swift in Sources */,
Expand All @@ -933,6 +962,7 @@
4E630F372B9F91DE0008F887 /* PlayerSprite.swift in Sources */,
14970F502BA814D500CC1E8A /* ScoreComponent.swift in Sources */,
E69FDDE42BADC2F10089D5F3 /* SpriteConstants.swift in Sources */,
E64F31672BB3D14600EC5371 /* UIView.swift in Sources */,
143AA3912BA4D7AC009C28E7 /* MonsterDeathEvent.swift in Sources */,
E64361102BA4C2CD003850FD /* SpriteModule.swift in Sources */,
4604BBD52BA819C70078B84C /* InventoryComponent.swift in Sources */,
Expand Down
13 changes: 13 additions & 0 deletions star-dash/star-dash/Extensions/UIView.swift
@@ -0,0 +1,13 @@
import UIKit

/**
* The extension introduces an initisalisation method to add subviews
* to a frame that is rotated, however the view is not rotated.
*/
extension UIView {
convenience init(frame: CGRect, rotatedBy rotation: CGFloat) {
self.init(frame: frame.applying(CGAffineTransform(rotationAngle: rotation)))
self.transform = CGAffineTransform(rotationAngle: rotation)
self.center = CGPoint(x: frame.midX, y: frame.midY)
}
}
9 changes: 8 additions & 1 deletion star-dash/star-dash/GameBridge/GameBridge.swift
Expand Up @@ -85,7 +85,14 @@ class GameBridge {
modules.forEach {
$0.create(for: newObject, from: entity)
}
self.scene.addObject(newObject)

if type(of: entity) == Player.self {
if let playerIndex = entityManager.component(ofType: PlayerComponent.self, of: entity.id)?.playerIndex {
self.scene.addPlayerObject(newObject, playerIndex: playerIndex)
}
} else {
self.scene.addObject(newObject)
}
}

private func removeObject(from entityId: EntityId) {
Expand Down
10 changes: 6 additions & 4 deletions star-dash/star-dash/GameEngine/Entities/EntityManager.swift
Expand Up @@ -73,10 +73,12 @@ class EntityManager {
entityMap[entityId]
}

func playerEntityId() -> EntityId? {
// TODO: Add parameter to specify the player index
for entityId in entityMap.keys where component(ofType: PlayerComponent.self, of: entityId) != nil {
return entityId
func playerEntityId(with playerIndex: Int) -> EntityId? {
for entityId in entityMap.keys {
if let playerComponent = component(ofType: PlayerComponent.self, of: entityId),
playerComponent.playerIndex == playerIndex {
return entityId
}
}

return nil
Expand Down
30 changes: 11 additions & 19 deletions star-dash/star-dash/GameEngine/GameEngine.swift
Expand Up @@ -20,21 +20,21 @@ class GameEngine {
setUpSystems()
}

func gameInfo() -> GameInfo? {
func gameInfo(forPlayer playerIndex: Int) -> GameInfo? {
guard let scoreSystem = systemManager.system(ofType: ScoreSystem.self),
let playerEntityId = entityManager.playerEntityId(),
let playerEntityId = entityManager.playerEntityId(with: playerIndex),
let score = scoreSystem.score(of: playerEntityId) else {
return nil
}

return GameInfo(
playerScore: score,
playersInfo: playersInfo()
playersInfo: playersInfo(of: playerIndex)
)
}

func playersInfo() -> [PlayerInfo] {
guard let playerEntityId = entityManager.playerEntityId(),
func playersInfo(of playerIndex: Int) -> [PlayerInfo] {
guard let playerEntityId = entityManager.playerEntityId(with: playerIndex),
let positionSystem = systemManager.system(ofType: PositionSystem.self) else {
return []
}
Expand All @@ -61,16 +61,16 @@ class GameEngine {
eventManager.add(event: event)
}

func handlePlayerJump() {
guard let playerEntityId = entityManager.playerEntityId() else {
func handlePlayerJump(playerIndex: Int) {
guard let playerEntityId = entityManager.playerEntityId(with: playerIndex) else {
return
}

eventManager.add(event: JumpEvent(on: playerEntityId, by: PhysicsConstants.jumpImpulse))
}

func handlePlayerMove(toLeft: Bool) {
guard let playerEntityId = entityManager.playerEntityId(),
func handlePlayerMove(toLeft: Bool, playerIndex: Int) {
guard let playerEntityId = entityManager.playerEntityId(with: playerIndex),
let playerComponent = entityManager.component(ofType: PlayerComponent.self, of: playerEntityId),
playerComponent.canMove else {
return
Expand All @@ -79,22 +79,14 @@ class GameEngine {
eventManager.add(event: MoveEvent(on: playerEntityId, toLeft: toLeft))
}

func handlePlayerStoppedMoving() {
guard let playerEntityId = entityManager.playerEntityId() else {
func handlePlayerStoppedMoving(playerIndex: Int) {
guard let playerEntityId = entityManager.playerEntityId(with: playerIndex) else {
return
}

eventManager.add(event: StopMovingEvent(on: playerEntityId))
}

func playerPosition() -> CGPoint? {
guard let playerEntityId = entityManager.playerEntityId(),
let positionComponent = entityManager.component(ofType: PositionComponent.self, of: playerEntityId) else {
return nil
}
return positionComponent.position
}

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

0 comments on commit ba6c8ee

Please sign in to comment.