交通灯控制行人和车辆的大致原理
对于行人是通过给行人的lane添加FZoneGraphTag(CloseLaneTag、WaitingLaneTag)来告知AI哪些道路当前禁止行走。车辆则是通过修改车道数据的bIsOpen来表示是否开放,进而影响车对道路的选择(详细代码在UMassTrafficChooseNextLaneProcessor::Execute)。
UMassTrafficIntersectionSpawnDataGenerator主要负责交叉路口信息搜集、红绿灯周期的创建(讲道理应该离线生成,目前是运行时执行)。目前一共有四种周期模式:两岔路口、标准十字路口、其他有红绿灯路口、stop-sign路口(无红绿灯)。
UMassTrafficUpdateIntersectionsProcessor负责红绿灯周期的切换逻辑。
前置知识
Mass基础可以参看:UE5 Mass初体验。
MassSpawner
Entity生成器,主要职责包括:生成的总数量、出生点位的生成(FMassSpawnDataGenerator负责)、Entity配置(有哪些Fragment,通过UMassEntityConfigAsset进行配置)、各类Entity数量占比。
场景中需要放置一个MassSpawner用来生成所有的交通路口Entity,并且需要在MassSpawner的SpawnDataGenerators中需要配置UMassTrafficIntersectionSpawnDataGenerator。
ZoneGraph
路网,包括人行道、机动车道。一个AZoneShape对应一个Zone。FZoneGraphBuilder负责将场景中的AZoneShape转换成数据存储到FZoneGraphStorage。具体构建代码详见:void FZoneGraphBuilder::Build(AZoneGraphData& ZoneGraphData)。
ZoneGraph相关文档:ZoneGraph Quick Start Guide
USTRUCT()
struct ZONEGRAPH_API FZoneGraphStorage // ZoneGraph的数据存储
{
GENERATED_BODY()
void Reset();
const FZoneData& GetZoneDataFromLaneIndex(int32 LaneIndex) const { return Zones[Lanes[LaneIndex].ZoneIndex]; }
// 所有的zone
UPROPERTY()
TArray<FZoneData> Zones;
// 所有的Lane数组,Zone会有保存自身所属Lane的起始和结束索引
UPROPERTY()
TArray<FZoneLaneData> Lanes;
// 所有Zone的边界点的数组
UPROPERTY()
TArray<FVector> BoundaryPoints;
// 所有Lane的路点。
UPROPERTY()
TArray<FVector> LanePoints;
// All the lane up vectors, referred by lanes.
UPROPERTY()
TArray<FVector> LaneUpVectors;
// 所有Lane的切线
UPROPERTY()
TArray<FVector> LaneTangentVectors;
// 所有lane的距离进度
UPROPERTY()
TArray<float> LanePointProgressions;
// 所有Lane之间的连接信息
UPROPERTY()
TArray<FZoneLaneLinkData> LaneLinks;
// 所有Zone的Bounding box.
UPROPERTY()
FBox Bounds = FBox(ForceInit);
// BV-Tree of Zones
UPROPERTY()
FZoneGraphBVTree ZoneBVTree;
// The handle that this storage represents, updated when data is registered to ZoneGraphSubsystem, used for query results.
FZoneGraphDataHandle DataHandle;
};
USTRUCT()
struct ZONEGRAPH_API FZoneData // 对应一个AZoneShape,代表一段路,里面包含多条Lane。
{
GENERATED_BODY()
int32 GetLaneCount() const { return LanesEnd - LanesBegin; }
// Zone边界线的第一个点在FZoneGraphStorage::BoundaryPoints中的下标。
UPROPERTY()
int32 BoundaryPointsBegin = 0;
// Zone边界线最后一个点的下标.
UPROPERTY()
int32 BoundaryPointsEnd = 0;
// Zone第一条Lane在FZoneGraphStorage::Lanes中的下标
UPROPERTY()
int32 LanesBegin = 0;
// Zone最后一条Lane的下标
UPROPERTY()
int32 LanesEnd = 0;
// Zone的Bounding box
UPROPERTY()
FBox Bounds = FBox(ForceInit);
// Zone tags
UPROPERTY()
FZoneGraphTagMask Tags = FZoneGraphTagMask(1); // Default Tag
};
Lane
路网上的一条路线,由路点组成,有方向,有宽度。Lane之间互相连接。
USTRUCT()
struct ZONEGRAPH_API FZoneLaneData
{
GENERATED_BODY()
int32 GetLinkCount() const { return LinksEnd - LinksBegin; }
int32 GetNumPoints() const { return PointsEnd - PointsBegin; }
int32 GetLastPoint() const { return PointsEnd - 1; }
// lane的宽度
UPROPERTY()
float Width = 0.0f;
// Lane上的标签
UPROPERTY()
FZoneGraphTagMask Tags = FZoneGraphTagMask(1); // Default Tag
// Lane的第一个点在FZoneGraphStorage::LanePoints中的下标
UPROPERTY()
int32 PointsBegin = 0;
// Lane最后一个点的下标
UPROPERTY()
int32 PointsEnd = 0;
// 当前Lane的第一条连接的索引
UPROPERTY()
int32 LinksBegin = 0;
// 当前Lane的最后一条连接的索引
UPROPERTY()
int32 LinksEnd = 0;
// Lane所属的Zone
UPROPERTY()
int32 ZoneIndex = 0;
// Source data entry ID, this generally corresponds to input data point index.
UPROPERTY()
uint16 StartEntryId = 0;
// Source data entry ID.
UPROPERTY()
uint16 EndEntryId = 0;
};
// 描述两条Lane之间的空间关系
UENUM()
enum class EZoneLaneLinkType : uint8
{
None = 0,
All = MAX_uint8,
Outgoing = 1 << 0, // 离开当前Lane,通往另一条Lane
Incoming = 1 << 1, // 从另一条Lane进入当前Lane
Adjacent = 1 << 2, // 两条Lane归属于同一个Zone,紧邻当前Lane, 两条Lane的方向可能相同也可能不同。
};
ENUM_CLASS_FLAGS(EZoneLaneLinkType)
// 描述紧邻的两条Lane之间更多空间细节(只用在紧邻的两条Lane之间)
UENUM()
enum class EZoneLaneLinkFlags : uint8
{
None = 0,
All = MAX_uint8,
Left = 1 << 0, // 在当前Lane的左侧
Right = 1 << 1, // 在Lane的右侧
Splitting = 1 << 2, // 分叉,两条Lane的起点相同
Merging = 1 << 3, // 两条Lane归并到一条Lane,两条Lane的终点相同
OppositeDirection = 1 << 4, // 两条Lane方向相反
};
ENUM_CLASS_FLAGS(EZoneLaneLinkFlags)
USTRUCT()
struct ZONEGRAPH_API FZoneLaneLinkData // 描述当前Lane跟另一条Lane的连接信息
{
GENERATED_BODY()
FZoneLaneLinkData() = default;
FZoneLaneLinkData(const int32 InDestLaneIndex, const EZoneLaneLinkType InType, const EZoneLaneLinkFlags InFlags) : DestLaneIndex(InDestLaneIndex), Type(InType), Flags((uint8)InFlags) {}
bool HasFlags(const EZoneLaneLinkFlags InFlags) const { return (Flags & (uint8)InFlags) != 0; }
EZoneLaneLinkFlags GetFlags() const { return (EZoneLaneLinkFlags)Flags; }
void SetFlags(const EZoneLaneLinkFlags InFlags) { Flags = (uint8)InFlags; }
/** 另一条Lane的索引*/
UPROPERTY()
int32 DestLaneIndex = 0;
/** 连接的类型 */
UPROPERTY()
EZoneLaneLinkType Type = EZoneLaneLinkType::None;
/** 当两条Lane毗邻时,更多信息 */
UPROPERTY()
uint8 Flags = 0;
};
交叉路口参数含义
项目配置中交叉路口各参数含义
MassTrafficSettings.h
/**
* Settings for the MassTraffic plugin.
*/
UCLASS(config = Plugins, defaultconfig, DisplayName = "Mass Traffic",
dontCollapseCategories)
class MASSTRAFFIC_API UMassTrafficSettings : public UMassModuleSettings
{
/** ...........省略红绿灯无关配置 */
/** 黄灯持续的时间 */
UPROPERTY(EditAnywhere, Config, Category="Intersections|Durations|Standard")
float StandardTrafficPrepareToStopSeconds = 2.0f;
/** 人行通道开放需要的最少等待人数,个人建议配置成1 */
UPROPERTY(EditAnywhere, Config, Category="Intersections|Pedestrians")
int32 MinPedestriansForCrossingAtTrafficLights = 3;
/** stop-sign intersections特殊人行通道开放需要的最少等待人数 */
UPROPERTY(EditAnywhere, Config, Category="Intersections|Pedestrians")
int32 MinPedestriansForCrossingAtStopSigns = 3;
/** 普通交叉路口人行道开放的概率 */
UPROPERTY(EditAnywhere, Config, Category="Intersections|Pedestrians")
float TrafficLightPedestrianLaneOpenProbability = 1.0f;
/** stop-sign intersections特殊人行通道开放概率 */
UPROPERTY(EditAnywhere, Config, Category="Intersections|Pedestrians")
float StopSignPedestrianLaneOpenProbability = 0.2f;
}
交叉路口Generator各参数含义
MassTrafficIntersectionSpawnDataGenerator.h
UCLASS()
class MASSTRAFFIC_API UMassTrafficIntersectionSpawnDataGenerator :
public UMassEntitySpawnDataGeneratorBase
{
public:
/** 每一条分叉路搜寻自己所对应的红绿灯的搜寻距离 */
UPROPERTY(EditAnywhere, Category="Traffic Lights")
float TrafficLightSearchDistance = 400.0f;
/** 每一条分叉路搜寻自己所控制的人行道的最大搜寻距离 */
UPROPERTY(EditAnywhere, Category="Pedestrians")
float IntersectionSideToCrosswalkSearchDistance = 500.0f;
/** 标准情况下机动车道绿灯持续的时间 */
UPROPERTY(EditAnywhere, Category="Durations|Standard")
float StandardTrafficGoSeconds = 20.0f;
/** 标准情况下机动车道绿灯的最小持续时间,确保车子可以开进十字路口*/
UPROPERTY(EditAnywhere, Category="Durations|Standard")
float StandardMinimumTrafficGoSeconds = 5.0f;
/** 标准情况下人行道绿灯持续时间 */
UPROPERTY(EditAnywhere, Category="Durations|Standard")
float StandardCrosswalkGoSeconds = 10.0f;
/** 车道可以同时向前、向左、向右通行时绿灯持续时间 */
UPROPERTY(EditAnywhere, Category="Durations|FourWay")
float UnidirectionalTrafficStraightRightLeftGoSeconds = StandardTrafficGoSeconds / 2.0f;
/** 车道可以向前以及向右通行时绿灯持续时间 */
UPROPERTY(EditAnywhere, Category="Durations|FourWay")
float UnidirectionalTrafficStraightRightGoSeconds = StandardTrafficGoSeconds / 2.0f;
/** 当前车道以及对向车道同时允许直行以及右转的绿灯时间 */
UPROPERTY(EditAnywhere, Category=