XNA Creators Club Online
Page 1 of 1 (6 items)
Sort Posts: Previous Next

Static Classes at Build Time

Last post 8/22/2009 7:34 AM by fkassad. 5 replies.
  • 8/21/2009 6:21 PM

    Static Classes at Build Time

    I have xml files that are built in the content pipeline.  These xml represent a specific sprite in the game (ie. player, enemy1, enemy2).  Within each there are paths to animations, and sprite sheets.  Each animation is associated with a sprite sheet, and multiple animations can belong to a single sprite sheet.  This is what the file can look like:

    File: Player.xml
    <Sprite SpriteType="Player">
      <Animation SpriteSheet="ss1.sst">Idle.spa</Animation>
      <Animation SpriteSheet="ss1.sst">Walk.spa</Animation>
      <Animation SpriteSheet="ss2.sst">Attack.spa</Animation>
    </Sprite>

    (.sst = sprite sheet file, .spa = sprite animation file)

    So when Player.xml is built, the processor is run, and then for each SpriteAnimation line in the file, a SpriteSheet processor is called, as well as a SpriteAnimation processor.  The problem is on some of these lines the sprite sheets are the same, so I would not want to call the sprite sheet processor for a duplicate sprite sheet. 

    I have a separate class called Resources that manages the resources in the game.  I have a static Dictionary in the Resources class that keeps track of all loaded sprite sheets.  My question is: During build time (when Player.xml is passed through the content pipeline) can I use this static class to keep track of the currently loaded sprite sheets?  I am not sure if this is possible since this is done at build time, and not run time.  And, is it ok to embed processors within processors like this?

    Here's the processor code for the Sprite Processor:
    public override SpriteContent Process(XmlDocument input, 
                                                       ContentProcessorContext context) 
            { 
                SpriteContent spriteContent = new SpriteContent(); 
                XmlNodeList animationsList = input.GetElementsByTagName("Animation"); 
                XmlNode element = input.FirstChild; 
                spriteContent.SpriteType = element.Attributes["SpriteType"].Value; 
     
                foreach (XmlNode animation in animationsList) 
                { 
                    // Reference to the sprite animation 
                    ExternalReference<SpriteAnimationContent> sac = context.BuildAsset<XmlDocument, SpriteAnimationContent>( 
                        new ExternalReference<XmlDocument>(animation.InnerText), 
                        "SpriteAnimationProcessor"
                        null
                        "SpriteAnimationImporter"
                        null); 
     
                    // Reference to the sprite sheet 
                    // Check to make sure the sprite sheet has not already been loaded 
                    if (!Resources.SpriteSheetsLoadedDictionary.ContainsKey(animation.Attributes["SpriteSheet"].Value)) 
                    { 
                        ExternalReference<SpriteSheetContent> ssc = context.BuildAsset<string[], SpriteSheetContent>( 
                            new ExternalReference<string[]>(animation.Attributes["SpriteSheet"].Value), 
                            "SpriteSheetProcessor"); 
     
                        Resources.SpriteSheetsLoadedDictionary.Add(animation.Attributes["SpriteSheet"].Value, ssc); 
                    } 
     
                    // Create the sprite data item. 
                    SpriteData spriteData; 
                     
                    spriteData.SpriteAnimationContent = sac; 
                    spriteData.SpriteSheetContent = Resources.SpriteSheetsLoadedDictionary[animation.Attributes["SpriteSheet"].Value]; 
                    spriteContent.SpriteDataList.Add(spriteData); 
                } 
                return spriteContent; 
            } 

     
  • 8/21/2009 7:00 PM In reply to

    Re: Static Classes at Build Time

    Let's try and figure out what you're trying to do here. First off, build time for your content is really runtime for your content processors. So you can certainly use static classes during your build process if that's something you need to do.

    If you are using the Resources static class to ensure that each asset is only processed once during the build that should work fine.

    If you expect your Resources class to hold the data it generated during processing (content building) when the game stars up (which I assume isn't what you are describing), that won't work. Static data is not preserved across program runs.
  • 8/21/2009 7:01 PM In reply to

    Re: Static Classes at Build Time

    It's totally fine and normal for one processor to call into other processors. Our built in types do this a lot, for instance ModelProcessor will call into TextureProcessor and EffectProcessor to build any textures and shaders that are used by a model.

    I don't really understand what you are using the static dictionary for, but in general I'm not sure that static collections make a lot of sense at build time. You want to make the build processor for each asset independent of that for any other assets, except for using Context.BuildAsset where you have cross dependencies between multiple files.

    Remember that the build system is incremental, so subsequent builds may want to only rebuild one or two assets in isolation, if only a couple of files have changed. If you are sharing data between several different processor invocations, this could give different results when just a subset of your assets are rebuilt, which is unlikely to be what you want!

    BuildAsset is designed to understand and play nice with partial/incremental rebuilds, so it can do smart things like not rebuilding the referenced asset if that has not changed, and also rebuilding referenced assets but not the asset which originally asked for them to be built (eg. if you build a model, which references a texture, then change the texture, it knows how to rebuild just that texture but not the original model).
    XNA Framework Developer - blog - homepage
  • 8/21/2009 7:54 PM In reply to

    Re: Static Classes at Build Time

    dadoo Games:
    Let's try and figure out what you're trying to do here. First off, build time for your content is really runtime for your content processors. So you can certainly use static classes during your build process if that's something you need to do.

    If you are using the Resources static class to ensure that each asset is only processed once during the build that should work fine.

    If you expect your Resources class to hold the data it generated during processing (content building) when the game stars up (which I assume isn't what you are describing), that won't work. Static data is not preserved across program runs.


    The first one, using it so each asset is only processed once.

    Shawn Hargreaves:
    It's totally fine and normal for one processor to call into other processors. Our built in types do this a lot, for instance ModelProcessor will call into TextureProcessor and EffectProcessor to build any textures and shaders that are used by a model.

    I don't really understand what you are using the static dictionary for, but in general I'm not sure that static collections make a lot of sense at build time. You want to make the build processor for each asset independent of that for any other assets, except for using Context.BuildAsset where you have cross dependencies between multiple files.

    Remember that the build system is incremental, so subsequent builds may want to only rebuild one or two assets in isolation, if only a couple of files have changed. If you are sharing data between several different processor invocations, this could give different results when just a subset of your assets are rebuilt, which is unlikely to be what you want!

    BuildAsset is designed to understand and play nice with partial/incremental rebuilds, so it can do smart things like not rebuilding the referenced asset if that has not changed, and also rebuilding referenced assets but not the asset which originally asked for them to be built (eg. if you build a model, which references a texture, then change the texture, it knows how to rebuild just that texture but not the original model).


    Ignore the processor code I posted above for a second.  Lets use the file I posted above as an example:

    File: Player.xml
    <Sprite SpriteType="Player">
      <Animation SpriteSheet="ss1.sst">Idle.spa</Animation>
      <Animation SpriteSheet="ss1.sst">Walk.spa</Animation>
      <Animation SpriteSheet="ss2.sst">Attack.spa</Animation>
    </Sprite>

    Assume the processor does the following:
    Process - Player.xml (Sprite Processor)
          Process - ss1.sst (Sprite Sheet Processor)
          Process - Idle.spa (Sprite Animation Processor)
          Process - ss1.sst (Sprite Sheet Processor)
          Process - Walk.spa (Sprite Animation Processor)
          Process - ss2.sst (Sprite Sheet Processor)
          Process - Attack.spa (Sprite Animation Processor)

    There is a duplicate of ss1.sst.  Will the content pipeline recognize that ss1.sst has already been built, or is it my responsibility to make sure I do not process the same asset twice?  Let me post my SpriteReader Code:
    1 protected override Sprite Read(ContentReader input, 
    2                                             Sprite existingInstance) 
    3         { 
    4             Sprite sprite = null
    5             string spriteType = input.ReadString(); 
    6             if (spriteType == "Player"
    7             { 
    8                 sprite = new PlayerSprite(); 
    9             } 
    10  
    11             if (sprite != null
    12             { 
    13                 int numAnimations = input.ReadInt32(); 
    14  
    15                 for (int i = 0; i < numAnimations; i++) 
    16                 { 
    17                     SpriteAnimation spriteAnimation = input.ReadExternalReference<SpriteAnimation>(); 
    18                     SpriteSheet spriteSheet = input.ReadExternalReference<SpriteSheet>(); 
    19  
    20                     sprite.AddAnimation(spriteAnimation, spriteSheet); 
    21                 } 
    22             } 
    23             return sprite; 
    24         } 

    This code will create the sprite using the animations and sprite sheets defined in the xml file.  Line 15 loops through each animation for the sprite, and adds that animation/sprite sheet to the sprite.  Based on our example, line 18 will read in:

    Pass 1: ss1.sst
    Pass 2: ss1.sst
    Pass 3: ss2.sst

    Will the spriteSheet variable reference the same sprite sheet in pass 1 and 2? Or will it be referencing different sprite sheets?  Hope this is clear. 

    Thanks for the responses.
  • 8/21/2009 8:22 PM In reply to

    Re: Static Classes at Build Time

    ContentProcessorContext.BuildAsset merges duplicate build request - that is it's main purpose for existing in the first place!  If it did not do this, common situations like several models that all share the same texture would be badly broken.

    ContentProcessorContext.BuildAndLoadAsset, on the other hand, returns a separate instance each time you call it.
    XNA Framework Developer - blog - homepage
  • 8/22/2009 7:34 AM In reply to

    Re: Static Classes at Build Time

    Thanks.
Page 1 of 1 (6 items) Previous Next