{"id":14961035,"url":"https://github.com/peturdarri/genericeventbus","last_synced_at":"2025-08-17T16:31:33.512Z","repository":{"id":187919641,"uuid":"417831314","full_name":"PeturDarri/GenericEventBus","owner":"PeturDarri","description":"A synchronous event bus for Unity, using strictly typed events and generics to reduce runtime overhead.","archived":false,"fork":false,"pushed_at":"2024-06-13T01:35:23.000Z","size":78,"stargazers_count":80,"open_issues_count":0,"forks_count":13,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-05T07:21:44.250Z","etag":null,"topics":["event-bus","eventbus","unity","upm","upm-package"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PeturDarri.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-16T13:08:05.000Z","updated_at":"2024-12-02T10:17:26.000Z","dependencies_parsed_at":"2024-09-22T12:00:26.562Z","dependency_job_id":null,"html_url":"https://github.com/PeturDarri/GenericEventBus","commit_stats":{"total_commits":25,"total_committers":1,"mean_commits":25.0,"dds":0.0,"last_synced_commit":"aa81a297d6d5b83c9a66c9f69ae3d91a4465076f"},"previous_names":["peturdarri/genericeventbus"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeturDarri%2FGenericEventBus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeturDarri%2FGenericEventBus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeturDarri%2FGenericEventBus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PeturDarri%2FGenericEventBus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PeturDarri","download_url":"https://codeload.github.com/PeturDarri/GenericEventBus/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230143636,"owners_count":18180024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["event-bus","eventbus","unity","upm","upm-package"],"created_at":"2024-09-24T13:23:40.821Z","updated_at":"2025-08-17T16:31:33.486Z","avatar_url":"https://github.com/PeturDarri.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Generic Event Bus\nA synchronous event bus for Unity written in C#, using strictly typed events and generics to reduce runtime overhead.\n\n## Features\n* Events are defined as types, instead of as members in some class or as string IDs.\n* Generics are used to move runtime overhead to compile time. _(There's no `Dictionary\u003cType, Listeners\u003e`)_ \n* Listeners can include a [priority](#priority) number when subscribing to an event to control their order in the event execution, regardless of _when_ the listener subscribes.\n* Built-in support for [targeting events](#targeted-events) to specific objects, with an optional source object that raised the event.\n* Event data can be [modified by listeners](#modifying-event-data), or completely [consumed](#consuming-events) to stop it.\n* Events can be queued if other events are currently being raised. \n\n## Usage\nTo create an event bus, use the `GenericEventBus\u003cTBaseEvent\u003e` type:\n```c#\nvar eventBus = new GenericEventBus\u003cTBaseEvent\u003e();\n```\n`TBaseEvent` is the base type all event types must inherit/implement. You can use `System.Object` as the base type to allow any type to be used as an event, but I recommend defining an empty interface as the base type:\n```c#\npublic interface IEvent {}\n```\n```c#\nvar eventBus = new GenericEventBus\u003cIEvent\u003e();\n```\nOtherwise, _any_ object can be raised as an event, which is weird and confusing.\n\n---\nFor ease of use, I recommend inheriting `GenericEventBus\u003cTBaseEvent\u003e` with your own type and using that in your code instead:\n```cs\npublic class GameEventBus : GenericEventBus\u003cIEvent\u003e {}\n```\n\u003e 💡 If you want to use the event bus as a static class for easier access, you can make a static wrapper using [this template](https://gist.github.com/PeturDarri/233cae6571041e8b48d3584fb781f902).\n\n---\n\nTo define new events, just define a type that inherits/implements your base event type:\n```c#\npublic struct GameStartedEvent : IEvent\n{\n}\n```\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cem\u003eCan events be defined as classes instead?\u003c/em\u003e\u003c/summary\u003e\n\nEvents can be defined as either `class` or `struct`, but I recommend using structs to avoid allocation when creating new instances. Events are passed around in the event bus and to listeners by references using `ref`, so you don't have to worry about the overhead of struct copying.\n\nAnd you also don't need to worry about the struct being boxed. Generic type parameters ensure it is never boxed.\n\u003c/details\u003e\n\n---\n\nThis event can now be raised:\n```c#\neventBus.Raise(new GameStartedEvent());\n```\nIncluding data with events is very simple:\n```c#\npublic struct GameStartedEvent : IEvent\n{\n    public int NumberOfPlayers;\n}\n```\n```c#\neventBus.Raise(new GameStartedEvent { NumberOfPlayers = 1 });\n```\n\n---\n\nHere's how you subscribe to and unsubscribe from events:\n```c#\nprivate void OnEnable()\n{\n    eventBus.SubscribeTo\u003cGameStartedEvent\u003e(OnGameStartedEvent);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFrom\u003cGameStartedEvent\u003e(OnGameStartedEvent);\n}\n\nprivate void OnGameStartedEvent(ref GameStartedEvent eventData)\n{\n    Debug.Log($\"Game started with {eventData.NumberOfPlayers} player(s)\");\n}\n```\n### Priority\nYou can also include a `float priority` argument when calling `SubscribeTo`. Subscribing to an event with a high priority means you'll receive the event before other listeners that have a lower priority. This is great for defining the order of listeners without having to worry about _when_ each listener subscribes to the event.\n```c#\nprivate void OnEnable()\n{\n    eventBus.SubscribeTo\u003cGameStartedEvent\u003e(OnGameStartedEvent);\n    eventBus.SubscribeTo\u003cGameStartedEvent\u003e(OnGameStartedEventPriority, 10f);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFrom\u003cGameStartedEvent\u003e(OnGameStartedEvent);\n    eventBus.UnsubscribeFrom\u003cGameStartedEvent\u003e(OnGameStartedEventPriority);\n}\n\nprivate void OnGameStartedEvent(ref GameStartedEvent eventData)\n{\n    Debug.Log($\"Game started with {eventData.NumberOfPlayers} player(s)\");\n}\n\nprivate void OnGameStartedEventPriority(ref GameStartedEvent eventData)\n{\n    Debug.Log(\"This will be invoked first, even though it was added last!\");\n}\n```\nThe default `priority` is `0` and listeners with the same priority will be invoked in the order they were added.\n\n### Targeted events\nThings get a lot more interesting when using targeted events. You can think of this more like a message bus, where objects can raise events that are meant to be heard by a specific target object.\n\nTo use targeted events, you must include a second generic type parameter in `GenericEventBus` to specify what type of object can be a target, like `GameObject`:\n```c#\nvar eventBus = new GenericEventBus\u003cIEvent, GameObject\u003e();\n```\n\nYou get all the same methods in this event bus as in the other one, so you can still raise non-targeted events, but now you can include a target and source object with raised events:\n```c#\neventBus.Raise(new DamagedEvent { Damage = 10f }, targetGameObject, sourceGameObject);\n```\nIn this example, `DamagedEvent` is defined just like any other event:\n```c#\npublic struct DamagedEvent : IEvent\n{\n    public float Damage;\n}\n```\n---\nTo listen to this event, use the `SubscribeToTarget` method:\n```c#\nprivate float health = 100f;\n\nprivate void OnEnable()\n{\n    eventBus.SubscribeToTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFromTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent);\n}\n\nprivate void OnDamagedEvent(ref DamagedEvent eventData, GameObject target, GameObject source)\n{\n    health -= eventData.Damage;\n    \n    Debug.Log($\"{target} received {eventData.Damage} damage from {source}\");\n}\n```\n---\nThis pattern allows you to have objects communicate with each other in a very decoupled way. If no one is listening to the target object, the event is ignored.\n\nAnother benefit from this pattern is that now you have an event of when objects are damaged, which any script can listen to.\n\nFor example, if you wanted to have some UI showing damage numbers on anything the player damages, you could do that like this:\n```c#\nprivate void OnEnable()\n{\n    eventBus.SubscribeToSource\u003cDamagedEvent\u003e(playerObject, OnPlayerInflictedDamageEvent);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFromSource\u003cDamagedEvent\u003e(playerObject, OnPlayerInflictedDamageEvent);\n}\n\nprivate void OnPlayerInflictedDamageEvent(ref DamagedEvent eventData, GameObject target, GameObject source)\n{\n    SpawnDamageNumberOn(target, eventData.Damage);\n}\n```\n---\nAnd any listeners that don't specify a target or source will simply get all events, regardless of the target or source. Perfect for something like a kill feed UI:\n```c#\npublic struct KilledEvent : IEvent\n{\n    public IWeapon Weapon;\n}\n\nprivate void OnEnable()\n{\n    eventBus.SubscribeTo\u003cKilledEvent\u003e(OnKilledEvent);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFrom\u003cKilledEvent\u003e(OnKilledEvent);\n}\n\nprivate void OnKilledEvent(ref KilledEvent eventData, GameObject target, GameObject source)\n{\n    Debug.Log($\"{source} killed {target} with {eventData.Weapon}!\");\n}\n```\n---\n### Modifying event data\nListeners can modify the event data they receive, so listeners afterwards will receive the modified data. This can be extremely useful for implementing features like damage type resistance/weakness:\n```c#\npublic enum DamageType\n{\n    Bludgeoning,\n    Fire,\n    Cold\n}\n\npublic struct DamagedEvent : IEvent\n{\n    public DamageType Type;\n    public float Amount;\n}\n```\n```c#\n[SerializeField]\nprivate DamageType resistanceType;\n\nprivate void OnEnable()\n{\n    // Subscribe to the damage event targeting this game object with a higher priority than default.\n    eventBus.SubscribeToTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent, 100f);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFromTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent);\n}\n\nprivate void OnDamagedEvent(ref DamagedEvent eventData)\n{\n    // If we are resistant to this damage type, halve the damage.\n    if (eventData.Type == resistanceType)\n    {\n        eventData.Amount *= 0.5f;\n    }\n}\n```\n#### Consuming events\nYou can also stop the event completely using `ConsumeCurrentEvent()`. This can be used to implement a quick god mode script that's completely decoupled from the rest of the health/damage scripts:\n```c#\n[SerializeField]\nprivate bool godMode;\n\nprivate void OnEnable()\n{\n    // Subscribe to the damage event targeting this game object with a higher priority than default.\n    eventBus.SubscribeToTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent, 100f);\n}\n\nprivate void OnDisable()\n{\n    eventBus.UnsubscribeFromTarget\u003cDamagedEvent\u003e(gameObject, OnDamagedEvent);\n}\n\nprivate void OnDamagedEvent(ref DamagedEvent eventData)\n{\n    // If we're in god mode, consume the event.\n    if (godMode)\n    {\n        eventBus.ConsumeCurrentEvent();\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeturdarri%2Fgenericeventbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeturdarri%2Fgenericeventbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeturdarri%2Fgenericeventbus/lists"}