1. Repast Simphony入门指南
第一次接触Repast Simphony时,我也被这个强大的多Agent仿真平台震撼到了。它就像是一个虚拟世界的实验室,让我们能够用代码构建各种复杂系统的动态模型。今天要分享的僵尸感染传播模型,就是我在学习Repast时完成的第一个完整项目。
Repast Simphony最吸引我的地方在于它提供了完整的仿真开发生命周期支持。从模型设计、代码实现到可视化展示和数据分析,全部都能在一个集成环境中完成。对于初学者来说,最需要适应的就是它的项目结构。在Eclipse中切换到Repast Simphony视图后,你会发现标准Java项目被扩展成了包含context.xml、scenario.rs等配置文件的特殊结构。
这里有个小技巧:创建新项目时,建议直接使用Repast提供的模板。我第一次手动配置项目花了整整半天时间排查各种路径问题,而用模板只需要几分钟就能搭建好基础环境。项目中的关键目录包括:
- src:存放所有Java源代码
- config:包含模型运行的各类配置文件
- test:单元测试代码
- repast.settings:IDE相关配置
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模型设计与空间配置
2.1 空间投影类型选择
在僵尸模型中,我们主要用到三种空间投影:
- ContinuousSpace:模拟连续空间移动
- Grid:处理离散网格位置
- Network:建立感染关系网络
实际开发中发现,ContinuousSpace和Grid的配合使用特别重要。僵尸和人类的移动在视觉上需要连续平滑,但感染判断又需要精确到具体网格位置。这就涉及到坐标转换的问题:
java复制// 连续坐标转网格坐标
NdPoint continuousPt = space.getLocation(obj);
GridPoint gridPt = new GridPoint((int)continuousPt.getX(), (int)continuousPt.getY());
// 网格坐标转连续坐标
NdPoint newContinuousPt = new NdPoint(gridPt.getX(), gridPt.getY());
2.2 边界处理策略
WrapAroundBorders是我最推荐的边界处理方式,它让空间变成环形拓扑结构。这意味着当Agent移动到边界时会从另一侧重新出现,这种设计既符合游戏场景需求,又能避免复杂的边界条件判断。在初始化空间时这样配置:
java复制ContinuousSpace<Object> space = spaceFactory.createContinuousSpace(
"space", context,
new RandomCartesianAdder<Object>(),
new WrapAroundBorders(), 50, 50);
3. Agent行为逻辑实现
3.1 僵尸Agent设计
僵尸的核心行为包括追踪人类和感染传播。在step()方法中,我实现了基于网格邻域查询的追踪逻辑:
java复制@ScheduledMethod(start = 1, interval = 1)
public void step() {
GridPoint currentPos = grid.getLocation(this);
GridCellNgh<Human> ngh = new GridCellNgh<>(grid, currentPos, Human.class, 1, 1);
List<GridCell<Human>> neighbors = ngh.getNeighborhood(true);
// 找出人类最多的网格点
GridPoint target = findMostHumans(neighbors);
moveTowards(target);
infect();
}
这里有个性能优化点:SimUtilities.shuffle()可以随机打乱邻居列表顺序,避免Agent总是朝固定方向移动产生规律性模式。
3.2 人类Agent设计
人类的逃跑行为通过@Watch注解实现事件驱动,这是Repast中非常实用的特性:
java复制@Watch(watcheeClassName = "jzombies.Zombie",
watcheeFieldNames = "moved",
query = "within_moore 1",
whenToTrigger = WatcherTriggerSchedule.IMMEDIATE)
public void run() {
// 寻找僵尸最少的移动方向
GridPoint safest = findSafestDirection();
if(energy > 0) {
moveTowards(safest);
energy--;
} else {
rest(); // 能量恢复
}
}
能量系统的设计让模型更有趣 - 人类不能无限逃跑,必须合理安排移动和休息。这个机制后来被我扩展成了资源收集系统。
4. 模型环境构建
4.1 ContextBuilder实现
JZombiesBuilder类是整个模型的组装车间。我建议按这个顺序构建环境:
- 创建空间投影(ContinuousSpace和Grid)
- 构建关系网络(Network)
- 初始化Agent
- 设置初始位置
java复制@Override
public Context build(Context<Object> context) {
context.setId("jzombies");
// 1. 创建空间
createSpaces(context);
// 2. 构建网络
buildNetwork(context);
// 3. 添加Agent
populateAgents(context);
return context;
}
4.2 参数化设计
通过Parameters实现运行时参数配置是个好习惯:
xml复制<!-- 在scenario.rs文件中定义 -->
<parameter name="zombie_count" type="int" value="5" />
<parameter name="human_count" type="int" value="20" />
代码中这样读取参数:
java复制Parameters params = RunEnvironment.getInstance().getParameters();
int zombies = params.getInteger("zombie_count");
int humans = params.getInteger("human_count");
5. 模型运行与可视化
5.1 显示配置技巧
在Displays配置时,我总结了几点经验:
- 2D显示性能最好,适合大规模仿真
- 给不同Agent设置鲜明颜色(僵尸红色,人类绿色)
- 网络投影建议单独显示,避免视觉混乱
- 调整Agent显示大小,确保在网格中清晰可见
5.2 数据收集与分析
Repast的数据收集系统非常强大。我通常会设置这些数据集:
- 僵尸/人类数量变化(Time Series Chart)
- 感染网络节点度数分布(Histogram)
- 人类平均生存时间(Console Sink)
java复制// 示例:设置时间序列图表
TimeSeriesChart chart = new TimeSeriesChart("Population Dynamics");
chart.addSeries("Zombies", zombieCountDataSource);
chart.addSeries("Humans", humanCountDataSource);
6. 模型扩展与优化
完成基础版本后,我尝试了几种有趣的扩展:
- 地形系统:在网格中设置障碍物
- 超级僵尸:有一定概率生成移动更快的变种
- 免疫人类:部分人类有概率抵抗感染
- 昼夜循环:白天僵尸移动变慢
性能优化方面,这些措施很有效:
- 减少不必要的空间查询
- 优化@Watch的查询范围
- 使用Batch参数批量更新
- 关闭调试期的详细日志
记得第一次运行时,我的模型在100个Agent时就卡顿了。通过优化邻域查询和减少实时渲染元素,最终版本可以流畅运行1000+Agent的仿真。
