I would like to have an elegant solution to an admittedly trivial problem, if possible. I currently have a solution, but it uses If/Else and I feel, in this case, it may be unnecessary. It's not so much that I'm concerned about the cycles, it's more about the principal of writing good code, and I'm unable to think of how to write this a better way.
-
I have a retro game where objects' positions are ints, rather than floats, so they can be rendered at exact pixels (rather than float locations), and, so that two objects narrowly scraping by each other do not collide. (i.e. if I were to use floats, and if the bottom of one object is at 6.98, and the top of another object is at 7.03, there would be a collision, which I don't want.) The distance an object travels per frame is still: m_speed * deltaTime.
I have considered storing the x,y as floats, and then simply casting back to an int before using them in rendering/collision, like:
1 2
|
float m_x;
int GetX() { return (int)(m_x + 0.5f); }
|
(No negative x,y vals are possible in the game, so adding the 0.5f to round it is ok. i.e. 2.66 should round to 3, and adding +0.5f before casting to int accomplishes that.) This would work, but I feel like that's a little sloppy as well-- casting/rounding every single time I need any objects position.
Also, consider an object moving on a track (predefined behavior). At a defined speed, it shall move down 64 pixels until it's in line with a precise tile row, and then start moving right 64 pixels at the same speed. I don't want it to move down "64.353 pixels" and, as it hits the proper row and begins to move right, it still has that extra ".353" on the Y. Instead, ideally, that remainder of ".353" would've carried over to the next frame, as part of the (now) right-heading movement. The cast method wouldn't behave this way.
And what I mean by carry over is that the remainder should build up, which can ultimately determine whether or not I add +1 or subtract -1 to the overall distance.
For example:
If a float-based object in some game is moving at a rate of 3.1pix per frame, then, on the 10th frame, it will have moved 31 total pixels.
In my game, where the positions are ints, it needs to behave as such:
1 2 3
|
Movement amount: +3.1 +3.1 +3.1 +3.1 +3.1 +3.1 +3.1 +3.1 +3.1 +3.1
Remainder: +0.1 +0.2 +0.3 +0.4 +0.5 -0.4 -0.3 -0.2 -0.1 -0.0
Int position: 3 6 9 12 16 19 22 25 28 31
|
Once the remainder goes >= 0.5, I want to say, Alright, we've stored enough remainder to be able to round up +1, so round up one, and minus -1.0 to the saved remainder. The purpose of this is so I'm not "losing" any accuracy/movement due to rounding up/down. (could happen both ways)
What I have works, but I can't help but think that this is a poor implementation of this functionality. I feel as if there's probably a way to do this without conditionals, but I can't think of how.
Here is an Update example:
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
|
int Object::Update(float deltaTime)
{
// Get movement distance
float distance = deltaTime * m_speed; // Float distance
int rounded = (int)(distance + 0.5f); // Round it up/dn
float leftover = -1.0f * (rounded - distance); // Save remainder
int finaldist = rounded; // Set to rounded at first
// Accumulate the remainder
m_remainder += leftover;
// Should we +1/-1 this frame?
if(m_remainder <= -0.5f)
{
m_remainder += 1.0f;
--finaldist;
}
else if(m_remainder >= 0.5f)
{
m_remainder -= 1.0f;
++finaldist;
}
// Iterate movement per pixel
for(int i = 0; i < finaldist; ++i)
{
if(1 == AttemptMove())
return 1;
}
return 0;
}
|
fyi, AttemptMove tries to move, and checks for collisions, which happens every pixel of movement. i.e. if the object was meant to move 5 pixels that frame, it runs that 5 times.
If there really isn't a more efficient/better way to write this, then, this does work, but I'm not as experienced as some people here so if there is a better way, guidance would be appreciated. Thank you