Scrolling Game Development Kit Forum
SGDK Version 2 => Help, Errors, FAQ => Topic started by: Vincent on 2008-01-16, 12:34:02 PM
-
Hi guys!
I have question related to sprite priority. I'm quoting the help file here:
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! :)
-
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:
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.
-
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?
-
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! :)
-
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?
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!
-
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.
-
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":
-
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.
-
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:
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:
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!
-
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:
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?
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! :)
-
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!
-
i feel an invasion of the master coders... ^^
-
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:
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:
public SpriteBase this[int index]
{
get
{
return (SpriteBase)InnerList[index];
}
}
Must be transformed into this:
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!
-
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.
-
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" ;)
-
Wow!
I can't wait to have this honorific title. ;D
I am going to work on this double-time!
I have a demo that does work, but since it is not adding any dynamic sprites and, of course, not removing any dynamic sprites, the problem does not show up. I will probably have to work with dynamic sprites eventually in my project, so I guess I will have to solve this problem eventually.
If you want to see what it does right now, I could post the demo. Do you?
-
Oh, sorry for the double post! :-[
I made a couple of changes to the sprite collection system. Rather than having a sprite type detection (static or dynamic) based on the index of m_Sprites, I added a field and a property to the SpriteBase. A field bool isStatic and the property bool IsStatic that Get and Set the field. All sprites added at runtime are isStatic = false by default. And when the initial SpriteCollection is constructed, all sprites contained within have their isStatic field set to true. I guess it is not as optimal, because when the SpriteCollection is running it's Clean method, it iterates through all the list rather than from the staticSize index value. And it checks each sprite to determine if it is isStatic before actually removing them.
To sum it up, I changed code in two files: SpriteCollection.cs and SpriteBase.cs. The project compiles and everything seems fine. There are no runtime errors from what I can see in my small project.
Now, bluemonkmn I really need your help (in fact, anyone capable of helping me is more than welcomed): I have no debugging tool for C#, and my project is not advanced or complicated enough to throughly test the modifications I've made. I would really like it if someone could give it a look. To ease things up, all the modifications I've made to the source code are within these markers:
///********************************************************************
///**************************added by Vincent**************************
///*******************************Begin********************************
//staticSize = sprites.Length;
//This is to make sure all initial sprites are considered static
int i;
for (i=0; i < InnerList.Count; i++)
{
((SpriteBase)InnerList[i]).IsStatic = true;
}
///********************************End*********************************
///**************************added by Vincent**************************
///********************************************************************
As you can see, I commented the original code out (rather than deleting it) so it can still serve as a reference. And by searching for my name through the code, you can easily find what I modified.
I would really like it if someone could give a look or tell me how to test it efficiently. I am attaching the code files to this message, including my SpritePriorityFunctions.
If you like it bluemonkmn, I give it all to you. Feel free to include it in your next SGDK2 release. I'm happy to help. :)
If it does not work or if you're not interested, then discard it! :laugh:
I hope to get news soon! :)
-
In SpritePriorityFunction, you are checking:
if (iSpriteToInsert < iSpriteRightUnder)
twice. I think the second one should be
if (iSpriteToInsert > iSpriteRightUnder)
Also, I think you could change
for (i = iSpriteToInsert; i > iSpriteRightUnder; i--)
to
for (i = iSpriteToInsert; i > iSpriteRightUnder + 1; i--)
to avoid one unnecessary copy/move operation, but it doesn't matter much because the end is the same.
I don't think I would want to expose this function to everyone because, as you noted, it's somewhat less efficient, and also it allows the possibility that named (static) sprites could be completely removed from the layer (not just de-activated) and could leave someone very confused why activating a sprite doesn't do anything. (That's probably why I made the collection read only.) But for people like you who are clever enough to edit the code, you probably understand the dangers, so feel free to play with your project's version of the code as much as you like. I didn't test the code, but other than the things I mentioned above, it looks reasonably sound.
Have you used it to actually affect priorities and see the effect yet?
-
You are absolutely right bluemonkmn! I made the two changes in my code.
Ok, then I'll keep this code for me! (unless someones asks for it!)
Yes, I do have a demo in which sprite priorities are changing. There is a sprite that move in front and behing another copy of the same sprite, and it's actually working perfectly from what I can see. I'm going to post it with this message. I zipped the ".exe" file.
The fighter on the left can be moved with the arrow keys (he's binded to the default player 1 keys), the fighter on the right is the second player. With button 1, the fighter does a kick (but no collision detection or anything, the fighter cannot actually harm themselves) and with button 2 (you have to hold it) the fighters move up and down the mat.
So, if you press the upward arrow, the fighter jumps. If you hold button 2 and press upward arrow, the fighter moves up the mat.
If you play around with the fighters, you will be able to see that they are painted in front and then behind each other without any problem.
I also added a detection so the fighters cannot actually pass through one another. You have to circle around the other fighter to get to the other side of the mat.
Have fun!
-
Impressive, I say. This could end up being a very nice show that what SGDK2 is capable of is far beyond the basics included.
-
i double-clicked at the exe and got an error message about missing files. can't post a screenshot, the forum doesn't let my. try it later.
-
To Morgengrauen
Well I did not included the dll required to play the game (the zip file would have been too heavy). If you place the exe within one of your project folders it should run.
I believe the required dll are:
microsoft.directx.direct3d.dll
microsoft.directx.direct3dx.dll
microsoft.directx.directinput.dll
microsoft.directx.dll
d3dx9_30.dll
Just put the exe in a folder with all of these dll, it should work.
But don't ask me what each individual dll does: i'm clueless on this!
Thanks durnurd! :)
What do you think bluemonkmn? :)
-
Now I see how to move in front and behind (I should have read this first before asking in my other post). I think if you only post the SGDK2 file then people using SGDK2 will be able to compile the project on their own, and all the binary files will be copied to the right place, so it's best not to include the EXE when packaging the program for other SGDK2 users.
I think this demo qualifies you as clever ... but you will have to learn how to distribute your files ;)
-
the best would be when you would declare the buttons/actions with "logDebugLabel" directly in the game. at the first try, only after reading about the player controls, i had big difficulties to manage going down/up. anyway, great demo!
-
when
wenn = If
wann = When
(I think "if" is better here) ;)
-
one of these little traps all over the path... ;D
-
Thanks a lot guys! (woohoo! I'm clever! :P)
By the way Morgengrauen, I'm not sure I understand what you mean with "logDebugLabel" and controls? Do you mean that I should explain how the controls of the game work by using the "LogDebugLabel"? If it is the case, wouldn't it take too much space and hide much of the game?
Oh, and bluemonkmn, is there a guide or something that could explain to me how to distribute my files? Or is it simply that I must remove the 5 dll and the exe file?
See ya! :)
-
i don't know if the labels would take too much space of the display (you understood me right with the labels).
but i know you can manipulate color and size of the labels. but if you have described this already, you don't need it to do this way. just a suggestion.
edit: LOL, this is my "half evil" post... post count: 333
-
I added a new item to the SGDK2 project listing site (http://sgdk2.enigmadream.com/) home page this morning that explains a few tips about submitting projects for the listing. But here is a brief summary:
1. If you want to distribute the source code for your game, just distribute the SGDK2 file (probably compressed).
2. If you want to distribute your compiled game, use the "Generate Project" command and then use "Delete Intermediate Output Files" and deliver only the contents of your project folder (not the SGDK2 file itself).
I prefer only the source code in the project listing, when that's acceptable to the author.
-
Also of note: LogDebugLabel only works in debug mode. It won't display anything when actually playing the game as a user.