前言
最近在研究Arma 3的SQF脚本,心想要是能收集一些游戏基础数据就好了,于是就有了这个项目。
所需Mod
FileXT
3den Enhanced(打开任务场景时需要)
制作流程
1. 创建任务文件
打开任务编辑器,创建一个空场景。
在场景内放置步枪兵 (未武装)
x 1,类名为 B_Soldier_unarmed_F
在场景内放置野营桌
若干 (要测试几种导弹就放几个),类名为 Land_CampingTable_F
打开任务所在文件夹,路径为:文档 -> Arma 3 - Other Profiles -> 你的 ID -> missions -> 任务名
创建Description.ext
文件,输入:
class CfgFunctions
{
class rickg
{
class Utilities
{
class missileTrack {};
};
};
};
创建Functions
文件夹,并在其中创建Utilities
文件夹
在Utilities
文件夹中创建fn_missileTrack.sqf
文件
2. 编写相关脚本
在任务编辑器中双击野营桌
,打开属性编辑页面。
在初始化
一栏中填入以下脚本:
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
:
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,运行任务,导弹测试结果应该如下图
测试完成后,打开Arma3安装目录/!Workshop/@FileXT/storage
文件夹,可以发现生成了导弹名_Time.txt
和导弹名_Dist.txt
两个文件
4. 整理数据文件
用Visual Studio Code打开数据文件,可以发现有大量特殊符号,这是FileXT模组的存储模式导致的(详见https://github.com/Vindicta-Team/FileXT/wiki)
打开侧边栏的搜索
一项,点击搜索框中的第三个图标启用正则表达式,而后将f.*\u0001\u0000{3}
替换为空白,去掉文件开头的特殊符号
再将(.)\u0000(.)
替换为$1,$2
,替换掉两个数字之间的NUL
而后将剩下的\u0000
替换为空白,这样所有特殊符号就被消除了
在两个文件中搜索"1000",0
的无效数据项,将其删除
在两个文件中搜索"a",0
的无效数据项,将其删除(a的值与导弹飞行的时间相似)
5. 分析数据文件并导出为图片
新建一个文件夹,在其下创建processData.py,内容如下:
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('./导弹飞行数据/中距弹'), '中距弹')
在其下创建目录,目录树如下:
└─导弹飞行数据
├─中距弹
├─图片
│ ├─中距弹
│ ├─地地导弹
│ ├─地空导弹
│ ├─空地导弹
│ └─近距弹
├─地地导弹
├─地空导弹
├─空地导弹
└─近距弹
将你分析得到的导弹数据按分类放入导弹飞行数据
文件夹下的分类即可
运行Python文件,在图片
目录下即可查看输出的数据
6. 关于测试场景
测试场景与Python代码我会一同打包放到网盘
下载地址:https://wwb.lanzoue.com/iTmkn0gvll7c