1.目标
一分钟实现游戏库改造之JNA
2.JNA调用
2.1 JNA调用C++库
上一节我们写了一个简单的JNI调用,步骤显得相当繁琐。
而且我们之前提到过,目的是要跨语言(不仅Java使用,Python和Lua也能使用),JNI直接调用方式引入了jdk下的jni相关头文件,这种方式侵入了C++源代码,就达不到初衷了。
为了更简单的调用本地库,而且不让JNI逻辑侵入C++,接下来我们使用JNA。
2.1.1 编译C++库
在使用JNA之前,我们移除原C++代码中的所有JNI相关头文件,重写编译一下C++库。
调整后,目录结构如下:
C++动态库如果要导出方法,则需要有宏定义__declspec(dllexport)。
为了更好的被其他语言兼容使用,我们还需要加上extern "C"。extern "C"修饰的方法或模块会按照 C 语言的规则编译代码,确保方法名导出时不被更改。
让我们将hello方法的名称改成pineaInit,并修改代码:
// Pinea.h
#pragma once
// 条件预定义宏,如果定义了PINEA_BUILD_DLL则PINEA_API替换为 __declspec(dllexport)
#ifdef PINEA_BUILD_DLL
#define PINEA_API __declspec(dllexport)
#else
#define PINEA_API __declspec(dllimport)
#endif
extern "C" {
PINEA_API void pineaInit(const char* title, int width, int height);
}
// Pinea.cpp
#include "Pinea.h"
#include <iostream>
void pineaInit() {
// 简单打印
std::cout << "Hello Pinea!" << std::endl;
}
接着,在CMakeLists.txt中添加预定义宏PINEA_BUILD_DLL:
保存代码并编译:
2.1.2 引入JNA依赖
在pom.xml引入jna的maven依赖:
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.13.0</version>
</dependency>
</dependencies>
2.1.3 定义Native方法
定义Pinea接口继承Library:
import com.sun.jna.Library;
public interface Pinea extends Library {
void pineaInit();
}
2.1.4 调用动态库
通过JNA调用本地方法前,我们需要先设置JNA要加载库的目录及库名称:
运行后,控制台打印正常,相当完美。
3.中章
邪宗C/C++不知名结界处。
张平安大手一挥,伴随着金光闪烁,一颗约莫拳头大小的水晶珠子从他的袖口飞出,骤然止于结界光幕半尺之处。
水晶珠子悬于半空,表面流光溢彩,一眼看去便知不凡。
张平安双目微凝,指尖在身前飞快捻转,嘴唇开合间,似有晦涩咒文断续传出,每一段咒文都带着灵力震颤,让周围空间都跟着颤动起来。
“编译C++为动态库;”
“设置JNA库加载目录,加载本地库;”
“调用本地方法;”
“……”
“妈妈咪咪哄,开!”
随着张平安一声大喝,半空的水晶珠子仿若有所感应,“砰”的一生爆开,化为点点零星附于结界光幕上,但这些零星并没有消失,而是在光幕上缓缓汇聚,形成某种玄妙的纹路。
王富贵抬头看去,满脸惊叹:“终于要打开结界通道了吗,JNA符宝果真名不虚传啊!”
片刻后,半空中的结界光幕破开一个人形大小的洞口。
王富贵和张平安相互对视一眼,没有犹豫,纷纷闪进洞口,进入了C/C++上古邪宗。
入宗后,一阵天旋地转。
好半晌,两人才恢复过来,还没来得及松一口气,各自脸上皆是充满震惊。
四周天昏地暗,空气中弥漫着一层诡异的红雾,呼吸间更是有着阵阵阴寒之意袭来,那种寒意侵入肺腑,令得浑身不自觉地颤栗。
“这里果然邪门!”
两人立即开启金光护盾,每迈出一步都小心翼翼,犹如摸着石头过河。
就在这时,蓦地从身后传来了一道熟悉的声音。
“王师兄,张师兄,你们两也来这里了?”
“是李师弟。”
两人惊喜回头,只见李吉祥正立于不远处,两团诡异的黑气缭绕在他周身,在其眼中,竟是有着骇人红芒不断闪烁。
“皇天不负有心人,探索这么多年了,我终于得偿所愿。”
李吉祥微微抬手,刹那间,一把漆黑细长的锻刀显现而出,刀身处,竟是龙飞凤舞雕刻着几个醒目的Ascii码:
“S - D - L - 3!”
……
4.SDL3初探
4.1 SDL3环境
4.1.1 下载库
4.1.2 添加库
将SDL3和SDL3Image相关头文件和库添加到csrc,目录结构如下:
修改CMakeLists.txt配置,添加lib:
Pinea.cpp引入了SDL3的头文件,编译测试一下环境的正确性:
4.2 SDL3显示窗口
之前,我们起步于Swing创建窗口,现在,我们的第一步依然是用SDL3创建一个窗口:
void pineaInit() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Pinea", 800, 600, 0);
SDL_Event event;
bool running = true;
while (running) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
running = false;
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
}
如果你已经封装过Pinea库,这段代码一定很熟悉了:创建一个窗口,不停地获取事件判断是否退出,以及最后删除窗口终止程序。
保存代码后编译,没有任何问题。我们不用修改任何Java代码,直接启动Java程序,屏幕上便出现一个黑色的窗口啦!
6.思考:如何通过JNA的方式绑定SDL3呢?
我们已经能够使用JNA的方式调用C++库了,也初步了解了SDL3是什么,如何将这两者结合起来,实现类似我们之前Pinea库呢?
欢迎继续关注《JAVA一分钟游戏编程系列》,下期不见不散。