aa
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <game.hpp>
|
||||
#include <mesh.hpp>
|
||||
|
||||
namespace openceg::render {
|
||||
class InputMap;
|
||||
}
|
||||
|
||||
namespace openceg {
|
||||
|
||||
class Actor {
|
||||
private:
|
||||
size_t id;
|
||||
std::string nickName_;
|
||||
mutable std::mutex transform_mutex_;
|
||||
std::array<float, 2> position_{0.0F, 0.0F};
|
||||
std::array<float, 2> facing_{1.0F, 0.0F};
|
||||
std::shared_ptr<Mesh> mesh_;
|
||||
|
||||
float age_ = 0.0F;
|
||||
bool initialized_ = false;
|
||||
std::function<void(Actor&)> init_handler_;
|
||||
std::function<void(Actor&, float, const render::InputMap&)> tick_handler_;
|
||||
|
||||
public:
|
||||
Actor() noexcept = default;
|
||||
virtual ~Actor() = default;
|
||||
|
||||
Actor(const Actor&) = delete;
|
||||
Actor& operator=(const Actor&) = delete;
|
||||
Actor(Actor&&) noexcept = delete;
|
||||
Actor& operator=(Actor&&) noexcept = delete;
|
||||
|
||||
Actor(std::string nickName, Game& game) {
|
||||
id = game.new_id();
|
||||
nickName_ = std::move(nickName);
|
||||
}
|
||||
|
||||
Actor(std::string nickName, Game& game, std::shared_ptr<Mesh> mesh)
|
||||
: Actor(std::move(nickName), game) {
|
||||
mesh_ = std::move(mesh);
|
||||
}
|
||||
|
||||
static Actor& getInstance() {
|
||||
static Actor instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
const std::string& name() const { return nickName_; }
|
||||
|
||||
float age() const { return age_; }
|
||||
|
||||
void set_init_handler(std::function<void(Actor&)> handler) {
|
||||
init_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
void set_tick_handler(
|
||||
std::function<void(Actor&, float, const render::InputMap&)> handler) {
|
||||
tick_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
// 类似 Godot 的 _ready()。
|
||||
void initialize() {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
if (init_handler_) {
|
||||
init_handler_(*this);
|
||||
}
|
||||
}
|
||||
|
||||
// 类似 Godot 的 _process(delta)。
|
||||
void process(float dt, const render::InputMap& input) {
|
||||
age_ += dt;
|
||||
if (tick_handler_) {
|
||||
tick_handler_(*this, dt, input);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<float, 2> position() const {
|
||||
std::lock_guard<std::mutex> lock(transform_mutex_);
|
||||
return position_;
|
||||
}
|
||||
|
||||
void set_position(std::array<float, 2> position) {
|
||||
std::lock_guard<std::mutex> lock(transform_mutex_);
|
||||
position_ = position;
|
||||
}
|
||||
|
||||
std::array<float, 2> facing() const {
|
||||
std::lock_guard<std::mutex> lock(transform_mutex_);
|
||||
return facing_;
|
||||
}
|
||||
|
||||
void set_facing(std::array<float, 2> facing) {
|
||||
std::lock_guard<std::mutex> lock(transform_mutex_);
|
||||
facing_ = facing;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Mesh>& mesh() const { return mesh_; }
|
||||
void set_mesh(std::shared_ptr<Mesh> mesh) { mesh_ = std::move(mesh); }
|
||||
};
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <actor.hpp>
|
||||
|
||||
namespace openceg::render {
|
||||
class InputMap;
|
||||
}
|
||||
|
||||
namespace openceg {
|
||||
|
||||
using ActorInitHandler = std::function<void(Actor&)>;
|
||||
using ActorTickHandler = std::function<void(Actor&, float, const render::InputMap&)>;
|
||||
|
||||
// 全局行为函数注册表。config.ceg 中的 init/tick 名字在这里解析为 C++ 处理函数。
|
||||
class BehaviorRegistry {
|
||||
public:
|
||||
static BehaviorRegistry& getInstance() {
|
||||
static BehaviorRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
BehaviorRegistry(const BehaviorRegistry&) = delete;
|
||||
BehaviorRegistry& operator=(const BehaviorRegistry&) = delete;
|
||||
|
||||
void register_init(const std::string& name, ActorInitHandler handler) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
init_handlers_[name] = std::move(handler);
|
||||
}
|
||||
|
||||
void register_tick(const std::string& name, ActorTickHandler handler) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
tick_handlers_[name] = std::move(handler);
|
||||
}
|
||||
|
||||
ActorInitHandler init(const std::string& name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
const auto it = init_handlers_.find(name);
|
||||
if (it == init_handlers_.end()) {
|
||||
return {};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ActorTickHandler tick(const std::string& name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
const auto it = tick_handlers_.find(name);
|
||||
if (it == tick_handlers_.end()) {
|
||||
return {};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
BehaviorRegistry() = default;
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, ActorInitHandler> init_handlers_;
|
||||
std::unordered_map<std::string, ActorTickHandler> tick_handlers_;
|
||||
};
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openceg {
|
||||
|
||||
struct WindowConfig {
|
||||
int width = 800;
|
||||
int height = 600;
|
||||
std::string title = "OpenCGE";
|
||||
};
|
||||
|
||||
struct MeshConfig {
|
||||
std::string shape = "quad";
|
||||
std::array<float, 4> color{0.5F, 0.5F, 0.5F, 1.0F};
|
||||
};
|
||||
|
||||
struct ActorConfig {
|
||||
std::string name;
|
||||
std::string init_function;
|
||||
std::string tick_function;
|
||||
std::array<float, 2> position{0.0F, 0.0F};
|
||||
std::array<float, 2> facing{1.0F, 0.0F};
|
||||
MeshConfig mesh;
|
||||
};
|
||||
|
||||
struct InputBindingConfig {
|
||||
std::string action;
|
||||
std::vector<int> keys;
|
||||
std::vector<int> mouse_buttons;
|
||||
};
|
||||
|
||||
struct GameConfig {
|
||||
WindowConfig window;
|
||||
std::vector<InputBindingConfig> input_bindings;
|
||||
std::vector<ActorConfig> actors;
|
||||
};
|
||||
|
||||
// 从 JSON 加载游戏场景配置(类似 Godot 的场景描述)。
|
||||
class ConfigLoader {
|
||||
public:
|
||||
bool load(const std::string& path);
|
||||
const GameConfig& config() const;
|
||||
const std::string& last_error() const;
|
||||
|
||||
private:
|
||||
GameConfig config_;
|
||||
std::string last_error_;
|
||||
};
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace openceg {
|
||||
|
||||
// 通用引擎入口:读取 config.ceg,实例化场景并驱动 Actor 的 init/tick。
|
||||
// 具体游戏行为通过 BehaviorRegistry 注册,因此引擎本身不内置任何游戏逻辑。
|
||||
int run_engine(int argc, char** argv, const char* default_config_path = "config.ceg");
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
namespace openceg {
|
||||
class Game {
|
||||
private:
|
||||
size_t id_cnt;
|
||||
|
||||
public:
|
||||
Game();
|
||||
~Game();
|
||||
Game(const Game&) = delete;
|
||||
Game& operator=(const Game&) = delete;
|
||||
Game(Game&&) noexcept = delete;
|
||||
Game& operator=(Game&&) noexcept = delete;
|
||||
|
||||
static Game& getInstance();
|
||||
void launch();
|
||||
size_t new_id();
|
||||
};
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openceg {
|
||||
|
||||
// 渲染组件:描述 Actor 的几何形状、颜色和纹理占位。
|
||||
class Mesh {
|
||||
public:
|
||||
struct Vertex {
|
||||
std::array<float, 2> position;
|
||||
std::array<float, 4> color;
|
||||
};
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
std::string texture; // 纹理路径占位,暂未采样。
|
||||
|
||||
std::array<float, 4> color() const {
|
||||
std::lock_guard<std::mutex> lock(*color_mutex_);
|
||||
return color_;
|
||||
}
|
||||
|
||||
void set_color(std::array<float, 4> color) {
|
||||
std::lock_guard<std::mutex> lock(*color_mutex_);
|
||||
color_ = color;
|
||||
}
|
||||
|
||||
static Mesh quad() {
|
||||
Mesh mesh;
|
||||
mesh.vertices = {
|
||||
{{-1.0F, -1.0F}, {1.0F, 1.0F, 1.0F, 1.0F}},
|
||||
{{1.0F, -1.0F}, {1.0F, 1.0F, 1.0F, 1.0F}},
|
||||
{{1.0F, 1.0F}, {1.0F, 1.0F, 1.0F, 1.0F}},
|
||||
{{-1.0F, 1.0F}, {1.0F, 1.0F, 1.0F, 1.0F}},
|
||||
};
|
||||
mesh.indices = {0, 1, 2, 2, 3, 0};
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::mutex> color_mutex_ = std::make_unique<std::mutex>();
|
||||
std::array<float, 4> color_{1.0F, 1.0F, 1.0F, 1.0F};
|
||||
};
|
||||
|
||||
} // namespace openceg
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <game.hpp>
|
||||
#include <mesh.hpp>
|
||||
#include <actor.hpp>
|
||||
#include <behavior.hpp>
|
||||
#include <render.hpp>
|
||||
#include <config.hpp>
|
||||
#include <engine.hpp>
|
||||
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <actor.hpp>
|
||||
|
||||
namespace openceg::render {
|
||||
|
||||
struct InputEvent {
|
||||
enum class Type {
|
||||
Key,
|
||||
MouseButton,
|
||||
CursorMove,
|
||||
Scroll,
|
||||
};
|
||||
|
||||
enum class Action {
|
||||
Release = 0,
|
||||
Press = 1,
|
||||
Repeat = 2,
|
||||
};
|
||||
|
||||
Type type = Type::Key;
|
||||
Action action = Action::Press;
|
||||
int key = -1;
|
||||
int scancode = -1;
|
||||
int mods = 0;
|
||||
int button = -1;
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
};
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer();
|
||||
~Renderer();
|
||||
|
||||
Renderer(const Renderer&) = delete;
|
||||
Renderer& operator=(const Renderer&) = delete;
|
||||
Renderer(Renderer&&) = delete;
|
||||
Renderer& operator=(Renderer&&) = delete;
|
||||
|
||||
void init(int width, int height, const char* title);
|
||||
void draw(const Actor* const* actors, size_t actor_count);
|
||||
bool should_close() const;
|
||||
void poll_events() const;
|
||||
|
||||
using InputCallback = std::function<void(const InputEvent&)>;
|
||||
void set_input_callback(InputCallback callback);
|
||||
|
||||
bool is_key_down(int key) const;
|
||||
bool is_mouse_button_down(int button) const;
|
||||
std::array<double, 2> cursor_position() const;
|
||||
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
Impl* impl_;
|
||||
};
|
||||
|
||||
// 类似 Godot InputMap 的按键动作绑定。
|
||||
class InputMap {
|
||||
public:
|
||||
void add_action(const std::string& action);
|
||||
void bind_key(const std::string& action, int key);
|
||||
void unbind_key(const std::string& action, int key);
|
||||
void bind_mouse_button(const std::string& action, int button);
|
||||
void unbind_mouse_button(const std::string& action, int button);
|
||||
|
||||
void handle_event(const InputEvent& event);
|
||||
void end_frame();
|
||||
|
||||
bool is_action_pressed(const std::string& action) const;
|
||||
bool is_action_just_pressed(const std::string& action) const;
|
||||
bool is_action_just_released(const std::string& action) const;
|
||||
float get_action_strength(const std::string& action) const;
|
||||
float get_axis(const std::string& negative_action,
|
||||
const std::string& positive_action) const;
|
||||
|
||||
private:
|
||||
struct ActionState {
|
||||
std::unordered_set<int> keys;
|
||||
std::unordered_set<int> mouse_buttons;
|
||||
std::unordered_map<int, bool> held_keys;
|
||||
std::unordered_map<int, bool> held_buttons;
|
||||
size_t held_key_count = 0;
|
||||
size_t held_button_count = 0;
|
||||
bool pressed = false;
|
||||
bool just_pressed = false;
|
||||
bool just_released = false;
|
||||
};
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, ActionState> actions_;
|
||||
};
|
||||
|
||||
// 渲染线程包装:主线程更新 Actor,渲染线程持续读取并提交绘制。
|
||||
class RenderLoop {
|
||||
public:
|
||||
RenderLoop() = default;
|
||||
~RenderLoop();
|
||||
|
||||
RenderLoop(const RenderLoop&) = delete;
|
||||
RenderLoop& operator=(const RenderLoop&) = delete;
|
||||
RenderLoop(RenderLoop&&) = delete;
|
||||
RenderLoop& operator=(RenderLoop&&) = delete;
|
||||
|
||||
void start(int width, int height, const char* title);
|
||||
void stop();
|
||||
|
||||
void set_actors(std::vector<Actor*> actors);
|
||||
void poll_events() const;
|
||||
bool should_close() const;
|
||||
|
||||
InputMap& input();
|
||||
const InputMap& input() const;
|
||||
|
||||
private:
|
||||
void run();
|
||||
|
||||
Renderer renderer_;
|
||||
InputMap input_map_;
|
||||
std::thread thread_;
|
||||
std::atomic<bool> running_{false};
|
||||
mutable std::mutex actors_mutex_;
|
||||
std::vector<Actor*> actors_;
|
||||
};
|
||||
|
||||
} // namespace openceg::render
|
||||
Reference in New Issue
Block a user