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

Feat/rendering multiplayer #33

Merged
merged 16 commits into from Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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: 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
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the camera only follow the x position of the player?

Copy link
Contributor

Choose a reason for hiding this comment

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

By doing so, we need to initialize y position of camera to half of the height of the scene.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed it was previously only tracking the x position, but the player may jump up to higher platforms and exit the height of what the view can see...

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm that's true. My rationale for fixing the camera's y position was because if the camera track's the player's y position as well, then we may run out of the background image when going up. I guess it can be said that levels should not be limited in height (maybe can have a level that's focused in the sky or something), but with the lack of assets, would it be better to just cap how high the player can go?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we resolve this after we have a complete level ready? We can test then what feels most normal/natural. We can create an issue in the meantime

Copy link
Contributor

Choose a reason for hiding this comment

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

okay that sounds good!

}
}
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)
}
50 changes: 35 additions & 15 deletions star-dash/star-dash.xcodeproj/project.pbxproj
Expand Up @@ -103,13 +103,16 @@
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 */; };
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 @@ -253,13 +256,16 @@
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>"; };
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 @@ -305,6 +311,7 @@
isa = PBXGroup;
children = (
143AA38E2BA4D61A009C28E7 /* CGVector.swift */,
E64F31662BB3D14600EC5371 /* UIView.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -614,6 +621,18 @@
path = SyncModule;
sourceTree = "<group>";
};
E64F31682BB400CB00EC5371 /* PlayerView */ = {
isa = PBXGroup;
children = (
E6A7451A2BA0C1890080C1BE /* PlayerView.swift */,
E6A7451C2BA0CAD90080C1BE /* JoystickView.swift */,
E6A745122BA057040080C1BE /* ControlView.swift */,
E6B550A02BA15E9C00DC7396 /* OverlayView.swift */,
E6B0AAD22BAAE438009CB939 /* ControlViewDelegate.swift */,
);
path = PlayerView;
sourceTree = "<group>";
};
E6A011152BA5D111006904D9 /* GameBridge */ = {
isa = PBXGroup;
children = (
Expand All @@ -624,14 +643,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 @@ -643,18 +672,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 */,
);
path = MTKRenderer;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -845,6 +862,7 @@
4E86605F2BA095E30035530D /* Collectible.swift in Sources */,
E64361122BA4C2CD003850FD /* ObjectModule.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 */,
Expand Down Expand Up @@ -887,6 +905,7 @@
E64361142BA4C2CD003850FD /* CreationModule.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 @@ -905,6 +924,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
2 changes: 1 addition & 1 deletion star-dash/star-dash/Constants/PhysicsConstants.swift
Expand Up @@ -52,6 +52,6 @@ struct PhysicsConstants {
}

static let jumpImpulse = CGVector(dx: 15, dy: 250)
static let runVelocity = CGVector(dx: 15, dy: 0)
static let runVelocity = CGVector(dx: 60, dy: 0)
static let maxRunVelocity = CGVector(dx: 250, dy: 0)
}
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
11 changes: 7 additions & 4 deletions star-dash/star-dash/GameEngine/Entities/EntityManager.swift
Expand Up @@ -58,10 +58,13 @@ 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? {
// TODO: Add parameter to specify the player index
Junyi00 marked this conversation as resolved.
Show resolved Hide resolved
for entityId in entityMap.keys {
if let playerComponent = component(ofType: PlayerComponent.self, of: entityId),
playerComponent.playerIndex == playerIndex {
return entityId
}
}

return nil
Expand Down
24 changes: 8 additions & 16 deletions star-dash/star-dash/GameEngine/GameEngine.swift
Expand Up @@ -20,9 +20,9 @@ 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
}
Expand All @@ -47,16 +47,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 @@ -65,22 +65,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
6 changes: 0 additions & 6 deletions star-dash/star-dash/Rendering/ControlViewDelegate.swift

This file was deleted.