Files
Boundard/src/engine.cpp
T

208 lines
6.7 KiB
C++

#include <engine.hpp>
#include <chrono>
#include <cstdio>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include <boundard.hpp>
#include <camera.hpp>
#include <sprite_actor.hpp>
namespace boundard {
namespace {
std::shared_ptr<Actor> create_actor(const ActorConfig& actor_config, Game& game,
BehaviorRegistry& behaviors) {
auto mesh = std::make_shared<Mesh>();
if (actor_config.mesh.shape == "quad") {
*mesh = Mesh::quad();
} else {
// 当前渲染器只提供 quad;未知形状先按 quad 创建,便于后续扩展。
*mesh = Mesh::quad();
}
mesh->set_color(actor_config.mesh.color);
mesh->texture = actor_config.mesh.texture;
std::shared_ptr<Actor> actor;
if (actor_config.type == "camera") {
actor = std::make_shared<Camera>(actor_config.name, game);
} else {
actor = std::make_shared<SpriteActor>(actor_config.name, game, mesh);
}
actor->set_position(actor_config.position);
actor->set_facing(actor_config.facing);
if (auto* camera = dynamic_cast<Camera*>(actor.get())) {
camera->set_zoom(actor_config.zoom);
}
for (const ActorConfig& child_config : actor_config.children) {
actor->add_child(create_actor(child_config, game, behaviors));
}
if (!actor_config.init_function.empty()) {
auto handler = behaviors.init(actor_config.init_function);
if (handler) {
actor->set_init_handler(std::move(handler));
} else {
std::fprintf(stderr, "未找到 init 处理函数: %s\n",
actor_config.init_function.c_str());
}
}
if (!actor_config.tick_function.empty()) {
auto handler = behaviors.tick(actor_config.tick_function);
if (handler) {
actor->set_tick_handler(std::move(handler));
} else {
std::fprintf(stderr, "未找到 tick 处理函数: %s\n",
actor_config.tick_function.c_str());
}
}
return actor;
}
std::shared_ptr<Scene> build_scene(const SceneConfig& scene, Game& game,
BehaviorRegistry& behaviors) {
auto runtime_scene = std::make_shared<Scene>(scene.name);
if (scene.grid_background) {
auto grid = std::make_shared<SpriteActor>("__grid", game,
std::make_shared<Mesh>(Mesh::grid()));
grid->mesh()->set_color({0.20F, 0.28F, 0.36F, 0.75F});
runtime_scene->add_actor(std::move(grid));
}
for (const ActorConfig& actor_config : scene.actors) {
auto actor = create_actor(actor_config, game, behaviors);
runtime_scene->add_actor(std::move(actor));
}
runtime_scene->set_default_camera(scene.default_camera);
return runtime_scene;
}
const SceneConfig* find_scene(const std::vector<SceneConfig>& scenes,
const std::string& name) {
for (const SceneConfig& scene : scenes) {
if (scene.name == name) {
return &scene;
}
}
return nullptr;
}
} // namespace
int run_engine(int argc, char** argv, const char* default_config_path) {
std::string config_path = default_config_path != nullptr ? default_config_path : "config.ceg";
if (argc > 1) {
config_path = argv[1];
}
ConfigLoader loader;
if (!loader.load(config_path)) {
std::fprintf(stderr, "%s\n", loader.last_error().c_str());
return 1;
}
const GameConfig& config = loader.config();
Game& game = Game::getInstance();
BehaviorRegistry& behaviors = BehaviorRegistry::getInstance();
std::vector<SceneConfig> scenes = config.scenes;
if (scenes.empty() && !config.actors.empty()) {
SceneConfig legacy;
legacy.name = "default";
legacy.path = config_path;
legacy.actors = config.actors;
scenes.push_back(std::move(legacy));
}
std::string start_scene = config.start_scene;
if (start_scene.empty() && !scenes.empty()) {
start_scene = scenes.front().name;
}
const SceneConfig* active_scene = find_scene(scenes, start_scene);
if (active_scene == nullptr && !scenes.empty()) {
active_scene = &scenes.front();
std::fprintf(stderr, "未找到起始场景 %s,已回退到 %s\n", start_scene.c_str(),
active_scene->name.c_str());
}
// 把配置文件中的场景包装为运行时 Scene,并注册到 Game。
for (const SceneConfig& scene_config : scenes) {
std::shared_ptr<Scene> runtime_scene = build_scene(scene_config, game, behaviors);
game.add_scene(std::move(runtime_scene));
}
render::RenderLoop render_loop;
for (const InputBindingConfig& binding : config.input_bindings) {
render_loop.input().add_action(binding.action);
for (int key : binding.keys) {
render_loop.input().bind_key(binding.action, key);
}
for (int button : binding.mouse_buttons) {
render_loop.input().bind_mouse_button(binding.action, button);
}
}
// 引擎级默认退出动作,不包含具体游戏逻辑。
render_loop.input().add_action("quit");
render_loop.input().bind_key("quit", 256);
auto apply_scene = [&](const std::string& name) {
std::shared_ptr<Scene> scene = game.scene(name);
if (scene == nullptr) {
std::fprintf(stderr, "未找到场景: %s\n", name.c_str());
return;
}
scene->initialize();
game.set_current_scene(scene);
render_loop.set_actors(scene->actors());
render_loop.set_camera(scene->primary_camera());
};
if (active_scene != nullptr) {
apply_scene(active_scene->name);
}
render_loop.start(config.window.width, config.window.height,
config.window.title.c_str());
auto last = std::chrono::steady_clock::now();
while (!render_loop.should_close()) {
render_loop.poll_events();
if (render_loop.input().is_action_just_pressed("quit")) {
break;
}
const std::string requested_scene = game.take_scene_request();
if (!requested_scene.empty() && requested_scene != game.current_scene_name()) {
apply_scene(requested_scene);
}
const auto now = std::chrono::steady_clock::now();
const float dt = std::chrono::duration<float>(now - last).count();
last = now;
std::shared_ptr<Scene> scene = game.current_scene();
if (scene != nullptr) {
scene->process(dt, render_loop.input());
}
render_loop.input().end_frame();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
render_loop.stop();
return 0;
}
} // namespace boundard