Created
January 29, 2021 17:06
-
-
Save malj/3416b0d6dc01eb277e401c7ea917900f to your computer and use it in GitHub Desktop.
Godot ECS
This file contains 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
""" | |
Base class for components. | |
Components are nodes which only store state data. They belong to an entity, and | |
can optionally provide accessor methods, but don't contain any behavior logic. | |
""" | |
class_name Component extends "./ecs_node.gd" | |
func _enter_tree() -> void: | |
if owner != null: | |
# Register entity | |
var entities_group := get_entities_group(get_script()) | |
if owner.is_in_group(entities_group): | |
printerr("Entity %s can't have multiple components of the same type" % owner.name) | |
queue_free() | |
return | |
owner.add_to_group(entities_group) | |
# Register component (automatically unregistered when exiting tree) | |
var components_group := get_components_group(owner) | |
add_to_group(components_group) | |
else: | |
printerr("Component %s must be assigned to an entity" % name) | |
queue_free() | |
func _exit_tree() -> void: | |
if owner != null: | |
# Unregister entity | |
var entities_group := get_entities_group(get_script()) | |
if owner.is_in_group(entities_group): | |
owner.remove_from_group(entities_group) |
This file contains 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
""" | |
Base class for all ECS nodes. | |
Sorts the nodes into worlds and provides standardized opaque group names | |
for its descendants to use. | |
""" | |
extends Node | |
export var world := 1 | |
func get_entities_group(component_type: Script) -> String: | |
return "world_%d_component_type_%d_entities" % [world, component_type.get_instance_id()] | |
func get_components_group(entity: Node) -> String: | |
return "_world_%d_entity_%d_components" % [world, entity.get_instance_id()] | |
func get_singleton_components_group() -> String: | |
return "_world_%d_singleton_components" % world |
This file contains 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
""" | |
Base class for singleton components. | |
Singleton components are components without an associated entity. Only one | |
singleton component of the same type can exist in each world. | |
""" | |
class_name SingletonComponent extends "./ecs_node.gd" | |
func _enter_tree() -> void: | |
# Register component (automatically unregistered when exiting tree) | |
var component_type := get_script() as Script | |
var components_group := get_singleton_components_group() | |
for component in get_tree().get_nodes_in_group(components_group): | |
if component is component_type: | |
printerr("World %d can't have multiple singleton components of the same type" % world) | |
queue_free() | |
return | |
add_to_group(components_group) |
This file contains 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
""" | |
Base class for systems. | |
Systems are nodes which operate on sets of components. They provide behavior | |
logic but don't contain any state data. | |
""" | |
class_name System extends "./ecs_node.gd" | |
func get_entities(component_types: Array) -> Array: | |
var entities := [] | |
if component_types: | |
var first_entities_group := get_entities_group(component_types[0]) | |
for entity in get_tree().get_nodes_in_group(first_entities_group): | |
if has_components(entity, component_types): | |
entities.append(entity) | |
return entities | |
func has_component(entity: Node, component_type: Script) -> bool: | |
var entities_group := get_entities_group(component_type) | |
return entity.is_in_group(entities_group) | |
func has_components(entity: Node, component_types: Array) -> bool: | |
for component_type in component_types: | |
if not has_component(entity, component_type): | |
return false | |
return true | |
func get_component(entity: Node, component_type: Script) -> Component: | |
if has_component(entity, component_type): | |
var components_group := get_components_group(entity) | |
for component in get_tree().get_nodes_in_group(components_group): | |
if component is component_type: | |
return component | |
return null | |
func add_component(entity: Node, component_type: Script) -> bool: | |
if not has_component(entity, component_type): | |
var component := component_type.new() as Component | |
if component != null: | |
component.world = world | |
component.owner = entity | |
entity.add_child(component) | |
return true | |
return false | |
func remove_component(entity: Node, component_type: Script) -> bool: | |
if has_component(entity, component_type): | |
var component := get_component(entity, component_type) | |
component.get_parent().remove_child(component) | |
component.queue_free() | |
return true | |
return false | |
func get_singleton_component(component_type: Script) -> SingletonComponent: | |
var components_group := get_singleton_components_group() | |
for component in get_tree().get_nodes_in_group(components_group): | |
if component is component_type: | |
return component | |
return null | |
func add_singleton_component(component_type: Script) -> bool: | |
if get_singleton_component(component_type) == null: | |
var component := component_type.new() as SingletonComponent | |
if component != null: | |
component.world = world | |
get_tree().get_root().add_child(component) | |
return true | |
return false | |
func remove_singleton_component(component_type: Script) -> bool: | |
var components_group := get_singleton_components_group() | |
for component in get_tree().get_nodes_in_group(components_group): | |
if component is component_type: | |
component.get_parent().remove_child(component) | |
component.queue_free() | |
return true | |
return false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment