摄像机系统,和子对象绑定

This commit is contained in:
ArchZer0
2026-08-17 16:15:05 +08:00
parent 63d97f058a
commit 7b7756f0fe
20 changed files with 744 additions and 132 deletions
+85 -9
View File
@@ -5,6 +5,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <game.hpp>
#include <mesh.hpp>
@@ -15,14 +16,16 @@ class InputMap;
namespace boundard {
class Actor {
class Actor : public std::enable_shared_from_this<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_;
mutable std::mutex tree_mutex_;
std::weak_ptr<Actor> parent_;
std::vector<std::shared_ptr<Actor>> children_;
float age_ = 0.0F;
bool initialized_ = false;
@@ -43,11 +46,6 @@ class Actor {
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;
@@ -75,6 +73,9 @@ class Actor {
if (init_handler_) {
init_handler_(*this);
}
for (const std::shared_ptr<Actor>& child : children()) {
child->initialize();
}
}
// 类似 Godot 的 _process(delta)。
@@ -83,6 +84,9 @@ class Actor {
if (tick_handler_) {
tick_handler_(*this, dt, input);
}
for (const std::shared_ptr<Actor>& child : children()) {
child->process(dt, input);
}
}
std::array<float, 2> position() const {
@@ -95,6 +99,19 @@ class Actor {
position_ = position;
}
std::array<float, 2> global_position() const {
const std::array<float, 2> local_position = position();
const std::shared_ptr<Actor> parent = this->parent();
if (parent == nullptr) return local_position;
const std::array<float, 2> parent_position = parent->global_position();
const std::array<float, 2> parent_facing = parent->global_facing();
return {parent_position[0] + parent_facing[0] * local_position[0] -
parent_facing[1] * local_position[1],
parent_position[1] + parent_facing[1] * local_position[0] +
parent_facing[0] * local_position[1]};
}
std::array<float, 2> facing() const {
std::lock_guard<std::mutex> lock(transform_mutex_);
return facing_;
@@ -105,8 +122,67 @@ class Actor {
facing_ = facing;
}
const std::shared_ptr<Mesh>& mesh() const { return mesh_; }
void set_mesh(std::shared_ptr<Mesh> mesh) { mesh_ = std::move(mesh); }
std::array<float, 2> global_facing() const {
std::array<float, 2> local_facing = facing();
const std::shared_ptr<Actor> parent = this->parent();
if (parent == nullptr) return local_facing;
const std::array<float, 2> parent_facing = parent->global_facing();
return {parent_facing[0] * local_facing[0] - parent_facing[1] * local_facing[1],
parent_facing[1] * local_facing[0] + parent_facing[0] * local_facing[1]};
}
virtual const std::shared_ptr<Mesh>& mesh() const {
static const std::shared_ptr<Mesh> no_mesh;
return no_mesh;
}
virtual void set_mesh(std::shared_ptr<Mesh>) {}
std::shared_ptr<Actor> parent() const {
std::lock_guard<std::mutex> lock(tree_mutex_);
return parent_.lock();
}
std::vector<std::shared_ptr<Actor>> children() const {
std::lock_guard<std::mutex> lock(tree_mutex_);
return children_;
}
bool add_child(const std::shared_ptr<Actor>& child) {
if (child == nullptr) return false;
const std::shared_ptr<Actor> self = shared_from_this();
if (child == self) return false;
for (std::shared_ptr<Actor> ancestor = self; ancestor != nullptr;
ancestor = ancestor->parent()) {
if (ancestor == child) return false;
}
if (const std::shared_ptr<Actor> old_parent = child->parent()) {
old_parent->remove_child(child);
}
{
std::lock_guard<std::mutex> lock(tree_mutex_);
children_.push_back(child);
}
{
std::lock_guard<std::mutex> lock(child->tree_mutex_);
child->parent_ = self;
}
return true;
}
bool remove_child(const std::shared_ptr<Actor>& child) {
if (child == nullptr) return false;
std::lock_guard<std::mutex> lock(tree_mutex_);
for (auto it = children_.begin(); it != children_.end(); ++it) {
if (*it != child) continue;
children_.erase(it);
std::lock_guard<std::mutex> child_lock(child->tree_mutex_);
child->parent_.reset();
return true;
}
return false;
}
};
} // namespace boundard
+2
View File
@@ -3,6 +3,8 @@
#include <game.hpp>
#include <mesh.hpp>
#include <actor.hpp>
#include <sprite_actor.hpp>
#include <camera.hpp>
#include <behavior.hpp>
#include <render.hpp>
#include <config.hpp>
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <actor.hpp>
namespace boundard {
class Camera : public Actor {
public:
Camera(std::string name, Game& game) : Actor(std::move(name), game) {}
float zoom() const { return zoom_; }
void set_zoom(float zoom) { zoom_ = zoom > 0.0F ? zoom : 0.0001F; }
private:
float zoom_ = 1.0F;
};
} // namespace boundard
+5
View File
@@ -21,11 +21,14 @@ struct MeshConfig {
struct ActorConfig {
std::string name;
std::string type = "actor";
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};
float zoom = 1.0F;
MeshConfig mesh;
std::vector<ActorConfig> children;
};
struct InputBindingConfig {
@@ -38,6 +41,8 @@ struct SceneConfig {
std::string name;
std::string path;
std::vector<ActorConfig> actors;
std::string default_camera;
bool grid_background = false;
};
struct GameConfig {
+27
View File
@@ -44,6 +44,33 @@ class Mesh {
return mesh;
}
static Mesh grid(int half_cells = 12) {
Mesh mesh;
const float world_to_local = 1.0F / 0.35F;
const float half_extent = static_cast<float>(half_cells) * world_to_local;
const float line_half_width = 0.012F * world_to_local;
const std::array<float, 4> line_color{1.0F, 1.0F, 1.0F, 1.0F};
auto add_rect = [&](float left, float bottom, float right, float top) {
const uint32_t base = static_cast<uint32_t>(mesh.vertices.size());
mesh.vertices.insert(mesh.vertices.end(), {
{{left, bottom}, line_color, {0.0F, 0.0F}},
{{right, bottom}, line_color, {1.0F, 0.0F}},
{{right, top}, line_color, {1.0F, 1.0F}},
{{left, top}, line_color, {0.0F, 1.0F}},
});
mesh.indices.insert(mesh.indices.end(),
{base, base + 1, base + 2, base + 2, base + 3, base});
};
for (int cell = -half_cells; cell <= half_cells; ++cell) {
const float coordinate = static_cast<float>(cell) * world_to_local;
add_rect(coordinate - line_half_width, -half_extent, coordinate + line_half_width,
half_extent);
add_rect(-half_extent, coordinate - line_half_width, half_extent,
coordinate + line_half_width);
}
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};
+7
View File
@@ -13,6 +13,10 @@
#include <actor.hpp>
namespace boundard {
class Camera;
}
namespace boundard::render {
struct InputEvent {
@@ -51,6 +55,7 @@ class Renderer {
void init(int width, int height, const char* title);
void draw(const Actor* const* actors, size_t actor_count);
void draw(const Actor* const* actors, size_t actor_count, const Camera* camera);
bool should_close() const;
void poll_events() const;
@@ -119,6 +124,7 @@ class RenderLoop {
void stop();
void set_actors(std::vector<Actor*> actors);
void set_camera(Camera* camera);
void poll_events() const;
bool should_close() const;
@@ -134,6 +140,7 @@ class RenderLoop {
std::atomic<bool> running_{false};
mutable std::mutex actors_mutex_;
std::vector<Actor*> actors_;
Camera* camera_ = nullptr;
};
} // namespace boundard::render
+6 -1
View File
@@ -11,6 +11,7 @@ class InputMap;
namespace boundard {
class Actor;
class Camera;
// 运行时场景:持有场景名和其中的 Actor,并统一驱动 Actor 的 init/tick。
class Scene {
@@ -30,6 +31,9 @@ class Scene {
void set_actors(std::vector<std::shared_ptr<Actor>> actors);
void clear();
void set_default_camera(std::string name);
Camera* primary_camera() const;
const std::vector<Actor*>& actors() const;
std::vector<Actor*>& actors();
@@ -39,7 +43,8 @@ class Scene {
private:
std::string name_;
std::vector<std::shared_ptr<Actor>> actor_storage_;
std::vector<Actor*> actors_;
mutable std::vector<Actor*> actors_;
std::string default_camera_;
};
} // namespace boundard
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <actor.hpp>
namespace boundard {
class SpriteActor : public Actor {
public:
SpriteActor(std::string name, Game& game, std::shared_ptr<Mesh> mesh)
: Actor(std::move(name), game), mesh_(std::move(mesh)) {}
const std::shared_ptr<Mesh>& mesh() const override { return mesh_; }
void set_mesh(std::shared_ptr<Mesh> mesh) override { mesh_ = std::move(mesh); }
private:
std::shared_ptr<Mesh> mesh_;
};
} // namespace boundard