Hello,
I am using vectors for my Sprite class. I was wondering how to implement sorting so that I could order my sprites using ZOrder attribute that I have given it?
| 1 | class CSprite |
| 2 | { |
| 3 | protected: |
| 4 | // Attributes |
| 5 | int m_AnimIndex; |
| 6 | int m_AnimCount; |
| 7 | |
| 8 | float revPerSec; |
| 9 | float theta; |
| 10 | |
| 11 | float m_x; |
| 12 | float m_y; |
| 13 | float m_vx; |
| 14 | float m_vy; |
| 15 | int m_dx; |
| 16 | int m_dy; |
| 17 | int m_Active; |
| 18 | int m_ZOrder; |
| 19 | |
| 20 | int m_PrevAnimation; |
| 21 | vector<CAnimation*> m_Animations; |
| 22 | |
| 23 | public: |
| 24 | // Constructor |
| 25 | CSprite(); |
| 26 | // Destructor |
| 27 | virtual ~CSprite(); |
| 28 | |
| 29 | // Methodes |
| 30 | void Add(CAnimation *ptrAnimation); |
| 31 | virtual void Input(); |
| 32 | virtual void Render(BITMAP *buffer); |
| 33 | virtual void Update(float dt); |
| 34 | virtual void ChangeAnimation(int _id); |
| 35 | virtual void ChangeAnimation(string _name); |
| 36 | void SetPosition(int x, int y) {m_x = x; m_y = y;} |
| 37 | void SetDirection(int x, int y) {m_dx = x; m_dy = y;} |
| 38 | void SetVelocity(float x, float y) {m_vx = x; m_vy = y;} |
| 39 | void SetZOrder(int z) {m_ZOrder = z;} |
| 40 | bool Collision(CSprite *spr1, CSprite *spr2); |
| 41 | // Helpers |
| 42 | }; |
Thanks,
Mariusz
Overload the < operator on that class. Then you can use STL's regular sort algorithm.
Mariuz:
overload with this:
bool operator<(const CSprite &a, const CSprite &b) { return a.m_ZOrder < b.m_ZOrder; }
and sort with this:
vector<CSprite> sprites; sort(sprites.begin(), sprites.end());
it might be squirly because m_ZOrder is protected.
-----
I was about to post a very similar question, but more about the nature of overloading:
this is my code that does not compile:
| 1 | class time_stamp_object |
| 2 | { |
| 3 | public: |
| 4 | unsigned int min; |
| 5 | unsigned int sec; |
| 6 | unsigned int hun_sec; |
| 7 | |
| 8 | int get_length() |
| 9 | { |
| 10 | int num = hun_sec; |
| 11 | num += sec*100; |
| 12 | num += min*60*100; |
| 13 | return num; |
| 14 | } |
| 15 | }; |
| 16 | |
| 17 | bool operator<(const time_stamp_object &a, const time_stamp_object &b) |
| 18 | { |
| 19 | return a.get_length() < b.get_length(); |
| 20 | } |
for some reason it doesn't like that I'm using a function (get_laugh()) to compare the two objects, and I get the errors:
main.cpp: In function `bool operator<(const time_stamp_object&, const time_stamp_object&)': main.cpp:108: error: passing `const time_stamp_object' as `this' argument of `int time_stamp_object::get_length()' discards qualifiers main.cpp:108: error: passing `const time_stamp_object' as `this' argument of `int time_stamp_object::get_length()' discards qualifiers
what am I doing wrong?
Mark: get_length needs to be declared const, otherwise the compiler is unsure if get_length will modify the object, therefore possibly violating the const in operator<'s parameters.
Thanks for your quick response. Well it has been ages since I have worked with operators. It apears that I have to put this outside my CSprite class, which then like you said my m_ZOrder would not be accessible, unless I make it public. Is there another way to make this operator part of my class?
Right now If I make < part of CSprite I get:
error C2804: binary 'operator <' has too many parameters
Thanks,
Mariusz
Because it should only be one parameter, and you'd do
this->m_ZOrder < other_thing.m_ZOrder
I'm not sure if it'll still complain about protected, though. If it does you'll need to create a function that returns m_ZOrder, or just move m_ZOrder to public.
EDIT:
bool operator<(const CSprite &b) { return this->m_ZOrder < b.m_ZOrder; }
William: I tried adding const in front of the function but didn't work ... ??
Thats right now I remember. Thanks a lot
You don't need to overload anything if you don't want to. You can just write something like:
struct sprite_sorter { bool operator()(const CSprite &a, const CSprite &b) { return a.m_ZOrder < b.m_ZOrder; } }; /* And then */ std::sort(sprites.begin(), sprites.end(), sprite_sorter);
Thats cool, but I have just implemented William's suggestion and it seems that everything is working just fine. Anyhow, I really appreciate your input, it helps me to refresh my rusty C/C++ stuff.
All the best,
Mariusz
-----------------------------------------------------------------------------------
Few minutes later......
Wow, now I am confused. The sorting works just fine when I use QuickWatch everything is sorted as it should, but my Render function draws somehow different.
I have BG with zOrder of 1000 (background with size of full screen).
I have two sprites zOrder 1 and 1005
My Render function looks like this:
| 1 | void CGame::Draw() |
| 2 | { |
| 3 | const int h = text_height( font ); |
| 4 | const int c = makecol( 255, 255, 255 ); |
| 5 | |
| 6 | // Draw Sprites |
| 7 | acquire_bitmap(m_Buffer); |
| 8 | |
| 9 | sprite_list::const_iterator spr=sprites.begin(); |
| 10 | while (spr!=sprites.end()) |
| 11 | { |
| 12 | (*spr)->Render(m_Buffer); |
| 13 | spr++; |
| 14 | } |
| 15 | |
| 16 | release_bitmap(m_Buffer); |
| 17 | |
| 18 | // Blit all |
| 19 | acquire_screen(); |
| 20 | blit(m_Buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); |
| 21 | release_screen(); |
| 22 | } |
I would expect fro one sprite to be hidden behind my Background according to its ZOrder.
Any suggestions?
(oops wrong thread)
Mark: Hmm, learn something new everyday. You need to put it after the () of the function.
"const int some_function" apparently means that the result is constant, while
"int some_function() const" means that the function will not modify *this.
ah, thanks. I'd give a cookie if I could.
struct sprite_sorter {
bool operator()(const CSprite &a, const CSprite &b) {
return a.m_ZOrder < b.m_ZOrder;
}
};
/* And then */
std::sort(sprites.begin(), sprites.end(), sprite_sorter);
Why bother with the struct?
#include <algorithm> static bool comp(const CSprite &a, const CSprite &b) { return a.m_ZOrder < b.m_ZOrder; } std::sort(sprites.begin(), sprites.end(), comp);
[edit]
Right now If I make < part of CSprite I get:
error C2804: binary 'operator <' has too many parameters
Make the operator < take only one argument and compare whether *this < the parameter.
I would probably go with overloaded operator< if maximum speed is necessary. I'm not sure if functors can get inlined.
// should be inside class definition bool operator<(const CSprite &a) const { return m_ZOrder < a.m_ZOrder; } //Somewhere in code std::sort(sprites.begin(), sprites.end());
If I was Mark I'd use this kind of code:
| 1 | class time_stamp_object |
| 2 | { |
| 3 | public: |
| 4 | unsigned int min; |
| 5 | unsigned int sec; |
| 6 | unsigned int hun_sec; |
| 7 | |
| 8 | int get_length() const |
| 9 | { |
| 10 | int num = hun_sec; |
| 11 | num += sec*100; |
| 12 | num += min*60*100; |
| 13 | return num; |
| 14 | } |
| 15 | bool operator<(const time_stamp_object &a) const |
| 16 | { |
| 17 | return get_length() < a.get_length(); |
| 18 | } |
| 19 | }; |
| 20 | |
| 21 | //somewhere in code |
| 22 | std::sort(timestamps.begin(), timestamps.end()); |
If all this code is in headers those functions should get inlined.
#include <algorithm> inline bool comp(const CSprite &a, const CSprite &b) { return a.m_ZOrder < b.m_ZOrder; } std::sort(sprites.begin(), sprites.end(), comp);
Any speed boost you're getting from using operator < is imagined.
What HoHo said. The overloaded operator should be a member func. That's why the compiler complains about the "this" argument.
What HoHo said. The overloaded operator should be a member func. That's why the compiler complains about the "this" argument.
No, thats wrong. A standard conforming compiler accepts both ways, one takes 2 parameters and the other only takes 1.
Hi,
I have a question in regards to STL list sorting.
This is a snap of my code that I use:
| 1 | |
| 2 | CSprite *Background; |
| 3 | |
| 4 | CSprite *Player; |
| 5 | CSprite *Alien; |
| 6 | |
| 7 | . |
| 8 | . |
| 9 | . |
| 10 | Player->SetZOrder(1); |
| 11 | Alien->SetZOrder(2); |
| 12 | Background->SetZOrder(0); |
| 13 | |
| 14 | sprites.push_back(Player); |
| 15 | sprites.push_back(Alien); |
| 16 | sprites.push_back(Background); |
| 17 | sprites.sort(); |
| 18 | |
| 19 | |
| 20 | . |
| 21 | . |
| 22 | . |
| 23 | . |
| 24 | |
| 25 | //Sprite Class |
| 26 | void SetZOrder(int z) {m_ZOrder = z;} |
| 27 | |
| 28 | bool CSprite::operator<(const CSprite &b) |
| 29 | { |
| 30 | return this->m_ZOrder < b.m_ZOrder; |
| 31 | } |
Problem I am having is that Alien sprite is hidden behind Background and unless I add sprites in order Background, player, alien I have alien sprite hidden.
It appears that the sort function is not working for me. Any suggestions would be appreciated.
Thanks,
Mariusz
This is a guess, but I think you are sorting pointers, instead of objects (it's a vector of pointers to CSprite, instead of an vector of CSprite). Try adding some logging to the operator< member function to see if it is actually called.
You could create a custom compare function that compares pointers to CSprite and use that as an argument to the sort algorithm. See Bob's message above.
Well, I am not exactly sure where to place Bobs code. Is the structure a part of Sprite Class, or a shared function?
Where do I call sort from, so far I get an error telling me that sort is not a member of std namespace.
error C2039: 'sort' : is not a member of 'std'
error C2275: 'sprite_sorter' : illegal use of this type as an expression
f:\Dev\App\Galaxy\Game.cpp(9) : see declaration of 'sprite_sorter'
error C3861: 'sort': identifier not found, even with argument-dependent lookup
Galaxy.cpp
Thanks,
M.
You probably forgot to #include <algorithm>.
I have made a small example:
| 1 | #include <vector> |
| 2 | #include <string> |
| 3 | #include <algorithm> // contains std::sort |
| 4 | #include <iostream> |
| 5 | |
| 6 | class Sprite |
| 7 | { |
| 8 | public: |
| 9 | Sprite( std::string const& name, int z_order ) : name_(name), z_order_(z_order) {} |
| 10 | int getZOrder() const { return z_order_; } |
| 11 | void print() const { std::cout << "sprite " << name_ << std::endl; } |
| 12 | private: |
| 13 | std::string name_; |
| 14 | int z_order_; |
| 15 | }; |
| 16 | |
| 17 | // compare pointer to sprites |
| 18 | int compareSprites( Sprite const* s1, Sprite const* s2 ) |
| 19 | { |
| 20 | return s1->getZOrder() < s2->getZOrder(); |
| 21 | } |
| 22 | |
| 23 | int main() |
| 24 | { |
| 25 | std::vector<Sprite*> ps; |
| 26 | ps.push_back( new Sprite( "player", 2 ) ); |
| 27 | ps.push_back( new Sprite( "alien", 1 ) ); |
| 28 | ps.push_back( new Sprite( "background", 0 ) ); |
| 29 | |
| 30 | |
| 31 | std::cout << "unsorted:" << std::endl; |
| 32 | std::for_each( ps.begin(), ps.end(), std::mem_fun( &Sprite::print ) ); |
| 33 | |
| 34 | std::sort( ps.begin(), ps.end() ); |
| 35 | std::cout << "\nsorted by pointer address:" << std::endl; |
| 36 | std::for_each( ps.begin(), ps.end(), std::mem_fun( &Sprite::print ) ); |
| 37 | |
| 38 | std::sort( ps.begin(), ps.end(), compareSprites ); |
| 39 | std::cout << "\nsorted by z-order (using compareSprites):" << std::endl; |
| 40 | std::for_each( ps.begin(), ps.end(), std::mem_fun( &Sprite::print ) ); |
| 41 | } |
Thank you, I will check this as soon I have a chance.
M.