Skip to content

Commit

Permalink
Added ECS helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
mario-deluna committed Dec 31, 2024
1 parent b53b1cc commit 63161fe
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/ECS/EntitiesInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public function detachAll(int $entity) : void;

/**
* Returns a component for the given entity
* ! Warning: This method does no error checking and assumes you made sure the component needs to actually exist!
*
*
* @template T
* @param int $entity The entitiy ID of the component to be retrieved
Expand All @@ -89,6 +91,17 @@ public function detachAll(int $entity) : void;
*/
public function get(int $entity, string $componentClassName);

/**
* Returns a component for the given entity or null if it does not exist
*
* @template T
* @param int $entity The entitiy ID of the component to be retrieved
* @param class-string<T> $componentClassName
*
* @return T|null
*/
public function tryGet(int $entity, string $componentClassName);

/**
* Returns boolean if an entity has a component
*
Expand All @@ -113,6 +126,16 @@ public function components(int $entity) : array;
* @return \Generator<int, T>
*/
public function view(string $componentClassName) : Generator;

/**
* Iterates over all entities having the given components and will pass the components as arguments to the callback
* Please note that the order is important, choose the "rarest" component first as this will be used
* to iterate and filter the entities.
*
* @param class-string ...$componentClassNames
* @return \Generator<int, array<object>>
*/
public function viewWith(string ...$componentClassNames) : Generator;

/**
* Returns the first component of the given class name
Expand Down
43 changes: 43 additions & 0 deletions src/ECS/EntityRegisty.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ public function get(int $entity, string $componentClassName)
return $this->entityComponents[$entity][$componentClassName];
}

/**
* Returns a component for the given entity or null if it does not exist
*
* @template T
* @param int $entity The entitiy ID of the component to be retrieved
* @param class-string<T> $componentClassName
*
* @return T|null
*/
public function tryGet(int $entity, string $componentClassName)
{
// @phpstan-ignore-next-line
return $this->entityComponents[$entity][$componentClassName] ?? null;
}

/**
* Returns boolean if an entity has a component
*
Expand Down Expand Up @@ -242,6 +257,34 @@ public function view(string $componentClassName) : Generator
}
}

/**
* Iterates over all entities having the given components and will pass the components as arguments to the callback
*
* @param class-string ...$componentClassNames
* @return \Generator<int, array<object>>
*/
public function viewWith(string ...$componentClassNames) : Generator
{
if (empty($componentClassNames)) {
return;
}

$mainComponent = array_shift($componentClassNames);

foreach ($this->view($mainComponent) as $entity => $mainComponentInstance) {
$components = [$mainComponentInstance];

foreach ($componentClassNames as $componentClassName) {
if (!isset($this->entityComponents[$entity][$componentClassName])) {
continue 2; // Skip this entity if it does not have the required component
}
$components[] = $this->entityComponents[$entity][$componentClassName];
}

yield $entity => $components;
}
}

/**
* Returns the first component of the given class name
*
Expand Down
43 changes: 43 additions & 0 deletions tests/ECS/EntityRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,47 @@ public function testSerialization() : void
$this->assertEquals('e2', $entites->get($e2, \Error::class)->getMessage());
$this->assertEquals(3, $entites->create());
}

public function testTryGet() : void
{
$entites = new EntityRegisty();
$entites->registerComponent(\Exception::class);

$e1 = $entites->create();
$entites->attach($e1, new \Exception('e1'));

$e2 = $entites->create();
$entites->attach($e2, new \Exception('e2'));

$this->assertEquals('e1', $entites->tryGet($e1, \Exception::class)->getMessage());
$this->assertNull($entites->tryGet($e2, \Error::class));
}

public function testViewWith() : void
{
$entites = new EntityRegisty();
$entites->registerComponent(\Exception::class);
$entites->registerComponent(\Error::class);

$e1 = $entites->create();
$entites->attach($e1, new \Exception('e1'));

$e2 = $entites->create();
$entites->attach($e2, new \Exception('e2'));
$entites->attach($e2, new \Error('e2'));

$e3 = $entites->create();
$entites->attach($e3, new \Error('e3'));
$entites->attach($e3, new \Exception('e3'));

$e4 = $entites->create();
$entites->attach($e4, new \Error('e4'));

$actualBuffer = [];
foreach($entites->viewWith(\Exception::class, \Error::class) as $entity => $components) {
$actualBuffer[] = $entity;
}

$this->assertEquals([$e2, $e3], $actualBuffer);
}
}

0 comments on commit 63161fe

Please sign in to comment.