Jump to content

Component & Property/Attribute design


Rick
 Share

Recommended Posts

The only downside to components that I have heard of, is some people say they can run a little slower due to the searching to get components. However I have also read there are ways to solve the speed issues. Also the major dev houses have been successfully using them for 5-10 years, so they must have overcome these problems.

 

The speed issue will be something I'll have to deal with as there are a lot of variables per entity in my game, so there will be a lot of searching.

Win 7 Pro 64 bit

AMD Phenom II X3 720 2.8GHz

GeForce 9800 GTX/9800 GTX+

4 GB RAM

Link to comment
Share on other sites

Yeah, that is one of my concerns. Adding that on top of using C# and speed will take a hit, but I wonder how much. Per component object it's probably best to keep the attributes/properties to a minimum and the data that's just needed internally to do the stuff for the component that doesn't need to be exposed to be normal variables.

Link to comment
Share on other sites

I think I also need a messaging system. The more I look at this in being in an editor the more I see that I need the ability to send messages when events happen. Since components & attributes are accessed via string this should be pretty easy to be dynamic in an editor. The idea will be to have a list of messages per event for a component. So when said event is fired it will loop through the list of messages and send them out to the given component.

 

The message structure will be made up of the component name to send the message to, the message string, and any extra message data as a string.

 

This would be interesting as the connectors between your objects and ultimately your game flow would be all preconfigured in the setup of your game objects and their components.

 

A simple example of this in action would be when you press a button to load a scene. You create en empty game object. Attach the GUIButton component to it. Then attach a LoadScene component also. Then you provide a message to the GUIButton component OnClick event that'll send a "load" message to any "LoadScene" component attached to the game object.

 

The GUIButton's OnUpdate() looks for the button area to be clicked. When it does it fires it's OnClick event. The GUIButton's local OnClick event has this message that says call "LoadScene" game objects OnMessage() method passing to it the message of "load", with extra data as the scene to load.

 

I think this will be my first "real world" test with this.

Link to comment
Share on other sites

@ZioRed:I uploaded an example with some component base code, as well as C# events integrated into the Leadwerks callbacks. Did you get a chance to check it out? I think it would be best if we all worked together, as opposed to having 3 completely separate implementations of this pattern.

52t__nvidia.png nVidia 530M cpu.gif Intel Core i7 - 2.3Ghz 114229_30245_16_hardware_memory_ram_icon.png 8GB DDR3 RAM Windows7_Start.gif Windows 7 Ultimate (64x)

-----

IconVisualStudio16.png Visual Studio 2010 Ultimate google-Chrome.png Google Chrome PhotoshopLinkIndicator.png Creative Suite 5 icon28.gif FL Studio 10 MicrosoftOfficeLive.png Office 15

-----

csharp.png Expert cpp.png Professional lua_icon.png Expert BMX Programmer

-----

i-windows-live-messenger-2009.pngskype-icon16.pngaim_online.pnggmail.pngicon_48x48_prism-facebook.pngtunein-web.pngyahoo.giftwitter16.png

Link to comment
Share on other sites

Yes I'm checking it just now and agree to work on the same way and not waste time to create more libs. Mine was more an attempt for study purpose than a really implementation, I've seen that you added the GameEntity, AI and UI projects on SVN and I will work with you on those. I find useful if we release a complete game library, and may be an adapted editor with the concept of game entities if we have time. For the editor task would be very useful if the winform Control worked perfectly, so that the interface buttons and layout creation would not be a pain :)

?? FRANCESCO CROCETTI ??

http://skaredcreations.com

Link to comment
Share on other sites

So I've put my events & exposed methods to the test and it works!! This gives great ability to link events exposed via string name and methods via string name that the component creator exposes to be called. Component events are a dictionary with a key of string and the value is a list of a specific delegate sig. Component writers add the events they want to expose and in the OnUpdate() method would be where those events are fired.

 

I'm finding this communication between components to be very slick. It really allows visual programming when put into an editor. Since everything is string based it's very easy to do in an editor without any need for components to need to know about each other. Here is an example of the button and load level working together.

 

// build the player from components. the player itself is just a component
           player = new Component.Component(null);

           // create the components setting it's parent to the player component
           ComponentGUIButton.ComponentGUIButton button = new ComponentGUIButton.ComponentGUIButton(player);
           ComponentLoadLevel.ComponentLoadLevel loadLevel = new ComponentLoadLevel.ComponentLoadLevel(player);

           // add these components to the players list of components
           player.Components.Add(button.Type, button);
           player.Components.Add(loadLevel.Type, loadLevel);

           // make the link between when the button is clicked and the load level method being called
           player.Components["Component.GUIButton"].Events["OnClick"].Add(player.Components["Component.LoadLevel"].ExposedMethods["LoadLevel"]);

 

So the last line is the magic. You can see that via strings I'm able to link a components function (that the component creator specifically exposed) to another components event. Since it's a list it can also be chained. So in the above example, when the GUIButton component reads that it's been clicked (which the component handles in it's update method) it's fire off the OnClick event. Since the LoadLevel component linked one of it's functions to that event, that function will get called. The result is that when the button is clicked the LoadLevel() method of the other component will get called. All this and the development of these 2 components know 100% nothing about each other.

 

This information that describes game objects and their components and the links can all be in config files since it's all just string names. Each component will be it's own .NET DLL so in order to create them via strings I'll probably have to use some reflection.

 

This is exciting stuff!

Link to comment
Share on other sites

Nice you are mostly done, these are some suggestions from me if you like..

 

Optional owner parameter in the Component.Component constructor to avoid "new Component.Component(null)" in favor of "new Component.Component()".

 

You could save the developer from having manually add a component to its owner after creation, since you are passing it in the constructor than you can automatically add this to the owner's Components directly from the constructor itself.

 

What are the keys you used in your Components dictionary? I thought it was the name of the component, but you haven't assigned "Component.GUIButton" as name of "button".

 

For the loading from DLLs and reflection I generally use the following class to allow plugin management in my programs:

namespace PluginManager {

public class PluginManager {

	private Dictionary<String, PluginInfo> mLoadedPlugins = new Dictionary<String, PluginInfo>();

	public PluginManager( 
		String	pluginDirectory, 
		String	interfaceName) {

		ScanDirectory(pluginDirectory, interfaceName);
	}

	private void ScanDirectory( 
		String directoryName, 
		String interfaceName) {

		foreach (String fileName in System.IO.Directory.GetFiles(
			directoryName, "*.dll")) {
			AnalyzeAssemblyFile(fileName, interfaceName);
		}
	}

	private void AnalyzeAssemblyFile(
		String fileName, 
		String interfaceName) {

      try {
          System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFrom(fileName);
          foreach (Type Ty in asm.GetTypes()) {
            if (Ty.GetInterface(interfaceName) != null) {
				 mLoadedPlugins.Add(Ty.Name, new PluginInfo(Ty.Name, Ty));
            }                   
          }
		} catch (Exception Ex) {
			System.Diagnostics.Debug.WriteLine(Ex.Message);
        }
     }  

	public IEnumerable<String> GetPluginNames() {
		return mLoadedPlugins.Keys;
	}

	public object CreateInstance( 
		String pluginName) {
		return mLoadedPlugins[pluginName].CreateInstance();
	}
}
}

 

Its use (IPlugin is only an interface which you create as you need):

PluginManager.PluginManager mPluginManager = new PluginManager.PluginManager(".\\Plugins", "IPlugin");
List<String> plugins = mPluginManager.GetPluginNames();
IPlugin plugin = (IPlugin)mPluginManager.CreateInstance("IPlugin");

 

Hope could be useful to you.

?? FRANCESCO CROCETTI ??

http://skaredcreations.com

Link to comment
Share on other sites

Optional owner parameter in the Component.Component constructor to avoid "new Component.Component(null)" in favor of "new Component.Component()".

 

Yeah good call.

 

You could save the developer from having manually add a component to its owner after creation, since you are passing it in the constructor than you can automatically add this to the owner's Components directly from the constructor itself.

 

Yeah I'll do this also. I think the way I had it was left over from one of the iterations of this design I did.

 

 

What are the keys you used in your Components dictionary? I thought it was the name of the component, but you haven't assigned "Component.GUIButton" as name of "button".

 

That's the Type property. It's a virtual property that other components override. Components aren't given unique names like button1. They are stored per game object component as "Component.GUIButton". Per game object you would only ever have 1 of the same component type.

 

Thanks for the plugin class. I'll put that in and use that directly. Very nice.

 

 

So here is with the changes what it would look like to do this via code:

 

// build the player from components. the player itself is just a component
           player = new Component.Component();

           // create the components setting it's parent to the player component
           ComponentGUIButton.ComponentGUIButton button = new ComponentGUIButton.ComponentGUIButton(player);
           ComponentLoadLevel.ComponentLoadLevel loadLevel = new ComponentLoadLevel.ComponentLoadLevel(player);

           // make the link between when the button is clicked and the load level method being called
           player.Components["Component.GUIButton"].Events["OnClick"].Add(player.Components["Component.LoadLevel"].ExposedMethods["LoadLevel"]);

           player.OnUpdate();

 

Now, I'll come up with an xml file that describes the above game object and use that to create it. At that point the editor becomes a visual tool for build the xml file that describes all the game object components in a project. So basically a config file would end up describing ones entire game.

 

This is really looking like a very nice system. A true separation of components that programmers can create and distribute easily for others to use easily. I'll have to create a few more complex components to see if I'm missing any vital issues, but so far so good.

Link to comment
Share on other sites

Guest Red Ocktober

i've been following this... with interest... so lemme ask...

 

if i'm understanding this correctly, a player object in a game would be a collection (so to speak) of various components?

 

 

--Mike

Link to comment
Share on other sites

Storing only one component per type is not so flexible... i.e. how could I create a chain of actions (cast abilities or also more animation sequences) to my toon? Or group more meshes? I would consider into turning to component's name as key for the Components.

 

I'm not sure how Rick's code works, but you are supposed to be able to have more than one component of a type, as long as you think up a unique name for each one.

 

The container class stores a list of components, each of which is addressed by a string label that you give it.

Win 7 Pro 64 bit

AMD Phenom II X3 720 2.8GHz

GeForce 9800 GTX/9800 GTX+

4 GB RAM

Link to comment
Share on other sites

Guest Red Ocktober
It's a component itself and each component can own more components into it.

 

so, how's this gonna work Z... a player can have 2 leg components, 2 arms components, etc...

 

or is it intended for more higher level component inclusion... like a gun component, or a flag component, or stuff like that?

 

--Mike

Link to comment
Share on other sites

so, how's this gonna work Z... a player can have 2 leg components, 2 arms components, etc...

 

or is it intended for more higher level component inclusion... like a gun component, or a flag component, or stuff like that?

 

It can be at whatever level you like it to be. Well, at whatever level the component writer wants it to be. Personally I wouldn't go to the level of arm component, leg component although you could if you like. The company that did the Prototype game (I think) used the class name Behavior instead of component. That name might better describe what a component is. These components are really behaviors. So that company listed a few behaviors they had. For example they had a HitReaction behavior component.The general idea I think is to separate out these different behaviors as components.

 

 

As far as models go, one approach (the one I'll use) is to just have a 3DVisual component that has an attribute that stores the actual model. So if a game object needs a visual model you can attach this to it. That's important to see because that means game object components don't always have to be models in the game. They can be logical game objects also.

 

 

 

@ZioRed, I think I need more convincing that a game object component would want more than 1 of the same component. I don't know if I'm sold on 1 game object component wanting 2 or more 3D model components attached to it. I'm just thinking of Unity in this situation and to me it seems like if you wanted that you would create a separate game object component, and then parent that to the first game object component. Can you think of any other situations where multiple of the same components could be used? The reason I'm hesitant on this is because currently with the 1 component type per game object you don't have to worry about giving anything a name. The way I'll have it setup is that you could give a name to a component but that was more stored for the game object components so you could have something like "player" and then be able to query that game object component via it's name only.

 

Do you have a few other ideas of wanting the same component attached multiple times to the same game object component?

Link to comment
Share on other sites

Guest Red Ocktober

k... thx... just wanted to make sure i had the right idea behind what you guys are doing...

 

can ya start to see now why i was ribbing you guys with the rube-goldberg analogy...

 

it still seems as if your going around the dark side of the moon to get from new york to las vegas...

 

now i can see where this logic might be usefull, for example, in a completely component based simulation, where the outcome is to use different components to arrive at an assembly...

 

but keep in mind, each of these types systems has rules which the components designers must abide by... right... you seemed to have ignored this aspect...

 

second, and what i was saying way back at the beginning... you can accomplish this much simpler, whith much less code, without xml files, by used a class hierarchy approach...

 

sorry guys, but i still think this will result in nothing but an excercise... i haven't seen anything yet here to convince me otherwise...

 

but hey, maybe i'm wrong... i'm still following with interest... i can't wait to see what the outcome of all this is...

 

good luck...

 

 

--Mike

Link to comment
Share on other sites

@Rick: How do you create a GUI panel which must have 2 or more buttons since all the buttons have type "ComponentGuiButton"? I think that the Component itself should decide if it cannot be attached multiple times. Another example is the chain of "use ability" which I talked about, like "Dark Age of Camelot" in which you hit on some buttons from the quickbars to perform some ability action and the game add every action in a queue and do in sequence.

?? FRANCESCO CROCETTI ??

http://skaredcreations.com

Link to comment
Share on other sites

but keep in mind, each of these types systems has rules which the components designers must abide by... right... you seemed to have ignored this aspect...

 

I haven't. This is structure around how a game is to be made. It isn't a restrictive structure, since components can do anything you can imagine and anyone can code a component, but it does provide rules that need to be followed to provide common interfaces between components. This however in no way restricts any functionality ability though, so I'm happy with it.

 

 

second, and what i was saying way back at the beginning... you can accomplish this much simpler, whith much less code, without xml files, by used a class hierarchy approach...

 

I agree that you can accomplish this easier with a class hierarchy. This approach is more about maintenance, flexibility, reuse, and code sharing. For me it's also about creating a framework for non programmers to be able to use components visually to create logic without programming. Plus an ability for programmers to create components for others to use. These components would be as plug and play that one can get.

 

For me, this is a game engine design using LE as the graphics/input library.

Link to comment
Share on other sites

@ZioRed Each gui element would be it's own game object component. So you would have 3 game object components in your example. Then attach the correct component to each one. So you would have a panel game object, and 2 different button game objects. Then attach the PanelComponent/Behavior to the panel game object, and etc for the buttons. To me that is actually a simple example of how this would work.

 

I don't really know what you other example really means. Could you provide a specific idea around it? Maybe that'll help me understand that example if there was more specifics around it.

Link to comment
Share on other sites

Guest Red Ocktober
This approach is more about maintenance, flexibility, reuse, and code sharing. For me it's also about creating a framework for non programmers to be able to use components visually to create logic without programming. Plus an ability for programmers to create components for others to use. These components would be as plug and play that one can get.

 

i like this... lets quote this and use it as a point of reference for future discussion... ok...

 

there's already a sense of competition between GameObjects and your components approach... and i like your statement above... seems like it defines a worthwhile set of goals...

 

so lets use that to gauge how close the results of each comes...

 

 

--Mike

Link to comment
Share on other sites

Well I guess I wasn't considering this a competition but I can see how it could be seen that way. I don't plan on hitting any sort of strict timeline or anything like that as this is my spare time that I'm working on this, so the time spent isn't very constant. For me this is more about trying to find a new way to allow non programmers to create games with functionality without typing any code and without being restrictive like a FPS creator would be.

 

The bad thing about this design is that it really requires editor support that the current LE editor can't provide. So I'll be creating an editor around this design to aid in the creation of the game object components visually instead of directly via code or xml files. The editor basically will generate the xml files from visual feedback from the users. That in itself presents a ton of challenges.

Link to comment
Share on other sites

Guest Red Ocktober

actually... i didn't look at this as a competition either... initially i didn't even give you a chance of succeeding and completing this effort...

 

but you seem determined... and that's half of any battle...

 

as far as a time frame is concerned... noone mentioned any time limits...

 

as far as the editor is concerned... this is what i've been trying to tell you all along... your approach isn't gonna save anyone any work... and maintenance wise... it's just gonna add more work...

 

by adding the need for an additional editor you've already introduced another step in the workflow... (actually, i don't think you've even thought that all the way through... the sbx file is a text file that can be parsed by any xml or if necessary, preprocessed to accomodate whatever xml template you're proposing)...

 

the idea making a new editor just to accomodate a design concept, i think is really going toooo far out on the fringe...

 

 

anyways... you've got other heads to collaborate with... me... it's just me... and i don't have the luxury of wasting time like you can...

 

by the end of the week i'll be finished with the GameObjects framework, and using it to progress with game development...

 

it won't need any additional editor... it'll seamlessly use the current (for me LW 2.26) editor so that anyone can add GameObjects...

 

the code will be as simple as A.. B..C.. to use... any four year old who can read and type will be able to make a game with this...

 

simplicity is power...

 

 

--Mike

Link to comment
Share on other sites

This is what I have so far with my C# property container. I'm copying Steve Yegge's Universal Design pattern, so it will work differently to Rick's and may be too slow in the end (I hope not).

 

I haven't posted the code yet, because it might have a minor bug or two that I can't see. So if anyone thinks of some good tests that might break something like this, please post them.

 

public class StateData
{
   private string mName;
   private LinkedHashMap List;
   private bool mIsReadOnly;
   private StateData mParent;
   private const string cBlank = "$_Blank_$";


   public StateData(string name)
   public void SetParent(StateData parent)

   // If the key in your parent exists, create a new key here
   public void Put<T>(string key, T val)
   public bool Get<T>(string key, ref T ret_val)
   public bool Has<T>(string key)
   public void Remove<T>(string key)
   public void SetIsBlank<T>(string key)
   public void PrintContents()
}

 

Sample usage:

 

       StateData state_parent = new StateData("parent");

       // ****************************
       // state_parent
       // ****************************

       state_parent.Put<float>("floatKey1", 6.0f);
       state_parent.Put<string>("stringKey1", "this is a string");
       state_parent.Put<int>("intKey1", 1);
       state_parent.Put<uint>("uintKey1", 1);

       // ****************************
       // state_child
       // ****************************

       StateData state_child = new StateData("child");
       state_child.SetParent(state_parent);

       state_child.Put<uint>("uintKey2", 122);
       state_child.Put<uint>("uintKey1", 7);
       state_child.Put<uint>("uintKey1", 99);

       state_child.SetIsBlank<int>("intKey1");
       //state_child.Remove<int>("intKey1");

       int int_val = 0;
       if (state_child.Get<int>("intKey1", ref int_val))
       {
           Console.WriteLine("intKey1 = " + int_val);
           Console.WriteLine("");
       }
       else
       {
           Console.WriteLine("Get intKey1 failed ");
           Console.WriteLine("");
       }


       state_child.PrintContents();

 

Once you give the child a parent, the parent's data becomes read only. If the child wants to Put a variable (create or set) that already exists in the parent, it creates a copy in itself and henceforth accesses it instead of the one in the parent.

 

Setting a variable as blank, forces the child class to read it (as null) instead of an identically named variable that may exist in the parent. That way you can get a 'variable does not exist' message back, even if an identically named one exists in the parent.

 

If you Remove a variable, it will be removed from the child list completely and from then on a call to Get will search for the same named variable in the parent.

 

Yes I know its a very odd way of doing things. You would have to read Steve Yegge's article to understand the reasons for it.

Win 7 Pro 64 bit

AMD Phenom II X3 720 2.8GHz

GeForce 9800 GTX/9800 GTX+

4 GB RAM

Link to comment
Share on other sites

Looking good Davaris. Have you tried to get an real world example working yet?

 

 

 

Here is my new main program. This would be the main exe and the entire game would be defined by an xml file and the components that make up the game. This code will be of no concern to the people making games with this. The entire game executable will just be this code. All a user needs to do it pass the xml file that the editor will create to this program and their game will run! Or I hope so anyway :D

 

public void Run(string[] args)
       {
           if (args.Length <= 0)
               return;

           plugins = new PluginManager.PluginManager(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Plugins"), "Component");

           // this is the 1 master game object. since we have parent/child chains making this the parent of every game object will allow us to
           // be able to query for any game object or component attached to a game object from any other components if we so choose to.
           baseObject = new Component.Component();

           // this is where we would read in the xml file and loop through all game objects creating them and adding their components based off the xml file
           XmlDocument doc = new XmlDocument();
           doc.Load(args[0]);

           XmlNodeList gameObjects = doc.GetElementsByTagName("game_object_component");

           foreach (XmlNode node in gameObjects)
           {
               List<EventInfo> eventInfo = new List<EventInfo>();

               XmlElement gameObjectElement = (XmlElement)node;

               string name = gameObjectElement.Attributes["name"].InnerText;

               // create the game object component here
               Component.Component gameObject = new Component.Component(baseObject, name);

               foreach (XmlNode component in gameObjectElement.GetElementsByTagName("component"))
               {
                   XmlElement componentElement = (XmlElement)component;

                   string type = componentElement.Attributes["type"].InnerText;

                   // create this component and add it to the game object
                   plugins.CreateComponent(type, gameObject);

                   foreach (XmlNode events in componentElement.GetElementsByTagName("event_link"))
                   {
                       XmlElement eventElement = (XmlElement)events;

                       string eventName = eventElement.Attributes["name"].InnerText;
                       string toComponent = eventElement.Attributes["to_component"].InnerText;
                       string method = eventElement.Attributes["method"].InnerText;

                       // store off this event/method link and add after all components have been added through this game object
                       eventInfo.Add(new EventInfo(type, eventName, toComponent, method));
                   }
               }

               // make the link between events and methods
               foreach (EventInfo e in eventInfo)
               {
                   gameObject.Components[e.eventComponent].Events[e.eventName].Add(gameObject.Components[e.toComponent].ExposedMethods[e.toMethod]);
               }
           }

           // this is testing to make events happen
           baseObject.Components["player"].Components["Component.Health"].Attributes["health"] = 0;

           // this one call updates every game object because of parent/child of everything. would be in a loop but outside for testing reasons
           baseObject.OnUpdate();

           while (true) { }
       }

 

 

Here is an example of an xml file that I pass to this.

 

<game_objects>
           <game_object_component name="player">
                       <component type="Component.Respawn"> <!-- this tells us what type to create from the type loaded from the dll's-->
                                   <event_link name="OnRespawn" to_component="Component.Health" method="Respawn" />
                                   <!-- can have multiple links to the same events -->
                       </component>
                       <component type="Component.Health">
                                   <event_link name="OnDeath" to_component="Component.Respawn" method="Death" />
                       </component>
           </game_object_component>
</game_objects>

 

 

So now onto the editor to help create this xml configuration file! This will be the rough part I think :o

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...