Author Topic: Sprite priority question  (Read 15943 times)

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Sprite priority question
« on: 2008-01-16, 12:34:02 PM »
Hi guys!

I have question related to sprite priority.  I'm quoting the help file here:

Quote
If two sprites have the same priority, the order in which they will be drawn is arbitrarily assigned when the project is compiled, but will never change after that.

In my fighting game, the players sprites can move toward the background and toward the foreground.  Thus, at some times Fighter1 must draw in front of Fighter2 and some times it is the opposite.  I looked around to find a way the change a sprite priority with a script during play, but I can't find anything.  Is there a way to apply the "interleaved" capability between "tiles and sprites" on the same priority to "sprites and sprites"?

Is there another workaround?

Thanks a lot!  :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Sprite priority question
« Reply #1 on: 2008-01-16, 02:28:44 PM »
I don't know if I have an answer for you, but I can provide some additional info.
The priority of the sprites is actually only used to determine the order in which they get added to the collection when the project is compiled.  The number doesn't actually exist in the compiled project -- just the order in which the sprites exist in the collection of sprites on a layer.  So one possibility is that you could try to change the order of the sprites in the layer.  I can tell you that dynamic sprites (sprites added with functions like AddSpriteHere or AddSpriteAtPlan instead of TileActivateSprite or ActivateSprite) are added to the end of the collection and will always draw in front of sprites added before them.  You might be able to control the sequence by re-adding sprites to the layer in the proper order.

The collection of sprites within a layer is in the "m_Sprites" property of each layer (defined in LayerBase.cs).  The sprites are "injected" into the layer by the LayerBase function called "InjectSprites".  That function goes thorugh 3 steps to separately handle the sprites that need to be added 1) behind the layer's tiles; 2) between the layer's tiles and 3) in front of the layers tiles.  You might also be able to do something there.

The easiest thing, though, is probably to just switch the order of sprites within m_Sprites with something like:
Code: [Select]
SpriteBase tempSprite = m_Sprites[1];
m_Sprites[1] = m_Sprites[0];
m_Sprites[0] = tempSprite;

(You might have to provide additional code for locate m_Sprites in the correct layer depending on where that code goes.)  Unfortunately, there's no easy way around it.  But there are ways around it.  I'll have to consider this carefully for some future version of SGDK2.  My mind is always on platform games, so I wasn't thinking adjustable priority at runtime was very important.

Tanja

  • Clever
  • Fanatic
  • ***
  • Posts: 606
    • View Profile
Re: Sprite priority question
« Reply #2 on: 2008-01-16, 02:47:04 PM »
what when vincent would use the template for isometric games? i wonder all the time, there is pseudo-3D. why can't he use this template to make his back- and foregrounds?

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #3 on: 2008-01-18, 11:06:07 AM »
Hello everyone!

Sorry, I've been busy lately and I couldn't answer you!

To Morgengrauen:
I looked at the IsoSample project, but it does have the same problem that I encountered with sprites always showing in front of other sprites rather than showing in front or behind another sprite.  I modified the sample to this on my computer and it is easy to reproduce:  simply add a sprite on level 1 on the main layer.  Then create a Map player 1 to inputs script in the Main layer plan and deactivate the Map player to inputs script in the sprite plan.  (otherwise, when you move the sprite, both sprite move).  Then, you should be able to see the behavior I am describing.

To bluemonkmn:
I'm going to try the modify the source code of my project to include a custom function to make this possible.  I will use the sample project as a reference (I believe you there is a custom object in the sample, I guess it is the exampleI need.)  I guess I'm going to ask you more questions on this, since it doesn't seem documented in the help file. (I'm new to C# too.)

By the way, this is out of the matter at hand (sorry), but I have another quick question.  If I wanted to add more buttons to a player (a max of 4 buttons seems a little limitative), should I alter my project source code in the same way I'm going to do with the sprite priority changing script?

Thanks a lot for your time and advices! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #4 on: 2008-01-18, 12:48:17 PM »
Hello again! :)

I tried to make the script function, but I encountered a problem...  Sorry if it is a newbie question:  from the CustomObjects namespace, how can I get to modify the m_Sprites collection?  I can't get to it, maybe I am missing a namespace or m_Sprites is contained within the LayerBase object?

I'm posting my code here, could someone help me out please?

Code: [Select]
using System.ComponentModel;

namespace CustomObjects
{
   // added by Vincent
   public class SpritePriorityFunctions
   {
      [Description("Changes the priority of sprites ")] 
      public void SwitchSpritesPriority(SpriteBase FirstSprite, SpriteBase SecondSprite)
      {
         int i;
         int iFirstSprite;
         int iSecondSprite;     
         for (i = 0; i < m_Sprites.Count; i++)
         {
            if (Sprites[i] = FirstSprite)
            {
               iFirstSprite = i;
            }
            if (Sprites[i] = SecondSprite)
            {
               iSecondSprite = i;
            }
         }
         SpriteBase tempSprite = m_Sprites[iFirstSprite];
         m_Sprites[iFirstSprite] = m_Sprites[iSecondSprite];
         m_Sprites[iSecondSprite] = tempSprite;
      }
   }
}

Thanks a lot!
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Sprite priority question
« Reply #5 on: 2008-01-18, 02:58:44 PM »
By the way, this is out of the matter at hand (sorry), but I have another quick question.  If I wanted to add more buttons to a player (a max of 4 buttons seems a little limitative), should I alter my project source code in the same way I'm going to do with the sprite priority changing script?
I would first suggest that you try to find a way to work with 4 buttons.  You can do a lot with 4 buttons + 4 directions, and you might not have thought of all the possibilities.  For example, assume you need to distinguish 4 types of actions -- punch, high kick, low kick and block.  Instead of using 4 buttons for this, you could define button 1 as punch and button 2 as kick.  Then you can say that, if you press back and punch, it means block, and if you press down and kick it means low kick.  So you could use just 2 buttons instead of 4.  Then maybe you can use up and kick for jump, if you're short on buttons (or button 3 for jump, if you have enough buttons). You see there are many combinations.  And if you keep the number of buttons low, the player will have an easier time remembering what they are and using them.  One reason SGDK2 is limited to 4 buttons is to make it more flexible with different controllers.  Many joysticks and gamepads don't have more than 4 buttons, so your game would not work with them.  Of course you can also use IsKeyPressed if it's a 1-player game, and then you have as many buttons as there are keyboard keys (but then the player can't customize the controls).

If you really need more than 4 buttons, you would have to change a number of things, and I'm not sure I could remember them all:
1) SpriteBase.cs has InputBits enum that needs more buttons.
2) SpriteBase.cs also has a MapPlayerToInputs function (and maybe others) that will only transfer a limited number of inputs from the player to the sprite inputs.
3) Player.cs defines the interface for getting controller input from a player.
4) Controls.cs defines the screen that is displayed to the player to customize the buttons.

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Sprite priority question
« Reply #6 on: 2008-01-18, 03:07:12 PM »
I tried to make the script function, but I encountered a problem...  Sorry if it is a newbie question:

Hey, SGDK2 has only been officially released for less than a month.  Everyone's a newbie :).  I'm just glad you're brave enough to take on the challenge of writing some custom code in your project!

from the CustomObjects namespace, how can I get to modify the m_Sprites collection?  I can't get to it, maybe I am missing a namespace or m_Sprites is contained within the LayerBase object?

Try "FirstSprite.ParentLayer.m_Sprites" to get at the sprites collection for the layer that contains the first sprite.  If you want to store a reference to the m_Sprites collection to re-use (within the scope of the function) without going through FirstSprite every time, use:
SpriteCollection layerSprites = FirstSprite.ParentLayer.m_Sprites;

Then you can use "layerSprites" wherever you are currently using "m_Sprites":

Tanja

  • Clever
  • Fanatic
  • ***
  • Posts: 606
    • View Profile
Re: Sprite priority question
« Reply #7 on: 2008-01-19, 02:08:35 AM »
Many joysticks and gamepads don't have more than 4 buttons, so your game would not work with them. 

i guess the most common thing with gamepads atm is four action buttons + four shoulder buttons. no clue how it is with joysticks.

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #8 on: 2008-01-22, 10:45:36 AM »
Hi guys!

Thank you for all the info.  I couldn't work on this for the past 3 days, but now I'm back.  So, I changed my function to this:
Code: [Select]
public class SpritePriorityFunctions
   {
      [Description("Changes the priority of sprites ")] 
      public void SwitchSpritesPriority(SpriteBase FirstSprite, SpriteBase SecondSprite)
      {
         int i;
         int iFirstSprite;
         int iSecondSprite; 
         for (i = 0; i < FirstSprite.ParentLayer.m_Sprites.Count; i++)
         {
            if (FirstSprite.ParentLayer.m_Sprites[i] == FirstSprite)
            {
               iFirstSprite = i;
            }
            if (FirstSprite.ParentLayer.m_Sprites[i] == SecondSprite)
            {
               iSecondSprite = i;
            }
         }
         SpriteBase tempSprite = FirstSprite.ParentLayer.m_Sprites[iFirstSprite];
         FirstSprite.ParentLayer.m_Sprites[iFirstSprite] = FirstSprite.ParentLayer.m_Sprites[iSecondSprite];
         FirstSprite.ParentLayer.m_Sprites[iSecondSprite] = tempSprite;
      }
   }

I really believe it should work but it won't compile.  It gives me this error:
Quote
Library\Projects\TKDremake\SpritePriorityFunctions.cs(26,10) : error CS0200: Property or indexer 'SpriteCollection.this[int]' cannot be assigned to -- it is read only
Library\Projects\TKDremake\SpritePriorityFunctions.cs(27,10) : error CS0200: Property or indexer 'SpriteCollection.this[int]' cannot be assigned to -- it is read only

Have I done something wrong?  Or is it just impossible to replace the sprites in this collection?

Thanks!
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #9 on: 2008-01-22, 01:04:02 PM »
Hi again!

I continued my work on the sprite priority functions and, as it may interest some people who might run into the same problems than me, I'll explain what I've done.  By the way, when I'm done, maybe bluemonkmn could make my code file available ont the sgdk2 website for others to use! :)

I worked on another "sprite priority function" that allows the insertion of a sprite right on the of another specfied one.
Example:
My collection of sprites contains
  -Sprite1
  -Sprite2
  -Sprite3
  -Sprite4

This functions allows to insert a Sprite 4 over Sprite1 without having to make multiple uses of the SwitchSpritePriority function.
With SwitchSpritePriority, you would have to make 2 uses of the (switch Sprite4 with Sprite3, then switch Sprite4 with Sprite2).
With InsertSpriteOnSprite, you just specify which sprite must be moved (Sprite4) and which is the sprite that is supposed to be right under it (Sprite1)
Both should give a final result of:
  -Sprite1
  -Sprite4
  -Sprite2
  -Sprite3

And, I would really like to test these 2 functions, but they both get the same error that I specified before.  Which is:
Quote
Library\Projects\TKDremake\SpritePriorityFunctions.cs(26,10) : error CS0200: Property or indexer 'SpriteCollection.this[int]' cannot be assigned to -- it is read only
Library\Projects\TKDremake\SpritePriorityFunctions.cs(27,10) : error CS0200: Property or indexer 'SpriteCollection.this[int]' cannot be assigned to -- it is read only

Here is the code of my custom class:
Could someone help me out?

Code: [Select]
using System.ComponentModel;

namespace CustomObjects
{
   // added by Vincent
   public class SpritePriorityFunctions
   {
      [Description("Switch the priority of sprites")] 
      public void SwitchSpritesPriority(SpriteBase FirstSprite, SpriteBase SecondSprite)
      {
         int i;
         int iFirstSprite;
         int iSecondSprite; 
         for (i = 0; i < FirstSprite.ParentLayer.m_Sprites.Count; i++)
         {
            if (FirstSprite.ParentLayer.m_Sprites[i] == FirstSprite)
            {
               iFirstSprite = i;
            }
            if (FirstSprite.ParentLayer.m_Sprites[i] == SecondSprite)
            {
               iSecondSprite = i;
            }
         }
         SpriteBase tempSprite = FirstSprite.ParentLayer.m_Sprites[iFirstSprite];
         FirstSprite.ParentLayer.m_Sprites[iFirstSprite] = FirstSprite.ParentLayer.m_Sprites[iSecondSprite];
         FirstSprite.ParentLayer.m_Sprites[iSecondSprite] = tempSprite;
      }

      [Description("Inserts a sprite right on top of another specified one")] 
      public void InsertSpriteOnSprite(SpriteBase SpriteToInsert, SpriteBase SpriteRightUnder)
      {
         int i;
         int iSpriteToInsert = -1;
         int iSpriteRightUnder = -1; 
         
         //Detecting where are the sprites in the list
         for (i = 0; i < SpriteRightUnder.ParentLayer.m_Sprites.Count; i++)
         {
            if (SpriteRightUnder.ParentLayer.m_Sprites[i] == SpriteToInsert)
            {
               iSpriteToInsert = i;
            }
            if (SpriteRightUnder.ParentLayer.m_Sprites[i] == SpriteRightUnder)
            {
               iSpriteRightUnder = i;
            }
         }   
         
         //Don'tdo anything if the values are not correct
         if (iSpriteToInsert != -1 && iSpriteRightUnder != -1)
         {
            //Don't do anything if everything is already ok
            if (iSpriteRightUnder != iSpriteToInsert - 1)
            {
               //We must change the list from the iSpriteToInsert index to the iSpriteRightUnder index
               if (iSpriteToInsert < iSpriteRightUnder)
               {
                  for (i = iSpriteToInsert; i < iSpriteRightUnder; i++)
                  {
                     SpriteRightUnder.ParentLayer.m_Sprites[i] = SpriteRightUnder.ParentLayer.m_Sprites[i + 1];
                  }
                  SpriteRightUnder.ParentLayer.m_Sprites[iSpriteRightUnder] = SpriteToInsert;
               } 
               //We must change the list from the iSpriteRightUnder index to the iSpriteToInsert index
               if (iSpriteToInsert < iSpriteRightUnder)
               {
                  for (i = iSpriteToInsert; i > iSpriteRightUnder; i--)
                  {
                     SpriteRightUnder.ParentLayer.m_Sprites[i] = SpriteRightUnder.ParentLayer.m_Sprites[i - 1];
                  }
                  SpriteRightUnder.ParentLayer.m_Sprites[iSpriteRightUnder + 1] = SpriteToInsert;
               }         
            }
         }       
      }
   }
}

To bluemonkmn:
I read the code in the SpriteCollection file, and I realized that playing with the order of the sprites in the m_Sprites collection could cause some problems with the fact that the sprites considered static and the sprites considered dynamic may get messed up.  Maybe it would be interesting to alter this system in a future release.  Or maybe you have a better way to handle this situation?  I am all ears!  ;D

By the way, I'm having a great time using SGDK2!  :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #10 on: 2008-01-22, 01:59:57 PM »
Well, finally I got one step further.  I realized that m_Sprites was read-only because the was no "Set", only "Get".  So I changed the code into SpriteCollections.cs to implement a "Set".  I'll keep you informed!
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

Tanja

  • Clever
  • Fanatic
  • ***
  • Posts: 606
    • View Profile
Re: Sprite priority question
« Reply #11 on: 2008-01-22, 02:05:01 PM »
i feel an invasion of the master coders... ^^

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 612
  • Legacy of Kain: Revival is completed!!!
    • View Profile
    • Chivalrous Games
    • Email
Re: Sprite priority question
« Reply #12 on: 2008-01-22, 03:13:52 PM »
Wow!!!  It's great!!!  It really does work!

For those interested to implement these new functions you need to add this class into your code:
Code: [Select]
using System.ComponentModel;

namespace CustomObjects
{
   public class SpritePriorityFunctions
   {
      //added by Vincent
      [Description("Switch the priority of sprites on the same layer")] 
      public static void SwitchSpritesPriority(SpriteBase FirstSprite, SpriteBase SecondSprite)
      {
         int i;
         int iFirstSprite = -1;
         int iSecondSprite = -1; 
         for (i = 0; i < FirstSprite.ParentLayer.m_Sprites.Count; i++)
         {
            if (FirstSprite.ParentLayer.m_Sprites[i] == FirstSprite)
            {
               iFirstSprite = i;
            }
            if (FirstSprite.ParentLayer.m_Sprites[i] == SecondSprite)
            {
               iSecondSprite = i;
            }
         }
         //Don't do anything if the values are not correct
         if (iFirstSprite != -1 && iSecondSprite != -1)
         {
            SpriteBase tempSprite = FirstSprite.ParentLayer.m_Sprites[iFirstSprite];
            FirstSprite.ParentLayer.m_Sprites[iFirstSprite] = FirstSprite.ParentLayer.m_Sprites[iSecondSprite];
            FirstSprite.ParentLayer.m_Sprites[iSecondSprite] = tempSprite;
         }
      }

      [Description("Inserts a sprite right on top of another specified one on the same layer")] 
      public static void InsertSpriteOnSprite(SpriteBase SpriteToInsert, SpriteBase SpriteRightUnder)
      {
         int i;
         int iSpriteToInsert = -1;
         int iSpriteRightUnder = -1; 
         
         //Detecting where are the sprites in the list
         for (i = 0; i < SpriteRightUnder.ParentLayer.m_Sprites.Count; i++)
         {
            if (SpriteRightUnder.ParentLayer.m_Sprites[i] == SpriteToInsert)
            {
               iSpriteToInsert = i;
            }
            if (SpriteRightUnder.ParentLayer.m_Sprites[i] == SpriteRightUnder)
            {
               iSpriteRightUnder = i;
            }
         }   
         
         //Don't do anything if the values are not correct
         if (iSpriteToInsert != -1 && iSpriteRightUnder != -1)
         {
            //Don't do anything if everything is already ok
            if (iSpriteRightUnder != iSpriteToInsert - 1)
            {
               //We must change the list from the iSpriteToInsert index to the iSpriteRightUnder index
               if (iSpriteToInsert < iSpriteRightUnder)
               {
                  for (i = iSpriteToInsert; i < iSpriteRightUnder; i++)
                  {
                     SpriteRightUnder.ParentLayer.m_Sprites[i] = SpriteRightUnder.ParentLayer.m_Sprites[i + 1];
                  }
                  SpriteRightUnder.ParentLayer.m_Sprites[iSpriteRightUnder] = SpriteToInsert;
               } 
               //We must change the list from the iSpriteRightUnder index to the iSpriteToInsert index
               if (iSpriteToInsert < iSpriteRightUnder)
               {
                  for (i = iSpriteToInsert; i > iSpriteRightUnder; i--)
                  {
                     SpriteRightUnder.ParentLayer.m_Sprites[i] = SpriteRightUnder.ParentLayer.m_Sprites[i - 1];
                  }
                  SpriteRightUnder.ParentLayer.m_Sprites[iSpriteRightUnder + 1] = SpriteToInsert;
               }         
            }
         }       
      }
   }
}

And you also need to alter SpriteCollection.cs file:

This part:
Code: [Select]
public SpriteBase this[int index]
   {
      get
      {
         return (SpriteBase)InnerList[index];
      }
}
Must be transformed into this:
Code: [Select]
public SpriteBase this[int index]
   {
      get
      {
         return (SpriteBase)InnerList[index];
      } 
      set
      {
         InnerList[index] = value;
      }
   }

I sure hope it proves useful for someone!

Thanks again guys!
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

See also my company website:
http://chivalrousgames.com

durnurd

  • Lead Lemming
  • Expert
  • Fanatic
  • *****
  • Posts: 1234
  • Games completed so far: 0
    • MSN Messenger - durnurd@hotmail.com
    • View Profile
    • Find My Ed
Re: Sprite priority question
« Reply #13 on: 2008-01-22, 03:16:34 PM »
Well, finally I got one step further.  I realized that m_Sprites was read-only because the was no "Set", only "Get".  So I changed the code into SpriteCollections.cs to implement a "Set".  I'll keep you informed!
Hmm... I'm not sure (haven't looked) but I get the feeling that there was no Set for a very good reason, such as the fact that changing order would screw things up in the game loop, especially if you're dealing with indices into a collection of sprites on a per-frame basis in the rules section of sprites or plans.
Edward Dassmesser

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2761
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Sprite priority question
« Reply #14 on: 2008-01-22, 06:13:55 PM »
Actually I can't think of a good reason for not having a set accessor except that it opens up the possibility that dynamic sprites and static sprites could get mixed up.  That may be a very good reason in itself, but I can't think of any other reason.

Vincent, your cleverness is scaring me -- if you get this to work, I'm going to have to make you "Clever" ;)