Skip to content

Commit c925796

Browse files
Revert Signals documentation to 9.1.0
1 parent 4243eab commit c925796

File tree

1 file changed

+0
-146
lines changed

1 file changed

+0
-146
lines changed

Documentation/Signals.md

Lines changed: 0 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
* <a href="#signalbusinstaller">SignalBusInstaller</a>
1212
* <a href="#when-to-use-signals">When To Use Signals</a>
1313
* Advanced
14-
* <a href="#abstract-signals">Abstract Signals</a>
1514
* <a href="#use-with-subcontainers">Signals With Subcontainers</a>
1615
* <a href="#async-signals">Asynchronous Signals</a>
1716
* <a href="#settings">Signal Settings</a>
@@ -480,151 +479,6 @@ These are just rules of thumb, but useful to keep in mind when using signals. T
480479

481480
When event driven program is abused, it is possible to find yourself in "callback hell" where events are triggering other events etc. and which make the entire system impossible to understand. So signals in general should be used with caution. Personally I like to use signals for high level game-wide events and then use other forms of communication (unirx streams, c# events, direct method calls, interfaces) for most other things.
482481

483-
## <a id="abstract-signals"></a>Abstract Signals
484-
485-
One of the problems of the signals is that when you subscribe to their types you are coupling your concrete signal types to the subscribers
486-
487-
For example, Lets say I have a player and i want to save the game when i finish a level.
488-
Ok easy, I create ``SignalLevelCompleted`` and then I subscribe it to my ``SaveGameSystem``
489-
then I also want to save when i reach a checkpoint, again i create ``SignalCheckpointReached``
490-
and then I subscribe it to my ``SaveGameSystem``
491-
you are begining to get something like this...
492-
```csharp
493-
public class Example
494-
{
495-
SignalBus signalBus;
496-
public Example(Signalbus signalBus) => this.signalBus = signalBus;
497-
498-
public void CheckpointReached() => signalBus.Fire<SignalCheckpointReached>();
499-
500-
public void CompleteLevel() => signalBus.Fire<SignalLevelCompleted>();
501-
}
502-
503-
public class SaveGameSystem
504-
{
505-
public SaveGameSystem(SignalBus signalBus)
506-
{
507-
signalBus.Subscribe<SignalCheckpointReached>(x => SaveGame());
508-
signalBus.Subscribe<SignalLevelCompleted>(x => SaveGame());
509-
}
510-
511-
void SaveGame() { /*Saves the game*/ }
512-
}
513-
514-
//in your installer
515-
Container.DeclareSignal<SignalLevelCompleted>();
516-
Container.DeclareSignal<SignalCheckpointReached>();
517-
518-
//your signal types
519-
public struct SignalCheckpointReached{}
520-
public struct SignalLevelCompleted{}
521-
```
522-
523-
And then you realize you are coupling the types``signalLevelCompleted`` and ``SignalCheckpointReached``to ``SaveGameSystem``.
524-
``SaveGameSystem`` shouldn't know about those "non related with saving" events...
525-
526-
So let's give the power of interfaces to signals!
527-
So i have the ``SignalCheckpointReached`` and ``SignalLevelCompleted`` both implementing **``ISignalGameSaver``**
528-
and my ``SaveGameSystem`` just Subscribes to **``ISignalGameSaver``** for saving the game
529-
So when i fire any of those signals the ``SaveGameSystem`` saves the game.
530-
Then you have something like this...
531-
```csharp
532-
public class Example
533-
{
534-
SignalBus signalBus;
535-
public Example(Signalbus signalBus) => this.signalBus = signalBus;
536-
537-
public void CheckpointReached() => signalBus.AbstractFire<SignalCheckpointReached>();
538-
539-
public void CompleteLevel() => signalBus.AbstractFire<SignalLevelCompleted>();
540-
}
541-
542-
public class SaveGameSystem
543-
{
544-
public SaveGameSystem(SignalBus signalBus)
545-
{
546-
signalBus.Subscribe<ISignalGameSaver>(x => SaveGame());
547-
}
548-
549-
void SaveGame() { /*Saves the game*/ }
550-
}
551-
552-
//in your installer
553-
Container.DeclareSignalWithInterfaces<SignalLevelCompleted>();
554-
Container.DeclareSignalWithInterfaces<SignalCheckpointReached>();
555-
556-
//your signal types
557-
public struct SignalCheckpointReached : ISignalGameSaver{}
558-
public struct SignalLevelCompleted : ISignalGameSaver{}
559-
560-
public interface ISignalGameSaver{}
561-
```
562-
563-
Now your ``SaveGameSystem`` doesnt knows about CheckPoints nor Level events, and just reacts to signals that save the game.
564-
The main difference is in the Signal declaration and Firing
565-
- ``DeclareSignalWithInterfaces`` works like ``DeclareSignal`` but it declares the interfaces too.
566-
- ``AbstractFire`` is the same that ``Fire`` but it fires the interfacesjust if you have Declared the signal with interfaces
567-
otherwise it will throw an exception.
568-
569-
Ok, let's show even more power.
570-
Now i create another signal for the WorldDestroyed Achievement "SignalWorldDestroyed"
571-
But i also want my SoundSystem to play sounds when i reach a checkpoint and/or unlock an Achievement
572-
So the code could look like this.
573-
```csharp
574-
public class Example
575-
{
576-
SignalBus signalBus;
577-
public Example(Signalbus signalBus) => this.signalBus = signalBus;
578-
579-
public void CheckpointReached() => signalBus.AbstractFire<SignalCheckpointReached>();
580-
581-
public void DestroyWorld() => signalBus.AbstractFire<SignalWorldDestroyed>();
582-
}
583-
584-
public class SoundSystem
585-
{
586-
public SoundSystem(SignalBus signalBus)
587-
{
588-
signalBus.Subscribe<ISignalSoundPlayer>(x => PlaySound(x.soundId));
589-
}
590-
591-
void PlaySound(int soundId) { /*Plays the sound with the given id*/ }
592-
}
593-
594-
public class AchievementSystem
595-
{
596-
public AchievementSystem(SignalBus signalBus)
597-
{
598-
signalBus.Subscribe<ISignalAchievementUnlocker>(x => UnlockAchievement(x.achievementKey));
599-
}
600-
601-
void UnlockAchievement(string key) { /*Unlocks the achievement with the given key*/ }
602-
}
603-
604-
//in your installer
605-
Container.DeclareSignalWithInterfaces<SignalCheckpointReached>();
606-
Container.DeclareSignalWithInterfaces<SignalWorldDestroyed>();
607-
608-
//your signal types
609-
public struct SignalCheckpointReached : ISignalGameSaver, ISignalSoundPlayer
610-
{
611-
public int SoundId { get => 2} //or configured in a scriptable with constants instead of hardcoded
612-
}
613-
public struct SignalWorldDestroyed : ISignalAchievementUnlocker, ISignalSoundPlayer
614-
{
615-
public int SoundId { get => 4}
616-
public string AchievementKey { get => "WORLD_DESTROYED"}
617-
}
618-
619-
//Your signal interfaces
620-
public interface ISignalGameSaver{}
621-
public interface ISignalSoundPlayer{ int SoundId {get;}}
622-
public interface ISignalAchievementUnlocker{ string AchievementKey {get;}}
623-
```
624-
625-
It offers a lot of modularity and abstraction for signals,
626-
you fire a concrete signal telling what you did and give them functionality trough Interface implementations
627-
628482
## <a id="use-with-subcontainers"></a>Signals With Subcontainers
629483

630484
Signals are only visible at the container level where they are declared and below. For example, you might use Unity's multi-scene support and split up your game into a GUI scene and an Environment scene. In the GUI scene you might fire a signal indicating that the GUI popup overlay has been opened/closed, so that the Environment scene can pause/resume activity. One way of achieving this would be to declare a signal in a ProjectContext installer (or a shared <a href="../README.md#scene-parenting">scene parent</a>), then subscribe to it in the Environment scene, and then fire it from the GUI scene.

0 commit comments

Comments
 (0)