0119
好的,明白了!根据你的确认,我来提供完整的实现代码。
完整修改方案
一、UdpSocketServer.h 修改
在文件中添加以下内容:
1. 添加委托声明(在现有委托声明区域,建议放在 FOnMissileGroupDataReceived 之后)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(
FOnLoiteringMunitionGroupDataReceived,
const TArray<uint8>&, MunitionIds,
const TArray<uint8>&, MunitionTypes,
const TArray<uint8>&, MunitionStates,
const TArray<FVector>&, MunitionPositions,
const TArray<FRotator>&, MunitionRotations
);
2. 添加委托实例(在public区域,建议放在 OnMissileGroupDataReceived 之后)
UPROPERTY(BlueprintAssignable, Category = "UDP Loitering Munition Delegate")
FOnLoiteringMunitionGroupDataReceived OnLoiteringMunitionGroupDataReceived;
3. 添加解析函数声明(在 ParseMissileGroupData 之后)
void ParseLoiteringMunitionGroupData(const TArray<uint8>& Data, FString& OutStr);
二、UdpSocketServer.cpp 修改
1. 在 DataRecv 函数的 switch 语句中添加 case 5
找到 DataRecv 函数中的 switch 语句,在 case 4 之后添加:
case 5:
{
FString DebugStr;
ParseLoiteringMunitionGroupData(ReceivedData, DebugStr);
str = DebugStr;
success = true;
break;
}
2. 实现 ParseLoiteringMunitionGroupData 函数
在文件末尾(建议放在 ParseMissileGroupData 函数之后)添加:
void UUdpSocketServer::ParseLoiteringMunitionGroupData(const TArray<uint8>& Data, FString& OutStr)
{
int32 Offset = 1;
uint8 MunitionCount = Data[Offset];
Offset += 1;
const int32 SingleMunitionDataSize = 51; // 1(ID) + 1(Type) + 1(State) + 24(Position) + 24(Rotation)
if ((Data.Num() - Offset) < (MunitionCount * SingleMunitionDataSize))
{
UE_LOG(LogTemp, Warning, TEXT("Insufficient data for loitering munition group. munition count: %d, hope length: %d, fact length: %d"),
MunitionCount, Offset + MunitionCount * SingleMunitionDataSize, Data.Num());
OutStr = TEXT("Error: Insufficient data for loitering munition group");
return;
}
TArray<uint8> MunitionIds;
TArray<uint8> MunitionTypes;
TArray<uint8> MunitionStates;
TArray<FVector> MunitionPositions;
TArray<FRotator> MunitionRotations;
FString GroupStr = TEXT("Loitering Munition Group: ");
for (int32 i = 0; i < MunitionCount; ++i)
{
uint8 MunitionId = Data[Offset];
uint8 MunitionType = Data[Offset + 1];
uint8 MunitionState = Data[Offset + 2];
// 解析位置
double X = 0, Y = 0, Z = 0;
FMemory::Memcpy(&X, &Data[Offset + 3], sizeof(double));
FMemory::Memcpy(&Y, &Data[Offset + 11], sizeof(double));
FMemory::Memcpy(&Z, &Data[Offset + 19], sizeof(double));
// 解析姿态
double Yaw = 0, Pitch = 0, Roll = 0;
FMemory::Memcpy(&Yaw, &Data[Offset + 27], sizeof(double));
FMemory::Memcpy(&Pitch, &Data[Offset + 35], sizeof(double));
FMemory::Memcpy(&Roll, &Data[Offset + 43], sizeof(double));
FVector Position(static_cast<float>(X), static_cast<float>(Y), static_cast<float>(Z));
FRotator Rotation(static_cast<float>(Pitch), static_cast<float>(Yaw), static_cast<float>(Roll));
GroupStr += FString::Printf(TEXT("[ID: %d, Type: %d, State: %d, Pos: %s, Rot: %s] "),
MunitionId, MunitionType, MunitionState, *Position.ToString(), *Rotation.ToString());
Position = ConvertUEOffset(Position);
if (MunitionId > 0) // 0 为无效巡飞弹
{
MunitionIds.Add(MunitionId);
MunitionTypes.Add(MunitionType);
MunitionStates.Add(MunitionState);
MunitionPositions.Add(Position);
MunitionRotations.Add(Rotation);
}
Offset += SingleMunitionDataSize;
}
OutStr = GroupStr;
if (bIsLogReceivedPackage)
UE_LOG(LogTemp, Warning, TEXT("the data parse loitering munition group is: %s"), *OutStr);
// 广播委托
OnLoiteringMunitionGroupDataReceived.Broadcast(
MunitionIds,
MunitionTypes,
MunitionStates,
MunitionPositions,
MunitionRotations
);
}
三、Python 测试脚本
创建一个测试脚本来验证功能:
import socket
import struct
import time
def send_loitering_munition_group(sock, target_ip, target_port, munitions_data):
"""
发送巡飞弹组数据
munitions_data: list of dict, 每个dict包含 id, type, state, position(x,y,z), rotation(yaw,pitch,roll)
"""
message_type = 5
munition_count = len(munitions_data)
# 构建数据包
packet = struct.pack('BB', message_type, munition_count)
for munition in munitions_data:
# 基本信息: ID, Type, State
packet += struct.pack('BBB',
munition['id'],
munition['type'],
munition['state'])
# 位置: X, Y, Z (double)
packet += struct.pack('ddd',
munition['position'][0],
munition['position'][1],
munition['position'][2])
# 姿态: Yaw, Pitch, Roll (double)
packet += struct.pack('ddd',
munition['rotation'][0], # Yaw
munition['rotation'][1], # Pitch
munition['rotation'][2]) # Roll
sock.sendto(packet, (target_ip, target_port))
print(f"发送巡飞弹组数据: {munition_count} 个巡飞弹")
print(f"数据包大小: {len(packet)} 字节")
# 测试代码
if __name__ == "__main__":
# 配置
TARGET_IP = "127.0.0.1" # 修改为你的目标IP
TARGET_PORT = 8888 # 修改为你的目标端口
# 创建UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 测试数据:3个巡飞弹
test_munitions = [
{
'id': 1,
'type': 1,
'state': 0,
'position': [100.0, 200.0, 50.0],
'rotation': [45.0, 10.0, 5.0] # Yaw, Pitch, Roll
},
{
'id': 2,
'type': 2,
'state': 1,
'position': [150.0, 250.0, 60.0],
'rotation': [90.0, 15.0, -5.0]
},
{
'id': 3,
'type': 1,
'state': 0,
'position': [200.0, 300.0, 70.0],
'rotation': [135.0, 20.0, 0.0]
}
]
try:
while True:
send_loitering_munition_group(sock, TARGET_IP, TARGET_PORT, test_munitions)
# 更新位置和姿态(模拟移动)
for munition in test_munitions:
munition['position'][0] += 1.0
munition['position'][1] += 0.5
munition['rotation'][0] = (munition['rotation'][0] + 1) % 360
time.sleep(0.1) # 100ms发送一次
except KeyboardInterrupt:
print("\n停止发送")
finally:
sock.close()
四、修改总结
修改的文件:
UdpSocketServer.h- 添加委托和函数声明UdpSocketServer.cpp- 添加解析逻辑
关键点:
- ✅ 复用
NumMissilesPerPacket配置 - ✅ 使用消息类型 5
- ✅ 数据包格式:1(消息类型) + 1(数量) + N×51(每个巡飞弹数据)
- ✅ 仅实现 Group 版本
- ✅ 包含位置和姿态数据
- ✅ 支持坐标转换
ConvertUEOffset
数据包结构:
[消息类型:1字节] [数量:1字节] [巡飞弹1数据:51字节] [巡飞弹2数据:51字节] ...
每个巡飞弹数据(51字节):
- ID: 1字节
- Type: 1字节
- State: 1字节
- Position X: 8字节(double)
- Position Y: 8字节(double)
- Position Z: 8字节(double)
- Yaw: 8字节(double)
- Pitch: 8字节(double)
- Roll: 8字节(double)
这样修改后,你就可以在蓝图中订阅 OnLoiteringMunitionGroupDataReceived 委托来接收和处理巡飞弹数据了!

浙公网安备 33010602011771号