1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| template <typename C>
class ComponentHolder{
/* on doit pouvoir représenter un "index invalide"
* en utilisant la valeur maximale représentable par un size_t, on est sur
* que l'index représenté ne pourra JAMAIS être atteint
*/
constexpr size_t invalidIndex{std::numeric_limits<size_t>::max()};
public:
/* un alias de type, plus facile pour la représentation des composants */
using ComponentType = Component<C>;
/* des alias de type sur les itérateur pour les composants */
using iterator = typename std::vector<ComponentType>::iterator;
usign const_iterator = typname std::vector<ComponentType>::const_iterator;
/* On veut pouvoir
* 1- ajouter un composant à une entité particulière (pour autant qu'il n'existe pas)
* 2- supprimer le composant pour une entité particulière (pour autant qu'il existe)
* 3- parcourir tous les composants qui existent, indépendamment de l'entité à laquelle ils sont assignés (en lecture et en écriture)
* 4- pouvoir accéder (en lecture et en écriture) au composant spécifiquement assigné à une entité particulière (pour autant qu'il existe)
* 5- éventuellement, on peux souhaiter savoir s'il existe un composant pour une entité particulière (quand on travaille sur un seul type de composant
* cela peut s'avérer "plus facile" que de commencer à travailler avec le "pass-key" dont j'ai parlé plus haut
*/
ComponentHolder(){
indexes_.resize(0xFFFF, invalidIndex); // je débute directement avec 65000 et quelques indexes, qui sont tous invalides ;)
/* NOTA: Je pourrais aussi réserver de l'espace pour les composants.
* Mais, dans l'ensemble, la politique d'augmentation de capacité de std::vector
* nous permettra de n'avoir "pas trop à nous en faire" sur ce point
*/
}
/* "le plus simple" : les fonctions begin() et end() (en version constante et non constante) */
iterator begin(){
return components_.begin();
}
iterator end(){
return components_.end();
}
const_iterator begin() const{
return components_.begin();
}
const_iterator end() const{
return components_.end();
}
/* ajouter un composant
*
* précondition : l'entité indiquée ne doit pas encore disposer du composant
*
* mode de fonctionnement :
* 1- j'ajoute le composant au premier tableau
* 2- je met à jour l'index dans le deuxième tableau
*/
void add(ComponentType const & comp){
assert(indexes_.size() < comp.id && "id too hight");
assert(indexes_[comp.id] == invalidIndex && "entity already has such component");
components_.push_back(comp);
indexes_[comp.id]= components_.size()-1; // on est dans un système "premier index à 0"
}
/* supprimer un composant
*
* précondition : l'entité indiquée doit disposer du composant
*
* mode de fonctionnement :
* 1- je supprime le composant au premier tableau
* 2- je met à jour l'index dans le deuxième tableau (en lui donnant la valeur invalidIndex)
*/
void remove(size_t entity){
assert(indexes_[comp.id] != invalidIndex && "entity already has such component");
/* une suppression peut s'effectuer en temps constant dans un tableau si on ne tiens "pas trop"
* à garder les élément dans un ordre particulier.
*
* Il suffit en effet d'inverser l'élément que l'on veut supprimer avec le dernier élément valide
* avant de mettre la taille du tableau à jour
*
* mais, pour cela, on a besoin de l'index (dans le premier tableau) de l'élément à supprimer
*/
auto index = indexOf(entity);
auto lastValid = indexes_.size()-1;
std::swap(components_[index], lastValid());
components_.resize(lastValid); // le redimentionnement à une taille inférieure ne provoque pas
//la réallocation de la mémoire du tableau
/* on met l'index à jour */
indexes[entity]= invalidIndex;
}
/* accéder (en lecture et en écriture) au composant spécifiquement assigné à une entité particulière
*
* précondition : l'entité indiquée ne doit pas encore disposer du composant
*/
ComponentType & get(size_t entity ){
assert(indexes_[entity] != invalidIndex && "Entity doesn't have such component");
return components_[indexOf(entity)];
}
ComponentType const & get(size_t entity) const{
return const_cast<ComponentHolder &>(this).get(entity);
}
/* savoir si un composant est associé à une entité particulière */
bool exists(size_t entity) const{
return indexOf(entity)!=invalidIndex;
}
private:
/* En interne, on doit pouvoir récupérer l'index (dans le premier tableau) d'une entité bien précise
*
* précondition : la taille de l'index doit être plus grande que l'identifiant de l'entité indiquée
*/
size_t indexOf(size_t entity) const{
assert(indexes_.size() < comp.id && "id too hight");
return indexes_[entity];
}
/* le tableau de composants */
std::vector<ComponentType> components_;
/* le tableau d'indexes */
std::vector<size_t> indexes_;
}; |
Partager