Unity3D学习笔记#8_虚拟空战系统开发

本文记录了使用Unity3D开发虚拟空战系统的经验,包括开发目标、总体框架、通信协议、C++部分的实现以及Unity3D中的工作内容。系统采用UDP通信,支持驾驶舱和上帝视角,可模拟空战、路径规划和多机协同。虽不追求高画质,但具备基本功能,适合研究和实验使用。
摘要由CSDN通过智能技术生成

学习U3D的主要目的是希望为实验室和自己开发一个虚拟空战的视景系统,经过一个月左右的学习和开发,目前已经完成了初步的版本(完成后又放飞自我了一段时间,自我批评中…),那么这篇主要介绍一下开发的这个系统,记录下思路和实现的基本过程。

本文仅供参考,工程不打算开源。

1. 开发目标

首先分析下需求。

空战演示有两种类型:一类是从飞机驾驶员的角度进行的,也就是我们从画面上看到的应该是驾驶舱中的重要仪表(多功能显示器、HUD等)和舱外的画面。这类研究中,我们只关注单个飞机上功能的设计和实现。比如,我们可以研究更好的飞机数据显示方案、研究辅助驾驶系统等等。另一类则是从上帝视角进行的,这也是现在比较热点的研究:协同和集群。主要目的是演示多机之间的配合,研究多机协同和决策算法等。

所以,我们希望开发的系统要支持驾驶舱内部视角,支持仪表的开发;也要支持上帝视角,从全局观察多个飞机的情况。

为了让软件功能更丰富,我们希望能够支持多种飞机类型,支持编辑建筑物和山地,支持显示路径规划的结果,支持在屏幕上绘制仪表,支持导弹、机炮射击、爆炸等效果。

总结起来,可以概括为:

  • 单机的研究。单机(弹)的控制率研究,无人机决策算法研究,民航飞机飞管系统研究,战斗机机动决策研究等等;
  • 多机的研究。多机协同研究,集群(蜂群)作战研究,民机调度研究等;
  • 路径规划。单机、多机路径规划算法研究。

这些内容通过U3D都是可以实现的。很多细节也是边开发边想清楚的,上面的讨论只是总体的方向。实际上,开发一个系统,很多时候最初并没有特别清晰的设计,只是有大概的想法和目标,细节方面很多都需要在实践中完善。

个人能力有限,不追求画面多么精致,能够基本支持预期的功能即可。系统的总体结构参考FlightGear软件,也就是基于通信的方式。

2. 总体框架

在这里插入图片描述
仿照FlightGear,让应用程序和视景演示程序分离,中间通过UDP进行连接,这有很多好处:首先软件耦合度低,视景部分完全独立,作为一个exe程序存在,可以放到任何机器上运行,研究的部分则可以使用任何语言开发(目前只提供了C++的接口,可以仿照C++接口实现其它语言的接口,或者包装C++接口),这样可以根据实际需要采用合适的语言,比如研究AI可能就使用Python了;其次,可以实现分布式结构,比如我们研究多机之间对抗,每个飞机在不同的计算机上运行,那么在局域网内,我们用一台机器作为视景环境,其它机器都把数据发送到这个演示的机器上就可以了。

视景软件的使用者只需要了解UDP消息的定义即可,其余都不必关心。因此这样的架构适用性是比较广的。此外,在第一小节中也进行了介绍,就是视景软件支持定制化显示功能,可以通过通信绘制UI界面,从而实现场景中数据的显示,个性化仪表的绘制等功能,可拓展性比较好。

3. 通信协议

由于不需要进行无线通信,也只在局域网下工作,因此不需要特别考虑通信效率的问题,那么为了便于理解和开发,直接采用了JSON的数据结构,其本质是一个字符串。

数据格式为:

{“msg_id”:…, “msg_type”:…, “msg_body”:{[…:…, ]…}}

其中,…表示省略的内容,根据实际数据填充,[]表示可选项,[]…表示可以重复多次的可选项,大括号和冒号是JSON规定的符号。msg_id字段表示消息的id号码,每条消息原则上应该使用递增的号码,msg_type字段表示消息的类型,msg_body字段表示消息的内容,其本身是json子对象,包含若干个特殊的字段。

需要注意,以上数据格式是字符串,因此在编程中直接写成ASCII字符串,注意字符串中的引号需要转义。

举个例子,对于battle_field_config类型的消息,其消息的格式如下:

"{\"msg_id\":0,\"msg_type\":\"battle_field_config\",\"msg_body\":{\"central_x\":0,\"central_y\":0,\"nswidth\":1000,\"wewidth\":1000}}"

为了直观,我们写出格式化的形式(实际发送还是按照上面的紧凑形式,这个只是便于查看):

{
   
	"msg_id": 0,
	"msg_type": "battle_field_config",
	"msg_body": {
   
		"central_x": 0,
		"central_y": 0,
		"nswidth": 1000,
		"wewidth": 1000
	}
}

这条消息的编号为0,那么下个消息的编号应该为1,消息类型是battle_field_config,也就是战场总体配置,消息体包含战场中心坐标和战场南北、东西范围。

目前系统支持的消息有如下几类:

消息类型 功能
battle_field_config 战场中心坐标、战场范围
building_info 增/删/缩放/移动一个建筑物
mountain_info 增/删/缩放/移动一个山
plane_info 增/删/移动一个飞机
weapon_info_missile 增/删/移动一个导弹
weapon_info_net 增/删/移动一个拦截网
weapon_info_bullet 某飞机发射子弹
weapon_info_laser 某飞机发射激光
explosion 产生一个爆炸
ui_draw_line ui绘制-线
ui_draw_circle ui绘制-椭圆
ui_draw_filledquad ui绘制-填充矩形
ui_draw_text ui绘制-文字
ui_destroy ui删除
scene_draw_line 3d绘制-线
scene_destroy 3d删除
message 提示信息

其中,JSON我们直接采用成熟的库,C++代码使用CJsonObject,C#代码使用SimpleJSON

4. C++部分

首先必须强调,这些东西可以比较容易地用任何语言替换,这里只是举例说明。

为了演示系统的效果,除了接口的部分,还实现了UDP通信、飞机模型(3dof)、导弹模型(3dof)的功能。

代码量应该不超过3000行,很多代码都是以前积累下来的。

UDP通信部分,直接使用了Windows下Socket通信方案,我把它稍微封装了下,使用起来非常简单,首先包含头文件comm_tools.h,然后:

 // 0. 定义数据接收线程函数,注意如果接收失败,则 num = -1
void socket_recv_thread_func(char* data, int num){
   ...}
// 1. 实例化类,下面四种选一个
CSocketTool iSocketTool("127.0.0.1", 5000, TCP_CLIENT);	// TCP 客户端
CSocketTool iSocketTool("127.0.0.1", 5000, IP_CLIENT);	// UDP 客户端
CSocketTool iSocketTool(5000, TCP_SERVER);				// TCP 服务端
CSocketTool iSocketTool(5000, IP_SERVER);				// UDP 服务端
// 2. 连接
if (!iSocketTool.Connect()){
   cout << "socket connect failed." << endl; exit(0);}
// 3. 创建数据接收线程
if (!iSocketTool.CreateRecvThread(socket_recv_thread))
{
   cout << "recv thread create failed." << endl; exit(0);}
// 4. 在需要的地方发送数据
if (!iSocketClient.Send(data)){
   cout << "data send failed." << endl; exit(0);}

模型部分,采用了最简单的三自由度模型,使用过载进行控制,并使用龙格库塔法进行插值运算。
核心代码如下:

// 对于固定翼飞机
SPlaneModelState CPlaneModelIn3Dof_FixedWing::Run(double _nx, double _nz, double _ny)
{
    // 下面的模型 nz 和 ny 和之前的定义是相反的,所以形参把顺序变了
	// 限制过载
	if (sqrt(_nx * _nx + _ny * _ny + _nz * _nz) <= this->dMaxOverload){
   
		// 过载柔性更新
		nx = alpha * nx + (1 - alpha) * _nx;
		ny = alpha * ny + (1 - alpha) * _ny;
		nz = alpha * nz + (1 - alpha) * _nz;
	}
	// 简化变量
	double v = sPlaneCurState.v;
	double atti[2] = {
   sPlaneCurState.pathpitch, sPlaneCurState.pathyaw};
	double K[5][4];
	double dAtt[3];
	double dPos[3];

	// 四阶龙格库塔法 - [pitch_dot, yaw_dot, pos_north, pos_up, pos_east]
	v = v + dTimeStep * G * nx;
	K[0][0] = G*(ny - cos(atti[0]))/v;
	K[1][0] = G*nz/v/cos(atti[0]);
	K[2][0] = v*cos(atti[0])
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值