这个程序是我上研二上学期时下一届师弟师妹们的面向对象课程大作业,当时我正好看过两三本 C++ 书籍,虽然忙着项目,但还是忙里偷闲检验了下自己。从设计到实现,耗时一周左右,完成于 2013 年年底。
虽是面向对象课程作业,但是只用到了“封装”,没有触及“继承”和“多态”,只是基于对象而已。
以前的做一个东西都会查阅不少资料,这次纯粹是靠想象了。
源码托管在 Github 上:点击进入链接,命名遵循“匈牙利命名法”尽量做到“self-documenting”,当然也有必要的注释。
可执行程序:点击进入链接
题目(完整pdf):
设计说明:
红绿灯类,保存灯的颜色、总持续时间、当前倒计时等,操作倒计时和颜色转换;
用一个枚举类型保存道路端点;
路径类,用一个 map 保存所有路径端点及其对应的折点,可通过路线端点获取其对应的路线折点;
车辆类,随机产生车辆时,就随机设置其颜色和行驶路线,类中保存车辆行驶路线端点、当前行驶区间、当前位置,可获取车辆状态或更新位置。
道路类,这是一个二维矩阵,安插了多个红绿灯,每一点都有一个标记,如果有车辆在此位置则激活。
仿真图示:定时扫描道路类中的所有点,获取对应的交通灯或车辆,然后绘制到界面上。
坐标假设:原点在左上角,向右为 x 轴正方向,向下为 y 轴正方向。
重难点:
1、红绿灯更替规则,封装到红绿灯类内从而得到简化;
2、复杂的转弯、让道规则,通过保存路线解决;
3、更新 1 秒:
1 // 车辆行进 1 秒 2 void CTrafficSimulationDlg::MoveOneSec(CMyPoint& ptCarPos) 3 { 4 for (auto it = m_vecCars.begin(); it != m_vecCars.end(); ++it) { 5 if (it->GetPos() == ptCarPos) { // 在车辆集合中找到该车辆 6 pair<CMyPoint, CMyPoint> pairCurInterval = it->GetCurInterval(); 7 CMyPoint ptFrom = pairCurInterval.first; 8 CMyPoint ptTo = pairCurInterval.second; 9 10 if (ptTo.GetX() == ptCarPos.GetX() 11 && ptTo.GetY() == ptCarPos.GetY()) { // 行驶到一个区间的终点 12 13 vector<CMyPoint>& vecTurningPoints 14 = m_paths.GetTurningPoints(it->GetPath()); 15 16 if (it->m_nPtIdx < vecTurningPoints.size()) { // 更新区间 17 ptFrom = ptTo; 18 ptTo = vecTurningPoints.at(it->m_nPtIdx++); 19 it->SetCurInterval(ptFrom, ptTo); 20 } 21 else { // 行驶到路线的终点,在车辆集合中删除该车 22 m_vecCars.erase(it); 23 m_roadMain.DeactivatePoint(ptCarPos); 24 return ; 25 } 26 } 27 28 CMyPoint ptNewCarPos; 29 if (ptFrom.GetX() != ptTo.GetX()) { // 沿着 x 轴行驶 30 int n = ptTo.GetX() - ptFrom.GetX() > 0 ? +1: -1; 31 ptNewCarPos = CMyPoint(ptCarPos.GetX()/gc_nScalar+n, ptCarPos.GetY()/gc_nScalar); 32 } 33 else { // 沿着 y 轴行驶 34 int n = ptTo.GetY() - ptFrom.GetY() > 0 ? +1: -1; 35 ptNewCarPos = CMyPoint(ptCarPos.GetX()/gc_nScalar, ptCarPos.GetY()/gc_nScalar+n); 36 } 37 38 bool bGreen = true; 39 if ( ptNewCarPos != ptTo // 如果不转弯,则查看红绿灯 40 && m_setLightPos.find(ptCarPos) != m_setLightPos.end() ) // 遇到红绿灯 41 bGreen = m_roadMain.IsGreen(ptCarPos); 42 43 // 车辆行进 1 秒 44 if ( bGreen && !m_roadMain.IsPointActivated(ptNewCarPos) ) { 45 m_roadMain.DeactivatePoint(ptCarPos); 46 m_roadMain.ActivatePoint(ptNewCarPos); 47 it->SetPos(ptNewCarPos); 48 } 49 50 return ; // 更新完毕 51 } 52 } 53 }
是否要更换行驶区间(当前车辆位置是否与区间终点重合)、是否行驶到尽头(删除车辆);
判断当前区间是水平的(区间 x 轴坐标不相等)还是竖直的(区间 y 轴坐标不相等),然后决定沿着 x 或 y 的正方向还是反方向;
通过判断是否在路口转弯(新车辆位置与区间终点重合),从而决定是否忽略红绿灯。
运行截图:
双十字路口交通仿真程序(VS2010+MFC)