6
\$\begingroup\$

I have a little problem with designing editor in xna. I have Content types like TilemapContent and then runtime types like TilemapData (that are automaticly converted by content pipeline)

but in editor, i think i need to work with TilemapContent (so that i can save it as such later), but TilemapContent contains things like ExternalReference<Texture2DContent> instead of Texture2D, so there is no way i could render such thing.

One "solution" that i can think of is every time i make a change to TilemapContent, i need to rebuild it and then reload TilemapData (and i guess this would be really slow)

Another solution would be to write third class TilemapEditorData and inside it write method that would convert that class to TilemapContent. But this would be third class, with almost same data, and it would require me to write something that would convert things like Texture2DContent to Texture2D.

So question is, how is this thing done? Can i have different content type and runtime type and still make an editor?

EDIT: source

ContentPipelineExtension project

[ContentSerializerRuntimeType("Data.TileMapData, Game")]
public class TilemapContent
{
     public int Width { get; set; }
     public int Height { get; set; }
     public ExternalReference<Texture2DContent> Texture { get; set; } // i need this in editor so i can change tileset texture
     public int[] Tiles { get; set; }
}

Map.xml

<XnaContent>
  <Asset Type="Content.TilemapContent">
    <Width>3</Width>
    <Height>3</Height>
    <Texture>
      <Reference>#External1</Reference>
    </Texture>
    <Tiles>
      0 1 0
      0 0 1
      0 0 0
    </Tiles>
  </Asset>
  <ExternalReferences>
    <ExternalReference ID="#External1" TargetType="Microsoft.Xna.Framework.Content.Pipeline.Graphics.Texture2DContent">raytileset.jpg</ExternalReference>
  </ExternalReferences>
</XnaContent>

(i have custom ContentProcessor that will build all external references in xml file)

Game project

   public class TilemapData
    {
        public int Width { get; set; }
        public int Height { get; set; }

        public Texture2D Texture; // and i need this in editor so i can render tilemap

        public int[] Tiles { get; set; }
   }
\$\endgroup\$
3
  • \$\begingroup\$ Good question. I'm interested to see a good answer. My first thought at a solution is some kind of shim type in your editor, that can hold both a regular texture and the content reference. \$\endgroup\$ Nov 27, 2010 at 9:51
  • \$\begingroup\$ Hopefully someone will think of something. \$\endgroup\$ Nov 29, 2010 at 0:47
  • \$\begingroup\$ Could you expand more about the internal structure of your TilemapContent and TilemapData classes? Source code would be useful. \$\endgroup\$
    – r2d2rigo
    Nov 29, 2010 at 9:18

2 Answers 2

2
\$\begingroup\$

To achieve this, separate your XNA solution into 4 projects:

  1. Content Project
  • This will contain all of your assets (like normal). Including your XML files that will be generated by your editor.
  1. Engine Project
  • This will contain common classes like TilemapContent and WhateverOtherContent that you want to load in-game as well as edit in the editor, as well as any utility classes common between the game and editor (or your whole engine if you want essentially a "game-with-editor-mode" project).
  • Any classes you wish to instantiate, edit, and reload should have appropriated attribute flags like [ContentSerializer] (or something, look at Shawn Hargreaves' blog.
  1. Game Project
  • This will contain the game logic. If you want to run the game, you run this project.
  • It will reference the Engine project to get access to TilemapContent, etc. for deserializing TilemapContent objects from the XML that's been compiled into the XNB files.
  1. Editor Project
  • This will contain your editor code. Mainly, the GUI code, and even if you'd like you can reference all the same files as the game engine project so running this project is the equivalent of running the game in editor mode.
  • Use conditional compilation to allow or disallow compilation & execution of editor functionality. You can set conditional compilation symbols on a per-project basis, and you can setup separate exes for editor and game.
  • This will also reference the engine project of course, but it will also reference the ContentPipeline assemblies.

A 2d XNA game framework I'm creating on and off for fun does this, so I can verify it works.

Note of course that making a level editor using the Content.Load<YourObjectType>("foo"); that's intended to run on Xbox won't work at all, and you'll have to go through the pain and misery of writing your own data format and intermediary classes. The serialization supported in XNA is designed to be part of the Content Pipeline since it uses reflection.

\$\endgroup\$
3
  • \$\begingroup\$ I don't see how that fixes the problem? Maybe I'm not getting something. \$\endgroup\$ Jan 18, 2011 at 21:43
  • \$\begingroup\$ Hmm I misread the question, and thought of the XML serialization stuff (it was 4 in the morning . . .). But it sounds like he's running into the issue of referencing the ContentPipeline in an editor project, but also reusing the class from the game and editor projects as well as the ContentPipeline project. Maybe I'm getting it wrong, but following the same principle from my answer (separating common classes into a separate assembly) could help. \$\endgroup\$ Jan 18, 2011 at 22:10
  • \$\begingroup\$ yes, i reference ContentPipelineExtension project (which contains TilemapContent) in Editor project, and i don't see problem with it. Problem is that TilemapContent can not be rendered and TilemapData can not be saved. (And i need to do both in editor) \$\endgroup\$
    – Kikaimaru
    Feb 27, 2011 at 15:44
1
\$\begingroup\$

From my experience, usually the content (xnb files) are compiled from some other intermediate format. (for images its bitmaps or .pngs, or what have you) I'm guessing you have some kind of xml format.

To get to that xml format, usually you write some kind of serializing code or use a serializer class (like XmlSerializer or IntermediateSerializer or some kind of serializing/persistence framework)

So you could use your TileData class in your editor, save the map as an xml file and you could run your visual studio build or set up some MSBuild stuff in your editor.

All in all, I think your looking at this the wrong way and that you shouldn't compile to xnb directly. I'm not an expert, but I think your a tad confused with the workflow, at least from my understanding.

My Understanding in Digest Format : Runtime Type (in editor) -> Save to intermediate format (xml or whatever) -> build later at design time (this is where that Content class comes in)

This is also a bit easier to debug because xnb files are binary while xml is filled with strings, far easier to look at and understand. Maybe I just didn't understand your question/situation.

Just in case I'm completely off: You could use the runtime type and when you save it you could instantiate a bunch of the content types to save with?

Edit: You need the texture name to edit the texture or something? I see your dilemma now. Probably creating a new class would be the easiest solution instead of trying to do a bunch of funky pairing of the two classes.

If you used interfaces, you could have a class that implements both the ITextureContent and ITextureData (that might make it easier if you had functions that took an TextureContent or something of that nature). If you didn't use interfaces (understandable for a data type), than maybe creating a new class that just holds both a texture content and texture data (these fields would be protected and there would be public properties that would access shared data between the two members or just one member, in the case of the texture name). You could then define a public property to get one of those members (the TextureContent or TextureData) for code that needs a specific member like a drawing function or something of that nature.

That could probably work.

\$\endgroup\$
4
  • \$\begingroup\$ i agree with that workflow, and that is why it does not work, because i cannot have RuntimeType (TilemapData) (from game) in editor, that type does not contain things like TextureName (why should it, it only needs a Texture2D). So i need to use class (TilemapContent) that contains TextureName, but this class does not contain Texture2D so i can not visualise it in editor... \$\endgroup\$
    – Kikaimaru
    Nov 28, 2010 at 9:44
  • \$\begingroup\$ I added more to my answer. I think I understand your problem. \$\endgroup\$ Nov 28, 2010 at 13:45
  • \$\begingroup\$ i have a TilemapContent that has TextureName (name of the Tileset texture). I need to edit TilemapContent.TextureName, because in editor you can choose what tileset you will use. And i need to render TilemapData.Texture because in editor you need to actually see the map you are making. Yes i can make class TilemapEditorData that has both [SerializerIgnore]Texture and TextureName, and then serialize it and manually change asset type in xml from TilemapEditorData to TilemapContent, but this seems like a hack... \$\endgroup\$
    – Kikaimaru
    Nov 28, 2010 at 14:28
  • \$\begingroup\$ Probably because it is a hack. I don't really think there is a clean way to do it unless you change one of the two classes. (the Content/Data classes) Maybe someone with more experience/innovation will come up with something. Hacks aren't always bad, as long as they don't start to grow in number and complexity. \$\endgroup\$ Nov 28, 2010 at 16:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .