Thursday, April 2, 2015

How to put sprite on top in AndEngine

Intruduction

    There are no easy ways to put sprite on top of the others in AndEngine. You ether have to manipulate z index or detach/attach sprites to scene to gain what you need. Both methods are performance expensive and ugly.

Solution

    We need a method in Entity which will look something like:
  
public void setOnTop(boolean pOnTop);
The method input is boolean which controls if our sprite is on top.

First of all we need to add the code below to IEntity.java(yes, we shall modify some part of the engine):

public boolean isOnTop();
public void setOnTop(final boolean pOnTop);

Then add implementation of these methods to BaseMenuDecorator.java
@Override
public boolean isOnTop() {
    return false;
}

@Override
public void setOnTop(boolean pOnTop) {

}
Nothing interesting is in here, just a stub.

Next let's come to main part in Entity.java. Override methods you've just created in IEntity.java with something like this:

    @Override
    public boolean isOnTop() {
        return mIsOnTop;
    }

    @Override
    public void setOnTop(boolean pOnTop) {
        mIsOnTop = pOnTop;
    }
mIsOnTop is a variable that holds if an Entity is on top.
Next go to method

void onManagedDraw(final GLState pGLState, final Camera pCamera);
This method is called every time IEntity is being drawn to, well, draw it:). We should check if entityes in draw loops are on top and if so draw them last. That's it. I'm putting here the whole modifyed method so you can compare it with original:

 protected void onManagedDraw(final GLState pGLState, final Camera pCamera) {
  pGLState.pushModelViewGLMatrix();
  {
   this.onApplyTransformations(pGLState);

   final SmartList<IEntity> children = this.mChildren;
   if ((children == null) || !this.mChildrenVisible) {
    /* Draw only self. */
    this.preDraw(pGLState, pCamera);
    this.draw(pGLState, pCamera);
    this.postDraw(pGLState, pCamera);
   } else {
    if (this.mChildrenSortPending) {
     ZIndexSorter.getInstance().sort(this.mChildren);
     this.mChildrenSortPending = false;
    }

    final int childCount = children.size();
    int i = 0;

                List<IEntity> topList = new LinkedList<>();
    { /* Draw children behind this Entity. */
     for (; i < childCount; i++) {
      final IEntity child = children.get(i);
      if (child.getZIndex() < 0) {
                            if (!child.isOnTop()) {
                                child.onDraw(pGLState, pCamera);
                            } else {
                                topList.add(child);
                            }
      } else {
       break;
      }
     }
    }

    /* Draw self. */
    this.preDraw(pGLState, pCamera);
    this.draw(pGLState, pCamera);
    this.postDraw(pGLState, pCamera);

    { /* Draw children in front of this Entity. */
     for (; i < childCount; i++) {
                        final IEntity child = children.get(i);
                        if (!child.isOnTop()) {
                            children.get(i).onDraw(pGLState, pCamera);
                        } else {
                            topList.add(child);
                        }
     }
    }

                { /* Draw top Entityes */
                    for (IEntity child : topList) {
                        child.onDraw(pGLState, pCamera);
                    }
                }
   }
  }
  pGLState.popModelViewGLMatrix();
 }
Nothing special as you can see. Now if you want sprite to became on top just call setOnTop(true).
Thanks for reading and have a good day.

You can find edited files here.