forked from kanium/starcheese
Event Bus
This commit is contained in:
parent
aa424b1783
commit
7542472833
43
eventbus/defaulteventbus.gd
Normal file
43
eventbus/defaulteventbus.gd
Normal file
@ -0,0 +1,43 @@
|
||||
class_name DefaultEventBus
|
||||
extends EventBus
|
||||
|
||||
var subscribers: Dictionary = {}
|
||||
|
||||
func _init():
|
||||
subscribers = {}
|
||||
|
||||
func subscribers_of(event_scope: EventScope, event_type: String) -> Array[Subscription]:
|
||||
var key = [event_scope, event_type]
|
||||
return subscribers.get(key, [])
|
||||
|
||||
func subscribe_to(event_scope: EventScope, event_type: String, fn: Callable) -> Subscription:
|
||||
var key = [event_scope, event_type]
|
||||
var subscriber_set: Array[EventBusSubscription] = subscribers.get(key, [] as Array[EventBusSubscription])
|
||||
var subscription = EventBusSubscription.new(key, fn, self)
|
||||
subscriber_set.append(subscription)
|
||||
subscribers[key] = subscriber_set
|
||||
return subscription
|
||||
|
||||
func publish(event_scope: EventScope, event: Event) -> void:
|
||||
var key = [event_scope, event.get_type()]
|
||||
var subscriber_set: Array[EventBusSubscription] = subscribers.get(key, [] as Array[EventBusSubscription]).duplicate(true)
|
||||
for subscriber in subscriber_set:
|
||||
var result = subscriber.callback.call(event)
|
||||
if result == EventResult.ResultType.DISPOSE_SUBSCRIPTION:
|
||||
subscriber.dispose()
|
||||
|
||||
func cancel_scope(event_scope: EventScope) -> void:
|
||||
var keys_to_cancel: Array = []
|
||||
for key in subscribers.keys():
|
||||
if key[0] == event_scope:
|
||||
keys_to_cancel.append(key)
|
||||
for key in keys_to_cancel:
|
||||
var subscriber_set: Array[EventBusSubscription] = subscribers[key]
|
||||
for subscriber in subscriber_set:
|
||||
subscriber.dispose()
|
||||
|
||||
func dispose() -> void:
|
||||
for subscriber_set in subscribers.values():
|
||||
for subscriber in subscriber_set:
|
||||
subscriber.dispose()
|
||||
subscribers.clear()
|
||||
2
eventbus/defaultscope.gd
Normal file
2
eventbus/defaultscope.gd
Normal file
@ -0,0 +1,2 @@
|
||||
class_name DefaultScope
|
||||
extends EventScope
|
||||
12
eventbus/event.gd
Normal file
12
eventbus/event.gd
Normal file
@ -0,0 +1,12 @@
|
||||
class_name Event
|
||||
extends RefCounted
|
||||
|
||||
var type: String
|
||||
var source: Variant
|
||||
|
||||
func _init(_source: Variant = null):
|
||||
source = _source
|
||||
type = self.get_class()
|
||||
|
||||
func get_type() -> String:
|
||||
return type
|
||||
17
eventbus/eventbus.gd
Normal file
17
eventbus/eventbus.gd
Normal file
@ -0,0 +1,17 @@
|
||||
class_name EventBus
|
||||
extends Node
|
||||
|
||||
func subscribers_of(_event_scope: EventScope, _event_type: String) -> Array[Subscription]:
|
||||
return Array()
|
||||
|
||||
func subscribe_to(_event_scope: EventScope, _event_type: String, _fn: Callable) -> Subscription:
|
||||
return Subscription.new()
|
||||
|
||||
func publish(_event_scope: EventScope, _event: Event) -> void:
|
||||
pass
|
||||
|
||||
func cancel_scope(_event_scope: EventScope) -> void:
|
||||
pass
|
||||
|
||||
func dispose() -> void:
|
||||
pass
|
||||
19
eventbus/eventbussubscription.gd
Normal file
19
eventbus/eventbussubscription.gd
Normal file
@ -0,0 +1,19 @@
|
||||
class_name EventBusSubscription
|
||||
extends Subscription
|
||||
|
||||
var key: Array
|
||||
var callback: Callable
|
||||
var bus: DefaultEventBus
|
||||
|
||||
func _init(_key: Array, _callback: Callable, _bus: DefaultEventBus):
|
||||
key = _key
|
||||
callback = _callback
|
||||
bus = _bus
|
||||
|
||||
func dispose() -> void:
|
||||
var subscriber_set: Array[EventBusSubscription] = bus.subscribers.get(key, [])
|
||||
subscriber_set.erase(self)
|
||||
if subscriber_set.is_empty():
|
||||
bus.subscribers.erase(key)
|
||||
else:
|
||||
bus.subscribers[key] = subscriber_set
|
||||
7
eventbus/eventresult.gd
Normal file
7
eventbus/eventresult.gd
Normal file
@ -0,0 +1,7 @@
|
||||
class_name EventResult
|
||||
extends RefCounted
|
||||
|
||||
enum ResultType {
|
||||
KEEP_SUBSCRIPTION,
|
||||
DISPOSE_SUBSCRIPTION
|
||||
}
|
||||
2
eventbus/eventscope.gd
Normal file
2
eventbus/eventscope.gd
Normal file
@ -0,0 +1,2 @@
|
||||
class_name EventScope
|
||||
extends Node
|
||||
5
eventbus/subscription.gd
Normal file
5
eventbus/subscription.gd
Normal file
@ -0,0 +1,5 @@
|
||||
class_name Subscription
|
||||
extends RefCounted
|
||||
|
||||
func dispose() -> void:
|
||||
pass
|
||||
10
input_event.gd
Normal file
10
input_event.gd
Normal file
@ -0,0 +1,10 @@
|
||||
class_name KeyboardInputEvent
|
||||
extends Event
|
||||
|
||||
var action: String
|
||||
var pressed: bool
|
||||
|
||||
func _init(_action: String, _pressed: bool):
|
||||
action = _action
|
||||
pressed = _pressed
|
||||
type = "KeyboardInputEvent"
|
||||
35
main.gd
Normal file
35
main.gd
Normal file
@ -0,0 +1,35 @@
|
||||
extends Node
|
||||
|
||||
func _init() -> void:
|
||||
var node = WeaponsSystem.new()
|
||||
node.set_name("WeaponsSystem")
|
||||
add_child(node)
|
||||
|
||||
node = DefaultEventBus.new()
|
||||
node.set_name("MainEventBus")
|
||||
add_child(node)
|
||||
|
||||
node = EventScope.new()
|
||||
node.set_name("InputEventScope")
|
||||
add_child(node)
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
func _process(delta: float) -> void:
|
||||
var actions = [
|
||||
"ui_fire",
|
||||
"ui_up",
|
||||
"ui_down",
|
||||
"ui_left",
|
||||
"ui_right",
|
||||
]
|
||||
for action in actions:
|
||||
if Input.is_action_pressed(action):
|
||||
get_node("/root/Main/MainEventBus").publish(get_node("/root/Main/InputEventScope"), KeyboardInputEvent.new(action, true))
|
||||
elif Input.is_action_just_released(action):
|
||||
get_node("/root/Main/MainEventBus").publish(get_node("/root/Main/InputEventScope"), KeyboardInputEvent.new(action, false))
|
||||
pass
|
||||
@ -1,10 +1,10 @@
|
||||
[gd_scene load_steps=13 format=3 uid="uid://8ocp10j32f62"]
|
||||
|
||||
[ext_resource type="Script" path="res://main.gd" id="1_eedai"]
|
||||
[ext_resource type="Texture2D" uid="uid://y6phkg4twpdm" path="res://images/bg_space_seamless.png" id="1_rpyi5"]
|
||||
[ext_resource type="Texture2D" uid="uid://cran7fr1i2qou" path="res://images/spaceship-placeholder.png" id="2_f2x66"]
|
||||
[ext_resource type="Script" path="res://spaceship.gd" id="3_ttkgl"]
|
||||
[ext_resource type="Script" path="res://laser.gd" id="4_uhf7q"]
|
||||
[ext_resource type="Script" path="res://weapons_system.gd" id="5_gf6oh"]
|
||||
[ext_resource type="Script" path="res://asteroid.gd" id="6_n4dsl"]
|
||||
[ext_resource type="Texture2D" uid="uid://bxgw2u7j4b634" path="res://images/laser_turret.png" id="6_qxhyw"]
|
||||
[ext_resource type="Texture2D" uid="uid://buirp1h6onqai" path="res://images/AsteroidBrown.png" id="7_0tjls"]
|
||||
@ -22,6 +22,7 @@ colors = PackedColorArray(1, 1, 0, 1, 1, 1, 1, 0.137255)
|
||||
radius = 88.0057
|
||||
|
||||
[node name="Main" type="Node2D"]
|
||||
script = ExtResource("1_eedai")
|
||||
|
||||
[node name="background" type="Sprite2D" parent="."]
|
||||
position = Vector2(955, 537)
|
||||
@ -70,9 +71,6 @@ offset_top = -71.0
|
||||
offset_right = 104.0
|
||||
offset_bottom = -20.0
|
||||
|
||||
[node name="WeaponsSystem" type="Node" parent="spaceship"]
|
||||
script = ExtResource("5_gf6oh")
|
||||
|
||||
[node name="HardPoint1" type="Node2D" parent="spaceship"]
|
||||
position = Vector2(0, -46)
|
||||
script = ExtResource("7_6cr6a")
|
||||
|
||||
@ -12,7 +12,7 @@ config_version=5
|
||||
|
||||
config/name="Star Cheese - The Final Fromage"
|
||||
run/main_scene="res://main.tscn"
|
||||
config/features=PackedStringArray("4.2", "Forward Plus")
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[display]
|
||||
@ -24,7 +24,7 @@ window/size/viewport_height=1080
|
||||
|
||||
ui_fire={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
25
spaceship.gd
25
spaceship.gd
@ -8,10 +8,12 @@ extends CharacterBody2D
|
||||
signal fire # Signal to start firing
|
||||
signal stop_fire # Signal to stop firing
|
||||
|
||||
var weapons_system
|
||||
|
||||
var weapons_system: WeaponsSystem
|
||||
var event_bus: EventBus
|
||||
func _ready():
|
||||
weapons_system = $WeaponsSystem
|
||||
weapons_system = get_node("/root/Main/WeaponsSystem")
|
||||
event_bus = get_node("/root/Main/MainEventBus")
|
||||
event_bus.subscribe_to(get_node("/root/Main/InputEventScope"), "KeyboardInputEvent", Callable(self, "handle_keyboard_input"))
|
||||
|
||||
func get_spaceship_orientation_vector() -> Vector2:
|
||||
return -global_transform.y
|
||||
@ -45,13 +47,14 @@ func handle_movement(delta: float) -> void:
|
||||
var collision_normal = collision.get_normal()
|
||||
velocity = velocity.slide(collision_normal)
|
||||
|
||||
func handle_weapons_input() -> void:
|
||||
if Input.is_action_pressed("ui_fire"):
|
||||
weapons_system.fire_all()
|
||||
|
||||
if Input.is_action_just_released("ui_fire"):
|
||||
weapons_system.cease_fire_all()
|
||||
|
||||
func handle_keyboard_input(event: KeyboardInputEvent):
|
||||
if( event.action == "ui_fire" ):
|
||||
if( event.pressed ):
|
||||
weapons_system.fire_all()
|
||||
else:
|
||||
weapons_system.cease_fire_all()
|
||||
|
||||
pass
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
handle_movement(delta)
|
||||
handle_weapons_input()
|
||||
|
||||
6
tests.tscn
Normal file
6
tests.tscn
Normal file
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dxxijo7vef7or"]
|
||||
|
||||
[ext_resource type="Script" path="res://tests/tests.gd" id="1_dngg8"]
|
||||
|
||||
[node name="tests" type="Node"]
|
||||
script = ExtResource("1_dngg8")
|
||||
105
tests/test_eventbus.gd
Normal file
105
tests/test_eventbus.gd
Normal file
@ -0,0 +1,105 @@
|
||||
# res://tests/test_eventbus.gd
|
||||
class_name EventBusTests
|
||||
extends Node
|
||||
|
||||
func _ready():
|
||||
run_tests()
|
||||
|
||||
func run_tests():
|
||||
test_subscribe_and_publish()
|
||||
test_dispose_subscription()
|
||||
test_cancel_scope()
|
||||
test_multiple_subscribers()
|
||||
|
||||
# Test function: Test subscribing and publishing events
|
||||
func test_subscribe_and_publish() -> void:
|
||||
var bus: DefaultEventBus = DefaultEventBus.new()
|
||||
var scope: DefaultScope = DefaultScope.new()
|
||||
var event_received: Flag = Flag.new()
|
||||
|
||||
var event_handler = func(_event: Event) -> int:
|
||||
event_received.value = true
|
||||
return EventResult.ResultType.KEEP_SUBSCRIPTION
|
||||
|
||||
bus.subscribe_to(scope, "TestEvent", event_handler)
|
||||
var event: TestEvent = TestEvent.new(self)
|
||||
bus.publish(scope, event)
|
||||
|
||||
# Assert that the event was received
|
||||
assert(event_received.value == true, "Event was not received by subscriber")
|
||||
bus.dispose()
|
||||
|
||||
# Test function: Test disposing of a subscription after handling an event
|
||||
func test_dispose_subscription() -> void:
|
||||
var bus: DefaultEventBus = DefaultEventBus.new()
|
||||
var scope: DefaultScope = DefaultScope.new()
|
||||
var event_received_times: Counter = Counter.new()
|
||||
|
||||
var event_handler = func(_event: Event) -> int:
|
||||
event_received_times.value += 1
|
||||
return EventResult.ResultType.DISPOSE_SUBSCRIPTION
|
||||
|
||||
bus.subscribe_to(scope, "TestEvent", event_handler)
|
||||
var event: TestEvent = TestEvent.new(self)
|
||||
bus.publish(scope, event)
|
||||
bus.publish(scope, event) # Publish a second time
|
||||
|
||||
# Assert that the event handler was called only once
|
||||
assert(event_received_times.value == 1, "Event handler should have been called only once due to disposal")
|
||||
bus.dispose()
|
||||
|
||||
# Test function: Test canceling an event scope
|
||||
func test_cancel_scope() -> void:
|
||||
var bus: DefaultEventBus = DefaultEventBus.new()
|
||||
var scope: DefaultScope = DefaultScope.new()
|
||||
var event_received: Flag = Flag.new()
|
||||
|
||||
var event_handler = func(_event: Event) -> int:
|
||||
event_received.value = true
|
||||
return EventResult.ResultType.KEEP_SUBSCRIPTION
|
||||
|
||||
bus.subscribe_to(scope, "TestEvent", event_handler)
|
||||
bus.cancel_scope(scope)
|
||||
var event: TestEvent = TestEvent.new(self)
|
||||
bus.publish(scope, event)
|
||||
|
||||
# Assert that the event was not received after scope cancellation
|
||||
assert(event_received.value == false, "Event should not have been received after scope cancellation")
|
||||
bus.dispose()
|
||||
|
||||
|
||||
# Test function: Test multiple subscribers receiving the same event
|
||||
func test_multiple_subscribers() -> void:
|
||||
var bus: DefaultEventBus = DefaultEventBus.new()
|
||||
var scope: DefaultScope = DefaultScope.new()
|
||||
var event_received_by_handler1: Flag = Flag.new()
|
||||
var event_received_by_handler2: Flag = Flag.new()
|
||||
|
||||
var event_handler1 = func(_event: Event) -> int:
|
||||
event_received_by_handler1.value = true
|
||||
return EventResult.ResultType.KEEP_SUBSCRIPTION
|
||||
|
||||
var event_handler2 = func(_event: Event) -> int:
|
||||
event_received_by_handler2.value = true
|
||||
return EventResult.ResultType.KEEP_SUBSCRIPTION
|
||||
|
||||
bus.subscribe_to(scope, "TestEvent", event_handler1)
|
||||
bus.subscribe_to(scope, "TestEvent", event_handler2)
|
||||
var event: TestEvent = TestEvent.new(self)
|
||||
bus.publish(scope, event)
|
||||
|
||||
# Assert that both handlers received the event
|
||||
assert(event_received_by_handler1.value == true, "First handler did not receive event")
|
||||
assert(event_received_by_handler2.value == true, "Second handler did not receive event")
|
||||
bus.dispose()
|
||||
|
||||
class Flag:
|
||||
var value = false
|
||||
|
||||
class Counter:
|
||||
var value = 0
|
||||
|
||||
class TestEvent extends Event:
|
||||
func _init(_source: Variant = null) -> void:
|
||||
super(_source)
|
||||
type = "TestEvent"
|
||||
8
tests/tests.gd
Normal file
8
tests/tests.gd
Normal file
@ -0,0 +1,8 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
EventBusTests.run_tests()
|
||||
get_tree().quit()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
pass
|
||||
@ -1,3 +1,4 @@
|
||||
class_name WeaponsSystem
|
||||
extends Node
|
||||
|
||||
var weapons = []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user