Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: [Theory] Sprite Layering  (Read 2629 times)

0 Members and 1 Guest are viewing this topic.

Mikebissle

  • Newbie
  • *
  • Posts: 10
    • MSN Messenger - freezingblue
    • Yahoo Instant Messenger - mikebissle
    • View Profile
[Theory] Sprite Layering
« on: January 23, 2012, 03:46:54 pm »
Hello, I'm testing RPG sprite movement, and I want it to be so that the top part of a sprite always overlaps the bottom part of a sprite that's above it. The way I did it is below, and it works perfectly.

Basically, I create a map that holds all the sprites and arranges them according to their Y-coordinate. Since maps order their numerical keys from lowest to highest, iterating through it to blit each sprite guarantees that the ones further down are always drawn over ones further up.
If there's a more efficient way, I would love to know!

Code: [Select]

  map<float, Charaset*> Sprites;
  map<float, Charaset*>::iterator i;
    Sprites[(Spritesheet.Sheet.y)]  =  &Spritesheet;
//Spritesheet is the movable character; 2 and 3 are NPCs.
    Sprites[Sprites[Spritesheet2.Sheet.y]? (Spritesheet2.Sheet.y) -1: (Spritesheet2.Sheet.y)] = &Spritesheet2;
    Sprites[Sprites[Spritesheet3.Sheet.y]? (Spritesheet3.Sheet.y) -1: (Spritesheet3.Sheet.y)] = &Spritesheet3;
//The question operator is to ensure that sprites on the same Y-axis don't override each other.
 
  for (i = Sprites.begin(); i != Sprites.end(); ++i) {
    i->second->Sheet.Blit();
  }
  Sprites.clear();


BlueMagic

  • Newbie
  • *
  • Posts: 49
    • View Profile
[Theory] Sprite Layering
« Reply #1 on: January 25, 2012, 06:20:40 am »
Seems that that could work fine. Try to measure your method with a profiler or something of sorts (measure FPS of that method vs not doing any kind of depth sorting).

I am really interested in the results and in finding a better way, so please keep us posted.

Mikebissle

  • Newbie
  • *
  • Posts: 10
    • MSN Messenger - freezingblue
    • Yahoo Instant Messenger - mikebissle
    • View Profile
[Theory] Sprite Layering
« Reply #2 on: January 26, 2012, 03:59:46 pm »
Thanks. I will post if I can think of a more efficient method. It does work, but so far the only other thing that comes to mind is changing the container from a map to a multimap--that would eliminate the need for a check of the y-coordinates since mutlimaps can have multiple elements with the same key.

lezebulon

  • Full Member
  • ***
  • Posts: 235
    • View Profile
[Theory] Sprite Layering
« Reply #3 on: January 26, 2012, 06:49:01 pm »
Hi
the easiest and most generic way I found to do this was:
1) place every sprite you are going to draw in an STL container (assuming that they are not already) - obviously just store a pointer and not a copy
2) sort this container with std::sort and a custom comparaison function that works on Y
3) iterate over the container to draw each item

For the problem of 2 sprites having the same Y, you can put in your comparaison function that if the two Y values are equal, you are then comparing with any other member value. For instance I compare the adresses of the items, which insures that the order will always be the same if 2 objects are not moving and have the same Y value

raycrasher

  • Newbie
  • *
  • Posts: 24
    • View Profile
[Theory] Sprite Layering
« Reply #4 on: January 30, 2012, 09:01:08 am »
I have been getting very good results using boost::multi_index, like this:

Code: [Select]

    struct DrawOrder {};
    struct UpdateOrder {};

    typedef multi_index_container<
        shared_ptr<WorldObject>,
        indexed_by<
            ordered_non_unique<tag<DrawOrder>,member<WorldObject,double,&WorldObject::drawPriority> >,
            ordered_non_unique<tag<UpdateOrder>,member<WorldObject,double,&WorldObject::updatePriority> >
        >
    > WorldObjectGroup;

    typedef WorldObjectGroup::index<DrawOrder>::type ObjectsByDrawOrder;
    typedef WorldObjectGroup::index<UpdateOrder>::type ObjectsByUpdateOrder;

    WorldObjectGroup objects; // our object list

///////////////////////////////////////////////////////////////////////////////
// Update all objects:

void World::Update(Duration deltaTime) {

    ObjectsByUpdateOrder &updateList = objects.get<UpdateOrder>();

    ObjectsByUpdateOrder::iterator iter, obj;

    for(iter=updateList.begin(); iter!=updateList.end(); ) {
        obj=iter;
        iter++;

        if(not (*obj)->Update(deltaTime)) {
            updateList.erase(obj);
        }
    }
}


///////////////////////////////////////////////////////////////////////////////
// Draw all objects:

void World::Render() {
    ObjectsByDrawOrder &renderList = objects.get<DrawOrder>();

    GameWindow.SetView(camera.view);
    sf::View view;


    for(auto obj : renderList) {
        view=camera.view;
        sf::Transformable xform = obj->transform;
        if(obj->parallax != 1.0f) {
            view.SetCenter(view.GetCenter() * obj->parallax);
            GameWindow.SetView(view);
            //xform.SetPosition(camera.view.GetCenter() * obj->parallax);

        }

        obj->Render(GameWindow, sf::Transform());
    }
}



Sorry for the mess of code, it's just a copy-paste from the current project I'm working on.

So I get my objects sorted not only according to z-order, but by update order as well! Also, if you're developing a top-down RPG like Zelda or whatnot you could always add an index using the Y coordinate of your object, so you could have characters going behind buildings, etc.

Hope this helps!