If you expect any library to behave the same way, with the same idioms and conventions and whatnot, then you clearly are off to a bad start - and more.
I think it's fair to point out that I'm here asking when I could have simply done the calculations myself and manually set the position. I've also specifically asked what the recommended method for doing it in SFML is.
Having said that, the mathematics behind this stuff doesn't change, only the API calls and the programmatic techniques used to describe them.
You're somehow contradicting yourself. Also, you seem pretty loose on the critics.
I don't understand the term 'loose on the critics', so I can't really respond to your statement except to say I don't feel like I'm contradicting myself, so perhaps some clarification about what you feel is contradictory would help me explain things.
So with that in mind, I'll try to describe what I'm doing in a little more depth, perhaps it will become clearer.
I describe the tetriminos in terms of:
4 vectors,
1 center,
X number of rotations.
Plus whatever resources the particular library needs for displaying images, etc.
Everything is described in terms of blocks of size 1.
The center is specified because tetrimino's don't rotate about the true center, they rotate about a specific block, so define the center of that block as the center for the tetrimino.
tetriminos also don't simply rotate, they rotate in specific patterns. The I tetrimino, for example, starts sideways, swings down, swings back up, swings down, swings back up. It does not simply rotate all the way around.
So we have an int vector describing the rotations. The 'I' you see is an enum, it's used to load the appropriate resource. And yes, I realize that's a horrible enum name.
Here is the function describing my I tetrimino.
Tetrimino createI() {
sf::Vector2f vecs[] = {
sf::Vector2f(-2.0, 0.0),
sf::Vector2f(-1.0, 0.0),
sf::Vector2f( 0.0, 0.0),
sf::Vector2f( 1.0, 0.0),
};
std::vector<int> rotations;
rotations.push_back(90);
rotations.push_back(-90);
return buildTetrimino(vecs,I,sf::Vector2f(-0.5,0.5),rotations);
}
I'm not going to post the BuildTetrimino function, suffice it to say, it loads up some resources, creates sprites, and hands them to the tetrimino constructor.
Here is the constructor. As you can see, I'm scaling out based upon the blockSize, and then setting the scale for the actual sprite (the textures are 64x64).
Tetrimino::Tetrimino(sf::Sprite inpBlocks[], sf::Vector2f center,std::vector<int> rotations, int blockSize)
: rotationIndex(0),rotations(rotations)
{
this->center.x = center.x*blockSize;
this->center.y = center.y*blockSize;
for(int i = 0; i < 4; i++) {
this->blocks[i] = inpBlocks[i];
sf::Sprite *b = &this->blocks[i];
sf::Vector2f sz(b->getTexture()->getSize());
b->setPosition(blockSize*b->getPosition().x, blockSize*b->getPosition().y);
b->setScale(blockSize/sz.x,blockSize/sz.y);
}
this->translateToTopLeft();
}
And here is the function to rotate.
void Tetrimino::rotateForward() {
if(this->rotations.size() == 0) return;
this->rotationIndex = (this->rotationIndex+1) % this->rotations.size();
int angle = this->rotations[this->rotationIndex];
this->rotateBlocks(angle);
}
I have tried several methods, some of them I've gotten to work, some of them I haven't.
1) I've tried setting the origin of all the blocks to the center of the tetrimino and then just rotate them, it was actually my first solution.
- it didn't work, they did some wierd dance that I couldn't explain. I think the position I'm assigning to them is interacting with the origin in some manner I didn't expect. I'd have to think about it.
- I don't necessarily like this approach due to the origin being defined in local coordinates. It actually works well for this particular application, but in general I don't feel like it's a good approach.
2) I've tried defining a transform for the tetrimino itself and just applying transforms for all moves, rotations, etc, and on draw of the sprites, handing that transform to the call to RenderTarget::draw
- it worked flawlessly.
- I don't necessarily like it. At the end of the day, I need to be able to grab the positions of the blocks, which means using that transform on each block every time, and grabbing the top left vector of the bounding box, or storing the information separately.
3) As you saw, I tried using the transform as defined in 2, to recalculate the top left corner, and grab the new top left corner (of each block). You're right that the center would be more convenient, but I would have expected it to work regardless, and it didn't. The tetrimono *would* rotate properly, but the rotations were not correct. What I mean by that is rotating by 180 degrees instead of 90.
- I actually don't dislike this approach as much as I should. It's a bit hackish, but the recalculation only happens on rotation, the information is stored in the sprite. I can sequester the ugliness to a single function, and everything else in the tetrimino object can treat the blocks as if we're not being ugly and hackish about the rotations. ie, no one else needs to have knowledge that all is not perfect within the world of our blocks.
4) I'm considering doing away with the sprites entirely and just having the tetrimino store the 4 vectors, and then using an approach similar to 2 above to track everything.
- I believe it would work, and most likely the code would be cleaner
- It feels like I'm not taking advantage of tools that are there for me to use. Not using sprites is all well and good, but if I *do* decide to keep using this library, I really need to understand what use cases the sprite class was meant for. Because my use case is pretty simple, but it feels like sprites aren't a good fit, in which case ... what are they useful for (and that goes for the entire Transformable idea).
5) manually doing the calculations. I would be done by now had I taken this approach
- I'm not using the tools in SFML, in which case I'm not learning, so why am I doing it? To write *another* tetris clone?
So there you have it, a brain dump.
also, fyi: this is how I learn new things. I do stupid shit, see the result, and then do more stupid shit, until I start to get a feel for the possibilities. I ask questions, I don't necessarily want a fix to the code, I want answers that help me understand the broader picture.
And then I go off and do more stupid shit.