游戏开发中Dog实例生成性能优化与订阅设计模式应用咨询
优化方案
1. 核心性能问题解决
你遇到的instanceof判断开销本质是「全量遍历+冗余类型判断」导致的,直接从存储结构上拆分即可彻底消除该开销:
- 狗实例注册类不再仅维护单一全量集合,拆分为两个独立的存储集合:
- 普通全服可见
Dog实例集合 - 按玩家生成的
PerPlayerDog实例集合
- 普通全服可见
- 注册狗实例时自动根据类型存入对应集合,定时任务不再需要遍历全量狗实例,也不需要做任何类型判断,直接遍历按玩家生成的实例集合即可,性能可以提升数倍。
补充:100次
instanceof判断本身的CPU开销极低,你感知到的性能瓶颈大概率还伴随每次循环重复获取狗/玩家位置、反复遍历玩家列表的冗余开销,后续优化可以顺便处理这些问题。
2. 重命名+订阅逻辑的合理性
完全合理。PerPlayerDog仅描述了实现层面的特性,重命名为SubscribedDog后直接匹配业务语义,后续不管是做距离触发的自动订阅,还是玩家主动领养的手动订阅,都不需要再调整接口的定位,扩展性更强。
具体代码优化建议
调整后接口定义
interface Dog { String getDogName(); UUID getUUID(); DogModel getDogModel(); } interface SubscribedDog extends Dog { // 玩家订阅该狗,重复订阅返回false boolean subscribe(Player player); // 玩家取消订阅 boolean unsubscribe(Player player); // 替代原getReceipts,语义更清晰 Iterable<Player> getSubscribers(); // 原生成逻辑保留 void spawn(Player player); // 内置订阅判断,减少外部重复逻辑 default boolean isSubscribed(Player player) { return getSubscribers().contains(player); } }
注册类优化示例
public class DogRegistry { // 全服可见普通狗存储 private final Set<Dog> globalDogs = ConcurrentHashMap.newKeySet(); // 订阅制狗存储 private final Set<SubscribedDog> subscribedDogs = ConcurrentHashMap.newKeySet(); // 可观测半径平方,避免开根号计算 private static final double SPAWN_RADIUS_SQUARED = X * X; // 注册时自动分类,仅做一次类型判断 public void register(Dog dog) { if (dog instanceof SubscribedDog subscribedDog) { subscribedDogs.add(subscribedDog); } else { globalDogs.add(dog); // 全服狗注册时直接触发全服生成,不需要等定时任务 triggerGlobalSpawn(dog); } } // 每秒定时任务,无任何类型判断开销 public void runScheduledTask() { List<Player> onlinePlayers = getOnlinePlayers(); for (SubscribedDog dog : subscribedDogs) { Location dogLoc = getDogCurrentLocation(dog.getUUID()); // 处理半径范围内未订阅的玩家 for (Player player : onlinePlayers) { if (player.getLocation().distanceSquared(dogLoc) > SPAWN_RADIUS_SQUARED) { continue; } if (!dog.isSubscribed(player)) { dog.subscribe(player); dog.spawn(player); } } // 可选:超出半径的玩家自动取消订阅,减少后续遍历开销 for (Player subscriber : dog.getSubscribers()) { if (subscriber.getLocation().distanceSquared(dogLoc) > SPAWN_RADIUS_SQUARED) { dog.unsubscribe(subscriber); despawnForPlayer(dog.getUUID(), subscriber); } } } } }
额外性能优化建议
- 用
distanceSquared替代distance做半径判断,避免开根号计算,数学上判断结果完全一致,性能提升明显。 - 如果狗的规模后续上涨到数千只,可以增加地图网格分区,每次定时任务仅遍历玩家所在网格及相邻网格内的狗,无需遍历全量订阅狗。
- 定时任务开始前统一拉取一次所有在线玩家的位置缓存,避免遍历过程中重复获取玩家位置产生额外开销。
内容的提问来源于stack exchange,提问作者Profeta
相关产品推荐
相关产品推荐

