基本架构,架构分析的前提是编译flightgear
在flightgear内部,命令行参数分为6种类型:BOOL、String、Double、Int、Channel、Func
其中Bool、String、Double、Int都是直接对相应的属性(可以理解为内部的变量)进行赋值;
如:--airport=ZSSS 初始化时设置机场ICAO为ZSSS(上海虹桥)
Func类型则是在初始化阶段直接调用内部函数进行处理。
如:--random-wind 初始化时调用fgOptRandomWind,设置随即风
不过有些Func类型的参数是需要输入参数的,如:--func_key=func_argv
Channel类型参数主要用于外部接口通信,一般都可以复用(如:在命令行中可以有多个--native-fdm=xxx)
这种类型的参数有大家熟知的--native-fdm、--generic、--atlas等
具体的源码解析流程见图
flightgear网络接口模块是有FGIO子系统来完成的。FGIO子系统支持各种通信协议。
通信协议包括大家常用的:Native-fdm,Native,generic,jsclient等(也即前篇介绍的channel 类型参数)
对应的参数形式:--protocol=medium,direction,hz,medium_options,...
在flightgear架构分析中提到了,对于子系统,flightgear在初始化阶段调用子系统的bind(),init(),主循环阶段调用子系统的update(),最后调用子系统的shutdown。
因此对于FGIO子系统,在程序中也主要体现在bind、init、update和shutdown这几个函数的处理上。关于FGIO的结构如下图
通信协议的具体实现在FGIO的FGProtocol列表中,FGIO在每个周期都会调用每个FGProtocol 的处理函数,关于FGProtocol的结构如下图
下面具体对generic通信进行介绍,generic是通过FGGeneric(FGProtocol子类)来完成的,结构如图所示
Flightgear多人飞行对应的参数是multiplay。
具体参数结构如下:
--multiplay=dir,freq,ip,port --callsign=your_name
其中callsign代表自己的ID
官方文档上给出的例子是:
For two players on a local network or across the internet:
----------------------------------------------------------
Player1:--multiplay=out,10,192.168.0.3,5500 --multiplay=in,10,192.168.0.2,5501
--callsign=player1
Player2:--multiplay=out,10,192.168.0.2,5501 --multiplay=in,10,192.168.0.3,5500
--callsign=player2
For multiple players on a local network:
----------------------------------------
Player1:--multiplay=out,10,255.255.255.255,5500 --multiplay=in,10,255.255.255.255,5500
--callsign=player1
Playern:--multiplay=out,10,255.255.255.255,5500 --multiplay=in,10,255.255.255.255,5500
--callsign=playern
对于局域网内,2人联机飞行是没问题的,但是多人联机,通过广播的方式通信时会报错!!!因为在程序内部不能对广播地址进行绑定。
所以文档上关于广播方式联机的例子是有误的。
原因在于,flightgear对于广播地址没有进行判断,直接按照常规地址进行绑定,所以会绑定失败。
对于局域网多人(至少3人)联机飞行,这里给大家提供3种方案:
1.自己单独在一台主机上搭建服务器,目的很简单:数据转发。所有联机的客户机把--multiplay=out,10,server_ip,5501中的地址设为服务器地址。服务器则把收到的数据发给所有的客户机。
2.通过组播方式进行通信,虽然flightgear(实质是simgear)没有对广播进行特殊处理,但是它能识别组播地址。
3.修改flightgear程序。
对于广域网多人联机飞行,由于路由器一般不支持广播和组播,因此这里提供2中方案:
1.使用flightgear提供的在线服务器,连上去即可
2.自己搭建服务器(思路与局域网方案1一致),但是服务器需要固定IP
下面来分析如果修改flightgear程序来达到联机飞行的目的:
multiplay主要是有FGMultiplayMgr子系统来管理的,根据前面分析,对于子系统的处理主要是以下几个过程:
bind()
init()
update()
shutdown()
FGMultiplayMgr的重点是init:
init进行以下几个处理:
mSocket.reset(new simgear::Socket());
mSocket->open(false)
mSocket->setBlocking(false);
mSocket->bind(rxAddress.c_str(), rxPort)
这几个处理没有判断地址的类型,因此在bind阶段就会出现广播地址绑定失败!
为了解决这个问题,可以把以上过程改成以下过程:
mSocket.reset(new simgear::Socket());
mSocket->open(false)
mSocket->setBlocking(false);
mSocket->setBroadcast(true);
mSocket->bind(“0.0.0.0.”, rxPort)
Flightgear支持4个输入方式:
鼠标输入
键盘输入
游戏杆输入
事件输入
对应的子系统分别为:FGMouseInput、FGKeyboardInput、FGJoystickInput、FGEventInput 这几种输入的处理方式大同小异,本文以键盘输入为例,分析flightgear是如何响应键盘消息,以及如何自定义键盘消息。
flightgear处理键盘消息分为以下3个过程:
1.加载配置文件,配置文件包括"preferences.xml",“(your_aircraft)-set.xml/(your_aircraft)-set-common.xml”等。其中键盘的配置信息在/input/keyboard节点内
2.FGKeyboardInput的初始化,也即flightgear注册按键响应函数
3.FGKeyboardInput 的postinit过程,也即绑定按键到对应对象(该对象实现具体的按键响应过程)
具体的流程图如下:
下面来具体看看flightgear是如何响应按键:
以777-20飞机为例,在飞机的配置文件中注册了Ctrl-L按键消息:
处理过程如下:
1.窗口接收到按键信号
2.窗口任何查询按键注册函数
3.按键注册函数根据按键的类型,得到对应的FGButton,FGButton触发绑定对应的处理函数fire
4.在处理函数中通过
5.传入该按键的属性节点处理:do_property_toggle(&property)
最后来谈谈如何注册自己的按键消息,该过程如果抛开nasal来说,过程是比较简单的:1.首先定位文件,一般来说在飞机的配置文件中注册比较合适,对于777飞机来说,可以在777-set-common.xml中注册
2.复制上面xml内容,修改按键的ascii、响应命令,如
这时按键'A'也可以到达CTL-L的效果。
最后,如果大家更深入的了解,可以跟进以下几方面知识:
1.nasal脚本(flightgear内嵌编译器的一种脚本语言)
2.flightgear支持的命令以及对应的字符串