Last active
July 6, 2017 03:50
-
-
Save bengsfort/81024aa7cb1ea7cc0ceec7b45bb2c08b to your computer and use it in GitHub Desktop.
Full implementation of an animated section end trigger using ECS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Bengsfort.Components.Core; | |
using UnityEngine; | |
namespace Bengsfort.Components | |
{ | |
// This component handles the visual rendering of the section end component. | |
[Serializable] | |
public class SectionEndComponent : BaseComponent | |
{ | |
public SpriteRenderer renderer; | |
public Sprite unlockedSprite; | |
public Sprite lockedSprite; | |
public float spriteTick = 0.1f; | |
// These are state related and handled at run time, so we don't need to serialize them. | |
[NonSerialized] | |
public float lastTick; | |
[NonSerialized] | |
public bool unlockSpriteActive; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using Bengsfort.Components; | |
using Bengsfort.Systems; | |
namespace Bengsfort.Entities | |
{ | |
// MonoBehaviour's are treated as entities since they can subscribe to the built-in | |
// Unity API messages. The components attached to the entities are serializable, so | |
// you can edit/tweak the default values for them in the editor like any other | |
// serializable properties. | |
[RequireComponent(typeof(CircleCollider2D))] | |
public class SectionEndEntity : MonoBehaviour | |
{ | |
// The components relevant to this entity. These can be tweaked in-editor. | |
public TimedUnlockComponent timedUnlockComponent; | |
public SectionEndComponent sectionEndComponent; | |
// The systems relevant to this entity. | |
private SectionEndSystem m_SectionEndSystem; | |
// Instantiate any required systems on Start. | |
void Start() | |
{ | |
m_SectionEndSystem = new SectionEndSystem(timedUnlockComponent, sectionEndComponent); | |
} | |
// This particular system should Tick every Update. | |
void Update() | |
{ | |
m_SectionEndSystem.Tick(); | |
} | |
// This system depends on a trigger, so initialize that when needed. | |
void OnTriggerEnter2D(Collider2D collision) | |
{ | |
if (timedUnlockComponent.unlocked) | |
return; | |
if (collision.CompareTag("Player")) | |
m_SectionEndSystem.StartTimer(); | |
} | |
// Reset it when the player leaves the collider. | |
void OnTriggerExit2D(Collider2D collision) | |
{ | |
if (timedUnlockComponent.unlocked) | |
return; | |
if (collision.CompareTag("Player")) | |
m_SectionEndSystem.ResetTimer(); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Bengsfort.Components; | |
using UnityEngine; | |
namespace Bengsfort.Systems | |
{ | |
// Technically this *should/could* just inherit from the base system, but I figured | |
// it didn't hurt to just use inheritence here. | |
public class SectionEndSystem : TimedUnlockSystem | |
{ | |
// Cached component state. | |
private SectionEndComponent m_SectionEnd; | |
// When the entity is 'unlocked'... | |
protected override void Unlock() | |
{ | |
// Let the timer do what it needs to do. | |
base.Unlock(); | |
// Increment the static stage state's active section property. | |
StageComponent.Instance.activeSection += 1; | |
StageComponent.Instance.sectionChanged = true; | |
// Reset the visual apperance of the trigger. | |
m_SectionEnd.lastTick = -1.0f; | |
m_SectionEnd.renderer.sprite = m_SectionEnd.unlockedSprite; | |
m_SectionEnd.unlockSpriteActive = true; | |
} | |
// Start our timer and also begin updating the visuals for the entity | |
public override void StartTimer() | |
{ | |
base.StartTimer(); | |
// Default to unlocked sprite to give player immediate feedback | |
m_SectionEnd.lastTick = Time.time; | |
m_SectionEnd.renderer.sprite = m_SectionEnd.unlockedSprite; | |
m_SectionEnd.unlockSpriteActive = true; | |
} | |
// Reset the timer when the player doesn't successfully unlock the timer. | |
public override void ResetTimer() | |
{ | |
// Let the timer do what it needs to do... | |
base.ResetTimer(); | |
// Then reset the visuals of the entity. | |
m_SectionEnd.lastTick = -1.0f; | |
m_SectionEnd.renderer.sprite = m_SectionEnd.lockedSprite; | |
m_SectionEnd.unlockSpriteActive = false; | |
} | |
public override void Tick() | |
{ | |
base.Tick(); | |
if (m_TimedUnlock.beingUnlocked) | |
{ | |
// If the entity is being unlocked and the sprite flash duration has elapsed | |
if (m_SectionEnd.lastTick + m_SectionEnd.spriteTick <= Time.time) | |
{ | |
// Swap the sprite | |
m_SectionEnd.lastTick = Time.time; | |
m_SectionEnd.renderer.sprite = m_SectionEnd.unlockSpriteActive | |
? m_SectionEnd.lockedSprite | |
: m_SectionEnd.unlockedSprite; | |
m_SectionEnd.unlockSpriteActive = !m_SectionEnd.unlockSpriteActive; | |
} | |
} | |
} | |
// Cache the relevant components | |
public SectionEndSystem(TimedUnlockComponent timer, SectionEndComponent sectionEnd) | |
{ | |
m_TimedUnlock = timer; | |
m_SectionEnd = sectionEnd; | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Bengsfort.Components.Core; | |
namespace Bengsfort.Components | |
{ | |
// This component deals with unlocking something via a specified-length timer. | |
[Serializable] | |
public class TimedUnlockComponent : BaseComponent | |
{ | |
public float unlockTimer = 1.5f; | |
// These are internal/runtime state related, so don't show them in the inspector. | |
[NonSerialized] | |
public float unlockStarted = -1.0f; | |
[NonSerialized] | |
public bool beingUnlocked; | |
[NonSerialized] | |
public bool unlocked; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Bengsfort.Systems.Core; | |
using Bengsfort.Components; | |
using UnityEngine; | |
namespace Bengsfort.Systems | |
{ | |
// This system deals with the logic of starting and maintaining an unlock timer. | |
public class TimedUnlockSystem : BaseSystem | |
{ | |
// Cached timer component. | |
protected TimedUnlockComponent m_TimedUnlock; | |
// When the player has unlocked the entity, we can set our state to unlocked and reset. | |
protected virtual void Unlock() | |
{ | |
m_TimedUnlock.unlocked = true; | |
ResetTimer(); | |
} | |
// Resets the internal state to allow for starting the timer up again when needed. | |
public virtual void ResetTimer() | |
{ | |
m_TimedUnlock.beingUnlocked = false; | |
m_TimedUnlock.unlockStarted = -1.0f; | |
} | |
// Set the timer state to start counting down next frame. | |
public virtual void StartTimer() | |
{ | |
m_TimedUnlock.beingUnlocked = true; | |
m_TimedUnlock.unlockStarted = Time.time; | |
} | |
public override void Tick() | |
{ | |
// If we're being unlocked... | |
if (m_TimedUnlock.beingUnlocked) | |
{ | |
// Check the elapsed time | |
var timeElapsed = m_TimedUnlock.unlockStarted + m_TimedUnlock.unlockTimer; | |
if (timeElapsed <= Time.time) | |
Unlock(); | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment