Skip to content

Commit

Permalink
Use a queue for sprite animations
Browse files Browse the repository at this point in the history
  • Loading branch information
STREGA committed Dec 8, 2023
1 parent 0ba6bab commit 8298869
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 18 deletions.
50 changes: 41 additions & 9 deletions Sources/GateEngine/ECS/2D Specific/Sprite/SpriteComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,50 @@ public final class SpriteComponent: Component {
}

public var animations: [SpriteAnimation]
public var activeAnimationIndex: Int
public var activeAnimationIndex: Int? {
return animationQueue.first
}
internal var activeAnimationIndexDidChange: Bool = true
public var animationQueue: [Int] {
didSet {
self.moveToNextAnimationIfNeeded = false
self.activeAnimationIndexDidChange = true
}
}
internal var moveToNextAnimationIfNeeded: Bool = false
public var activeAnimation: SpriteAnimation? {
get {
if animations.indices.contains(activeAnimationIndex) {
return animations[activeAnimationIndex]
if let index = self.animationQueue.first {
return self.animations[index]
}
return nil
}
set {
guard let newValue else { return }
animations[activeAnimationIndex] = newValue
if let newValue {
if let index = self.animationQueue.first {
self.animations[index] = newValue
}
}
}
}

/// Appends the given animation index to the end of the animation queue.
public func queueAnimation(_ animationIndex: Int) {
assert(animations.indices.contains(animationIndex), "Animations does not have an index \(animationIndex).")
animationQueue.append(animationIndex)
}

/// Replaces the entire animation queue with the given animation index.
public func setAnimation(_ animationIndex: Int) {
assert(animations.indices.contains(animationIndex), "Animations does not have an index \(animationIndex).")
animationQueue = [animationIndex]
}

/// Removes all animations form the animation queue.
public func clearAnimationQueue() {
animationQueue.removeAll(keepingCapacity: true)
}

public enum PlaybackState {
/// Moves through the active animation over time
case play
Expand All @@ -61,6 +91,8 @@ public final class SpriteComponent: Component {
case stop
/// Locks the active animation at the first frame next time it's encountered
case stopAtLoop
/// If the animation queue has another animation it will begin on the next last frame of the current animation
case playNextAnimationAtLoop
}
public var playbackState: PlaybackState

Expand All @@ -78,7 +110,7 @@ public final class SpriteComponent: Component {
let startFrame = (animation.spriteSheetStart.y * columns) + animation.spriteSheetStart.x
let endFrame = {
if let frameCount = animation.frameCount {
return startFrame + frameCount
return startFrame + (frameCount - 1)
}
let framesInAnimation = (columns * rows) - startFrame
return framesInAnimation
Expand All @@ -100,23 +132,23 @@ public final class SpriteComponent: Component {
public init() {
self.spriteRect = .zero
self.spriteSheet = nil
self.activeAnimationIndex = 0
self.animationQueue = [0]
self.animations = []
self.playbackState = .play
}

public init(spriteRect: Rect, spriteSheet: SpriteSheet, activeAnimationIndex: Int = 0, animations: [SpriteAnimation], playbackState: PlaybackState = .play) {
self.spriteRect = spriteRect
self.spriteSheet = spriteSheet
self.activeAnimationIndex = activeAnimationIndex
self.animationQueue = [activeAnimationIndex]
self.animations = animations
self.playbackState = playbackState
}

public init(spriteSize: Size2, spriteSheet: SpriteSheet, activeAnimationIndex: Int = 0, animations: [SpriteAnimation], playbackState: PlaybackState = .play) {
self.spriteRect = Rect(size: spriteSize)
self.spriteSheet = spriteSheet
self.activeAnimationIndex = activeAnimationIndex
self.animationQueue = [activeAnimationIndex]
self.animations = animations
self.playbackState = playbackState
}
Expand Down
46 changes: 37 additions & 9 deletions Sources/GateEngine/ECS/2D Specific/Sprite/SpriteSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,44 @@ public final class SpriteSystem: System {
public override func update(game: Game, input: HID, withTimePassed deltaTime: Float) async {
for entity in game.entities {
if let spriteComponent = entity.component(ofType: SpriteComponent.self) {
if spriteComponent.moveToNextAnimationIfNeeded {
spriteComponent.moveToNextAnimationIfNeeded = false
if spriteComponent.activeAnimation?.repeats == true {
let progress = spriteComponent.activeAnimation?.progress ?? 0
spriteComponent.animationQueue.removeFirst()
spriteComponent.activeAnimation?.progress = progress
spriteComponent.activeAnimationIndexDidChange = false
}else{
spriteComponent.animationQueue.removeFirst()
}
}
if spriteComponent.activeAnimationIndexDidChange {
spriteComponent.activeAnimationIndexDidChange = false
spriteComponent.activeAnimation?.progress = 0
}
switch spriteComponent.playbackState {
case .play, .stopAtLoop, .pauseAtLoop:
if spriteComponent.animations.indices.contains(spriteComponent.activeAnimationIndex) {
let didRepeat = spriteComponent.animations[spriteComponent.activeAnimationIndex].didRepeatAfterAppendingTime(deltaTime)
if didRepeat {
if spriteComponent.playbackState == .stopAtLoop {
spriteComponent.playbackState = .stop
fallthrough
}else if spriteComponent.playbackState == .pauseAtLoop {
spriteComponent.playbackState = .pause
case .play, .stopAtLoop, .pauseAtLoop, .playNextAnimationAtLoop:
if let activeAnimationIndex = spriteComponent.animationQueue.first {
if spriteComponent.animations.indices.contains(activeAnimationIndex) {
let didRepeat = spriteComponent.animations[activeAnimationIndex].didRepeatAfterAppendingTime(deltaTime)
if didRepeat {
if spriteComponent.playbackState == .playNextAnimationAtLoop {
spriteComponent.moveToNextAnimationIfNeeded = true
spriteComponent.playbackState = .play
}else if spriteComponent.playbackState == .stopAtLoop {
spriteComponent.playbackState = .stop
fallthrough
}else if spriteComponent.playbackState == .pauseAtLoop {
spriteComponent.playbackState = .pause
}
}else if spriteComponent.animationQueue.count > 1 {
if spriteComponent.playbackState == .play {
if let activeAnimation = spriteComponent.activeAnimation {
if activeAnimation.isFinished {
spriteComponent.animationQueue.removeFirst()
}
}
}
}
}
}
Expand Down

0 comments on commit 8298869

Please sign in to comment.