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
|
||||
参考代码/
|
||||
*.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/renderer.h>
|
||||
#include <frostbite2D/platform/switch.h>
|
||||
#include <frostbite2D/platform/windows.h>
|
||||
#include <frostbite2D/resource/asset.h>
|
||||
#include <frostbite2D/resource/npk_archive.h>
|
||||
#include <frostbite2D/resource/pvf_archive.h>
|
||||
@@ -47,6 +48,10 @@ bool Application::init(const AppConfig& config) {
|
||||
switchInit();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
windowsInit();
|
||||
#endif
|
||||
|
||||
// 初始化核心模块
|
||||
if (!initCoreModules()) {
|
||||
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/graphics/font_manager.h>
|
||||
|
||||
#include <frostbite2D/script/squirrel_vm.h>
|
||||
|
||||
using namespace frostbite2D;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
@@ -45,8 +47,10 @@ int main(int argc, char **argv) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize application!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// SDL_Log("Starting main loop...");
|
||||
SquirrelVM::get().setScriptDirectory("assets/scripts");
|
||||
SquirrelVM::get().init();
|
||||
|
||||
auto &pvf = PvfArchive::get();
|
||||
if (pvf.open("assets/Script.pvf")) {
|
||||
|
||||
@@ -4,6 +4,7 @@ add_requires("libsdl2_image")
|
||||
add_requires("libsdl2_mixer")
|
||||
add_requires("glm")
|
||||
add_requires("zlib")
|
||||
add_requires("squirrel")
|
||||
|
||||
target("Frostbite2D")
|
||||
set_kind("binary")
|
||||
@@ -19,6 +20,7 @@ target("Frostbite2D")
|
||||
add_packages("libsdl2_mixer")
|
||||
add_packages("glm")
|
||||
add_packages("zlib")
|
||||
add_packages("squirrel")
|
||||
|
||||
-- 复制着色器文件到输出目录
|
||||
after_build(function (target)
|
||||
|
||||
@@ -7,6 +7,7 @@ add_requires("libsdl2_mixer", {configs = {shared = true}})
|
||||
add_requires("libsdl2_ttf", {configs = {shared = true}})
|
||||
add_requires("glm")
|
||||
add_requires("zlib")
|
||||
add_requires("squirrel")
|
||||
|
||||
target("Frostbite2D")
|
||||
set_kind("binary")
|
||||
@@ -23,6 +24,7 @@ target("Frostbite2D")
|
||||
add_packages("libsdl2_ttf")
|
||||
add_packages("glm")
|
||||
add_packages("zlib")
|
||||
add_packages("squirrel")
|
||||
|
||||
-- 复制 assets 目录到输出目录
|
||||
after_build(function (target)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
|
||||
target("Frostbite2D")
|
||||
set_kind("binary")
|
||||
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_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")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user