NavigationComponent can handle position and orientation targets.

WorldChunkRefactoring
Martin Felis 2023-02-12 16:30:56 +01:00
parent 14e5d47d91
commit 716654a6fd
21 changed files with 798 additions and 208 deletions

32
Game.gd
View File

@ -1,32 +0,0 @@
extends Node2D
onready var SimpleEntity = preload("res://entities/SimpleEntity.tscn")
onready var WanderingEntity = preload("res://entities/WanderingEntity.tscn")
onready var player_movable_target_label = $UI/PlayerMovableTarget
onready var player = $PlayerEntity
onready var entities = $Entities
signal world_location_clicked
# Called when the node enters the scene tree for the first time.
func _ready():
pass
func _process(delta):
player_movable_target_label.text = str(player.movable_component.target)
func _on_AddEntityButton_pressed():
var entity_instance = SimpleEntity.instance()
var viewport_rect = get_viewport_rect()
entity_instance.transform.origin = Vector2(randf() * viewport_rect.size[0], randf() * viewport_rect.size[1])
entities.add_child(entity_instance)
func _on_AddWanderingEntity_pressed():
var entity_instance = WanderingEntity.instance()
var viewport_rect = get_viewport_rect()
entity_instance.transform.origin = Vector2(randf() * viewport_rect.size[0], randf() * viewport_rect.size[1])
entities.add_child(entity_instance)
entity_instance.set_target(Vector2(randf() * viewport_rect.size[0], randf() * viewport_rect.size[1]))

131
Game.tscn
View File

@ -1,131 +0,0 @@
[gd_scene load_steps=10 format=2]
[ext_resource path="res://entities/SimpleEntity.tscn" type="PackedScene" id=1]
[ext_resource path="res://entities/PlayerEntity.tscn" type="PackedScene" id=2]
[ext_resource path="res://assets/white.png" type="Texture" id=3]
[ext_resource path="res://materials/PhysicsObjects.tres" type="Material" id=4]
[ext_resource path="res://utils/CollisionLine.gd" type="Script" id=5]
[ext_resource path="res://Game.gd" type="Script" id=6]
[sub_resource type="RectangleShape2D" id=1]
extents = Vector2( 57.5347, 51.5794 )
[sub_resource type="RectangleShape2D" id=2]
[sub_resource type="SegmentShape2D" id=3]
a = Vector2( 37, 302 )
b = Vector2( 34, 570 )
[node name="Game" type="Node2D"]
script = ExtResource( 6 )
[node name="Control" type="HBoxContainer" parent="."]
margin_left = 30.0
margin_top = 30.0
margin_right = 40.0
margin_bottom = 40.0
[node name="AddEntityButton" type="Button" parent="Control"]
margin_right = 77.0
margin_bottom = 20.0
text = "Add Entity"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="AddWanderingEntity" type="Button" parent="Control"]
margin_left = 81.0
margin_right = 190.0
margin_bottom = 20.0
text = "Add Wandering"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Entities" type="Node2D" parent="."]
[node name="SimpleEntity" parent="Entities" instance=ExtResource( 1 )]
position = Vector2( 360, 118 )
[node name="SimpleEntity2" parent="Entities" instance=ExtResource( 1 )]
position = Vector2( 157, 306 )
[node name="PlayerEntity" parent="." instance=ExtResource( 2 )]
[node name="World" type="Node2D" parent="."]
[node name="Wall" type="KinematicBody2D" parent="World"]
position = Vector2( 582, 817 )
scale = Vector2( 7.56, 1 )
__meta__ = {
"_edit_group_": true
}
[node name="Sprite" type="Sprite" parent="World/Wall"]
material = ExtResource( 4 )
position = Vector2( 0.965286, 0.579379 )
scale = Vector2( 115, 104 )
texture = ExtResource( 3 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="World/Wall"]
position = Vector2( 0.465286, 0.57938 )
z_as_relative = false
shape = SubResource( 1 )
[node name="RigidBody2D" type="RigidBody2D" parent="World"]
position = Vector2( 649, 227 )
__meta__ = {
"_edit_group_": true
}
[node name="CollisionShape2D" type="CollisionShape2D" parent="World/RigidBody2D"]
rotation = -0.432491
scale = Vector2( 11.5039, 4.3795 )
shape = SubResource( 2 )
[node name="Sprite" type="Sprite" parent="World/RigidBody2D/CollisionShape2D"]
material = ExtResource( 4 )
use_parent_material = true
scale = Vector2( 20.002, 19.4893 )
texture = ExtResource( 3 )
[node name="Wall2" type="KinematicBody2D" parent="World"]
position = Vector2( 896, 386 )
scale = Vector2( 7.56, 1 )
__meta__ = {
"_edit_group_": true
}
[node name="Sprite" type="Sprite" parent="World/Wall2"]
material = ExtResource( 4 )
position = Vector2( 0.965286, 0.579379 )
scale = Vector2( 115, 104 )
texture = ExtResource( 3 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="World/Wall2"]
position = Vector2( 0.465286, 0.57938 )
z_as_relative = false
shape = SubResource( 1 )
[node name="CollisionLine" type="KinematicBody2D" parent="World"]
position = Vector2( 221, 205 )
script = ExtResource( 5 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="World/CollisionLine"]
shape = SubResource( 3 )
[node name="UI" type="CanvasLayer" parent="."]
[node name="PlayerMovableTarget" type="Label" parent="UI"]
margin_right = 40.0
margin_bottom = 14.0
text = "Blaaa"
__meta__ = {
"_edit_use_anchors_": false
}
[connection signal="pressed" from="Control/AddEntityButton" to="." method="_on_AddEntityButton_pressed"]
[connection signal="pressed" from="Control/AddWanderingEntity" to="." method="_on_AddWanderingEntity_pressed"]
[editable path="Entities/SimpleEntity"]
[editable path="Entities/SimpleEntity2"]

View File

@ -1,5 +1,9 @@
using System;
public static class Globals
{
public const float EpsPosition = 0.01f;
public const float EpsPositionSquared = 0.01f * 0.01f;
public const float EpsPositionSquared = EpsPosition * EpsPosition;
public const float EpsRadians = 0.1f * Godot.Mathf.Pi / 180f;
public const float EpsRadiansSquared = EpsRadians * EpsRadians;
}

View File

@ -13,4 +13,8 @@
<ItemGroup>
<PackageReference Include="Chickensoft.GoDotTest" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="LocalSystems" />
</ItemGroup>
</Project>

6
components/Component.cs Normal file
View File

@ -0,0 +1,6 @@
using Godot;
public class Component : Node
{
}

View File

@ -6,12 +6,14 @@ using Vector3 = Godot.Vector3;
/// <summary>
/// </summary>
public class GroundMotionComponent
public class GroundMotionComponent : Component
{
public float Accel = 50;
public float Damping = 0.2f;
public float MaxSpeed = 8;
public float RotationSpeedRadPerSecond = 270 * Mathf.Pi / 180;
private void CalcPlaneVelocity(float delta, Entity entity, Vector3 targetPosition)
{
@ -19,13 +21,14 @@ public class GroundMotionComponent
targetPosition.z - entity.GlobalTranslation.z);
float targetDistance = planeTargetVector.Length();
Vector2 planeTargetDirection = planeTargetVector / targetDistance;
Vector2 planeOrientation = new Vector2(entity.GlobalTransform.basis.z[0], entity.GlobalTransform.basis.z[2]);
Vector2 planeVelocity = new Vector2(entity.Velocity.x, entity.Velocity.z);
// GD.Print("-- Step: distance: " + targetDistance + " dir: " + planeTargetDirection + " speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
planeVelocity -= planeVelocity * Damping;
// GD.Print(" damp : speed: " + planeVelocity.Length() + " vel dir: " + planeVelocity.Normalized());
if (targetDistance < 0.01)
if (targetDistance < 0.01 || planeOrientation.Dot(planeTargetDirection) < 0.9)
{
planeVelocity = Vector2.Zero;
} else {
@ -79,15 +82,46 @@ public class GroundMotionComponent
}
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, TileWorld tileWorld)
public void PhysicsProcess(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation, TileWorld tileWorld)
{
CalcAndApplyOrientation(delta, entity, targetPosition, targetOrientation);
CalcPlaneVelocity(delta, entity, targetPosition);
CalcVerticalVelocity(delta, entity, tileWorld);
Vector3 prePhysicsVelocity = entity.Velocity;
entity.Velocity = entity.MoveAndSlide(entity.Velocity);
//GD.Print("Pre : speed: " + prePhysicsVelocity.Length() + " Velocity: " + prePhysicsVelocity);
//GD.Print("Post: speed: " + entity.Velocity.Length() + " Velocity: " + entity.Velocity);
}
private void CalcAndApplyOrientation(float delta, Entity entity, Vector3 targetPosition, Quat targetOrientation)
{
Vector3 direction_to_target = targetPosition - entity.GlobalTranslation;
if (direction_to_target.LengthSquared() > Globals.EpsPositionSquared)
{
direction_to_target = direction_to_target.Normalized();
Vector3 localXAxis = Vector3.Up.Cross(direction_to_target);
targetOrientation = new Basis(localXAxis, Vector3.Up, direction_to_target).Quat().Normalized();
}
float orientationErrorRadians = entity.GlobalTransform.basis.Quat().AngleTo(targetOrientation);
if (orientationErrorRadians > 0)
{
Transform entityTransform = entity.Transform;
if (orientationErrorRadians < RotationSpeedRadPerSecond * delta)
{
entityTransform.basis = new Basis(targetOrientation);
}
else if (orientationErrorRadians > 0f)
{
Quat entityRotation = new Quat(entityTransform.basis);
float slerpWeight = RotationSpeedRadPerSecond / (orientationErrorRadians / delta);
entityRotation = entityRotation.Slerp(targetOrientation, slerpWeight);
entityTransform.basis = new Basis(entityRotation);
}
entity.Transform = entityTransform;
}
}
}

View File

@ -1,7 +1,7 @@
using Godot;
using System;
public class MovableComponent : Node
public class MovableComponent : Component
{
public Vector3 targetPosition = Vector3.Zero;
public Vector3 currentPosition = Vector3.Zero;
@ -58,7 +58,7 @@ public class MovableComponent : Node
currentAngle = springDamperResult.Item1;
currentAngularVelocity = springDamperResult.Item2;
EmitSignal("OrientationUpdated", this.currentAngle);
// EmitSignal("OrientationUpdated", this.currentAngle);
}
if ((Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3)

View File

@ -12,22 +12,48 @@ using GoDotLog;
/// </summary>
public class NavigationComponent : Node
{
public class NavigationPoint
{
[Flags]
public enum NavigationFlags
{
Position = 1,
Orientation = 2
}
public Vector3 WorldPosition = Vector3.Zero;
public Quat WorldOrientation = Quat.Identity;
public NavigationFlags Flags = NavigationFlags.Position | NavigationFlags.Orientation;
public NavigationPoint(Vector3 worldPosition)
{
WorldPosition = worldPosition;
Flags = NavigationFlags.Position;
}
public NavigationPoint(Quat worldOrientation)
{
WorldOrientation = worldOrientation;
Flags = NavigationFlags.Orientation;
}
}
public TileWorld TileWorld { set; get; }
public Vector3 CurrentGoalWorld => _currentGoalWorld;
public Vector3 CurrentGoalPositionWorld => _currentGoalPositionWorld;
public Quat CurrentGoalOrientationWorld => _currentGoalOrientationWorld;
private Vector3 _currentGoalWorld = Vector3.Zero;
private Vector2 _currentGoalPlane = Vector2.Zero;
private Vector2 _currentGoalOffset = Vector2.Zero;
private Vector3 _currentPositionWorld = Vector3.Zero;
private NavigationPoint _currentGoal;
private Vector3 _currentGoalPositionWorld = Vector3.Zero;
private Quat _currentGoalOrientationWorld = Quat.Identity;
private List<Vector2> _pathOffsetCoords;
private List<NavigationPoint> _pathWorldNavigationPoints;
private HexCell[] _path;
public override void _Ready()
{
base._Ready();
_pathOffsetCoords = new List<Vector2>();
_pathWorldNavigationPoints = new List<NavigationPoint>();
}
public override void _Process(float delta)
@ -50,39 +76,103 @@ public class NavigationComponent : Node
_path = fromCell.LineTo(toCell);
Debug.Assert(_path.Length > 0);
_pathOffsetCoords = new List<Vector2>();
_pathWorldNavigationPoints = new List<NavigationPoint>();
foreach (int index in Enumerable.Range(1, _path.Length - 1))
{
_pathOffsetCoords.Add(_path[index].OffsetCoords);
_pathWorldNavigationPoints.Add(
new NavigationPoint(TileWorld.GetTileWorldCenterFromOffset(_path[index].OffsetCoords)));
}
if ((toPositionWorld - TileWorld.GetTileWorldCenterFromOffset(toCell.OffsetCoords)).LengthSquared() >
Globals.EpsPositionSquared)
{
_pathWorldNavigationPoints.Add(new NavigationPoint(toPositionWorld));
}
UpdateCurrentGoal();
}
public void Plan(Vector3 fromPositionWorld, Vector3 toPositionWorld, Quat toWorldOrientation)
{
Plan(fromPositionWorld, toPositionWorld);
_pathWorldNavigationPoints.Add(new NavigationPoint(toWorldOrientation));
}
private void UpdateCurrentGoal()
{
if (_pathOffsetCoords.Count == 0)
if (_pathWorldNavigationPoints.Count == 0)
{
return;
}
_currentGoalWorld = TileWorld.GetTileWorldCenterFromOffset(_pathOffsetCoords[0]);
_currentGoal = _pathWorldNavigationPoints[0];
GD.Print("Navigation: at " + _currentGoalWorld + " path length: " +
_pathOffsetCoords.Count);
GD.Print("Navigation Goal: pos " + _currentGoal.WorldPosition + " " + " rot: " + _currentGoal.WorldOrientation +
" flags: " + _currentGoal.Flags + " path length: " +
_pathWorldNavigationPoints.Count);
}
public void UpdateCurrentGoal(Vector3 currentPositionWorld)
public void UpdateCurrentGoal(Transform currentTransformWorld)
{
_currentPositionWorld = currentPositionWorld;
if (_pathWorldNavigationPoints.Count == 0)
{
return;
}
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position))
{
_currentGoalPositionWorld = _pathWorldNavigationPoints[0].WorldPosition;
}
else
{
_currentGoalPositionWorld = currentTransformWorld.origin;
}
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation))
{
_currentGoalOrientationWorld = _currentGoal.WorldOrientation;
}
else
{
_currentGoalOrientationWorld = currentTransformWorld.basis.Quat();
}
Vector3 currentPositionWorld = currentTransformWorld.origin;
Quat currentOrientationWorld = currentTransformWorld.basis.Quat();
Vector2 currentPositionPlane = new Vector2(currentPositionWorld.x, currentPositionWorld.z);
Vector2 currentGoalPlane = new Vector2(_currentGoalWorld.x, _currentGoalWorld.z);
if (_pathOffsetCoords.Count > 0 &&
(currentPositionPlane - currentGoalPlane).LengthSquared() < Globals.EpsPositionSquared)
Vector2 currentGoalPlane = new Vector2(_currentGoal.WorldPosition.x, _currentGoal.WorldPosition.z);
bool goalReached = false;
float positionErrorSquared = (currentPositionPlane - currentGoalPlane).LengthSquared();
float orientationError = _currentGoalOrientationWorld.AngleTo(currentOrientationWorld);
if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position)
&& _currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation)
&& positionErrorSquared < Globals.EpsPositionSquared
&& orientationError < Globals.EpsRadians)
{
_pathOffsetCoords.RemoveAt(0);
goalReached = true;
}
else if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Position) &&
positionErrorSquared < Globals.EpsPositionSquared)
{
goalReached = true;
}
else if (_currentGoal.Flags.HasFlag(NavigationPoint.NavigationFlags.Orientation) &&
orientationError < Globals.EpsRadians)
{
goalReached = true;
}
if (goalReached)
{
_pathWorldNavigationPoints.RemoveAt(0);
UpdateCurrentGoal();
}
}

View File

@ -0,0 +1,42 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
public class TaskQueueComponent : Component
{
public class Task
{
}
public class NavigationTask : Task
{
[Flags] public enum Flags
{
Position = 1,
Orientation = 2
}
public Vector3 TargetPositionWorld = Vector3.Zero;
public Quat TargetOrientationWorld = Quat.Identity;
public Flags NavigationFlags = Flags.Position | Flags.Orientation;
}
public class InteractionTask : Task
{
public Entity TargetEntity;
}
public Queue<Task> Queue;
public TaskQueueComponent()
{
Queue = new Queue<Task>();
Reset();
}
public void Reset()
{
Queue.Clear();
}
}

View File

@ -1,7 +1,7 @@
using Godot;
using System;
public class WorldInfoComponent : Node
public class WorldInfoComponent : Component
{
[Export] public NodePath World;

19
doc/ComponentStuff.puml Normal file
View File

@ -0,0 +1,19 @@
@startuml
'https://plantuml.com/class-diagram
class Entity
Entity *-- Component
Entity : update()
Entity : Component[] mComponents
Entity : LocalSystem[] mSystems;
class Component3 {
void update()
int counter
}
note right of Entity::update
lel
end note
@enduml

View File

@ -51,9 +51,4 @@ public class Chest : Entity
IsMouseOver = false;
_mesh.MaterialOverride = null;
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
}

10
entities/Chest.tscn Normal file
View File

@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://assets/Objects/chest.glb" type="PackedScene" id=1]
[ext_resource path="res://entities/Chest.cs" type="Script" id=2]
[node name="Chest" instance=ExtResource( 1 )]
script = ExtResource( 2 )
[node name="Armature" parent="." index="2"]
transform = Transform( -0.259808, 0, 0.15, 0, 0.3, 0, -0.15, 0, -0.259808, 0, 0, 0 )

33
entities/Flower.cs Normal file
View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using Godot;
using Godot.Collections;
namespace GodotComponentTest.entities;
public class Flower : Entity
{
public override void _Ready()
{
Array<Node> children = new Array<Node>();
GetAllChildren(this, ref children);
foreach (Node child in children)
{
if (child is ClickableComponent)
{
GD.Print("Found Clickable Component!");
}
}
}
void GetAllChildren(Node childNode, ref Array<Node> childList)
{
var children = childNode.GetChildren();
foreach (Node child in children)
{
childList.Add(child);
GetAllChildren(child, ref childList);
}
}
}

View File

@ -6,6 +6,8 @@ public class Player : Entity
// public members
public Vector3 TargetPosition = Vector3.Zero;
public TaskQueueComponent TaskQueueComponent;
public NavigationComponent Navigation
{
get { return _navigationComponent; }
@ -15,7 +17,6 @@ public class Player : Entity
private MovableComponent _movable;
private Spatial _geometry;
private WorldInfoComponent _worldInfo;
private Vector2 _offsetCoord = Vector2.Zero;
private GroundMotionComponent _groundMotion;
private NavigationComponent _navigationComponent;
@ -26,6 +27,7 @@ public class Player : Entity
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
_navigationComponent = (NavigationComponent)FindNode("Navigation", false);
_navigationComponent.TileWorld = _worldInfo.TileWorld;
TaskQueueComponent = new TaskQueueComponent();
_movable = (MovableComponent)FindNode("Movable", false);
if (_movable != null)
@ -46,9 +48,18 @@ public class Player : Entity
return;
}
_navigationComponent.UpdateCurrentGoal(GlobalTranslation);
if (TaskQueueComponent != null && TaskQueueComponent.Queue.Count > 0)
{
var currentTask = TaskQueueComponent.Queue.Peek();
if (currentTask is TaskQueueComponent.NavigationTask)
{
TaskQueueComponent.NavigationTask navigationTask = (TaskQueueComponent.NavigationTask)TaskQueueComponent.Queue.Dequeue();
_groundMotion.PhysicsProcess(delta, this, _navigationComponent.CurrentGoalWorld, _worldInfo.TileWorld);
}
}
_navigationComponent.UpdateCurrentGoal(GlobalTransform);
_groundMotion.PhysicsProcess(delta, this, _navigationComponent.CurrentGoalPositionWorld, _navigationComponent.CurrentGoalOrientationWorld, _worldInfo.TileWorld);
}
@ -56,11 +67,12 @@ public class Player : Entity
{
if (_navigationComponent != null)
{
_navigationComponent.UpdateCurrentGoal(GlobalTranslation);
_navigationComponent.UpdateCurrentGoal(GlobalTransform);
}
}
}
private void OnOrientationUpdated(float newOrientation)
{
_geometry.Transform = new Transform(new Quat(Vector3.Up, newOrientation), Vector3.Zero);

View File

@ -75,7 +75,7 @@ _global_script_class_icons={
[application]
config/name="i3sc1 GodotComponentTest"
run/main_scene="res://scenes/AdaptiveWorldStream.tscn"
run/main_scene="res://scenes/Game.tscn"
config/icon="res://icon.png"
[display]

View File

@ -1,6 +1,5 @@
[gd_scene load_steps=27 format=2]
[gd_scene load_steps=26 format=2]
[ext_resource path="res://scenes/AdaptiveWorldStream.cs" type="Script" id=1]
[ext_resource path="res://assets/CreatusPiratePack/characters/Pirate1final_0.01.glb" type="PackedScene" id=2]
[ext_resource path="res://components/WorldInfoComponent.cs" type="Script" id=3]
[ext_resource path="res://scenes/StreamContainer.cs" type="Script" id=4]
@ -104,7 +103,6 @@ tracks/1/keys = PoolRealArray( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0.133333, 1,
extents = Vector3( 0.2, 0.2, 0.332224 )
[node name="AdaptiveWorldStream" type="Spatial"]
script = ExtResource( 1 )
[node name="TileHighlight" parent="." instance=ExtResource( 7 )]
visible = false

View File

@ -6,7 +6,7 @@ using GoDotLog;
using Dictionary = Godot.Collections.Dictionary;
using Array = Godot.Collections.Array;
public class AdaptiveWorldStream : Spatial
public class Game : Spatial
{
// ui elements
private Label _framesPerSecondLabel;
@ -59,7 +59,7 @@ public class AdaptiveWorldStream : Spatial
_streamContainerArea = GetNode<Area>("StreamContainer/Area");
_streamContainerActiveTiles = GetNode<Spatial>("StreamContainer/ActiveTiles");
_player = GetNode<Player>("Player");
_chest = GetNode<Chest>("Chest");
_chest = GetNode<Chest>("Entities/Chest");
_tileWorld = GetNode<TileWorld>("TileWorld");
Debug.Assert(_tileWorld != null);
@ -193,6 +193,13 @@ public class AdaptiveWorldStream : Spatial
public void OnEntityClicked(Entity entity)
{
GD.Print("Clicked on entity at " + entity.GlobalTranslation);
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
if (mountPoint != null)
{
GD.Print("Mount point!");
_player.Navigation.Plan(_player.GlobalTranslation, mountPoint.GlobalTranslation, mountPoint.GlobalTransform.basis.Quat());
}
}

482
scenes/Game.tscn Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
public class LookupWorldSystem : IWorldSystemInterface
{
public void RegisterEntityComponent(Entity entity, Component component)
{
throw new System.NotImplementedException();
}
public void Update(float delta)
{
throw new System.NotImplementedException();
}
}

View File

@ -0,0 +1,5 @@
public interface IWorldSystemInterface
{
void RegisterEntityComponent(Entity entity, Component component);
void Update(float delta);
}