I just made C++ God and it's scary

This commit is contained in:
2023-02-26 21:56:39 -08:00
parent 451b5b067e
commit cd0a5a2155
15 changed files with 221 additions and 96 deletions

View File

@ -1,72 +1,26 @@
# Project Plan
This is my like 4th attempt to get this project off the ground and I'm getting
a little sick of my perfectionism getting in the way, so this is a project that
I am promising to myself will be used to get this thing off the ground, even if
it kills me.
## Issues with the various Dawn Versions
### "Dawn 0"
For lack of a better term it was inexperience when coding with a lower level
language like C and C++, I ran into a lot of problems trying to test and figure
out my coding styles and preferences. The code was fairly solid but not good
enough for a production code.
### "Dawn 1", aka "DawnPlusPlus"
Inexperience with C++ mostly, the engine was really good and adding/using stuff
felt fast and great, but the problems came with trying to use the classes I had
written in a reusable way given the differences between a language like C++ and
C# that I had not yet experienced.
Most learnings were done on how C++ doesn't really have an interface system, I
think I would be better off just writing C-Styled functions in these scenarios.
Additionally event subsystem was likely going to cause me massive issues.
### "Dawn 2" aka "DawnPure"
Really great, loved everything, but when it came to doing component lifecycles
it hit hard walls, particularly when it came to trying to do things that would
normally use classes, namely, UI. UI was a pain to work with and added a tonne
of inefficiencies.
## Focus
- Better usage of arrays, maps, hashmaps, key value pairs, etc.
- Better component lifecycle.
- Better event system.
- WAY better UI system.
These are my main areas of focus, everything else should fall in to place as a
result of these.
## Structure
Let's address each of the pain points *directly* and cut to the chase.
### Better usage of arrays
Main issue before;
```C
int32_t i;
arrayset_t someArraySet;
for(i = 0; i < 100; i++) {
if(someStatement(arraySetGet(&someArraySet, i))) {
// I need to remove from array set, this modifies both the indexes but can
// also affect the literal raw memory in the array set.
}
}
```
Ways to improve
- Use the std::vector and other standard models
- Create clones while iterating, or backstep during iteration
- Defer the removals to "next frame"
### Better Component Lifecycle
TODO: Detail in greater information, but essentially having "entities" be simple
structs, and all of the "other parts" not be related to the entity itself.
### Better Event System
TODO: Come up with one
### Better UI System
Classes should solve this issue, assuming I can use them somewhat sparringly.
# Project update 2023-02-26
Things are going quite smoothly but I feel like I'm a bit stuck where I am now.
There are parts of the engine that actually work really great and a few parts
that do not.
Currently, here are the parts of the engine that I think need a total rework, or
need more work to make good;
- Fonts. Currently they work "ok" but it's difficult to common font things, and
they are acting inconsistent. All fonts should "basically be the same", but
whenever I change fonts the baselines and line heights go WAY off. I also
believe it should be easier to change font colors, including in the MIDDLE of
a word. I also still need to handle things like variable replacement mid-text
so that I can do, say, %playername% or similar.
- Physics. It is brand new and still generally good how it is, but it will for
sure need revisiting at some point.
- State Management. This is more of a conceptual thing but I'd like to make it
easier for states to be influenced by properties, e.g. changing position can
be done with a simple += rather than a get() and set().
- Item Repositories. At the moment Scene items are attached to the scene, and
then components are attached to those, it should be more performant to simply
have each scene item type have a repo, and then map sceneitem ids to those.
That will improve performance of a lot of things, and make the memory
footprint smaller too.
- Using std:: methods more. I've learned a tonne of C++ since I started this
project, I'd love to use std pointers if they weren't a bit annoying, and
also use the std::function to support anonymous functions.

View File

@ -33,6 +33,7 @@ add_subdirectory(physics)
add_subdirectory(prefab)
add_subdirectory(save)
add_subdirectory(scene)
add_subdirectory(state)
add_subdirectory(time)
add_subdirectory(ui)

View File

@ -45,6 +45,10 @@ void SceneItemComponent::onDispose() {
}
void SceneItemComponent::onStateUpdate() {
}
SceneItemComponent::~SceneItemComponent() {
}

View File

@ -7,11 +7,12 @@
#include "dawnlibs.hpp"
#include "display/Transform.hpp"
#include "scene/SceneItem.hpp"
#include "state/State.hpp"
namespace Dawn {
class DawnGame;
class SceneItemComponent {
class SceneItemComponent : public StateOwner {
public:
SceneItem *item;
Transform *transform;
@ -70,6 +71,7 @@ namespace Dawn {
*/
virtual void onDispose();
virtual void onStateUpdate();
/**
* Cleanup the SceneItemComponent.

View File

@ -0,0 +1,11 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
StateOwner.cpp
StateProperty.cpp
)

9
src/dawn/state/State.hpp Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "StateOwner.hpp"
#include "StateProperty.hpp"

View File

@ -0,0 +1,8 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "State.hpp"
using namespace Dawn;

View File

@ -0,0 +1,73 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "assert/assert.hpp"
namespace Dawn {
template<class V>
class StateProperty;
class StateOwner {
private:
std::map<void*, std::vector<std::function<void()>>> effects;
public:
/**
* Invoked event listener for the subclass that listens for all changes
* to any part of this state updated.
*/
virtual void onStateUpdate() = 0;
/**
* Creates a new state property and listens for its change.
*
* @tparam V The type of the state that is held.
* @param initial The initial value of this state.
* @return The state that can then be listened for.
*/
template<class V>
StateProperty<V> useState(V initial) {
auto property = StateProperty<V>();
property.value = initial;
property.owner = this;
return property;
}
/**
* Listen for changees to a state property and involke the provided func
* when the value is changed.
*
* @tparam V The type of the state property.
* @param property Property to listen for affect changees to.
* @param fn The callback to be invoked when the state value changes.
*/
template<class V>
void useEffect(StateProperty<V> &property, const std::function<void()> &fn) {
this->effects[(void*)&property].push_back(fn);
}
/**
* Internal method (that has to be exposed) to listen for changes for when
* a state property that belongs to this state owner is updated.
*
* @tparam V The value type of the state property.
* @param prop The property that has its value changed in question.
* @param n The new, current value of the property.
* @param o The old, previous value of the property.
*/
template<class V>
void onStatePropertyUpdated(StateProperty<V> *prop, V n, V o) {
this->onStateUpdate();
auto eff = &this->effects[prop];
auto itEff = eff->begin();
while(itEff != eff->end()) {
(*itEff)();
++itEff;
}
}
};
}

View File

@ -0,0 +1,8 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "State.hpp"
using namespace Dawn;

View File

@ -0,0 +1,49 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "StateOwner.hpp"
namespace Dawn {
template<class V>
class StateProperty {
private:
StateOwner *owner;
V value;
void setInternal(V val) {
if(val == this->value) return;// TODO: can I omit this? kinda bad tbh.
assertNotNull(this->owner);
auto old = this->value;
this->value = val;
this->owner->onStatePropertyUpdated(this, val, old);
}
public:
const StateProperty& operator += (const V &value) {
this->setInternal(this->value + value);
return *this;
}
const bool_t operator != (const V &value) {
return value != this->value;
}
const bool_t operator == (const V &value) {
return value == this->value;
}
const StateProperty& operator = (const V &val) {
this->setInternal(val);
return *this;
}
operator V() const {
return this->value;
}
friend class StateOwner;
};
}

View File

@ -30,4 +30,5 @@ extern "C" {
#include <memory>
#include <algorithm>
#include <sstream>
#include <string>
#include <string>
#include <functional>

View File

@ -67,16 +67,16 @@ void TicTacToeGame::onSceneUpdate() {
auto t = itTiles->second;
if(t == hovered) {
if(t->state == TIC_TAC_TOE_EMPTY && getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK)) {
t->setState(nextMove);
if(t->tileState == TIC_TAC_TOE_EMPTY && getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK)) {
t->tileState = nextMove;
nextMove = nextMove == TIC_TAC_TOE_NOUGHT ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT;
} else if(t->state == TIC_TAC_TOE_EMPTY) {
} else if(t->tileState == TIC_TAC_TOE_EMPTY) {
t->hovered = true;
}
} else {
t->hovered = false;
}
tileMap[itTiles->second->tile] = itTiles->second->getState();
tileMap[itTiles->second->tile] = itTiles->second->tileState;
++itTiles;
}

View File

@ -12,17 +12,19 @@ TicTacToeTile::TicTacToeTile(SceneItem *item) : SceneItemComponent(item) {
}
void TicTacToeTile::onStart() {
this->setState(TIC_TAC_TOE_EMPTY);
}
tileState = useState(TIC_TAC_TOE_EMPTY);
hovered = useState(false);
enum TicTacToeTileState TicTacToeTile::getState() {
return this->state;
}
auto cb = [&]{
auto sprite = this->item->getComponent<TiledSprite>();
if(this->hovered) {
sprite->setTile(0x03);
} else {
sprite->setTile(tileState);
}
};
void TicTacToeTile::setState(enum TicTacToeTileState state) {
auto ts = this->item->getComponent<TiledSprite>();
ts->setTile(state);
this->state = state;
useEffect(tileState, cb);
useEffect(hovered, cb);
cb();
}

View File

@ -11,16 +11,12 @@
namespace Dawn {
class TicTacToeTile : public SceneItemComponent {
public:
enum TicTacToeTileState state = TIC_TAC_TOE_EMPTY;
bool_t hovered = false;
StateProperty<enum TicTacToeTileState> tileState;
StateProperty<bool_t> hovered;
uint8_t tile;
TicTacToeTile(SceneItem *item);
void onStart() override;
enum TicTacToeTileState getState();
void setState(enum TicTacToeTileState state);
};
}

View File

@ -10,8 +10,11 @@
#include "display/mesh/TriangleMesh.hpp"
#include "components/TicTacToeGame.hpp"
#include "state/State.hpp"
namespace Dawn {
class TicTacToeScene : public Scene {
class TicTacToeScene : public Scene, public StateOwner {
protected:
Camera *camera;
@ -39,6 +42,10 @@ namespace Dawn {
}
}
}
void onStateUpdate() override {
std::cout << "State Update Invoked" << std::endl;
}
std::vector<Asset*> getRequiredAssets() override {
auto assMan = &this->game->assetManager;