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

C#游戏开发:玩家移动事件触发多类逻辑的有序执行设计模式问询

适配玩家移动事件有序执行的C#设计方案

嘿,这个场景我在开发2D沙盒类游戏时刚好遇到过!核心需求就是保证玩家移动事件触发后,RenderGrid先执行瓦片实例化,再让WorldManager处理区块加载,下面几个设计模式都能完美适配,咱们一个个说:

1. 带优先级的观察者模式

标准的观察者模式(比如C#内置的EventHandler)默认是无序执行订阅者的,但咱们可以给每个订阅者加个优先级标记,触发事件时按优先级排序执行,刚好满足你的顺序需求。

实现思路:

  • 自定义一个带优先级的事件订阅容器,存储事件处理方法和对应的优先级值
  • 给RenderGrid设置高优先级(比如10),WorldManager设置低优先级(比如5)
  • 玩家移动事件触发时,先执行高优先级的RenderGrid逻辑,再执行低优先级的WorldManager

代码示例:

// 自定义带优先级的事件系统
public class PriorityEvent<TArgs>
{
    private SortedList<int, List<Action<TArgs>>> _subscribers = new SortedList<int, List<Action<TArgs>>>();

    public void Subscribe(Action<TArgs> handler, int priority)
    {
        if (!_subscribers.ContainsKey(priority))
        {
            _subscribers[priority] = new List<Action<TArgs>>();
        }
        _subscribers[priority].Add(handler);
    }

    public void Unsubscribe(Action<TArgs> handler, int priority)
    {
        if (_subscribers.ContainsKey(priority))
        {
            _subscribers[priority].Remove(handler);
        }
    }

    public void Invoke(TArgs args)
    {
        // 按优先级从高到低执行(SortedList默认升序,所以反向遍历)
        foreach (var pair in _subscribers.Reverse())
        {
            foreach (var handler in pair.Value)
            {
                handler.Invoke(args);
            }
        }
    }
}

// 玩家移动事件参数(包含bottomLeft和topRight向量)
public class PlayerMoveArgs
{
    public Vector2 BottomLeft { get; set; }
    public Vector2 TopRight { get; set; }
}

// 玩家控制器类
public class PlayerController
{
    public PriorityEvent<PlayerMoveArgs> OnPlayerMoved = new PriorityEvent<PlayerMoveArgs>();

    private void OnPlayerPositionChanged()
    {
        // 计算当前视图的bottomLeft和topRight
        var moveArgs = new PlayerMoveArgs
        {
            BottomLeft = CalculateBottomLeft(),
            TopRight = CalculateTopRight()
        };
        // 触发事件
        OnPlayerMoved.Invoke(moveArgs);
    }

    // 省略计算方法...
    private Vector2 CalculateBottomLeft() => default;
    private Vector2 CalculateTopRight() => default;
}

// RenderGrid类订阅(高优先级)
public class RenderGrid
{
    public RenderGrid(PlayerController player)
    {
        // 订阅事件,优先级设为10(越高越先执行)
        player.OnPlayerMoved.Subscribe(HandlePlayerMoved, 10);
    }

    private void HandlePlayerMoved(PlayerMoveArgs args)
    {
        // 立即实例化新可见的游戏瓦片
        InstantiateTiles(args.BottomLeft, args.TopRight);
    }

    private void InstantiateTiles(Vector2 bottomLeft, Vector2 topRight)
    {
        // 瓦片实例化逻辑...
    }
}

// WorldManager类订阅(低优先级)
public class WorldManager
{
    public WorldManager(PlayerController player)
    {
        // 订阅事件,优先级设为5
        player.OnPlayerMoved.Subscribe(HandlePlayerMoved, 5);
    }

    private void HandlePlayerMoved(PlayerMoveArgs args)
    {
        // 检查并加载新区块
        LoadChunks(args.BottomLeft, args.TopRight);
    }

    private void LoadChunks(Vector2 bottomLeft, Vector2 topRight)
    {
        // 区块加载逻辑...
    }
}

2. 责任链模式

如果后续可能要增加更多事件处理环节(比如加个AudioManager处理移动音效),责任链模式会更灵活——把每个处理类做成链条上的节点,事件按链条顺序传递,先经过RenderGrid,再到WorldManager,后续加节点只需要插入链条即可。

实现思路:

  • 定义抽象的事件处理基类,包含处理方法和后继节点引用
  • 让RenderGridHandler和WorldManagerHandler继承基类,实现各自的处理逻辑
  • 链条初始化时,把RenderGridHandler的后继设为WorldManagerHandler
  • 玩家移动事件触发时,从链条头部(RenderGridHandler)开始执行,自动传递给后继

代码示例:

// 抽象处理者基类
public abstract class PlayerMoveHandler
{
    protected PlayerMoveHandler _nextHandler;

    public void SetNext(PlayerMoveHandler nextHandler)
    {
        _nextHandler = nextHandler;
    }

    public abstract void Handle(PlayerMoveArgs args);
}

// RenderGrid处理者
public class RenderGridHandler : PlayerMoveHandler
{
    private RenderGrid _renderGrid;

    public RenderGridHandler(RenderGrid renderGrid)
    {
        _renderGrid = renderGrid;
    }

    public override void Handle(PlayerMoveArgs args)
    {
        // 先执行RenderGrid的逻辑
        _renderGrid.InstantiateTiles(args.BottomLeft, args.TopRight);
        
        // 传递给下一个处理者
        _nextHandler?.Handle(args);
    }
}

// WorldManager处理者
public class WorldManagerHandler : PlayerMoveHandler
{
    private WorldManager _worldManager;

    public WorldManagerHandler(WorldManager worldManager)
    {
        _worldManager = worldManager;
    }

    public override void Handle(PlayerMoveArgs args)
    {
        // 执行WorldManager的逻辑
        _worldManager.LoadChunks(args.BottomLeft, args.TopRight);
        
        // 如果有后续处理者,继续传递(这里可以省略,因为是最后一个)
        _nextHandler?.Handle(args);
    }
}

// 玩家控制器类调整
public class PlayerController
{
    private PlayerMoveHandler _handlerChain;

    public void InitializeHandlerChain(RenderGrid renderGrid, WorldManager worldManager)
    {
        // 构建链条:RenderGrid -> WorldManager
        var renderHandler = new RenderGridHandler(renderGrid);
        var worldHandler = new WorldManagerHandler(worldManager);
        renderHandler.SetNext(worldHandler);
        _handlerChain = renderHandler;
    }

    private void OnPlayerPositionChanged()
    {
        var moveArgs = new PlayerMoveArgs
        {
            BottomLeft = CalculateBottomLeft(),
            TopRight = CalculateTopRight()
        };
        // 触发链条处理
        _handlerChain.Handle(moveArgs);
    }

    // 省略其他方法...
}

3. 中介者模式

如果你的游戏模块比较多,不想让PlayerController直接和RenderGrid、WorldManager耦合,中介者模式是个不错的选择——用一个GameEventMediator来统一管理事件分发顺序,玩家只需要通知中介者,由中介者按预定顺序调用各个模块。

核心优势:

  • 解耦玩家控制器和各个业务模块,后续修改执行顺序只需要调整中介者即可
  • 集中管理所有游戏事件的分发逻辑,便于维护

简化示例:

public class GameEventMediator
{
    private RenderGrid _renderGrid;
    private WorldManager _worldManager;

    public GameEventMediator(RenderGrid renderGrid, WorldManager worldManager)
    {
        _renderGrid = renderGrid;
        _worldManager = worldManager;
    }

    public void OnPlayerMoved(PlayerMoveArgs args)
    {
        // 严格按顺序执行:先渲染瓦片,再加载区块
        _renderGrid.InstantiateTiles(args.BottomLeft, args.TopRight);
        _worldManager.LoadChunks(args.BottomLeft, args.TopRight);
    }
}

// 玩家控制器类
public class PlayerController
{
    private GameEventMediator _mediator;

    public PlayerController(GameEventMediator mediator)
    {
        _mediator = mediator;
    }

    private void OnPlayerPositionChanged()
    {
        var moveArgs = new PlayerMoveArgs
        {
            BottomLeft = CalculateBottomLeft(),
            TopRight = CalculateTopRight()
        };
        // 通知中介者
        _mediator.OnPlayerMoved(moveArgs);
    }
}

实践建议

  • 如果是Unity游戏,你可以结合UnityEvent但要注意:默认UnityEvent的订阅顺序是按添加顺序来的,所以只要先给RenderGrid注册事件,再给WorldManager注册,就能保证顺序,但这种方式不够灵活,后续调整顺序需要修改注册代码
  • 性能敏感的场景(比如大地图频繁移动),优先选择带优先级的观察者模式,因为责任链和中介者的调用开销几乎可以忽略,但观察者模式的排序逻辑要尽量高效(比如用SortedList提前排序,避免每次触发都排序)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:20:12