當初在寫完ECS之後測試完一直覺得怪怪的,對自己不是很自信,總感覺自己ECS寫壞了寫爛了,所以為了測試我寫的是不是對的,就去稍微聊解一下這個lib。
當然我沒有讀它的code
,畢竟有點太多,所以我只打算拿它來測試。
事實上EnTT
不只有ECS
的功能,作者寫完ECS
覺得不夠就加了許多功能近去,但這裏我們只測試它的ECS
。
它的ECS
當然比我們自己寫的功能要多,但我只使用了裡面的幾個,只要能達到成千上萬個沙奈朵掉下來就好。
這邊當初搞了我好一段時間,但其實只要把它當成你project
裡的submodule
就可以了。
或著把它的entt
資料夾直接複製一分到你的專案李。
submodule
弄好之後只要一個簡單的include
就好。
#include "entt/entt.hpp"
一樣的3個Component。
struct Transform {
float x_;
float y_;
};
struct RigidBody {
float v_;
};
struct Graphic {
Gardevoir *gardevoir;
sf::Texture texture_;
sf::Sprite sprite_;
};
這邊我會比較EnTT
與我們自己寫的用法小差異
entt::registry registry;
Entity
for (auto i = 0; i < MAX_ENTITES; ++i)
{
auto entity = registry.create();
registry.emplace<Transform>(entity, randPositionX(generator), randPositionY(generator));
registry.emplace<Graphic>(entity, gardevoir);
registry.emplace<RigidBody>(entity, 10.0f);
}
這樣就結束了。
回想一下我們寫的,我們要先進行註冊對吧。
當然或許它的emplace
裡有實現註冊的方法,不過我們不知道。
接這我們來寫系統吧。
一樣我們只有兩個系統,當我寫完時我才發現,根本87%像。
class PhysicSystem {
public:
PhysicSystem() = default;
void update(entt::registry& registry, float dt);
};
class GraphicSystem {
public:
GraphicSystem() = default;
void update(entt::registry& registry);
void setSprite(entt::registry& registry);
void render(entt::registry& registry, sf::RenderTarget& targer);
};
void PhysicSystem::update(entt::registry& registry, float dt) {
auto view = registry.view<Transform, RigidBody>();
for(auto entity: view) {
auto& transform = view.get<Transform>(entity);
auto& rigidBody = view.get<RigidBody>(entity);
transform.y_ += rigidBody.v_ * dt;
rigidBody.v_ += 10 * dt;
}
}
void GraphicSystem::update(entt::registry& registry) {
auto view = registry.view<Transform, Graphic>();
for (auto entity: view) {
auto& transform = view.get<Transform>(entity);
auto& graphic = view.get<Graphic>(entity);
graphic.sprite_.setPosition(transform.x_, transform.y_);
}
}
void GraphicSystem::setSprite(entt::registry& registry) {
auto view = registry.view<Graphic>();
for (auto entity: view) {
auto& graphic = view.get<Graphic>(entity);
graphic.sprite_.setTexture(graphic.gardevoir->texture_);
graphic.sprite_.setScale(0.1, 0.1);
}
}
void GraphicSystem::render(entt::registry& registry, sf::RenderTarget& target) {
auto view = registry.view<Graphic>();
for(auto entity : view) {
auto& graphic = view.get<Graphic>(entity);
target.draw(graphic.sprite_);
}
}
比較一下update
的地方
這是我們的
void PhysicSystem::update(float dt) {
for(auto const& entity : Entities_) {
auto& transform = ecs.getComponent<Transform>(entity);
auto& rigidBody = ecs.getComponent<RigidBody>(entity);
transform.y_ += rigidBody.v_ * dt;
rigidBody.v_ += 10 * dt;
}
}
這是EnTT
void PhysicSystem::update(entt::registry& registry, float dt) {
auto view = registry.view<Transform, RigidBody>();
for(auto entity: view) {
auto& transform = view.get<Transform>(entity);
auto& rigidBody = view.get<RigidBody>(entity);
transform.y_ += rigidBody.v_ * dt;
rigidBody.v_ += 10 * dt;
}
}
迴圈裡做的事是一模一樣的,根據Entity ID
拿到相對應的Component
,然後進行更新。
而Entt
的view
方法就只是在篩選所要的Entity
而已,而我們不需要這樣做是因為我們在為Entity
加入Component
的時候,就已經利用Signature
幫System
篩選它所要的Entity
。
所以用Entt
能夠在你初始化的時候少寫很多東西,用起來真的很方便!
接著我們的遊戲循環想當然的一模一樣囉
while(running) {
auto startTime = std::chrono::high_resolution_clock::now();
sf::Event event;
while(window.pollEvent(event)) {
if(event.type == sf::Event::Closed)
running = false;
}
graphicSystem.update(registry);
physicSystem.update(registry, dt);
window.clear(bgColor);
graphicSystem.render(registry, window);
window.display();
auto endTime = std::chrono::high_resolution_clock::now();
dt = std::chrono::duration<float, std::chrono::seconds::period>(endTime - startTime).count();
std::cout << dt << "\n";
}
我們會發現使用起來跟自己的大同小異,但實際上Entt
還有許多非常強大的功能,所以之後使用ECS的功能應該就用Entt
這個lib
。