问题
I'm having trouble sorting Actors in a LibGdx Stage object. When the Stage gets rendered the images are rendered in the order they are added. Stage uses an Array to hold the Actors. I've tried setting the ZIndex of each Actor, but it still didn't sort. Then I tried creating a comparator object like this:
public class ActorComparator implements Comparator < Actor > {
@Override
public int compare(Actor arg0, Actor arg1) {
if (arg0.getZIndex() < arg1.getZIndex()) {
return -1;
} else if (arg0.getZIndex() == arg1.getZIndex()) {
return 0;
} else {
return 1;
}
}
}
and then when I want to do the actual comparison I did:
Collections.sort(Stage.getActors(), new ActorComparator());
It gives me the following error and won't compile:
The method sort(List<T>, Comparator<? super T>) in the type Collections
is not applicable for the arguments (Array<Actor>, ActorComparator)
I have no clue what I'm doing wrong. Can someone explain this to me?
回答1:
Looks like your code Stage.getActors()
returns an Array
of Actors
instead of a List
.
Collections.sort()
method accepts only Lists.
Try:
Collections.sort(Arrays.asList(Stage.getActors().toArray()), new ActorComparator());
Update on sorting (by z-index in the question) from @James Holloway: z-index is overridden by the stage to be whatever the internal order of the Array is. So setting the Z-Index has no effect, except in the case that you set it as higher than the length of the list and then it just puts the image on top of the list (internal Stage does this). This is solved by sorting by name or ID.
回答2:
Rather than the accepted answer, I like it better to prepare several "layer" groups, then adding to those group in whatever order it pleases me.
Group bg = new Group();
Group fg = new Group();
// the order is important in the following two lines
stage.addActor(bg);
stage.addActor(fg);
bg.addActor(whatever);
fg.addActor(whatever);
bg.addActor(whatever);
fg.addActor(whatever);
(Of course the addActor()
order still matters among elements of bg
, and among elements of fg
, but not between an element of fg
and an element of bg
.)
This workflow works well in an inheritance scenario, where the base class owns protected Group layers (this.bg
, this.fg
, ...) and adds them in order to the stage. Then the derived class can add things to those layers without taking care of the order.
回答3:
I have all my actors extend a DepthActor
class that contains a depth value and implements Comparable<DepthActor>
:
import com.badlogic.gdx.scenes.scene2d.Actor;
import java.util.Comparator;
/**
* @author snd
* @since May 02, 2017
*/
public class DepthActor extends Actor implements Comparable<DepthActor>
{
public int depth;
@Override
public int compareTo(DepthActor o)
{
return depth < o.depth ? -1 : (depth > o.depth ? 1 : 0);
}
}
Then sorting in place is trivial:
com.badlogic.gdx.utils.Sort.instance().sort( stage.getActors() );
回答4:
Probably not suitable for all cases, but I'm using LibGdx Actor.userObject for storing my z-index, and then auto sorting based on that. In your Screen or Game class:
class ActorZOrderComparator implements Comparator<Actor> {
@Override
public int compare(Actor o1, Actor o2) {
if (o1 != null && o1.getUserObject() != null && o1.getUserObject() instanceof Integer
&& o2 != null && o2.getUserObject() != null && o2.getUserObject() instanceof Integer) {
int z1 = (Integer) o1.getUserObject();
int z2 = (Integer) o2.getUserObject();
return z1 - z2;
} else if (o1 != null && o2 != null) {
return o1.getZIndex() - o2.getZIndex();
} else if (o1 != null) {
return -1;
} else if (o2 != null) {
return 1;
} else {
return 0;
}
}
}
protected ActorZOrderComparator zOrderComparator = new ActorZOrderComparator();
private boolean invalidZOrder;
protected void addActor(Actor actor) {
stage.addActor(actor);
if (actor.getUserObject() instanceof Integer) {
invalidZOrder = true;
}
}
@Override
public void render(float delta) {
if (invalidZOrder) {
Arrays.sort(stage.getRoot().getChildren().begin(), zOrderComparator);
stage.getRoot().getChildren().end();
invalidZOrder = false;
}
}
and then you add actors using the new addActor method (not stage.addActor):
Image leftBar = new Image(skin, "leftBar");
leftBar.setPosition(0, GameSettings.VIEWPORT_HEIGHT * 0.5f, Align.left);
leftBar.setUserObject(1); // <-- this is the z-index, set it before calling addActor(..)
addActor(leftBar);
This will also avoid sorting the array on each render call. Hope it helps!
回答5:
I have used some float value to sort the my players. Larger the value higher the index of the player. Here is my snippet:-
Map<Actor, Float> map = new HashMap<Actor, Float>();
map.put(playerActor, playerArea);
for (int i = 0; i < totalEnemies; i++) {
if (i != playerImageNo) {
map.put(enemyActor[i], enemyArea[i]);
}
}
int noOfPlayers = map.size();
// Sort Map based on values(size)
Set<Entry<Actor, Float>> set = map.entrySet();
List<Entry<Actor, Float>> list = new ArrayList<Entry<Actor, Float>>(set);
Collections.sort(list, new Comparator<Map.Entry<Actor, Float>>() {
@Override
public int compare(Map.Entry<Actor, Float> o1,
Map.Entry<Actor, Float> o2) {
return (o2.getValue()).compareTo(o1.getValue());
}
});
for (Map.Entry<Actor, Float> entry : list) {
entry.getKey().setZIndex(noOfPlayers);
System.out.println("Player: " + entry.getKey() + ", index: " + noOfPlayers);
noOfPlayers--;
}
回答6:
My solution works for me in quick tests but there may still be some flaws. If people spot anything then please shout, but this works well for me and is fairly light.
I'm only running it on my cards (I'm writing a card game) so it only adjusts Card classes that are on the stage, and they will potentially be always above other things like Tables/Labels etc.
I've added a local int zIndex
to my class which is what is used in the Comparator and is set at the beginning of the setZIndex method.
The beginning part of the method is lifted from the original in the Actor class.
@Override
public void setZIndex(int index) {
this.zIndex = index;
if (index < 0) throw new IllegalArgumentException("ZIndex cannot be < 0.");
Group parent = this.getParent();
if (parent == null) return;
Array<Actor> children = parent.getChildren();
if (children.size == 1) return;
ArrayList<Card> allCards = new ArrayList<Card>();
for(Actor child : children) {
if(child instanceof Card) {
allCards.add((Card)child);
}
}
Collections.sort(allCards, new ZIndexComparator());
for (Card card : allCards) {
children.removeValue(card, false);
children.add(card);
}
}
class ZIndexComparator implements Comparator<Card> {
public int compare(Card card1, Card card2) {
return card1.zIndex - card2.zIndex;
}
}
回答7:
EASIEST SOLUTION!! I am ashamed it took me as long as it did to come up with this...
Make a "ZStage" class that extends Stage. Override the getActors method. Bubble-sort the actors based on their Y index to get their rendering order.
public class ZStage extends Stage {
public ZStage(Viewport vp){
super(vp);
}
@Override
/** Returns the root's child actors.
* @see Group#getChildren() */
public Array<Actor> getActors () {
SnapshotArray<Actor> children = getRoot().getChildren();
boolean sorting = true;
while(sorting){
sorting=false;
for(int x=0; x< children.size-1; x++){
if(children.get(x).getY() < children.get(x+1).getY()){
children.swap(x,x+1);
sorting=true;
}
}
}
return children;
}
}
来源:https://stackoverflow.com/questions/16129903/how-do-you-sort-actors-in-a-libgdx-stage