2
\$\begingroup\$

Firstly, I'm very new to slick2d, java, and coding in general, so if I apologise for any very obvious or basic errors that I've made.

I'm developing a JRPG type thing in slick 2d and the battle system revolves around players and enemies moving around a 7x4 grid, being able to move one space each turn before their normal action. I'm having problems getting different sprites to render in a set order so that an illusion of a three dimensional space is created, with sprites on the bottom row of the grid rendering above sprites in the second row, which in turn renders above the third row and so on.

I've found a few different sources on this website and others saying that the best way is to sort the characters by their y axis position and render them in that order, I've got as far as creating variables for each characters y axis position, put them into an array and made an algorithm that sorts them from lowest to highest, ie. from sprites I'd like to render at the bottom to sprites that should be rendered on the top. I can't figure out how to actually use this sorted array to draw the sprites in the correct order though.

Someone in another thread said:

"Sort the array based on the Y of those objects, from lower to higher. Now when you draw these objects, draw them from 0 to array.length, objects with low value will be drawn first, then objects with higher value will be drawn on top of them."

but I don't know how to draw them from 0 to array.length. Sorry if this is a silly question.

Edit:

for drawing the characters so far I just have:

public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{
    Backdrop.draw(0,0);
    Grid.draw(0,0);

    P3Stance.draw(p3x, p3y);
    P2Stance.draw(p2x, p2y);
    if(Helmeton) {
            P1Stance.draw(p1x, p1y);
        }
    if(Helmetoff) {
            P1StanceU.draw(p1x, p1y);
        }
    }

    Hud.draw(0,0);

the x and y values of the characters are just set to static values for now.

For the array and the sorting I have:

float[] zarr = new float[8];

and in my update method:

    zarr[0] = p1y;
    zarr[1] = p2y;
    zarr[2] = p3y;
    zarr[3] = p4y;
    zarr[4] = e1y;
    zarr[5] = e2y;
    zarr[6] = e3y;
    zarr[7] = e4y;

    int n = zarr.length;  
    float temp = 0;  
    for(int i=0; i < n; i++){  
        for(int j=1; j < (n-i); j++){  
            if(zarr[j-1] > zarr[j]){  
                temp = zarr[j-1];  
                zarr[j-1] = zarr[j];  
                zarr[j] = temp;  
                }           
            }  
        }  
\$\endgroup\$
2
\$\begingroup\$

You have correctly identified that sorting the y positions on their own will not help you draw things in the right order. What you need to do is bundle together the data needed to draw a character, and sort those bundles, then draw the bundles in that order. If it helps you can think of playing cards with the characters' pictures and x and y positions on them. We need to sort the stack of cards by the y position and then look at cards of the stack one by one and draw them.

It looks like you have your character data in separate variables right now, so you'll need to collect them together into bundles so we can sort them. The way data is usually bundled together in Java is with Objects. So let's make a character object. (I have not used slick2D in particular so I'm guessing that P2Stance and P3Stance are instances of something called a Stance. If that guess is not correct you'll have to correct this code slightly.)

public class Character {
    public float x;
    public float y;
    public Stance stance;

    Character(float x, float y, Stance stance) {
        this.x = x;
        this.y = y;
        this.stance = stance;
    }
}

The above code defines a class of Objects called Character. So now you can make new Characters with new like so:

    Character p3 = new Character(p3x, p3y, P3Stance);
    Character p2 = new Character(p2x, p2y, P2Stance);

And then you should be able to draw them like this:

    p3.stance.draw(p3.x, p3.y);
    p2.stance.draw(p2.x, p2.y);

Now you have some special logic for the first player character so we need to handle that case in particular. There are multiple ways to deal with that. The best way depends on the how many characters have helmets. From the code you've shown I can only assume that only the first character will have a helmet. If that is the case, the way that requires the fewest other changes would be to do the check when you make the character object.

   Character p1;
   if(Helmeton) {
        p1 = new Character(p1x, p1y, P1Stance);
   } else {
        p1 = new Character(p1x, p1y, P1StanceU);
   }

I'm assuming here that Helmeton is always true when Helmetoff is false and vice-versa. In that case Helmetoff is unnecessary. if you need to do something only when the helmet is off then you can use !Helmeton, where ! means "not" so !Helmeton is true when Helmeton is false.

Now that things are bundled you can put the characters in an array and sort them by their y positions. Following the way you did it with the floats:

    Character[] charactersArray = new Character[8];

    charactersArray[0] = p1y;
    charactersArray[1] = p2y;
    charactersArray[2] = p3y;
    charactersArray[3] = p4y;
    charactersArray[4] = e1y;
    charactersArray[5] = e2y;
    charactersArray[6] = e3y;
    charactersArray[7] = e4y;

    int n = charactersArray.length;  
    Character temp = null;  
    for(int i=0; i < n; i++){  
        for(int j=1; j < (n-i); j++){  
            if(charactersArray[j-1].y > charactersArray[j].y){  
                temp = charactersArray[j-1];  
                charactersArray[j-1] = charactersArray[j];  
                charactersArray[j] = temp;  
            }           
        }  
    }  

Then you can loop over the charactersArray and draw all of the characters, in the proper order:

int characterCount = charactersArray.length;
for(int i = 0; i < characterCount; i++){
    Character c = charactersArray[i];
    c.stance.draw(c.x, c.y);
}

I hope that helps!

\$\endgroup\$
  • \$\begingroup\$ Wow, not only has this fixed my problem completely but It's also taught me a lot in the process about objects, thanks for the help! A few small queries though, firstly the Stances were actually just animations, a method imported from slick, but that was easy enough to change as you said, also the values in the array should were coming up with an error, as p1y, p2y, etc. are floats, I changed them to just p1, p2, etc. and they worked fine though. Lastly, am I right in thinking that I would still need to define the variables p1x, p1y, etc. earlier on? Thanks so much for your help though. \$\endgroup\$ – Chrisjde May 27 at 18:13
  • 1
    \$\begingroup\$ I'm glad you were able to get the animations working. That's right the variables in the array were supposed to be characters, not floats. My mistake, While you can define p1x, p1y, etc before making the characters you can also pass the number directly into the constructor like this new Character(200.0, 100.0, /* and so on */). One thing to note if you decide to keep them as separate variables for whatever reason: float is a primitive type, which in Java means they are copied when they are passed into a method or a constructor. So modifying p1x would not change p1.x. \$\endgroup\$ – Ryan1729 May 28 at 0:16
  • \$\begingroup\$ @Ryan1729 I see, so if I add numbers straight into the character constructor rather than use predefined ints and floats, then I'll be able to modify them using p1.x? Also I tried to give an upvote, but I don't have enough rep to do that yet, I've only had this account for a few days! \$\endgroup\$ – Chrisjde May 28 at 18:01
  • 1
    \$\begingroup\$ In effect, yes, but what is really happening is that if you use predefined numbers, then when you pass them into the constructor they will be copied so p1x and p1.x would be different values in memory. So you would be able to change p1.x either way, but p1x would only change if you changed p1x specifically, and vice-versa. \$\endgroup\$ – Ryan1729 May 29 at 1:59
  • 1
    \$\begingroup\$ I’m not sure why the game crashes. My best guess would be those stance animations being null or some other invalid state. If my guess is correct then you should be able to store characters without animations just fine. But then you would need to find another way to match up characters with animations. One way to test that idea would be to make a new class and try storing it instances of it outside the update method and seeing if the game crashes. \$\endgroup\$ – Ryan1729 May 30 at 1:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.