-
Notifications
You must be signed in to change notification settings - Fork 0
Data types
ProtoJam provides several general purpose data types to help build your games without the extra boiler plate.
The Memoizer class is a basic in-memory cache for a single value (see memoization). When a value is requested, it checks the validity of the cached value returning it if valid and fetching if not. By default, the value is checked for nullness and instance validity but a custom validator may also be provided. Both the provider and validator may be coroutines.
This class is intended to help you cache values too expensive to lookup every frame like the player, an objective, or other object which must be found in the scene tree (though any type can be cached, not just nodes).
var _player_memo: Memoizer = Memoizer.new(_find_player)
func _process(_delta: float) -> void:
var player: Node3D = await _player_memo.get_value()
if null != player:
print(player.global_position)
# Will only get invoked once as long as the player remains valid
func _find_player() -> Node3D:
return get_tree().get_first_node_in_group(&"Player")ObservableDictionary performs the same function as Godot's Dictionary type but will invoke callbacks when values are added, removed, or changed. This is ideal inventories or other systems that need to perform some action when a dictionary is modified.
Important
Be aware that some functions, like set, have different names to avoid conflicts with Object.
func _ready() -> void:
var my_dictionary: ObservableDictionary = ObservableDictionary.new(_on_added, _on_removed, _on_changed)
my_dictionary.set_value("foo", 1) # Triggers _on_added
my_dictionary.set_value("foo", 2) # Triggers _on_changed
my_dictionary.erase("foo") # Triggers _on_removed
func _on_added(key: Variant, value: Variant) -> void:
print("Added %s: %s" % [key, value])
func _on_removed(key: Variant, value: Variant) -> void:
print("Removed %s: %s" % [key, value])
func _on_changed(key: Variant, new_value: Variant, old_value: Variant) -> void:
print("Changed %s from %s to %s" % [key, old_value, new_value])This example demonstrates adding a custom validator to ensure the cached value is still in the scene tree.
enemy.gd
class_name Enemy
extends Node3D
@export var speed: float = 30.0
var _objective_memo: Memoizer = Memoizer.new(_find_objective, _validate_objective)
func _physics_process(delta: float) -> void:
var objective: Node3D = await _objective_memo.get_value()
if null != objective:
global_position.move_toward(objective.global_position, speed * delta)
func _find_objective() -> Node3D:
return get_tree().get_first_node_in_group(&"Objectives")
func _validate_objective(objective: Variant) -> bool:
# A custom validator is run after the default validation so we can be sure
# this will not be called on a null value
return objective.is_inside_tree()This example demonstrates using a memoizer to fetch its data using a coroutine.
spawner.gd
class_name Spawner
extends Node3D
const _ENEMY_SCENE_PATH: String = "res://enemy.tres"
var _enemy_scene_memo: Memoizer = Memoizer.new(_load_enemy_scene)
func spawn() -> void:
var enemy_scene: PackedScene = await _enemy_scene_memo.get_value()
if null != enemy_scene:
var enemy: Node3D = enemy_scene.instantiate()
add_child(enemy)
func _load_enemy_scene() -> PackedScene:
var scene_handle: AsyncResourceHandle = BackgroundResourceLoader.load_async(_ENEMY_SCENE_PATH, "PackedScene")
# scene_handle.ready is a signal which will be called when the scene is loaded
if null != scene_handle and scene_handle.ready:
return scene_handle.get_resource()
return nullContributions are always welcome! Check out the contributing guide to get started.
Made with ❤️ for humans by humans.