本帖最后由 5271081文字狱 于 2023-5-24 14:10 编辑
长文预告!
引言 为什么要进行XP的插件开发: 笔者之前从事过DCS的插件开发,主要用于战术模拟以及飞控测试。但由于DCS的历史性原因,其文档较为老旧且附带大量俄语。做一些简单的插件尚可,但要进行复杂的开发则较为困难。XP作为军民两用航空模拟器的佼佼者,提供了实时计算的空气动力学数据以及最接近真实的飞行模拟体验。笔者的终极目标是想尝试做一些XP周边的软硬件,如家庭座舱,飞控算法测试等。 X-Plane (XP) 提供了官方SDK(Software Development Kit,软件开发包),为插件开发提供了良好的环境、全面的文档。插件使用C++编写,为.xpl后缀的动态链接库形式。其中一些较为著名且常用的插件包括:
- BetterPushback: 推出
- BetterMouseYoke: 鼠标摇杆
- AviTab: 飞行平板
- DataRefTool / DataRefEditor: 飞行参数查找及修改
- FlyWithLua: Lua接口插件开发的中间件
其中 FlyWithLua 提供了使用Lua进行插件开发的接口,例如BetterMouseYoke就是基于Lua开发。若您想参考更多有关资料,请查阅:FlyWithLua 。本教程使用XP原生SDK进行插件开发,不会涉及Lua。 用FlyWithLua 的好处(猜测)应该是能更快地开发一些跨平台的插件(由于用Lua编写不涉及系统环境)。
目前了解到的至少需要熟悉以下技能点: - 初级C++
- 熟悉Visual Studio (2017及以上版本)作为C++开发的IDLE
- 笔者使用Windows 11系统,尚未接触在Linux或者Mac下的插件开发
参考资料: - @alirezaghaderi up主的系列课程(youtube)
- XP官方开发教程
SDK的安装以及第一个插件示例: Hello X-Plane效果图:
(该插件将建立一个窗口并显示文字)详细步骤: 预备工作下载并安装Visual Studio (2017及以上版本),并安装响应Windows下开发C++的工具链。 若使用Mac或者Linux系统,使用最新版本的XCode或者gcc也可,但后续的配置工作可能稍为繁琐。
下载X-Plane官网的SDK开发包
https://developer.x-plane.com/sdk/plugin-sdk-downloads/
注意对应的版本。如果使用XP10则下载XPLM300即可。 下载完成并解压得到以下文件结构:
下载XP官网示例项目
下载VS版本 解压并打开VS项目 若使用VS2017以上版本则会提示升级工具集,直接升级到最新即可。 打开Hello-World-SDK-3.cpp文件:
构建项目项目目录中有一个SDK文件夹,实际上就是我们稍早前下载的SDK包。如果没有的话或者版本不对手动添加即可。注意目录。 构建项目前需要先检查配置:右键项目名-属性 使用Debug或者Release配置均可,注意保持配置属性和实际构建时的属性一致。建议直接使用默认的Debug配置。 C/C++-常规-附加包含目录,发现已经配置好了SDK中的头文件的XPLM以及Widgets目录。 接下来,链接器-常规:已经配置好了SDK文件夹中的链接库Libraries-Win(如果是Mac平台就添加Mac文件夹) 此时配置完成,可以重新生成解决方案: 生成的插件在项目的Debug-plugins 文件夹内,里面包含了64平台下的.xpl文件 接下来用正常方法安装插件即可,将生成的Hello-World-SDK-3文件夹放到X-Plane11主目录的Resources-Plugins文件夹下。也可以放置到某机型文件夹的Plugins文件夹内,例如笔者放到了Cessna 172中,这样该插件就只对该特定机型有效。 运行效果打开XP,选择C172(若采用第二种插件安装方法) 至此安装成功!
代码解释及修改主题代码一共一百多行,目的是构建.xpl这个动态链接库。其中包含了创建窗口,添加文字等。笔者认为没有必要完全搞懂每一行代码的意思,重要的是能把项目构建流程先搞懂,能够在源代码上稍微修改主要功能即可。 - // Downloaded from https://developer.x-plane.com/code-sample/hello-world-sdk-3/
- #include "XPLMDisplay.h"
- #include "XPLMGraphics.h"
- #include <string.h>
- #if IBM
- #include <windows.h>
- #endif
- #if LIN
- #include <GL/gl.h>
- #elif __GNUC__
- #include <OpenGL/gl.h>
- #else
- #include <GL/gl.h>
- #endif
- #ifndef XPLM300
- #error This is made to be compiled against the XPLM300 SDK
- #endif
- // An opaque handle to the window we will create
- static XPLMWindowID g_window;
- // Callbacks we will register when we create our window
- void draw_hello_world(XPLMWindowID in_window_id, void * in_refcon);
- int dummy_mouse_handler(XPLMWindowID in_window_id, int x, int y, int is_down, void * in_refcon) { return 0; }
- XPLMCursorStatus dummy_cursor_status_handler(XPLMWindowID in_window_id, int x, int y, void * in_refcon) { return xplm_CursorDefault; }
- int dummy_wheel_handler(XPLMWindowID in_window_id, int x, int y, int wheel, int clicks, void * in_refcon) { return 0; }
- void dummy_key_handler(XPLMWindowID in_window_id, char key, XPLMKeyFlags flags, char virtual_key, void * in_refcon, int losing_focus) { }
- PLUGIN_API int XPluginStart(
- char * outName,
- char * outSig,
- char * outDesc)
- {
- strcpy(outName, "HelloWorld3Plugin");
- strcpy(outSig, "xpsdk.examples.helloworld3plugin");
- strcpy(outDesc, "A Hello World plug-in for the XPLM300 SDK.");
- XPLMCreateWindow_t params;
- params.structSize = sizeof(params);
- params.visible = 1;
- params.drawWindowFunc = draw_hello_world;
- // Note on "dummy" handlers:
- // Even if we don't want to handle these events, we have to register a "do-nothing" callback for them
- params.handleMouseClickFunc = dummy_mouse_handler;
- params.handleRightClickFunc = dummy_mouse_handler;
- params.handleMouseWheelFunc = dummy_wheel_handler;
- params.handleKeyFunc = dummy_key_handler;
- params.handleCursorFunc = dummy_cursor_status_handler;
- params.refcon = NULL;
- params.layer = xplm_WindowLayerFloatingWindows;
- // Opt-in to styling our window like an X-Plane 11 native window
- // If you're on XPLM300, not XPLM301, swap this enum for the literal value 1.
- params.decorateAsFloatingWindow = xplm_WindowDecorationRoundRectangle;
- // Set the window's initial bounds
- // Note that we're not guaranteed that the main monitor's lower left is at (0, 0)...
- // We'll need to query for the global desktop bounds!
- int left, bottom, right, top;
- XPLMGetScreenBoundsGlobal(&left, &top, &right, &bottom);
- params.left = left + 50;
- params.bottom = bottom + 150;
- params.right = params.left + 200;
- params.top = params.bottom + 200;
- g_window = XPLMCreateWindowEx(¶ms);
- // Position the window as a "free" floating window, which the user can drag around
- XPLMSetWindowPositioningMode(g_window, xplm_WindowPositionFree, -1);
- // Limit resizing our window: maintain a minimum width/height of 100 boxels and a max width/height of 300 boxels
- XPLMSetWindowResizingLimits(g_window, 200, 200, 300, 300);
- XPLMSetWindowTitle(g_window, "Sample Window");
- return g_window != NULL;
- }
- PLUGIN_API void XPluginStop(void)
- {
- // Since we created the window, we'll be good citizens and clean it up
- XPLMDestroyWindow(g_window);
- g_window = NULL;
- }
- PLUGIN_API void XPluginDisable(void) { }
- PLUGIN_API int XPluginEnable(void) { return 1; }
- PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam) { }
- void draw_hello_world(XPLMWindowID in_window_id, void * in_refcon)
- {
- // Mandatory: We *must* set the OpenGL state before drawing
- // (we can't make any assumptions about it)
- XPLMSetGraphicsState(
- 0 /* no fog */,
- 0 /* 0 texture units */,
- 0 /* no lighting */,
- 0 /* no alpha testing */,
- 1 /* do alpha blend */,
- 1 /* do depth testing */,
- 0 /* no depth writing */
- );
- int l, t, r, b;
- XPLMGetWindowGeometry(in_window_id, &l, &t, &r, &b);
- float col_white[] = {1.0, 1.0, 1.0}; // red, green, blue
- XPLMDrawString(col_white, l + 10, t - 20, "Hello world!", NULL, xplmFont_Proportional);
- }
复制代码
例如,代码的最后一行的“Hello world!”显然是窗口显示的文字,我们可以改成"Hello X-Plane!":
- XPLMDrawString(col_white, l + 10, t - 20, "Hello X-Plane!", NULL, xplmFont_Proportional);
复制代码
在代码的73行左右我们可以更改窗口的标题:
- XPLMSetWindowTitle(g_window, "Sample Window");
复制代码 将”Sample Window”改为”Hello X-Plane” :
- XPLMSetWindowTitle(g_window, "Hello X-Plane");
复制代码此时重新构建插件,就可以得到置顶效果图中的样式。 代码中还有很多值得玩味和尝试修改的地方,如有兴趣不妨一试。例如以下三行代码可以更改插件自身的属性:插件名字,签名以及描述等。 - strcpy(outName, "HelloWorld3Plugin");
- strcpy(outSig, "xpsdk.examples.helloworld3plugin");
- strcpy(outDesc, "A Hello World plug-in for the XPLM300 SDK.");
复制代码
注:该插件虽然看似简单,但涉及到窗口绘制以及显示的功能,若要从0开始开发还是有不少难度。这个示例主要是为了能够对插件构建及开发流程有个直观感受。下一节将从DataRef开始着手XP的数据获取工作。XP几乎提供了一切飞行参数,包括飞机姿态、运动学数据、动力学数据、航电、仪表数据等,以及如何对可读写数据进行更改,有了它们就可以do everything you want。
|