You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EventListener中触发事件?PHP游戏核心中介者模式实现问询

问题1:如何在EventListener中触发事件?

Hey there! Triggering events inside an event listener is totally common—think of it as chaining actions after a core event is processed. The core idea is straightforward: you just need access to your event dispatcher instance, then call its dispatch method to fire off the new event.

Let's use PHP as an example (since your second question is PHP-focused). Here's a simple implementation using a custom or framework-provided dispatcher:

// Assume we have a basic EventDispatcher class
class EventDispatcher {
    private $listeners = [];

    public function addListener(string $eventName, callable $listener) {
        $this->listeners[$eventName][] = $listener;
    }

    public function dispatch(string $eventName, $eventData) {
        foreach ($this->listeners[$eventName] ?? [] as $listener) {
            call_user_func($listener, $eventData);
        }
    }
}

// A listener that triggers another event
class UserSignupListener {
    private $dispatcher;

    // Inject the dispatcher via constructor (dependency injection is key here!)
    public function __construct(EventDispatcher $dispatcher) {
        $this->dispatcher = $dispatcher;
    }

    public function handleSignup($userData) {
        // Do core signup logic: save user to DB, send confirmation email, etc.
        echo "User {$userData['email']} signed up!\n";

        // Trigger a post-signup event for follow-up actions (like onboarding)
        $this->dispatcher->dispatch('user.post_signup', [
            'userId' => $userData['id'],
            'email' => $userData['email']
        ]);
    }
}

// Usage
$dispatcher = new EventDispatcher();
$dispatcher->addListener('user.signup', [new UserSignupListener($dispatcher), 'handleSignup']);
$dispatcher->addListener('user.post_signup', function($data) {
    echo "Sending onboarding guide to {$data['email']}\n";
});

$dispatcher->dispatch('user.signup', ['id' => 1, 'email' => 'player@game.com']);

Key Notes:

  • Avoid infinite loops: Make sure you don't create a cycle where Listener A triggers Event X, which triggers Listener B, which triggers Event X again. Add checks or guard clauses if needed.
  • Dispatcher availability: Always inject the dispatcher into your listener (don't use singletons unless you have to)—this keeps your code testable and decoupled.

问题2:PHP游戏应用中实现带责任链机制的Mediator模式

Great call using Mediator (EventDispatcher) for your game—it's perfect for managing all those events and state changes. Adding a responsibility chain twist just means letting listeners decide whether to pass the event along to the next one. Here's how to implement this tailored to a game context:

Step 1: Create a GameEvent Class with Propagation Control

First, we need an event class that tracks if propagation should stop:

class GameEvent {
    private $propagationStopped = false;
    private $data;

    public function __construct(array $data) {
        $this->data = $data;
    }

    public function getData(): array {
        return $this->data;
    }

    public function setData(array $data): void {
        $this->data = $data;
    }

    public function stopPropagation(): void {
        $this->propagationStopped = true;
    }

    public function isPropagationStopped(): bool {
        return $this->propagationStopped;
    }
}

Step 2: Build a Mediator (EventDispatcher) with Responsibility Chain Logic

This dispatcher will run listeners in priority order and stop if any listener halts propagation:

class GameEventDispatcher {
    private $listeners = [];

    // Add listeners with priority (higher numbers run first)
    public function addListener(string $eventName, callable $listener, int $priority = 0): void {
        if (!isset($this->listeners[$eventName])) {
            $this->listeners[$eventName] = [];
        }
        $this->listeners[$eventName][$priority][] = $listener;

        // Sort priorities descending so higher values run first
        krsort($this->listeners[$eventName]);
    }

    // Dispatch events with responsibility chain behavior
    public function dispatch(string $eventName, GameEvent $event): void {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priorityListeners) {
            foreach ($priorityListeners as $listener) {
                // Execute the listener
                call_user_func($listener, $event);

                // Stop processing if propagation is halted
                if ($event->isPropagationStopped()) {
                    return;
                }
            }
        }
    }
}

Step 3: Example Game Listeners Using the Chain

Let's apply this to a "player takes damage" scenario, where multiple listeners handle the event in sequence:

// Listener 1: Apply armor damage reduction (high priority)
class ArmorReductionListener {
    public function __invoke(GameEvent $event): void {
        $data = $event->getData();
        $player = $data['player'];
        $originalDamage = $data['damage'];

        // Calculate reduced damage based on armor
        $armorFactor = 1 - ($player->getArmor() / 100);
        $reducedDamage = max(0, $originalDamage * $armorFactor);
        $data['damage'] = $reducedDamage;
        $event->setData($data);

        echo "Armor reduced damage from {$originalDamage} to {$reducedDamage}\n";

        // Stop propagation if damage is fully negated
        if ($reducedDamage <= 0) {
            $event->stopPropagation();
            echo "No damage taken—stopping further processing\n";
        }
    }
}

// Listener 2: Deduct health from the player (lower priority)
class HealthDeductListener {
    public function __invoke(GameEvent $event): void {
        $data = $event->getData();
        $player = $data['player'];
        $newHealth = max(0, $player->getHealth() - $data['damage']);
        $player->setHealth($newHealth);

        echo "Player health updated to {$newHealth}\n";

        // Trigger death event if health hits zero
        $dispatcher = new GameEventDispatcher(); // In real code, inject this!
        $dispatcher->dispatch('player.died', new GameEvent(['player' => $player]));
    }
}

// Mock Player class for the example
class Player {
    private $health = 100;
    private $armor = 70;

    public function getHealth(): int { return $this->health; }
    public function setHealth(int $health): void { $this->health = $health; }
    public function getArmor(): int { return $this->armor; }
}

// Usage
$dispatcher = new GameEventDispatcher();
$dispatcher->addListener('player.takes_damage', new ArmorReductionListener(), 10);
$dispatcher->addListener('player.takes_damage', new HealthDeductListener(), 5);

// Trigger the damage event
$player = new Player();
$dispatcher->dispatch('player.takes_damage', new GameEvent([
    'player' => $player,
    'damage' => 80
]));

Game-Specific Tips:

  • Prioritize critical listeners: For example, damage mitigation should run before health deduction, so give it a higher priority.
  • Isolate listener responsibilities: Each listener should only handle one part of the logic (armor reduction, health deduction, death handling) to keep your code maintainable.
  • Async processing for non-critical tasks: For things like logging achievements or sending in-game notifications, use an async queue to avoid blocking the game's main loop.

内容的提问来源于stack exchange,提问作者automatix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:48:52