feat: 添加Squirrel脚本支持并完善平台相关功能
- 新增SquirrelVM类实现脚本引擎功能 - 添加Windows平台初始化代码以支持UTF-8控制台输出 - 在各平台构建配置中添加squirrel依赖 - 创建测试脚本main.nut验证中文支持 - 更新.gitignore排除nul文件
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ build/
|
|||||||
.vscode/compile_commands.json
|
.vscode/compile_commands.json
|
||||||
参考代码/
|
参考代码/
|
||||||
*.pvf
|
*.pvf
|
||||||
|
nul
|
||||||
|
|||||||
13
Frostbite2D/include/frostbite2D/platform/windows.h
Normal file
13
Frostbite2D/include/frostbite2D/platform/windows.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
|
||||||
|
void windowsInit();
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
|
|
||||||
|
#endif
|
||||||
50
Frostbite2D/include/frostbite2D/script/squirrel_vm.h
Normal file
50
Frostbite2D/include/frostbite2D/script/squirrel_vm.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <frostbite2D/types/type_alias.h>
|
||||||
|
#include <frostbite2D/resource/asset.h>
|
||||||
|
#include <json/json.hpp>
|
||||||
|
#include <squirrel.h> // Squirrel核心头文件
|
||||||
|
#include <sqstdio.h> // Squirrel标准IO库
|
||||||
|
#include <sqstdaux.h> // 新增:包含 sqstd_seterrorhandlers 等辅助函数
|
||||||
|
#include <sqstdblob.h> // 新增:包含 sqstd_register_bloblib 函数
|
||||||
|
#include <sqstdsystem.h> // 新增:包含 sqstd_register_systemlib 函数
|
||||||
|
#include <sqstdmath.h> // 新增:包含 sqstd_register_mathlib 函数
|
||||||
|
#include <sqstdstring.h> // 新增:包含 sqstd_register_stringlib 函数
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
|
||||||
|
class SquirrelVM {
|
||||||
|
public:
|
||||||
|
static SquirrelVM &get();
|
||||||
|
|
||||||
|
void setScriptDirectory(const std::string &path);
|
||||||
|
void setDebugMode(bool enable);
|
||||||
|
|
||||||
|
bool init();
|
||||||
|
void shutdown();
|
||||||
|
void loadScript();
|
||||||
|
|
||||||
|
HSQUIRRELVM getVM() const { return vm_; }
|
||||||
|
bool isValid() const { return vm_ != nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SquirrelVM() = default;
|
||||||
|
~SquirrelVM() = default;
|
||||||
|
|
||||||
|
SquirrelVM(const SquirrelVM &) = delete;
|
||||||
|
SquirrelVM &operator=(const SquirrelVM &) = delete;
|
||||||
|
|
||||||
|
static void printCallback(HSQUIRRELVM vm, const char *s, ...);
|
||||||
|
static void errorCallback(HSQUIRRELVM vm, const char *s, ...);
|
||||||
|
|
||||||
|
bool loadScriptFile(const std::string &path);
|
||||||
|
|
||||||
|
HSQUIRRELVM vm_ = nullptr;
|
||||||
|
bool initialized_ = false;
|
||||||
|
std::string scriptDirectory_;
|
||||||
|
bool debugMode_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <frostbite2D/graphics/camera.h>
|
#include <frostbite2D/graphics/camera.h>
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
#include <frostbite2D/graphics/renderer.h>
|
||||||
#include <frostbite2D/platform/switch.h>
|
#include <frostbite2D/platform/switch.h>
|
||||||
|
#include <frostbite2D/platform/windows.h>
|
||||||
#include <frostbite2D/resource/asset.h>
|
#include <frostbite2D/resource/asset.h>
|
||||||
#include <frostbite2D/resource/npk_archive.h>
|
#include <frostbite2D/resource/npk_archive.h>
|
||||||
#include <frostbite2D/resource/pvf_archive.h>
|
#include <frostbite2D/resource/pvf_archive.h>
|
||||||
@@ -47,6 +48,10 @@ bool Application::init(const AppConfig& config) {
|
|||||||
switchInit();
|
switchInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
windowsInit();
|
||||||
|
#endif
|
||||||
|
|
||||||
// 初始化核心模块
|
// 初始化核心模块
|
||||||
if (!initCoreModules()) {
|
if (!initCoreModules()) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize core modules");
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize core modules");
|
||||||
|
|||||||
14
Frostbite2D/src/frostbite2D/platform/windows.cpp
Normal file
14
Frostbite2D/src/frostbite2D/platform/windows.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include <frostbite2D/platform/windows.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
|
||||||
|
void windowsInit() {
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
SetConsoleCP(CP_UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
|
|
||||||
|
#endif
|
||||||
155
Frostbite2D/src/frostbite2D/script/squirrel_vm.cpp
Normal file
155
Frostbite2D/src/frostbite2D/script/squirrel_vm.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include <frostbite2D/script/squirrel_vm.h>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <squirrel.h>
|
||||||
|
|
||||||
|
namespace frostbite2D {
|
||||||
|
|
||||||
|
SquirrelVM &SquirrelVM::get() {
|
||||||
|
static SquirrelVM instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::setScriptDirectory(const std::string &path) {
|
||||||
|
scriptDirectory_ = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::setDebugMode(bool enable) { debugMode_ = enable; }
|
||||||
|
|
||||||
|
bool SquirrelVM::init() {
|
||||||
|
if (initialized_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_ = sq_open(1024);
|
||||||
|
if (!vm_) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "[Squirrel] Failed to create VM");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqstd_seterrorhandlers(vm_);
|
||||||
|
sq_pushroottable(vm_);
|
||||||
|
sqstd_register_bloblib(vm_);
|
||||||
|
sqstd_register_iolib(vm_);
|
||||||
|
sqstd_register_systemlib(vm_);
|
||||||
|
sqstd_register_mathlib(vm_);
|
||||||
|
sqstd_register_stringlib(vm_);
|
||||||
|
|
||||||
|
sq_setprintfunc(vm_, printCallback, errorCallback);
|
||||||
|
|
||||||
|
loadScript();
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"[Squirrel] VM initialized (Squirrel %s)", SQUIRREL_VERSION);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::loadScript() {
|
||||||
|
|
||||||
|
std::string scriptDir =
|
||||||
|
scriptDirectory_.empty() ? "scripts" : scriptDirectory_;
|
||||||
|
std::string configPath = scriptDir + "/scripts.json";
|
||||||
|
|
||||||
|
Asset &asset = Asset::get();
|
||||||
|
std::optional<std::string> jsonContent = asset.readFileToString(configPath);
|
||||||
|
|
||||||
|
if (!jsonContent) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR,
|
||||||
|
"[Squirrel] Failed to load scripts config: %s",
|
||||||
|
configPath.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto json = nlohmann::json::parse(*jsonContent);
|
||||||
|
auto files = json["files"];
|
||||||
|
|
||||||
|
for (const auto &filePath : files) {
|
||||||
|
std::string fullPath = scriptDir + "/" + std::string(filePath);
|
||||||
|
loadScriptFile(fullPath);
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR,
|
||||||
|
"[Squirrel] Failed to parse scripts.json: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SquirrelVM::loadScriptFile(const std::string &path) {
|
||||||
|
Asset &asset = Asset::get();
|
||||||
|
std::optional<std::string> content = asset.readFileToString(path);
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "[Squirrel] Failed to read script: %s",
|
||||||
|
path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SQChar *script = content->c_str();
|
||||||
|
|
||||||
|
SQInteger size = static_cast<SQInteger>(content->size());
|
||||||
|
|
||||||
|
if (SQ_FAILED(sq_compilebuffer(vm_, script, size, path.c_str(), SQTrue))) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR,
|
||||||
|
"[Squirrel] Failed to compile script: %s", path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_pushroottable(vm_);
|
||||||
|
|
||||||
|
if (SQ_FAILED(sq_call(vm_, 1, SQTrue, SQTrue))) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR,
|
||||||
|
"[Squirrel] Failed to execute script: %s", path.c_str());
|
||||||
|
sq_pop(vm_, 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_pop(vm_, 2);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[Squirrel] Loaded script: %s",
|
||||||
|
path.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::shutdown() {
|
||||||
|
if (vm_) {
|
||||||
|
sq_close(vm_);
|
||||||
|
vm_ = nullptr;
|
||||||
|
}
|
||||||
|
initialized_ = false;
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[Squirrel] VM shutdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::printCallback(HSQUIRRELVM vm, const char *s, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, s);
|
||||||
|
|
||||||
|
int size = vsnprintf(nullptr, 0, s, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
std::vector<char> buffer(size + 1);
|
||||||
|
va_start(args, s);
|
||||||
|
vsnprintf(buffer.data(), buffer.size(), s, args);
|
||||||
|
va_end(args);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[Squirrel] %s", buffer.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SquirrelVM::errorCallback(HSQUIRRELVM vm, const char *s, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, s);
|
||||||
|
|
||||||
|
int size = vsnprintf(nullptr, 0, s, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
std::vector<char> buffer(size + 1);
|
||||||
|
va_start(args, s);
|
||||||
|
vsnprintf(buffer.data(), buffer.size(), s, args);
|
||||||
|
va_end(args);
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "[Squirrel Error] %s", buffer.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frostbite2D
|
||||||
2
Game/assets/scripts/main.nut
Normal file
2
Game/assets/scripts/main.nut
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
print("中文测试!")
|
||||||
|
error("中文测试错误!")
|
||||||
@@ -29,6 +29,8 @@
|
|||||||
#include <frostbite2D/2d/text_sprite.h>
|
#include <frostbite2D/2d/text_sprite.h>
|
||||||
#include <frostbite2D/graphics/font_manager.h>
|
#include <frostbite2D/graphics/font_manager.h>
|
||||||
|
|
||||||
|
#include <frostbite2D/script/squirrel_vm.h>
|
||||||
|
|
||||||
using namespace frostbite2D;
|
using namespace frostbite2D;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@@ -47,6 +49,8 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SDL_Log("Starting main loop...");
|
// SDL_Log("Starting main loop...");
|
||||||
|
SquirrelVM::get().setScriptDirectory("assets/scripts");
|
||||||
|
SquirrelVM::get().init();
|
||||||
|
|
||||||
auto &pvf = PvfArchive::get();
|
auto &pvf = PvfArchive::get();
|
||||||
if (pvf.open("assets/Script.pvf")) {
|
if (pvf.open("assets/Script.pvf")) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ add_requires("libsdl2_image")
|
|||||||
add_requires("libsdl2_mixer")
|
add_requires("libsdl2_mixer")
|
||||||
add_requires("glm")
|
add_requires("glm")
|
||||||
add_requires("zlib")
|
add_requires("zlib")
|
||||||
|
add_requires("squirrel")
|
||||||
|
|
||||||
target("Frostbite2D")
|
target("Frostbite2D")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
@@ -19,6 +20,7 @@ target("Frostbite2D")
|
|||||||
add_packages("libsdl2_mixer")
|
add_packages("libsdl2_mixer")
|
||||||
add_packages("glm")
|
add_packages("glm")
|
||||||
add_packages("zlib")
|
add_packages("zlib")
|
||||||
|
add_packages("squirrel")
|
||||||
|
|
||||||
-- 复制着色器文件到输出目录
|
-- 复制着色器文件到输出目录
|
||||||
after_build(function (target)
|
after_build(function (target)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ add_requires("libsdl2_mixer", {configs = {shared = true}})
|
|||||||
add_requires("libsdl2_ttf", {configs = {shared = true}})
|
add_requires("libsdl2_ttf", {configs = {shared = true}})
|
||||||
add_requires("glm")
|
add_requires("glm")
|
||||||
add_requires("zlib")
|
add_requires("zlib")
|
||||||
|
add_requires("squirrel")
|
||||||
|
|
||||||
target("Frostbite2D")
|
target("Frostbite2D")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
@@ -23,6 +24,7 @@ target("Frostbite2D")
|
|||||||
add_packages("libsdl2_ttf")
|
add_packages("libsdl2_ttf")
|
||||||
add_packages("glm")
|
add_packages("glm")
|
||||||
add_packages("zlib")
|
add_packages("zlib")
|
||||||
|
add_packages("squirrel")
|
||||||
|
|
||||||
-- 复制 assets 目录到输出目录
|
-- 复制 assets 目录到输出目录
|
||||||
after_build(function (target)
|
after_build(function (target)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
target("Frostbite2D")
|
target("Frostbite2D")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
|
||||||
@@ -40,7 +41,7 @@ target("Frostbite2D")
|
|||||||
add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2"))
|
add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2"))
|
||||||
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))
|
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))
|
||||||
|
|
||||||
add_syslinks("SDL2_mixer", "SDL2_image", "SDL2_ttf", "SDL2", "freetype", "harfbuzz" , "bz2" , "webp", "png", "jpeg", "z", "opusfile", "opus", "vorbisidec", "ogg","modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",{public = true})
|
add_syslinks("SDL2_mixer", "SDL2_image", "SDL2_ttf", "SDL2", "freetype", "harfbuzz" , "squirrel_static", "sqstdlib_static", "bz2" , "webp", "png", "jpeg", "z", "opusfile", "opus", "vorbisidec", "ogg","modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",{public = true})
|
||||||
add_syslinks("nx", "m")
|
add_syslinks("nx", "m")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user