发布于 

Arma 3 获取与分析导弹数据

前言

最近在研究Arma 3的SQF脚本,心想要是能收集一些游戏基础数据就好了,于是就有了这个项目。

所需Mod

FileXT

3den Enhanced(打开任务场景时需要)

制作流程

1. 创建任务文件

打开任务编辑器,创建一个空场景。

在场景内放置步枪兵 (未武装) x 1,类名为 B_Soldier_unarmed_F

在场景内放置野营桌若干 (要测试几种导弹就放几个),类名为 Land_CampingTable_F

image-20221125152852400
image-20221125152852400

打开任务所在文件夹,路径为:文档 -> Arma 3 - Other Profiles -> 你的 ID -> missions -> 任务名

创建Description.ext文件,输入:

1
2
3
4
5
6
7
8
9
10
class CfgFunctions
{
class rickg
{
class Utilities
{
class missileTrack {};
};
};
};

创建Functions文件夹,并在其中创建Utilities文件夹

Utilities文件夹中创建fn_missileTrack.sqf文件

2. 编写相关脚本

在任务编辑器中双击野营桌,打开属性编辑页面。

image-20221125153845020
image-20221125153845020

初始化一栏中填入以下脚本:

1
2
3
4
5
6
7
8
9
10
11
private _position = getPos this; // 获取桌子当前位置
_position set [2, (_position select 2) + 1.3]; // 使导弹悬浮在桌子上方
private _missile;
private _missileType = "PylonMissile_1Rnd_Missile_AA_04_F"; // 这里和下面的_missileType为CfgMagazines中的导弹类型,可在配置查看器中查看
_missile = createVehicle [getText (configFile >> "CfgMagazines" >> _missileType >> "ammo"), _position, [], 0, "CAN_COLLIDE"];
[_missile, [225.103, 0, 0]] call BIS_fnc_setObjectRotation; // 这行的225.103按你摆放桌子的方向来
_missile enableSimulation false; // 禁用模拟,使导弹静止
this addAction [format ["测试 %1", getText (configFile >> "CfgMagazines" >> _missileType >> "displayName")], {
private _missileType = "PylonMissile_1Rnd_Missile_AA_04_F";
[_missileType] call rickg_fnc_missileTrack;
}]; // 添加测试导弹的选项,这里使用了读取CfgMagazines动态获取导弹名

打开任务路径下的Functions/Utilities文件夹

编辑fn_missileTrack.sqf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
params ["_type"];
[_type] spawn {
params ["_type"]; // 获取参数
_typePlayer = player;
_positionMissile = [0, 0, 1000]; // 设置导弹,跟踪无人机初始坐标为[0, 0, 1000]
_positionDrone = [0, 0, 1000];
_missile = createVehicle [getText (configFile >> "CfgMagazines" >> _type >> "ammo"), _positionMissile]; // 创建导弹实体
_launchTime = time;
_UAV = createVehicle ["B_UAV_01_F", _positionDrone, [], 0, "CAN_COLLIDE"]; // 创建无人机实体
_UAV attachTo [_missile, [0, 0, 0]]; // 将无人机附属在导弹上
_UAV engineOn false;
_UAV setFuel 0;
_UAV allowDamage false; // 禁用无人机损害
hideObject _UAV; // 隐藏无人机模型
createVehicleCrew _UAV;
gunner _UAV allowDamage false; // 禁用无人机损害
selectPlayer gunner _UAV; // 将玩家视角移动到无人机上
// FileXT 获取文件名
private _fileName_Time = getText (configFile >> "CfgMagazines" >> _type >> "displayName") + "_Time.txt";
[_fileName_Time] call filext_fnc_deleteFile; // 删除旧文件
[_fileName_Time] call filext_fnc_open; // 打开文件
private _fileName_Dist = getText (configFile >> "CfgMagazines" >> _type >> "displayName") + "_Dist.txt";
[_fileName_Dist] call filext_fnc_deleteFile;
[_fileName_Dist] call filext_fnc_open;
while {alive _missile} do
{
hintSilent format ["型号: %1\n速度: %2 km/h\n高度变化: %3 m\n飞行距离: %4 m\n飞行时间: %5 s", getText (configFile >> "CfgMagazines" >> _type >> "displayName"), speed _missile, (getPos _missile select 2) - 1000, _positionMissile distance getPos _missile, time - _launchTime]; // 输出速度、高度变化、飞行距离等相关信息

[_fileName_Time, str(time - _launchTime), str(speed _missile)] call filext_fnc_set; // 添加键值到待写入项
[_fileName_Dist, str(_positionMissile distance getPos _missile), str((getPos _missile select 2) - 1000)] call filext_fnc_set;
};
[_fileName_Time] call filext_fnc_write; // 写入到文件
[_fileName_Dist] call filext_fnc_write;
[_fileName_Time] call filext_fnc_close; // 关闭文件流
[_fileName_Dist] call filext_fnc_close;
selectPlayer _typePlayer; // 切换回玩家
deleteVehicle _UAV;
};

3. 测试

挂载FileXT Mod,运行任务,导弹测试结果应该如下图

image-20221125155345765
image-20221125155345765

测试完成后,打开Arma3安装目录/!Workshop/@FileXT/storage文件夹,可以发现生成了导弹名_Time.txt导弹名_Dist.txt两个文件

image-20221125155841329
image-20221125155841329

4. 整理数据文件

用Visual Studio Code打开数据文件,可以发现有大量特殊符号,这是FileXT模组的存储模式导致的(详见https://github.com/Vindicta-Team/FileXT/wiki

image-20221125160355097
image-20221125160355097

打开侧边栏的搜索一项,点击搜索框中的第三个图标启用正则表达式,而后将f.*\u0001\u0000{3}替换为空白,去掉文件开头的特殊符号

再将(.)\u0000(.)替换为$1,$2,替换掉两个数字之间的NUL

而后将剩下的\u0000替换为空白,这样所有特殊符号就被消除了

image-20221125161100799
image-20221125161100799

在两个文件中搜索"1000",0的无效数据项,将其删除

在两个文件中搜索"a",0的无效数据项,将其删除(a的值与导弹飞行的时间相似)

5. 分析数据文件并导出为图片

新建一个文件夹,在其下创建processData.py,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import pandas as pd
import os
import matplotlib.pyplot as plt
import re


def dirProcess(fileList: list, missileType: str):
fileListTime = []
fileListDist = []
for i in fileList:
if (re.search('.*_Time.txt', i) != None):
fileListTime.append(re.search('.*_Time.txt', i).group())
if (re.search('.*_Dist.txt', i) != None):
fileListDist.append(re.search('.*_Dist.txt', i).group())

for i in fileListTime:
dataFrame = pd.read_table(
'./导弹飞行数据/' + missileType + '/' + i, sep=',', header=None)
dataFrame = dataFrame.sort_values(by=0)
x = []
y = []
for index, row in dataFrame.iterrows():
x.append(row[0])
y.append(row[1])
figTime = plt.figure()
ax = figTime.add_axes([0.12, 0.12, 0.8, 0.8])
ax.grid(True)
ax.plot(x, y)
ax.set_xlabel("飞行时间 (s)")
ax.set_ylabel("速度 (km/h)")
ax.set_title(i.split('_')[0] + "导弹数据")
plt.savefig('./导弹飞行数据/图片/' + missileType + '/' + i.split('_')
[0] + "导弹数据_时间-速度.png", dpi=288)
plt.close()

for i in fileListDist:
dataFrame = pd.read_table(
'./导弹飞行数据/' + missileType + '/' + i, sep=',', header=None)
dataFrame = dataFrame.sort_values(by=0)
x = []
y = []
for index, row in dataFrame.iterrows():
x.append(row[0])
y.append(row[1])
figDist = plt.figure()
ax = figDist.add_axes([0.12, 0.12, 0.8, 0.8])
ax.grid(True)
ax.plot(x, y)
ax.set_xlabel("飞行距离 (m)")
ax.set_ylabel("相对高度 (m)")
ax.set_title(i.split('_')[0] + "导弹数据")
plt.savefig('./导弹飞行数据/图片/' + missileType + '/' + i.split('_')
[0] + "导弹数据_距离-高度.png", dpi=288)
plt.close()


def dirProcessinAll(fileList: list, missileType: str):
fileListTime = []
fileListDist = []
for i in fileList:
if (re.search('.*_Time.txt', i) != None):
fileListTime.append(re.search('.*_Time.txt', i).group())
if (re.search('.*_Dist.txt', i) != None):
fileListDist.append(re.search('.*_Dist.txt', i).group())
figure, axes = plt.subplots(1, 2, figsize=(38.4, 10.8), dpi=200)
labels_time = []
labels_dist = []
axes[0].set_xlabel("飞行时间 (s)")
axes[0].set_ylabel("速度 (km/h)")
axes[0].set_title(missileType + " 时间 - 速度 表")
axes[0].grid(True)
axes[1].set_xlabel("飞行距离 (m)")
axes[1].set_ylabel("相对高度 (m)")
axes[1].set_title(missileType + " 距离 - 高度 表")
axes[1].grid(True)
for i in fileListTime:
dataFrame = pd.read_table(
'./导弹飞行数据/' + missileType + '/' + i, sep=',', header=None)
dataFrame = dataFrame.sort_values(by=0)
x = []
y = []
for index, row in dataFrame.iterrows():
x.append(row[0])
y.append(row[1])
axes[0].plot(x, y)
labels_time.append(i.split('_')[0])
axes[0].legend(tuple(labels_time), loc='best')
for i in fileListDist:
dataFrame = pd.read_table(
'./导弹飞行数据/' + missileType + '/' + i, sep=',', header=None)
dataFrame = dataFrame.sort_values(by=0)
x = []
y = []
for index, row in dataFrame.iterrows():
x.append(row[0])
y.append(row[1])
axes[1].plot(x, y)
labels_dist.append(i.split('_')[0])
axes[1].legend(tuple(labels_dist), loc='best')
# plt.show()
plt.savefig('./导弹飞行数据/图片/' + missileType + '/' + "各导弹数据对比.png")
plt.close()


print("Please enter what the version you want to process\n")
print("1. Single")
print("2. All in One")
cin = input()
if (cin == '1'):
# Process Ground to Ground Missiles
dirProcess(os.listdir('./导弹飞行数据/地地导弹'), '地地导弹')
# Process Air to Ground Missiles
dirProcess(os.listdir('./导弹飞行数据/空地导弹'), '空地导弹')
# Process Surface to Air Missiles
dirProcess(os.listdir('./导弹飞行数据/地空导弹'), '地空导弹')
# Process AA SR Missiles
dirProcess(os.listdir('./导弹飞行数据/近距弹'), '近距弹')
# Process AA MR Missiles
dirProcess(os.listdir('./导弹飞行数据/中距弹'), '中距弹')
elif (cin == '2'):
# Process Ground to Ground Missiles
dirProcessinAll(os.listdir('./导弹飞行数据/地地导弹'), '地地导弹')
# Process Air to Ground Missiles
dirProcessinAll(os.listdir('./导弹飞行数据/空地导弹'), '空地导弹')
# Process Surface to Air Missiles
dirProcessinAll(os.listdir('./导弹飞行数据/地空导弹'), '地空导弹')
# Process AA SR Missiles
dirProcessinAll(os.listdir('./导弹飞行数据/近距弹'), '近距弹')
# Process AA MR Missiles
dirProcessinAll(os.listdir('./导弹飞行数据/中距弹'), '中距弹')

在其下创建目录,目录树如下:

1
2
3
4
5
6
7
8
9
10
11
12
└─导弹飞行数据
├─中距弹
├─图片
│ ├─中距弹
│ ├─地地导弹
│ ├─地空导弹
│ ├─空地导弹
│ └─近距弹
├─地地导弹
├─地空导弹
├─空地导弹
└─近距弹

将你分析得到的导弹数据按分类放入导弹飞行数据文件夹下的分类即可

运行Python文件,在图片目录下即可查看输出的数据

6. 关于测试场景

测试场景与Python代码我会一同打包放到网盘

下载地址:https://wwb.lanzoue.com/iTmkn0gvll7c