근황과 MIR 1.0¶
1. 1월에 게시한 그래픽스 API 다 쳐 깨부수기 이후,¶
정말 정말 정말로 나는 DX11을 마스터하는 과정 중에 있었다. 네이티브로 FPS를 만드는 무모한 짓을 하고 있었다.
그러다 윈도우즈 개발자로 취업을 하게 됐다! (오예!)
2. 몇달간 일하고 있는 와중에도 아이덴티티를 지키고자, 게임을 만들고 있다.¶
그래서...이걸 만드는 중이다!
3. MIR 1.0 개발 중이다.¶
이번에 업그레이드 될 MIR 1.0은 다음과 같은 특징을 지닌다.
-
완전 배열 컴포넌트
이전에 제작한 MIR 0.1버전은, 뭐랄까...너무 줏대가 없었다. 캐시 친화력을 높이려고 배열을 썼다고 했는데...map을 더 많이 사용하고 있었다. 그래서 이번에는 네임스페이스 기반으로, 컴포넌트의 모든 요소를 id로 접근 가능한 배열로 관리한다.위 구조와 같이, 이제는 모든 컴포넌트가 단순 배열로 되어 있어, ID로 모두 가져올 수 있다.namespace transform{ static inline std::array<sf::Vector2f, MAX_ENTITIES> Positions; static inline std::array<sf::Vector2f, MAX_ENTITIES> Velocities; static inline std::array<sf::Vector2f, MAX_ENTITIES> Scales; static inline std::array<float, MAX_ENTITIES> Rotations; static inline void Clear(){ for(ID id = 1; id < MAX_ENTITIES; ++id){ if(!entity::IsAvailables[id]) continue; Positions[id] = sf::Vector2f(0.f, 0.f); Velocities[id] = sf::Vector2f(0.f, 0.f); Scales[id] = sf::Vector2f(0.f, 0.f); Rotations[id] = 0.f; } } } -
완전 헤더 프레임워크
MIR 1.0은 특수 class를 제외한 모든 기능이 헤더로 제공된다.
헤더로 제공되면서, CMake 빌드에서의 번거로움을 상쇄시킬 수 있었다.
뿐만 아니라 모든 기능들은 헤더에서 inline으로 선언되어 있다.
다만, 이 과정에서 순환 참조를 조심해야 한다.
의도하지 않게 컴파일 에러가 발생할 수 있다. -
더 나아진 이벤트 시스템
이전의 이벤트 시스템은 너무 중구난방이었다. 현재는 이벤트 버스 시스템을 구현하여, 언제든 구독하고 해제할 수 있도록 바꾸었다.namespace{ using SubID = std::size_t; class Base{ public: Base() = default; Base(const Base& other) = default; Base& operator=(const Base& other) = default; Base(Base&& other) noexcept = default; Base& operator=(Base&& other) noexcept = default; virtual ~Base() = default; }; template<typename T> class Listener : public Base{ public: explicit Listener(std::function<void(const T&)> callBack) : callback(std::move(callBack)) {} void Exec(const T& event){ callback(event); } private: std::function<void(const T&)> callback; }; static inline std::unordered_map<std::type_index, std::unordered_map<SubID, std::unique_ptr<Base>>> Listeners; static inline SubID NextID = 0; } - 타입 래퍼
C++의 타입에 익숙하지 않은 개발자들을 위해, 더 직관적인 네이밍으로 타입들을 래핑했다.namespace mir { template<typename T> using List = std::vector<T>; template<typename K, typename V> using Dictionary = std::unordered_map<K, V>; template<typename T> using Queue = std::queue<T>; template<typename T> using Stack = std::stack<T>; template<typename... TArgs> using Action = std::function<void(TArgs...)>; template<typename TReturn, typename... TArgs> using Func = std::function<TReturn(TArgs...)>; template<typename T> using Point2 = sf::Vector2<T>; template<typename T> using Rect = sf::Rect<T>; using Int = std::int32_t; using Uint = std::uint16_t; using Real = float; using Bool = bool; using String = std::string; using Color = sf::Color; using Texture = sf::Texture; using Sprite = sf::Sprite; using Sound = sf::Sound; using Music = sf::Music; using Font = sf::Font; using Text = sf::Text; static constexpr Int I_MAX = std::numeric_limits<Int>::max(); static constexpr Int I_MIN = std::numeric_limits<Int>::min(); static constexpr Uint U_MAX = std::numeric_limits<Uint>::max(); static constexpr Uint U_MIN = std::numeric_limits<Uint>::min(); static constexpr Real R_MAX = std::numeric_limits<Real>::max(); static constexpr Real R_MIN = std::numeric_limits<Real>::min(); }