Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 189 additions & 17 deletions src/Core/GameObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,17 @@ public static function find(string $gameObjectName): ?GameObjectInterface
{
if ($activeScene = SceneManager::getInstance()->getActiveScene()) {
foreach ($activeScene->getRootGameObjects() as $gameObject) {
if ($gameObject->getName() === $gameObjectName) {
return $gameObject;
if (!$gameObject instanceof GameObject) {
continue;
}

$match = self::findFirstInHierarchy(
$gameObject,
static fn (GameObject $candidate): bool => $candidate->getName() === $gameObjectName
);

if ($match !== null) {
return $match;
}
}
}
Expand All @@ -176,8 +185,17 @@ public static function findWithTag(string $gameObjectTag): ?GameObjectInterface
{
if ($activeScene = SceneManager::getInstance()->getActiveScene()) {
foreach ($activeScene->getRootGameObjects() as $gameObject) {
if ($gameObject->getTag() === $gameObjectTag) {
return $gameObject;
if (!$gameObject instanceof GameObject) {
continue;
}

$match = self::findFirstInHierarchy(
$gameObject,
static fn (GameObject $candidate): bool => $candidate->getTag() === $gameObjectTag
);

if ($match !== null) {
return $match;
}
}
}
Expand All @@ -204,9 +222,15 @@ public static function findAll(string $gameObjectName): array

if ($activeScene = SceneManager::getInstance()->getActiveScene()) {
foreach ($activeScene->getRootGameObjects() as $gameObject) {
if ($gameObject->getName() === $gameObjectName) {
$gameObjects[] = $gameObject;
if (!$gameObject instanceof GameObject) {
continue;
}

self::collectHierarchyMatches(
$gameObject,
static fn (GameObject $candidate): bool => $candidate->getName() === $gameObjectName,
$gameObjects
);
}
}

Expand All @@ -222,9 +246,15 @@ public static function findAllWithTag(string $gameObjectTag): array

if ($activeScene = SceneManager::getInstance()->getActiveScene()) {
foreach ($activeScene->getRootGameObjects() as $gameObject) {
if ($gameObject->getTag() === $gameObjectTag) {
$gameObjects[] = $gameObject;
if (!$gameObject instanceof GameObject) {
continue;
}

self::collectHierarchyMatches(
$gameObject,
static fn (GameObject $candidate): bool => $candidate->getTag() === $gameObjectTag,
$gameObjects
);
}
}

Expand Down Expand Up @@ -290,6 +320,7 @@ public function __clone(): void
$this->starting = false;

$originalComponents = $this->components;
$originalChildren = $this->getChildren();
$position = clone $this->transform->getPosition();
$rotation = clone $this->transform->getRotation();
$scale = clone $this->transform->getScale();
Expand All @@ -312,6 +343,11 @@ public function __clone(): void

$this->components[] = $this->cloneComponentForInstance($component);
}

foreach ($originalChildren as $child) {
$childClone = clone $child;
$childClone->getTransform()->setParent($this->transform);
}
}

/**
Expand Down Expand Up @@ -407,6 +443,21 @@ public function getTransform(): Transform
return $this->transform;
}

/**
* Returns the direct child game objects parented to this object.
*
* @return array<GameObject>
*/
public function getChildren(): array
{
return array_values(
array_map(
static fn (Transform $childTransform): GameObject => $childTransform->getGameObject(),
$this->transform->getChildren()
)
);
}

/**
* @inheritDoc
*/
Expand Down Expand Up @@ -480,9 +531,17 @@ public function equals(CanEquate $equatable): bool
*/
public function renderAt(?int $x = null, ?int $y = null): void
{
if ($this->isActive() && $this->renderer->isEnabled()) {
if (!$this->isActive()) {
return;
}

if ($this->renderer->isEnabled()) {
$this->renderer->renderAt($x, $y);
}

foreach ($this->getChildren() as $child) {
$child->renderAt($x, $y);
}
}

/**
Expand All @@ -498,7 +557,13 @@ public function isActive(): bool
*/
public function eraseAt(?int $x = null, ?int $y = null): void
{
if ($this->isActive() && $this->renderer->isEnabled()) {
$children = array_reverse($this->getChildren());

foreach ($children as $child) {
$child->eraseAt($x, $y);
}

if ($this->renderer->isEnabled()) {
$this->renderer->eraseAt($x, $y);
}
}
Expand All @@ -514,6 +579,10 @@ public function resume(): void
$component->resume();
}
}

foreach ($this->getChildren() as $child) {
$child->resume();
}
}
}

Expand All @@ -523,6 +592,10 @@ public function resume(): void
public function suspend(): void
{
if ($this->isActive()) {
foreach ($this->getChildren() as $child) {
$child->suspend();
}

foreach ($this->components as $component) {
if ($component->isEnabled()) {
$component->suspend();
Expand Down Expand Up @@ -554,6 +627,10 @@ public function start(): void
}
}

foreach ($this->getChildren() as $child) {
$child->start();
}

$this->starting = false;
$this->started = true;
}
Expand All @@ -563,6 +640,10 @@ public function start(): void
*/
public function stop(): void
{
foreach ($this->getChildren() as $child) {
$child->stop();
}

if ($this->isActive()) {
foreach ($this->components as $component) {
if ($component->isEnabled()) {
Expand All @@ -583,6 +664,10 @@ public function fixedUpdate(): void
$component->fixedUpdate();
}
}

foreach ($this->getChildren() as $child) {
$child->fixedUpdate();
}
}
}

Expand All @@ -597,6 +682,10 @@ public function update(): void
$component->update();
}
}

foreach ($this->getChildren() as $child) {
$child->update();
}
}
}

Expand All @@ -622,9 +711,17 @@ public function activate(): void
*/
public function render(): void
{
if ($this->isActive() && $this->renderer->isEnabled()) {
if (!$this->isActive()) {
return;
}

if ($this->renderer->isEnabled()) {
$this->renderer->render();
}

foreach ($this->getChildren() as $child) {
$child->render();
}
}

/**
Expand All @@ -646,18 +743,16 @@ public function deactivate(): void
$this->suspend();
}

$this->erase();
$this->active = false;
$this->getRenderer()->erase();
}

/**
* @inheritDoc
*/
public function erase(): void
{
if ($this->isActive() && $this->renderer->isEnabled()) {
$this->renderer->erase();
}
$this->eraseAt();
}

/**
Expand All @@ -669,11 +764,17 @@ public function erase(): void
*/
public function broadcast(string $methodName, array $args = []): void
{
$arguments = array_is_list($args) ? $args : array_values($args);

foreach ($this->components as $component) {
if (method_exists($component, $methodName)) {
$component->$methodName(...$args);
$component->$methodName(...$arguments);
}
}

foreach ($this->getChildren() as $child) {
$child->broadcast($methodName, $arguments);
}
}

/**
Expand Down Expand Up @@ -726,7 +827,78 @@ private function belongsToActiveScene(): bool
return false;
}

return in_array($this, $activeScene->getRootGameObjects(), true);
foreach ($activeScene->getRootGameObjects() as $gameObject) {
if ($gameObject instanceof GameObject && self::hierarchyContains($gameObject, $this)) {
return true;
}
}

return false;
}

/**
* Finds the first matching game object within a hierarchy branch.
*
* @param GameObject $gameObject
* @param callable(GameObject): bool $predicate
* @return GameObject|null
*/
private static function findFirstInHierarchy(GameObject $gameObject, callable $predicate): ?GameObject
{
if ($predicate($gameObject)) {
return $gameObject;
}

foreach ($gameObject->getChildren() as $child) {
$match = self::findFirstInHierarchy($child, $predicate);

if ($match !== null) {
return $match;
}
}

return null;
}

/**
* Collects every matching game object within a hierarchy branch.
*
* @param GameObject $gameObject
* @param callable(GameObject): bool $predicate
* @param array<GameObject> $matches
* @return void
*/
private static function collectHierarchyMatches(GameObject $gameObject, callable $predicate, array &$matches): void
{
if ($predicate($gameObject)) {
$matches[] = $gameObject;
}

foreach ($gameObject->getChildren() as $child) {
self::collectHierarchyMatches($child, $predicate, $matches);
}
}

/**
* Returns whether the target game object exists somewhere beneath the supplied root.
*
* @param GameObject $root
* @param GameObject $target
* @return bool
*/
private static function hierarchyContains(GameObject $root, GameObject $target): bool
{
if ($root === $target) {
return true;
}

foreach ($root->getChildren() as $child) {
if (self::hierarchyContains($child, $target)) {
return true;
}
}

return false;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/Core/Rendering/Renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ public final function renderAt(?int $x = null, ?int $y = null): void
return;
}

$xOffset = $this->getGameObject()->getTransform()->getPosition()->getX() + ($x ?? 0);
$yOffset = $this->getGameObject()->getTransform()->getPosition()->getY() + ($y ?? 0);
$worldPosition = $this->getGameObject()->getTransform()->getWorldPosition();
$xOffset = $worldPosition->getX() + ($x ?? 0);
$yOffset = $worldPosition->getY() + ($y ?? 0);
$width = $this->sprite->getRect()->getWidth();
$height = $this->sprite->getRect()->getHeight();
$spriteBufferedImage = $this->sprite->getBufferedImage();
Expand Down
Loading
Loading