189 lines
5.6 KiB
C++
189 lines
5.6 KiB
C++
#pragma once
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <game.hpp>
|
|
#include <mesh.hpp>
|
|
|
|
namespace boundard::render {
|
|
class InputMap;
|
|
}
|
|
|
|
namespace boundard {
|
|
|
|
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};
|
|
mutable std::mutex tree_mutex_;
|
|
std::weak_ptr<Actor> parent_;
|
|
std::vector<std::shared_ptr<Actor>> children_;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
for (const std::shared_ptr<Actor>& child : children()) {
|
|
child->initialize();
|
|
}
|
|
}
|
|
|
|
// 类似 Godot 的 _process(delta)。
|
|
void process(float dt, const render::InputMap& input) {
|
|
age_ += dt;
|
|
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 {
|
|
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> 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_;
|
|
}
|
|
|
|
void set_facing(std::array<float, 2> facing) {
|
|
std::lock_guard<std::mutex> lock(transform_mutex_);
|
|
facing_ = facing;
|
|
}
|
|
|
|
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
|