Partie 21: Objets - Partie 1: Dans l'éditeur
Téléchargements
Stocker les objets
class CMap
{
[...]
std::vector<CObjectStack> mObjectsStacks;
};
On aura besoin de quelques fonctions pour manipuler ces tas (en ajouter un à la liste, en retirer un, trouver si
//---------------------------------------------------------------------------------------------
int CMap::findObjectsStackIndex(CVec2 pos)
{
for (size_t i = 0; i < mObjectsStacks.size(); ++i)
if (mObjectsStacks[i].mPos == pos)
return i;
return -1;
}
//---------------------------------------------------------------------------------------------
CObjectStack* CMap::findObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
return &mObjectsStacks[i];
else
return NULL;
}
//---------------------------------------------------------------------------------------------
CObjectStack* CMap::addObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
{
return &mObjectsStacks[i];
}
else
{
CObjectStack newStack;
newStack.mPos = pos;
mObjectsStacks.push_back(newStack);
return &mObjectsStacks[mObjectsStacks.size() - 1];
}
}
//---------------------------------------------------------------------------------------------
void CMap::removeObjectsStack(CVec2 pos)
{
int i = findObjectsStackIndex(pos);
if (i != -1)
mObjectsStacks.erase(mObjectsStacks.begin() + i);
}
Dans le fichier map, ces tas seront sauvés après les données des cases et des murs:
void CMap::save(char* fileName)
{
FILE* handle = fopen(fileName, "wb");
if (handle != NULL)
{
// entête, version, révision
[...]
// taille de la map
[...]
// données de la map
[...]
// objets
uint32_t numStacks = mObjectsStacks.size();
fwrite(&numStacks, 4, 1, handle);
for (size_t i = 0; i < numStacks; ++i)
mObjectsStacks[i].save(handle);
fclose(handle);
}
}
Bien entendu, le code est similaire pour le chargement.
La classe "tas"
class CObjectStack
{
public:
CObjectStack();
virtual ~CObjectStack();
void addObject(CObject object);
CObject& getObject(int index);
void removeObject(int index);
size_t getSize();
CObjectStack& operator=(const CObjectStack& rhs);
void load(FILE* handle);
void save(FILE* handle);
CVec2 mPos;
private:
std::vector<CObject> mObjects;
};
Elle stocke sa position dans la map (mPos) et une liste de ses objets (mObjects).
//---------------------------------------------------------------------------------------------
void CObjectStack::addObject(CObject object)
{
mObjects.push_back(object);
}
//---------------------------------------------------------------------------------------------
CObject& CObjectStack::getObject(int index)
{
return mObjects[index];
}
//---------------------------------------------------------------------------------------------
void CObjectStack::removeObject(int index)
{
mObjects.erase(mObjects.begin() + index);
}
//---------------------------------------------------------------------------------------------
size_t CObjectStack::getSize()
{
return mObjects.size();
}
//---------------------------------------------------------------------------------------------
CObjectStack& CObjectStack::operator=(const CObjectStack& rhs)
{
mPos = rhs.mPos;
mObjects.resize(rhs.mObjects.size());
for (size_t i = 0; i < mObjects.size(); ++i)
mObjects[i] = rhs.mObjects[i];
return *this;
}
//---------------------------------------------------------------------------------------------
void CObjectStack::load(FILE* handle)
{
mObjects.clear();
fread(&mPos.x, sizeof(int32_t), 1, handle);
fread(&mPos.y, sizeof(int32_t), 1, handle);
uint32_t size;
fread(&size, sizeof(uint32_t), 1, handle);
for (uint32_t i = 0; i < size; ++i)
{
CObject object;
object.load(handle);
mObjects.push_back(object);
}
}
//---------------------------------------------------------------------------------------------
void CObjectStack::save(FILE* handle)
{
fwrite(&mPos.x, sizeof(int32_t), 1, handle);
fwrite(&mPos.y, sizeof(int32_t), 1, handle);
uint32_t size = mObjects.size();
fwrite(&size, sizeof(uint32_t), 1, handle);
for (size_t i = 0; i < mObjects.size(); ++i)
mObjects[i].save(handle);
}
La classe objet
class CObject
{
public:
CObject();
virtual ~CObject();
void setType(uint8_t type);
uint8_t getType();
CObject& operator=(const CObject& rhs);
void load(FILE* handle);
void save(FILE* handle);
private:
uint8_t mType;
};
Il contient seulement un type, mais j'en ai fait une classe qui ressemble à celles pour les cases et les murs,
//---------------------------------------------------------------------------------------------
CObject::CObject()
{
mType = 0;
}
//---------------------------------------------------------------------------------------------
CObject::~CObject()
{
}
//---------------------------------------------------------------------------------------------
void CObject::setType(uint8_t type)
{
mType = type;
}
//---------------------------------------------------------------------------------------------
uint8_t CObject::getType()
{
return mType;
}
//---------------------------------------------------------------------------------------------
CObject& CObject::operator=(const CObject& rhs)
{
setType(rhs.mType);
return *this;
}
//---------------------------------------------------------------------------------------------
void CObject::load(FILE* handle)
{
uint8_t type;
fread(&type, 1, 1, handle);
setType(type);
}
//---------------------------------------------------------------------------------------------
void CObject::save(FILE* handle)
{
fwrite(&mType, 1, 1, handle);
}
Afficher les tas dans l'éditeur
QImage CEditor::displayMap(CMap& map, CVec2 mousePos, ETabIndex tabIndex)
{
[...]
if (map.isLoaded() == true)
{
// dessine les cases
[...]
// dessine les murs
[...]
// dessine les objets
for (int y = 0; y < map.mSize.y * 2; ++y)
for (int x = 0; x < map.mSize.x * 2; ++x)
{
CVec2 pos(x, y);
if (map.findObjectsStack(pos) != NULL)
{
QImage objImage = fileCache.getImage("tiles/Object.png");
CVec2 pos2 = pos * (TILE_SIZE / 2);
graph2D.drawImage(&image, pos2, objImage);
}
}
}
[...]
}
On boucle simplement sur toutes les positions possibles de la map, et on affiche un point si on a trouvé un tas à
La liste d'objets
<?xml version="1.0" encoding="utf-8"?>
<items>
<item name="[Aucun]"/>
<item name="Oeil du Temps"/>
<item name="Rond d'Eclairs"/>
<item name="Torche"/>
[...]
</items>
Le jeu utilisera une base de données différente, comme certains noms d'objets diffèrent de ceux qu'on utilise dans
L'outil addObject
addObjectTool::addObjectTool(QUndoCommand *parent)
: QUndoCommand(parent)
{
}
bool addObjectTool::init(CVec2 pos, int type)
{
mPos = pos / (TILE_SIZE / 2);
mType = type;
return true;
}
void addObjectTool::undo()
{
CObjectStack* stack = map.findObjectsStack(mPos);
if (stack != NULL)
{
size_t size = stack->getSize();
stack->removeObject(size - 1);
if (size == 1)
map.removeObjectsStack(mPos);
}
bridge.updateQMLImage();
}
void addObjectTool::redo()
{
CObject newObject;
newObject.setType(mType);
CObjectStack* stack = map.addObjectsStack(mPos);
stack->addObject(newObject);
bridge.updateQMLImage();
}
Notez que dans la fonction undo() on teste si le tas est vide pour le retirer complètement de la liste des tas
Selection d'un tas
void CEditor::addObjParamsItems()
{
CVec2 pos = mSelectStart / (TILE_SIZE / 2);
CObjectStack* stack = map.findObjectsStack(pos);
size_t size = 0;
if (stack != NULL)
{
for (size_t i = 0; i < stack->getSize(); ++i)
{
addButton(objSelGrid, "effacer", &mObjParamItems, i);
addComboBox(objSelGrid, bridge.itemsList, &mObjParamItems, i, stack->getObject(i).getType());
}
size = stack->getSize();
}
addButton(objSelGrid, "ajouter", &mObjParamItems, size);
}
Finalement, j'ai aussi du faire beaucoup de changements à "clipboard.cpp" et dans les outils couper/coller pour