208 lines
6.7 KiB
C++
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
|