Skip to content

Commit

Permalink
Finalise render stages
Browse files Browse the repository at this point in the history
  • Loading branch information
zheng-ze committed Mar 29, 2024
1 parent f2b7b46 commit 166cb5b
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 89 deletions.
2 changes: 2 additions & 0 deletions TowerForge/TowerForge/Scenes/Rendering/RenderStage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import Foundation

protocol RenderStage {
func createAndAdd(node: TFNode, for entity: TFEntity)
func transform(node: TFNode, for entity: TFEntity)
func update(node: TFNode, for entity: TFEntity)
}

extension RenderStage {
func createAndAdd(node: TFNode, for entity: TFEntity) {}
func transform(node: TFNode, for entity: TFEntity) {}
func update(node: TFNode, for entity: TFEntity) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// ButtonRenderStage.swift
// TowerForge
//
// Created by Zheng Ze on 29/3/24.
//

import Foundation

class ButtonRenderStage: RenderStage {
static let name = "button"
func createAndAdd(node: TFNode, for entity: TFEntity) {
guard let buttonComponent = entity.component(ofType: ButtonComponent.self) else {
return
}
let buttonNode = TFButtonNode(action: buttonComponent.onTouch, size: buttonComponent.size)
buttonNode.name = ButtonRenderStage.name
buttonNode.zPosition = 1_000
node.add(child: buttonNode)
}

func update(node: TFNode, for entity: TFEntity) {
guard let buttonComponent = entity.component(ofType: ButtonComponent.self),
let buttonNode = node.child(withName: ButtonRenderStage.name) else {
return
}
buttonNode.isUserInteractionEnabled = buttonComponent.userInteracterable
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// HealthRenderStage.swift
// TowerForge
//
// Created by Zheng Ze on 29/3/24.
//

import SpriteKit

class HealthRenderStage: RenderStage {
static let name = "health"
static let size = CGSize(width: 100, height: 10)
static let color: UIColor = .green
func createAndAdd(node: TFNode, for entity: TFEntity) {
guard entity.hasComponent(ofType: HealthComponent.self),
let spriteComponent = entity.component(ofType: SpriteComponent.self) else {
return
}

let healthNode = TFSpriteNode(color: HealthRenderStage.color, size: HealthRenderStage.size)
healthNode.name = HealthRenderStage.name
healthNode.position = CGPoint(x: -spriteComponent.size.width / 2,
y: spriteComponent.size.height / 2 + HealthRenderStage.size.height + 10)
healthNode.anchorPoint = CGPoint(x: 0, y: 0)
node.add(child: healthNode)
}

func update(node: TFNode, for entity: TFEntity) {
guard let healthNode = node.child(withName: HealthRenderStage.name),
let healthComponent = entity.component(ofType: HealthComponent.self) else {
return
}
let healthPercentage = healthComponent.currentHealth / healthComponent.maxHealth
healthNode.xScale = healthPercentage
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ import Foundation

class LabelRenderStage: RenderStage {
static let name = "label"
func transform(node: TFNode, for entity: TFEntity) {
func createAndAdd(node: TFNode, for entity: TFEntity) {
guard let labelComponent = entity.component(ofType: LabelComponent.self) else {
return
}
let labelNode = createLabelNode(with: labelComponent)
node.add(child: labelNode)

if let spriteComponent = entity.component(ofType: SpriteComponent.self) {
labelNode.position = CGPoint(x: spriteComponent.size.width, y: 0)
labelNode.position.x += labelComponent.displacement.dx
labelNode.position.y += labelComponent.displacement.dy
}
}

func update(node: TFNode, for entity: TFEntity) {
Expand All @@ -30,8 +36,9 @@ class LabelRenderStage: RenderStage {
labelNode.fontColor = labelComponent.fontColor
labelNode.fontName = labelComponent.fontName
labelNode.fontSize = labelComponent.fontSize
labelNode.horizontalAlignementMode = labelComponent.horizontalAlignment
labelNode.verticalAlignementMode = labelComponent.verticalAlignment
labelNode.horizontalAlignmentMode = labelComponent.horizontalAlignment
labelNode.verticalAlignmentMode = labelComponent.verticalAlignment
labelNode.name = LabelRenderStage.name

return labelNode
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
import Foundation

class PositionRenderStage: RenderStage {
func update(node: TFNode, for entity: TFEntity) {
func transform(node: TFNode, for entity: TFEntity) {
guard let positionComponent = entity.component(ofType: PositionComponent.self) else {
return
}

node.position = positionComponent.position
}

if let spriteNode = node.child(withName: SpriteRenderStage.name) as? TFSpriteNode {
spriteNode.anchorPoint = positionComponent.anchorPoint
func update(node: TFNode, for entity: TFEntity) {
guard let positionComponent = entity.component(ofType: PositionComponent.self) else {
return
}

node.position = positionComponent.position
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

class SpriteRenderStage: RenderStage {
static let name = "sprite"
func transform(node: TFNode, for entity: TFEntity) {
func createAndAdd(node: TFNode, for entity: TFEntity) {
guard let spriteComponent = entity.component(ofType: SpriteComponent.self) else {
return
}
Expand All @@ -19,14 +19,6 @@ class SpriteRenderStage: RenderStage {
spriteNode.playAnimation()
}

func update(node: TFNode, for entity: TFEntity) {
guard let spriteNode = node.child(withName: SpriteRenderStage.name),
let spriteComponent = entity.component(ofType: SpriteComponent.self) else {
return
}

}

private func createAnimatableNode(with spriteComponent: SpriteComponent) -> TFAnimatableNode {
let textures = spriteComponent.textures
let size = spriteComponent.size
Expand Down
57 changes: 30 additions & 27 deletions TowerForge/TowerForge/Scenes/Rendering/Renderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ class Renderer {
private unowned var target: Renderable
private unowned var scene: SKScene?

private var renderedNodes: [UUID: TFSpriteNode] = [:]
private var renderedNodes: [UUID: TFNode] = [:]
private var renderStages: [RenderStage] = []

init(target: Renderable, scene: GameScene?) {
self.target = target
self.scene = scene

setupRenderStages()
}

func render() {
var nodesToBeRemoved = renderedNodes

Expand All @@ -35,6 +39,7 @@ class Renderer {
removeAndUncache(with: entityId)
}
}

func renderMessage(_ message: String) {
guard let scene = self.scene else {
return
Expand All @@ -53,42 +58,40 @@ class Renderer {
let sequence = SKAction.sequence([fadeInAction, waitAction, fadeOutAction, removeAction])
label.run(sequence)
}

private func setupRenderStages() {
renderStages.append(SpriteRenderStage())
renderStages.append(LabelRenderStage())
renderStages.append(PositionRenderStage())
renderStages.append(HealthRenderStage())
renderStages.append(PlayerRenderStage())
renderStages.append(ButtonRenderStage())
}

private func update(entity: TFEntity) {
guard let positionComponent = entity.component(ofType: PositionComponent.self),
let node = renderedNodes[entity.id] else {
guard let node = renderedNodes[entity.id] else {
return
}

node.position = positionComponent.position
for renderStage in renderStages {
renderStage.update(node: node, for: entity)
}
}

private func addAndCache(entity: TFEntity) {
guard let spriteComponent = entity.component(ofType: SpriteComponent.self),
let positionComponent = entity.component(ofType: PositionComponent.self),
let playerComponent = entity.component(ofType: PlayerComponent.self) else {
return
}
if let labelComponent = entity.component(ofType: LabelComponent.self) {
let label = SKLabelNode(text: labelComponent.text)
label.fontName = "HelveticaNeue-Bold"
label.fontSize = 60.0
label.fontColor = .white
label.horizontalAlignmentMode = .center
label.verticalAlignmentMode = .center
label.position = CGPoint(x: spriteComponent.node.width, y: 0)
label.name = labelComponent.name
spriteComponent.node.addChild(label)
let node = TFNode()
node.name = entity.id.uuidString

for renderStage in renderStages {
renderStage.createAndAdd(node: node, for: entity)
}

let node = spriteComponent.node
// Flips the image if it is the opposite team
if playerComponent.player == .oppositePlayer {
node.xScale *= -1
for renderStage in renderStages {
renderStage.transform(node: node, for: entity)
}
node.position = positionComponent.position
node.playAnimation()
node.name = entity.id.uuidString

renderedNodes[entity.id] = node
scene?.addChild(node)
scene?.addChild(node.node)
}

private func removeAndUncache(with id: UUID) {
Expand Down
1 change: 1 addition & 0 deletions TowerForge/TowerForge/TFCore/TFAnimatableNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TFAnimatableNode: TFSpriteNode, Animatable {

init(textures: TFTextures, size: CGSize, animatableKey: String) {
self.animatableKey = animatableKey
self.textures = textures
super.init(textures: textures, size: size)
}

Expand Down
44 changes: 0 additions & 44 deletions TowerForge/TowerForge/TFCore/TFButton.swift

This file was deleted.

41 changes: 41 additions & 0 deletions TowerForge/TowerForge/TFCore/TFButtonNode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// TFButton.swift
// TowerForge
//
// Created by Vanessa Mae on 20/03/24.
//

import SpriteKit

struct TFButtonDelegate {
var onTouchBegan: () -> Void
var onTouchEnded: () -> Void
}

private class TFButton: SKSpriteNode {
private var action: TFButtonDelegate?
init(buttonAction: TFButtonDelegate?, size: CGSize) {
self.action = buttonAction
super.init(texture: nil, color: .clear, size: size)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
action?.onTouchBegan()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
action?.onTouchEnded()
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

class TFButtonNode: TFNode {
init(action: TFButtonDelegate?, size: CGSize) {
super.init()
node = TFButton(buttonAction: action, size: size)
}
}
4 changes: 2 additions & 2 deletions TowerForge/TowerForge/TFCore/TFLabelNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ class TFLabelNode: TFNode {
set(fontSize) { labelNode.fontSize = fontSize }
}

var horizontalAlignementMode: AlignmentMode {
var horizontalAlignmentMode: AlignmentMode {
get { AlignmentMode(horizontalAlignment: labelNode.horizontalAlignmentMode) }
set(alignmentMode) { labelNode.horizontalAlignmentMode = alignmentMode.horizontalAlignmentMode }
}

var verticalAlignementMode: AlignmentMode {
var verticalAlignmentMode: AlignmentMode {
get { AlignmentMode(verticalAlignment: labelNode.verticalAlignmentMode) }
set(alignmentMode) { labelNode.verticalAlignmentMode = alignmentMode.verticalALignmentMode }
}
Expand Down
5 changes: 5 additions & 0 deletions TowerForge/TowerForge/TFCore/TFNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class TFNode {
node.frame.size
}

var isUserInteractionEnabled: Bool {
get { node.isUserInteractionEnabled }
set(isEnabled) { node.isUserInteractionEnabled = isEnabled }
}

init() {
node = SKNode()
}
Expand Down
5 changes: 5 additions & 0 deletions TowerForge/TowerForge/TFCore/TFSpriteNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class TFSpriteNode: TFNode {
set(size) { spriteNode.size = size }
}

init(color: UIColor, size: CGSize) {
super.init()
node = SKSpriteNode(color: color, size: size)
}

init(textures: TFTextures?, size: CGSize) {
super.init()
node = SKSpriteNode(texture: textures?.mainTexture, color: .clear, size: size)
Expand Down

0 comments on commit 166cb5b

Please sign in to comment.