Java 中的更新方法模式:通过系统更新提高游戏循环效率
也称为
- 更新机制
更新方法设计模式的意图
Java 中的更新方法模式通过指示每个对象处理一帧的行为来模拟独立对象的集合。
更新方法模式的详细说明,并提供现实世界的例子
现实世界中的例子
更新方法设计模式的现实世界例子是气象监测系统。在这个系统中,多个显示设备(例如移动应用程序、网站小部件和壁挂式数字显示器)需要显示当前的天气状况。这些显示器订阅来自中央气象站的更新,该气象站从各种传感器(温度、湿度、风速等)收集数据。当气象站检测到新数据时,它会触发一个更新方法,将新信息推送到所有已订阅的显示设备,确保它们都同时显示最新的天气状况。这确保了所有显示器同步并更新,而无需每个设备独立地检查更新。
简单来说
更新方法设计模式一次处理一帧系统对象的行為
游戏世界维护着一组对象。每个对象实现一个 update 方法,用于模拟该对象一帧的行为。每一帧,游戏都会更新集合中的每个对象。
Java 中更新方法模式的编程示例
更新方法设计模式是一种行为模式,它通过指示每个对象处理一帧的行为来模拟独立的游戏或应用程序对象的集合。这种模式通常用于游戏开发,其中游戏世界中的每个对象都需要每帧更新一次。
World
类代表游戏世界。它维护着一个实体列表 (List<Entity> entities
) 和一个布尔标志 (isRunning
) 来指示游戏是否正在运行。
public class World {
protected List<Entity> entities;
protected volatile boolean isRunning;
public World() {
entities = new ArrayList<>();
isRunning = false;
}
// Other properties and methods...
}
gameLoop
方法是主游戏循环。只要游戏正在运行,它就会持续处理用户输入、更新游戏状态并渲染下一帧。
private void gameLoop() {
while (isRunning) {
processInput();
update();
render();
}
}
processInput
方法模拟处理用户输入。在本例中,它只是引入一个随机的时间延迟来模拟现实生活中游戏的情况。
private void processInput() {
try {
int lag = new SecureRandom().nextInt(200) + 50;
Thread.sleep(lag);
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
Thread.currentThread().interrupt();
}
}
update
方法是实现更新方法模式的地方。它遍历游戏世界中的所有实体,并调用它们的 update
方法,允许每个实体处理一帧的行为。
private void update() {
for (var entity : entities) {
entity.update();
}
}
render
方法负责渲染下一帧。在本例中,它什么也不做,因为它与模式无关。
private void render() {
// Does Nothing
}
run
和 stop
方法用于启动和停止游戏循环。
public void run() {
LOGGER.info("Start game.");
isRunning = true;
var thread = new Thread(this::gameLoop);
thread.start();
}
public void stop() {
LOGGER.info("Stop game.");
isRunning = false;
}
addEntity
方法用于将新实体添加到游戏世界中。
public void addEntity(Entity entity) {
entities.add(entity);
}
在 App
类中,我们可以看到 World
类及其方法是如何用来创建游戏世界、向其中添加实体以及启动游戏循环的。
@Slf4j
public class App {
private static final int GAME_RUNNING_TIME = 2000;
public static void main(String[] args) {
try {
var world = new World();
var skeleton1 = new Skeleton(1, 10);
var skeleton2 = new Skeleton(2, 70);
var statue = new Statue(3, 20);
world.addEntity(skeleton1);
world.addEntity(skeleton2);
world.addEntity(statue);
world.run();
Thread.sleep(GAME_RUNNING_TIME);
world.stop();
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
}
}
控制台输出
14:46:33.181 [main] INFO com.iluwatar.updatemethod.World -- Start game.
14:46:33.280 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 11.
14:46:33.281 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 71.
14:46:33.452 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 12.
14:46:33.452 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 72.
14:46:33.621 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 13.
14:46:33.621 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 73.
14:46:33.793 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 14.
14:46:33.793 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 74.
14:46:33.885 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 15.
14:46:33.885 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 75.
14:46:34.113 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 16.
14:46:34.113 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 76.
14:46:34.324 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 17.
14:46:34.324 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 77.
14:46:34.574 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 18.
14:46:34.575 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 78.
14:46:34.730 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 19.
14:46:34.731 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 79.
14:46:34.803 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 20.
14:46:34.803 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 80.
14:46:34.979 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 21.
14:46:34.979 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 81.
14:46:35.045 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 22.
14:46:35.046 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 82.
14:46:35.187 [main] INFO com.iluwatar.updatemethod.World -- Stop game.
14:46:35.288 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 1 is on position 23.
14:46:35.289 [Thread-0] INFO com.iluwatar.updatemethod.Skeleton -- Skeleton 2 is on position 83.
这是更新方法模式的基本实现。在实际应用中,Entity
类可能会有额外的 methods 和 properties,update
method 将包含更复杂的逻辑来模拟实体的行为。
何时在 Java 中使用更新方法模式
更新方法在以下情况下效果良好
- 通常应用于多个对象需要同步更新而无需手动同步开销的场景中,使其成为高级 Java 开发人员的首选。
- 应用程序有许多需要同时运行的对象或系统。
- 每个对象的行為大多独立于其他对象。
- 这些对象需要随着时间的推移进行模拟。
Java 中更新方法模式的实际应用
- 实时游戏和数据处理应用程序,其中世界对象需要每帧更新一次。
更新方法模式的优点和权衡
优点
- 每个实体封装了自己的行为
- 使添加和删除实体变得容易
- 保持主循环整洁
权衡
- 由于每帧都会放弃控制,因此会增加复杂性
- 需要存储状态以在每帧后恢复更新
- 实体每帧都会被模拟,但它们并非真正并发