tag:blogger.com,1999:blog-87682859181862481632009-03-28T09:04:04.955-07:00the xna machine<b>the xna machine</b>emachine74noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-8768285918186248163.post-7487451855995168222008-10-14T11:27:00.000-07:002008-10-14T12:50:37.595-07:002008-10-14T12:50:37.595-07:00The WPF MachineWell I'm abandoning my XNA development efforts in favor of WPF/Silverlight. Primary reasons being:<ul><br /><li>WPF/Silverlight does not have a language barrier (you can use VB, etc.)</li><br /><li>The distribution agility of Silverlight vs. XNA is off the chart. The Silverlight plugin does not require Windows (let alone the .NET framework). It's simply a browser plugin ala Flash, available for both Mac and Windows.</li><br /><li>XNA Click-once. Very cool for C# XNA developers, but the prospect of creating a Click-once VB hack/workaround/replacement for VB XNA apps really ups the proverbial "is this really worth it" ante.</li><br /><li>The basis of my 2D XNA engine (i.e. the matrix hierarchy, and a scripting engine (which I never covered, but basically it allows you to target any property of a specific data type and interpolate it over time)) this was all very cool indeed. And it all already exists, practically verbatim, in WPF*.<br /><br /><font size=2>*While it's a bit satisfying to see I came up with the same solution that Microsoft did for scripting any object with time-based animation, it's also a very major /facepalm to see I've been reinventing the wheel for the past several months!</font></li><br /><li>WPF appears to be the future of Windows development, which I'd like to stay on top of for both my career and personal interest. What's more, you can totally <a href="http://silverlight.net/blogs/msnow/default.aspx" target="_new">make games with it</a>. You can even use <a href="http://wpfwonderland.wordpress.com/2008/10/06/wpf-shader-effects-library-posted/" target="_new">GPU shaders</a>. No, it's not as powerful as XNA but you have to admit; the whole ordeal is rather neato.</li></ul><br />So someday a new blog might crop up somewhere with my WPF/Silverlight stuff on it (altho for now I'm still pretty green). I still have those game ideas tucked away waiting for implementation... someday.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-748745185599516822?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-43838227684056620432008-06-20T03:18:00.000-07:002008-06-20T14:11:38.178-07:002008-06-20T14:11:38.178-07:00Off topicAs a potentially negative side-effect to now giving these blog posts more descriptive labels, there are now several non-XNA related topics on this blog.<br /><br />Namely, Off topic.<br /><br />Here are some Off topic links I highly suggest:<ul><li><a href="http://www.mozilla.com/en-US/firefox/" target="_new">FireFox 3</a></li><li><a href="http://www.spore.com/trial" target="_new">Spore Creature Creator</a></li></ul><div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-4383822768405662043?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-43597336381025338932008-06-19T13:32:00.001-07:002008-06-20T23:30:38.423-07:002008-06-20T23:30:38.423-07:00LINQ to SQL Server CEIn Visual Studio 2008, when trying to drag a local SQL Server CE database table onto your LINQ design surface, you receive the following error:<br /><br />"The selected object(s) use an unsupported data provider."<br /><br />This message is a little misleading, because you <i>can</i> in fact LINQ over your SQL Server CE database; it's just the drag &amp; drop LINQ design surface in Visual Studio that doesn't know how to interact with SQL CE.<br /><br />To work around this limitation of the IDE, you can call upon Microsoft's <a href="http://msdn.microsoft.com/en-us/library/bb386987.aspx" target="_new">SqlMetal</a> utility to create a .dbml file (aka, a new LINQ to SQL item) on your behalf. Here's a reasonably simple way to accomplish it:<br /><br />In your project's solution explorer, right-click the node containing your .sdf (database) file and "Open Folder in Windows Explorer". In the resultant window, create a new text document called MakeCeLINQ.bat and edit it, entering this command:<br /><br />C:\"Program Files\Microsoft SDKs\Windows\v6.0a\bin\sqlmetal.exe" /dbml:CeLINQ.dbml <i>Database</i>.sdf<br /><br />Replace <i>Database</i> with whatever your .sdf file is called. It's important that this batch file be created in the same folder as your .sdf file, since the command is structured in such a way that assumes the .sdf file is sitting next to the batch file. Close &amp; save MakeCeLINQ.bat, and double-click to run it. SqlMetal will create CeLINQ.dbml.<br /><br />In Visual Studio, click the button to show all files for your project and include your newly created CeLINQ.dbml file. Include the "MakeCeLINQ.bat" file, too - strictly speaking it's no longer required, but you may find it convenient to keep it with your project.<br /><br />So now you should have something like this:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/CeLINQ.png" /><br /><br />You can now work with the CeLINQ object within the IDE as normal (with the ever-present exception of drag &amp; drop functionality). If you change any of the schemas in your CE database, simply re-run the MakeCeLINQ.bat to update the CeLINQ object.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-4359733638102533893?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-36465177311414959812008-06-17T03:12:00.000-07:002008-06-20T03:07:41.281-07:002008-06-20T03:07:41.281-07:00LINQI did a little project for my wife this weekend and used LINQ for the first time. Visual Basic's implementation of LINQ over XML is just about the greatest thing since sliced bread.<br /><br />Looking at my Web Updater, I get an xml file called the "wum" (web update manifest, which contains a list of server-side files to compare against a client for making decisions about which files to update). To read these wum.xml files in my Web Updater, I was using an XMLReader to simply parse the file in a serial fashion. Which worked fine, really; although there was not any "knowledge" of the xml document itself within the IDE or the Web Updater code, per se; the parsing routine was a bunch of XMLReader.Read statements, with the occasional variable assignment thrown in whenever certain nodes were reached. Without any prior knowledge of the XML file in question, the parsing process is not exactly intuitive to behold, let alone modify/debug.<br /><br />With LINQ, this task of reading the wum.xml to determine which files on the disk require an update can be accomplished in a <span style="font-style:italic;">radically</span> different fashion.<br /><br />First of all, rather than simply associating a wum.xsd schema with the XMLReader's xml settings, you can drop said schema into the solution explorer / project itself and in doing so, define an XML <span style="font-style:italic;">type</span> much in the same way you'd define any given class. With the xml schema now available to the compiler, VB provides you with "xml intellisense", allowing you to traverse an xml document in no less than xml statements such as objMyDocument.&lt;customer&gt;.&lt;name&gt;.&lt;first&gt;.Value and things of that nature. It's a little strange at first but pretty fantastic. This obviously injects a great deal more knowledge of the xml structure itself into the code - now someone viewing or even editing this code for the first time has a far less abstract situation on their hands.<br /><br />If that weren't enough of an improvement, I realized I didn't really have to serially process the wum.xml at all. I can take that XMLReader I'd been using to create an Xml.Linq.XDocument. An XDocument is basically an xml document instance but it's queryable by LINQ - so you can treat it like a database. Now I can save literally dozens of XMLReader calls & parsing effort by simply querying the XDocument Where Not IO.File.Exists OrElse IO.File.LastWriteTime <> (queryResult.LastWriteTime) and viola, an IEnumrable object of XElements matching the query are returned. In addition, the query result can be stuffed into any given appropriate class of your choosing, so you don't even have to deal with XElements in the result set if you don't want to, and then ultimately .ToList(ed) into a generic List(Of &lt;whatever&gt;).<br /><br />It's all slightly mind-blowing the first time you sit down to use it, particularly when you consolidate 50 lines of mundane XML parsing into a single LINQ query in a matter of minutes, never even having used LINQ before. Just imagine what you can do with this stuff once you actually know how to use it... pretty cool!<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-3646517731141495981?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-76609707330051478462008-06-14T16:36:00.001-07:002008-06-20T03:07:41.282-07:002008-06-20T03:07:41.282-07:00Scope creepI updated the graphics of this blog a little bit. I'd been playing around with a VB/XNA logo hybrid in photoshop so I thought I'd use it for something.<br /><br />I've been working very hard on my Web Updater and Wummer applications over the past 2 weeks and I haven't been able to resist putting more and more functionality into them. They're turning into things which may actually be quite useful.<br /><br />The Wummer will now let you split up the files you want to publish over however many different hosts you specify; so for instance if you have 3 hosts at your disposal but they're slow uploaders (eg., homebrew DSL &amp; Cable broadband line hosting like I use for most things) the Wummer allows you to divide the publication of files amongst multiple hosts, and the manifest now groups files by host.<br /><br />You can also change the relative path binding of each file, so that any given number of remote URLs can map to the same local client install directory. This all required a somewhat extensive storage system for the Wummer, so I chose to use an sql server ce database, which keeps track of all the projects and files and hosts etc. of every probject you've ever published using the Wummer.<br /><br />In addition to being able to react property to the new manifest files, the Web Updater will now also attempt to update <span style="font-style:italic;">itself</span> by temporarily cloning it's own executable and launching it with the Web Updater's own manifest file specified. In this way the Web Updater sort of "cheats", using a copy of itself to update itself, which I thought was cute.<br /><br />There are a few small but important touches to add to the Web Updater before it's ready for prime time, right now I've got the XNA Content Assistant performing updates with it and it's working pretty well, it's just not quite as elegant as I would like yet (currently there is much shutting down and restarting of programs during an update process which, while automatic and not necessarily cumbersome to the user, is unprofessional looking and some of it can be avoided).<br /><br />At some point I would like to consolidate all this into an "xnamachine software portal" application, which installs the prerequisites &amp; runtimes on your PC (such as the Visual C++ redist for XNA games, .net 3.5, sql server ce, and provide convenient links to the XNA redist &amp; DirectX web installer, which are easier for the user to just run themselves at this time). That way, there would only ever be 1 thing you would ever have to "install" from xnamachine.com in order to use anything I release. The portal would be an updatable application launchpad that provides links to whatever I make available; the Wummer, the XNA Content Assistant, templates for Visual Studio, XNA games, whatever. Such a thing would not be very difficult to make so I may go ahead with that as a software distribution plan, unless I think of some horrible drawback to doing it that way.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-7660970733005147846?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-49318121558280745242008-05-24T02:24:00.000-07:002008-06-20T03:06:54.524-07:002008-06-20T03:06:54.524-07:00The wummerThe core of the Web Updater program is complete; it can now connect to web servers, obtain wum.xml files, validate them against a public wum schema, perform a differential check against the locally installed files on the client and download the obsolete/missing files. After downloading it can now replace the files on the client via decompression (if the wummer tool had compressed them when publishing) or, just renaming them into place. <br /><br />My "wummer" publishing tool is finished, save a few UI nuances, and so together they have created a functioning publish/update circuit.<br /><br />The Web Updater still needs a few more things; certain disaster recovery scenarios still must be accounted for, like if it can't access files it's trying to update or it's connection drops while downloading. It also still needs the ability to accept command line arguments when it runs, and finally it needs the ability to re-launch the target application it's updated. So I guess it's about 85% complete. <br /><br />Here it is, dutifully "updating" a bunch of nonsense files. I added the little treeview to it this evening, just for fun.<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/wubeta.png" /><div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-4931812155828074524?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-6225778267363253032008-05-20T01:12:00.000-07:002008-06-20T03:06:42.124-07:002008-06-20T03:06:42.124-07:00wumSo the XNA Content Assistant is largely finished and I wanted to distribute it using some kind of web update model, to make it easier for all of you to update the Assistant in the future. So I built a little update functionality into the Assistant under the Visual Studio ClickOnce model.<br /><br />ClickOnce is really neat and very easy to use, but unfortunately for the XNA Content Assistant it is unacceptable. The problem is that you can't effectively launch a ClickOnce application from another program e.g. via Process.Start while passing command line arguments. At best, you can submit a query string to a web-based application, but having the Assistant be entirely web based is not acceptable. The Content Assistant must be installed on the local machine and must be callable via command line with arguments.<br /><br />Therefore I abandoned the ClickOnce model and for the past couple days I've been crafting a web update system. This is something I will require anyway, to allow updates to my future XNA applications, so it's somewhat of an inevitable project.<br /><br />The Web Updater does its best to be somewhat generic; in a nutshell it just needs to know the URL of your application's "wum.xml" file (Web Updater Manifest), a simple xml file which describes the current files available on the update server (name, size, last update, etc). The Web Updater grabs the wum and checks it against whatever application it's updating on the local machine, and updates files as necessary.<br /><br />To that end, <span style="font-style:italic;">now</span> I'm in the process of writing a companion tool for wum publishing called the Wummer. The Wummer is for developers, we point it at our Release folder (or, whatever you want to publish), and the Wummer uses an IO.CompressionStream to publish your application to your web host as .gz files, creating the wum.xml file for you (an otherwise tedious task).<br /><br />So I've been pretty busy working on all this lately, juggling work and 3 of my own little mini-projects! I believe these will all be helpful tools though and I hope to have them finished up pretty soon.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-622577826736325303?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-22055197028470683882008-05-14T16:46:00.000-07:002008-06-20T03:05:53.094-07:002008-06-20T03:05:53.094-07:00XNA Content AssistantThe logistics of how content gets built in the VB template will change when GS 3.0 is released. Rather than hard-code all of the content building specifics into the VBContentManager class, this functionality has been moved into a stand-alone application, which will act as a bridge between a non-Game Studio project and the Content Pipeline. This application is called the XNA Content Assistant, and is a Windows form application with a graphical interface. However, it also supports being called from the command line in an invisible mode.<br /><br />I'm currently putting the XNA Content Assistant through beta, naturally I want to debug such an important component thoroughly. It is functional at this time, and so here's a preview of what it looks like, and what it does:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/xnaca1.jpg" /><br /><br />You start out by opening your game's Visual Studio or Visual Express project file. (when running from a command line, you would supply this as an argument)<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/xnaca2.jpg" /><br /><br />Based upon the location and contents of the specified project file, the XNA Content Assistant will automatically determine where your content folder is (even if you did not name it "content"). It will then automatically determine which XNA framework version your game is programmed against. The XNA Content Assistant understands how to build content for Game Studio Express (1.0 Refresh) projects, Game Studio 2.0, and Game Studio 3.0 projects created in either VB or C# in Visual Studio 2005, 2008, and the Express editions.<br /><br />On the GUI, we can see the Assistant has successfully harvested content from my "Timeclock" application, and I am proceeding to select a platform (this would be the 2nd and final parameter you would need to supply via command line)<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/xnaca3.jpg" /><br /><br />And lastly when we execute the build process, we can see that MSBuild's output is integrated nicely into the Assistant's UI, to help troubleshoot build issues such as missing fonts, etc. It also exports a log and a copy of the content project submited to MSBuild in your project's folder for reference.<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/xnaca4.jpg" /><br /><br />As future XNA versions are released, XNA Content Assistant will be updated and your existing template can simply continue to utilize the updated Assistant, rather than requiring code changes to the template itself (i.e., the VBContentManager class, which will be largely obviated by the Assistant).<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-2205519702847068388?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-10775187234306619432008-05-10T12:47:00.000-07:002008-06-20T02:49:59.565-07:002008-06-20T02:49:59.565-07:00XNA 3.0 CTPThe <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=DF4AF56A-58A7-474C-BFD0-7CF8ED3036A3&displaylang=en" target="_new">XNA/GS 3.0 CTP is out</a> and I'm in the process of updating the VBContentManager in preparation for the official release.<br /><br />XNA 3.0 content processing expects to use the .NET Framework v3.5 edition of MSBuild. For those not interested or ready to move to XNA 3.0, XNA 2.0 compatibility will be retained in the VBContentManager. <br /><br />To that end, the GS 3.0 release will mark a small change in how the VBContentManager works. A version enumeration is now required by the VBContentManager class, and moving forward, the VBContentManager will be augmented with new version support (rather than replaced wholesale) so that future editions of the VBContentManager can target prior XNA versions (dating back to 2.0). Just pass in which XNA version you're targeting, and it will process your content accordingly.<br /><br />The VBContentManager currently available for download was never updated with functional, XACT-based audio importing. I do have this working in my "production" VBContentManager, and as part of the 3.0 update I will include this for backward compatibility with 2.0's XACT-based audio. And of course the new audio importing for 3.0 will be available as well, which is what I'm working on now.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-1077518723430661943?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com4tag:blogger.com,1999:blog-8768285918186248163.post-54117315075521457732008-05-10T00:22:00.000-07:002008-06-19T16:06:53.138-07:002008-06-19T16:06:53.138-07:00Microsoft.VisualBaggageHere's an interesting article on Paul Vick's blog (not exactly new, but it was to me): <a href="http://www.panopticoncentral.net/archive/2007/05/31/20766.aspx" target="_new">VB Runtime agility, Orcas and new platforms</a>.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-5411731507552145773?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-34549541113021649422008-04-20T23:02:00.000-07:002008-06-20T02:50:23.939-07:002008-06-20T02:50:23.939-07:00TouringSomeone asked the creator's club a question about writing a <a href="http://en.wikipedia.org/wiki/Mille_Bornes" target="_new">Mille Bornes</a> game. I remember liking that game as a kid, but the first version of it I played was the older more simplistic game of <a href="http://en.wikipedia.org/wiki/Touring_%28card_game%29">Touring</a>.<br /><br />I've been thinking of putting together a simple game to test the XNAMachine engine with, something a little more simple than Star Maze just to see what it's like to build something with it, and this seemed like the perfect thing. So rather than <span style="font-style:italic;">completely</span> rip off that guy's idea of making Mille Bornes, I've been putting together a Touring card game (ok, so I'm <span style="font-style:italic;">mostly</span> ripping off his idea...)<br /><br />Making a 1-player Touring game would be a little "too easy" I think, so I'm building both server & client programs to allow for multi-player sessions of Touring to be started by whomever wants to play. If the engine proves to be a decent performer for this application I'll begin the process of "porting" what's completed of my Star Maze project over to the XNAMachine engine.<br /><br />At this point I've got most of the object structure done for Touring, the game logic is easy, and so now I'm writing the network code for the server. There are no movement predictions or tight synchronizations to worry about here, the server & client really just need to relay simple commands & acknowledgments back & forth so it's a really good starting place for network code I think.<br /><br />At the risk of enduring a copyright violation lecture I've obtained some rather pristine editions of old Touring games from eBay as texture sources, and I've been browsing around a neat little website called <a href="http://www.soundsnap.com/" target="_new">soundsnap</a>, which has a lot of nice free sounds & music loops.<br /><br />This is something of an unexpected diversion but I guess that's typical; the hardest part about programming is finishing something, right? No worries...<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-3454954111302164942?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-70326417266756059612008-04-08T14:48:00.000-07:002008-06-19T16:07:43.048-07:002008-06-19T16:07:43.048-07:00Vacation 2I'm in Japan this week. I don't speak or read Japanese so sometimes it feels a little crazy, but at least we haven't gotten lost yet. The train system here was a challenge at first (especially to a foreigner from a small US town without any serious public transport in the first place). But a lot of things are in English of course so it's not so bad. Today we're in Kawaguchi which is a small country town just north of Mt. Fuji. Just hanging out here, it's like a mountain resort type of place.<br /><br />I wanted to get some good work done on the XNAMachine engine on the 11 hour flight, but after a couple hours I realized I didn't have the right power adapter for the plane's weird DC plug.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-7032641726675605961?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-62566800238541348952008-04-03T01:00:00.000-07:002008-06-20T03:09:04.236-07:002008-06-20T03:09:04.236-07:00The XNAMachineI'm continuing to plug away at my new engine here and there, mainly by jotting ideas down into classes & interfaces, and then shaping them into workable objects. It's kind of like throwing lumps of clay down into the IDE & then shaping them into recognizable things. It's probably the world's most inefficient way to design something but I'm having fun with it.<br /><br />The XNAMachine (my obvious name for this new engine) has grown into 5 primary categories which I've split into as many Namespaces within the project. The primary namespaces are, in no particular order<ul><li>Core - this contains classes such as the base Game class, the VBContentManager, a static math helper class, all the primary singleton stuff</li><br /><li>Objects - the base game object is defined in here along with a base game component for including objects in the XNA Game's components collection. Future items in here will be other common bases; screen, camera, and some sort of base spacial partitioning class either a grid or a quadtree or both</li><br /><li>Augments - a base class + various primitive implementations of game object expansion pieces</li><br /><li>Automations - this is the bit I've been working on the past couple of days, it's a system of rudimentary classes that primarily hold information which can be used to modify various properties of augments. Automations are intended to be Augments, but it's such a big category of "stuff" that it got it's own namespace. An example of an Automation Augment would be, for example, if you have a Transformation augment attached to a game object, the game object gains the ability to be transformed by a matrix. You can then attach an automation augment to that game object, specify which element of the transformation object you want the automate, and this will allow for "scripting" various activities over pre-defined times & destinations. I have a system like this now, it's just not nearly half as elegant as this revision will hopefully be</li><br /><li>Augment Update Services - the way I'm experimenting/planning to update augments is to create a collection of service implementations that perform various tasks, each built to accept a single augment at a time. Every single augment will have a reference to the service it uses to update itself, so when an augment wants to update, it simply passes itself in to it's service reference.</li></ul>So that's kind of the gist of it so far, and I'm getting reasonably close to finishing up the classes in the Automation Namespace, but my wife is hardcore giving me the stink-eye so I better turn my laptop off before I get busted.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-6256680023854134895?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-88646610598280838942008-03-23T14:05:00.000-07:002008-06-20T03:09:04.239-07:002008-06-20T03:09:04.239-07:00CompositionWell I've been bitten by the Composition bug. In a big way. So to satiate my newfound curiosity, I must confess I've been spending time on an experimental clone of my 2D engine using a technique of dynamic <a href="http://en.wikipedia.org/wiki/Object_composition" target="_new">Composition</a>.<br /><br />The Composition version of the engine is based around a single "Game Object" class. This class is effectively nothing more than a collection of IAugmentation interfaces.<br /><br />In my existing engine, the Sprite class has become quite large; what's more, not all Sprites always utilize every member and feature of the class. By partitioning this class into several different stand-alone "mini-classes" (all of which implement the IAugmentation interface), these mini-classes become "Augmentations". From this, one can start with an empty game object and build it into anything by adding the appropriate behavioral and/or data Augmentations to it's IAugmentation collection.<br /><br />What's seductive about the Composition model is that this building process is not significantly different between design time and run time. To me this is the primary advantage of using Composition over Inheritance, which AFAIK is largely static at run time. I.e., once you define your class that inherits from &lt;whatever&gt;, you're stuck with that at run time. With Composition, you could simply remove one Augment and add another.<br /><br />My only concern with what I've developed so far is that dictionary lookups are becoming somewhat frequent - i.e., I find myself querying whether or not a game object has been augmented with &lt;augmentX&gt;, and if so, retrieving that augment for use. These queries don't exist so much in the Inheritance model, and this aspect may require some fine-tuning as things get progressively more complex. But, we shall see.<br /><br />Fun stuff!<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-8864661059828083894?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com2tag:blogger.com,1999:blog-8768285918186248163.post-49746119402103404022008-03-11T21:41:00.000-07:002008-06-20T02:53:26.646-07:002008-06-20T02:53:26.646-07:00PointAt v2.0In Tutorial 6 (The PointAt method), I suggested a way one sprite can "point" at another, given totally arbitrary tree nesting / matrix transform between the two objects. The code was:<br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Point&nbsp;one&nbsp;sprite's&nbsp;origin&nbsp;at&nbsp;another&nbsp;sprite's&nbsp;origin</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;PointAt(<k>ByVal</k>&nbsp;thePointer&nbsp;<k>As</k>&nbsp;Sprite,&nbsp;<k>ByVal</k>&nbsp;theTarget&nbsp;<k>As</k>&nbsp;Sprite)<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Dim</k>&nbsp;objTargetWorldPosition&nbsp;<k>As</k>&nbsp;Vector2<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>If</k>&nbsp;theTarget.Parent&nbsp;<k>IsNot</k>&nbsp;<k>Nothing</k>&nbsp;<k>Then</k><br /><n style="padding-left:88px;">&nbsp;</n>objTargetWorldPosition&nbsp;=&nbsp;Vector2.Transform(theTarget.Location,&nbsp;theTarget.Parent.DrawMatrix)<br /><n style="padding-left:56px;">&nbsp;</n><k>Else</k><br /><n style="padding-left:88px;">&nbsp;</n>objTargetWorldPosition&nbsp;=&nbsp;theTarget.Location<br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><br /><n style="padding-left:56px;">&nbsp;</n>thePointer.Rotation&nbsp;=&nbsp;0<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>With</k>&nbsp;Vector2.Transform(objTargetWorldPosition,&nbsp;Matrix.Invert(thePointer.DrawMatrix))&nbsp;-&nbsp;thePointer.Origin<br /><br /><n style="padding-left:88px;">&nbsp;</n>thePointer.Rotation&nbsp;=&nbsp;<k>CSng</k>(Math.Atan2(.Y,&nbsp;.X)&nbsp;+&nbsp;(Math.PI&nbsp;/&nbsp;2))<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>With</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />And that works, but I realized an alternative way to solve the same problem today. I haven't compared the raw performance of this new way vs. the other, so I can't say if it's <span style="font-style:italic;">faster</span>... but I suspect it might be.<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Gets&nbsp;the&nbsp;radian&nbsp;between&nbsp;two&nbsp;sprite&nbsp;origins</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Function</k>&nbsp;PointAt(<k>ByVal</k>&nbsp;thePointer&nbsp;<k>As</k>&nbsp;Sprite,&nbsp;<k>ByVal</k>&nbsp;theTarget&nbsp;<k>As</k>&nbsp;Sprite)&nbsp;<k>As</k>&nbsp;<k>Single</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Dim</k>&nbsp;a,&nbsp;b,&nbsp;c&nbsp;<k>As</k>&nbsp;Vector2<br /><br /><n style="padding-left:56px;">&nbsp;</n>Vector2.Transform(thePointer.Origin,&nbsp;thePointer.DrawMatrix,&nbsp;a)<br /><n style="padding-left:56px;">&nbsp;</n>Vector2.Transform(theTarget.Origin,&nbsp;theTarget.DrawMatrix,&nbsp;b)<br /><br /><n style="padding-left:56px;">&nbsp;</n>c&nbsp;=&nbsp;b&nbsp;-&nbsp;a<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Return</k>&nbsp;<k>CSng</k>(Math.Atan2(c.Y,&nbsp;c.X)&nbsp;+&nbsp;MathHelper.PiOver2)<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Function</k><br /></div><!-- <br />END --><br />I made it a function that returns the radian value, so that you can do with it whatever you so choose - either assign it to the Rotation value of thePointer, or just query the angle between two things for whatever reason.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-4974611940210340402?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-40922759844172995012008-03-07T12:37:00.001-08:002008-06-20T02:53:36.461-07:002008-06-20T02:53:36.461-07:00Uninventing some wheels - Stacks & DictionariesYou know the "Hashtable(Of Type)" class used by the VBContentManager?<br /><br />There's actually a <a href="http://msdn2.microsoft.com/en-us/library/xfhwa508(VS.80).aspx" target="_new">generic Dictionary class</a> in the .NET framework that serves the same purpose.<br /><br />The Dictionary class provides two type predicates, one for the key and one for the value, whereas my Hashtable class simply presumes the key to be of type String. We could do away with the Hashtable(Of Type) class all together, and simply start using Dictionaries.<br /><br />The other thing I started messing with is the <a href="http://msdn2.microsoft.com/en-us/library/3278tedw(VS.80).aspx" target="_new">generic Stack class</a>. My current ScreenManager class keeps track of Screen order via some rather sloppy and unnecessary list manipulation. But all of that can be done away with and replaced with a Stack(Of Screen). <br /><br />Stacks are a very convenient way to maintain Screen order within a ScreenManager, because there is a natural synchronization between the inherent nature of Stacks and the way we display Screens. <br /><br />At the beginning of the program, push Screen1 onto the Stack. When you transition away from that Screen1 to Screen47, pop the Stack (removing Screen1) and push Screen47 onto the Stack. If you want to display Screen2 <span style="font-style:italic;">over</span> Screen47, simply push Screen2 onto the Stack. Now Screen2 is above Screen47, which is exactly what we wanted to display. When Screen2 is finished, pop it off the Stack, and Screen47 resumes the top position. It's a very convenient way to maintain the order of Screens, and I will employ this technique in the Screen tutorial.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-4092275984417299501?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-20831159422577751892008-03-05T18:21:00.000-08:002008-06-19T16:09:54.426-07:002008-06-19T16:09:54.426-07:00Obligatory updateI've been busier than usual for a while, doing some work on my car (painting wheels - my garage looks, and smells, like a paint shop) and also I'm going through a crunch phase at work, making a great big middle-tier ADO.NET engine which will <span style="font-weight:bold;">really</span> expedite creation of business objects & UIs, which is rather cool.<br /><br />But anyway, I'll be up to my eyeballs in that for another week or so, after that I can get back to continuing with more XNA stuff.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-2083115942257775189?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-74084598953601165412008-02-25T21:12:00.000-08:002008-06-20T02:54:48.275-07:002008-06-20T02:54:48.275-07:00Tutorial scheduleWhile I've rambled on about Screens, Scenes and Cameras I haven't really put up any tutorials in 4 months since "Tutorial 8" and I'm getting a little bit ahead of myself. <br /><br />So here's a rough TODO list, sort of making this for myself, but it's what I want to cover in detail before I get too off track here:<ul><li><b>Screens</b> - a proper introduction to Screens, adding a base Screen class to the Engine</li><br /><li><b>Screen Manager</b> - a simple little tool to help shuffle Screens around</li><br /><li><b>Scenes</b> - like a Screen, but bigger - huge, even. I may rename Scenes to... "Worlds" or "Levels" or, something. Screens and Scenes are conceptually similar with similar names, kind of has an inherent confusion built into it</li><br /><li><b>SceneCameras</b> - this will be the in-depth tutorial on what I've been doing lately. Cameras physically exist on Screens, and are used to peek inside of Scenes</li><br /><li><b>Automation</b> - I have an automation system written, which I can actually take the opportunity to improve upon a little bit as I write it up here on the blog, which allows you to "script" change in game object properties over time. Things like movement, rotation, alpha (fade in/out), changing texture, scaling, stuff like that. This is a very useful for doing UI-ish tasks, like Screen transitions, but I use it for all sorts of other stuff</li><br /><li><b>SceneGraph? QuadTrees?</b> - In a big Scene with lots and lots of game objects, for efficiency sake we will need some way to make quick collision checks between potentially thousands of objects. If a bullet is flying through a level of your game with 5,000 potential objects it could strike, you don't want to check for collision with all 5,000 objects if you can help it. I haven't tackled this yet so it's coming up next on my R&D list</li><br /><li><b>AI</b> - Another R&D project</li><br /><li><b>Level Generation</b> - I've been reading up a little on algorithms for creating random mazes, it's kind of interesting and thankfully not too complicated. I'll use it in the Star Maze project</li><br /><li><b>Sound</b> - I'm assuming this won't be too difficult to implement (famous last words)</li><br /><li><b>Star Maze</b> - take everything covered and use it to re-create a classic 2D game</li><br /><li><b>And beyond that...</b> - like most wanna-be game hobbyists I have an ambitious vaporware project I want to turn into a reality. But first I'll create Star Maze, get some good practice there, and if all goes well we can start to tinker around with other things</li></ul>So that's the plan. There's no ETA :)<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-7408459895360116541?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-88726012122790423432008-02-25T11:37:00.000-08:002008-06-20T02:55:01.640-07:002008-06-20T02:55:01.640-07:00www.xnamachine.com<span style="font-weight:bold;">xnamachine.blogspot.com</span> should now redirect you to <span style="font-weight:bold;">www.xnamachine.com</span>. <span style="font-weight:bold;">www.xnamachine.com</span> is now the "official" address.<br /><br />Nothing else is different, I just decided a more succinct name would be cool.<br /><br />Well, ok I changed the layout just a little bit... just for fun (I don't know how IE-friendly it is yet). I noticed my formatted code posts don't show up right in IE, so that's kind of a bummer. I'll try to fix that... eventually.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-8872601212279042343?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-76881453249727743752008-02-17T05:35:00.000-08:002008-06-20T02:55:29.154-07:002008-06-20T02:55:29.154-07:00SceneCamera class nears completionHere is a video demonstrating the features of the SceneCamera class as it nears completion:<br /><br /><object width="425" height="350"> <param name="movie" value="http://www.youtube.com/v/nam-I_dLoDc"> </param> <embed src="http://www.youtube.com/v/nam-I_dLoDc" type="application/x-shockwave-flash" width="425" height="350"> </embed> </object><br /><a href="http://www.youtube.com/watch?v=nam-I_dLoDc" target="_new">direct link</a><br /><br />All SceneCameras will by default perform a translation of the subject (e.g., follow it's X/Y position), and all SceneCameras have the ability to scale (zoom in/out). In addition to these two default behaviors there are two optional properties to specify camera style.<br /><br /><span style="font-weight:bold;">Rotate with subject</span>: If this property is True, it causes the SceneCamera to lock rotation with its subject. This creates a camera style whereby the subject appears fixed and the entire Scene rotates. In the video, the SceneCamera on the right side rotates with it's subject. The SceneCamera on the left side has this property set to False, creating an opposite effect (the Scene appears fixed and the subject rotates).<br /><br /><span style="font-weight:bold;">Clamp to Scene</span>: If this property is True, the SceneCamera is not permitted to exit the perimeter of the Scene. In the video, the left side has this property set to True, and it is False on the right side.<br /><br />Finally, there is a 3rd SceneCamera moving around the perimeter of the Screen to demonstrate the "game object" nature of SceneCameras. Like a Sprite, a SceneCamera can easily be any size, and can move around the Screen without disturbing it's ability to view a subject.<br /><br />What I will be adding next is a property for <span style="font-weight:bold;">Chase lag</span>, so the SceneCamera can be given a variable amount of imprecision when following its subject. Once this is done, the SceneCamera class should be adequately functional.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-7688145324972774375?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com10tag:blogger.com,1999:blog-8768285918186248163.post-66772607284884854472008-02-06T11:53:00.000-08:002008-02-06T14:42:32.797-08:002008-02-06T14:42:32.797-08:00How to modify the VBContentManager to load other content typesHere's an example of how you can teach the VBContentManager to load other content types.<br /><br />This sample code below shows you what to add (in bold) in order to load .fbx models. Please note that to actually display a 3D model, you'll have to write your own 3D rendering code - 3D is out of scope of my 2D engine blog. This just demonstrates how you can teach the VBContentManager to load other content types, the example just happens to use .fbx models.<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:613px;height:613px;overflow-x:scroll;overflow-y:scroll;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;VBContentManager<br /><n style="padding-left:24px;">&nbsp;</n><k>Inherits</k>&nbsp;ContentManager<br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'Add&nbsp;a&nbsp;Hashtable(Of&nbsp;Model)&nbsp;called&nbsp;Modles,&nbsp;to</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'give&nbsp;your&nbsp;models&nbsp;a&nbsp;place&nbsp;to&nbsp;arrive&nbsp;in&nbsp;memory...</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><b><k>Public</k>&nbsp;Models&nbsp;<k>As</k>&nbsp;Hashtable(<k>Of</k>&nbsp;Model)</b><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'Add&nbsp;"intDupeModel"&nbsp;variable&nbsp;to&nbsp;this&nbsp;series...</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'This&nbsp;allows&nbsp;the&nbsp;VBContentManager&nbsp;to&nbsp;append&nbsp;a</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'unique&nbsp;number&nbsp;to&nbsp;any&nbsp;duplicate&nbsp;filenames&nbsp;it</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'may&nbsp;encounter&nbsp;when&nbsp;loading&nbsp;models&nbsp;from&nbsp;disk</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;intDupeSound,&nbsp;intDupeTexture,&nbsp;intDupeFont,&nbsp;intDupeEffect,&nbsp;<b>intDupeModel</b>&nbsp;<k>As</k>&nbsp;<k>Integer</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'Add&nbsp;"Models"&nbsp;to&nbsp;the&nbsp;ContentTypes&nbsp;enumeration,</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'and&nbsp;make&nbsp;it&nbsp;the&nbsp;next&nbsp;available&nbsp;value</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Enum</k>&nbsp;ContentTypes<br /><n style="padding-left:56px;">&nbsp;</n>Textures&nbsp;=&nbsp;0<br /><n style="padding-left:56px;">&nbsp;</n>Fonts&nbsp;=&nbsp;1<br /><n style="padding-left:56px;">&nbsp;</n>Sounds&nbsp;=&nbsp;2<br /><n style="padding-left:56px;">&nbsp;</n>Effects&nbsp;=&nbsp;3<br /><n style="padding-left:56px;">&nbsp;</n><b>Models&nbsp;=&nbsp;4</b><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Enum</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'Add&nbsp;"FbxImporter"&nbsp;to&nbsp;the&nbsp;ImporterName&nbsp;enumeration</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'Make&nbsp;sure&nbsp;it&nbsp;has&nbsp;the&nbsp;same&nbsp;value&nbsp;as&nbsp;Models&nbsp;in&nbsp;the</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'ContentTypes&nbsp;enumeration...</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Enum</k>&nbsp;ImporterName<br /><n style="padding-left:56px;">&nbsp;</n>TextureImporter&nbsp;=&nbsp;0<br /><n style="padding-left:56px;">&nbsp;</n>FontDescriptionImporter&nbsp;=&nbsp;1<br /><n style="padding-left:56px;">&nbsp;</n>XactImporter&nbsp;=&nbsp;2<br /><n style="padding-left:56px;">&nbsp;</n>EffectImporter&nbsp;=&nbsp;3<br /><n style="padding-left:56px;">&nbsp;</n><b>FbxImporter&nbsp;=&nbsp;4</b><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Enum</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'Add&nbsp;"ModelProcessor"&nbsp;to&nbsp;the&nbsp;ProcessorName&nbsp;enumeration</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'Make&nbsp;sure&nbsp;it&nbsp;has&nbsp;the&nbsp;same&nbsp;value&nbsp;as&nbsp;Models&nbsp;in&nbsp;the</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'ContentTypes&nbsp;enumeration.</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Enum</k>&nbsp;ProcessorName<br /><n style="padding-left:56px;">&nbsp;</n>TextureProcessor&nbsp;=&nbsp;0<br /><n style="padding-left:56px;">&nbsp;</n>FontDescriptionProcessor&nbsp;=&nbsp;1<br /><n style="padding-left:56px;">&nbsp;</n>XactProcessor&nbsp;=&nbsp;2<br /><n style="padding-left:56px;">&nbsp;</n>EffectProcessor&nbsp;=&nbsp;3<br /><n style="padding-left:56px;">&nbsp;</n><b>ModelProcessor&nbsp;=&nbsp;4</b><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Enum</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'In&nbsp;Public&nbsp;Sub&nbsp;New,&nbsp;add&nbsp;code&nbsp;to&nbsp;instantiate&nbsp;the&nbsp;Models&nbsp;hashtable</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'you&nbsp;added&nbsp;above</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;<k>New</k>(<k>ByVal</k>&nbsp;ServiceProvider&nbsp;<k>As</k>&nbsp;IServiceProvider,&nbsp;<k>ByVal</k>&nbsp;ContentFolder&nbsp;<k>As</k>&nbsp;String)<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'(existing&nbsp;code&nbsp;omitted)</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Add&nbsp;code&nbsp;to&nbsp;instantiate&nbsp;the&nbsp;models&nbsp;hashtable</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><b><k>Me</k>.Models&nbsp;=&nbsp;<k>New</k>&nbsp;Hashtable(<k>Of</k>&nbsp;Model)</b><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:16px;">&nbsp;</n><c>'In&nbsp;LoadAllContent,&nbsp;add&nbsp;a&nbsp;Try&nbsp;block&nbsp;for&nbsp;Model&nbsp;importing&nbsp;in&nbsp;the</c><br /><n style="padding-left:16px;">&nbsp;</n><c>'Catch&nbsp;block&nbsp;for&nbsp;Effects,&nbsp;and&nbsp;move&nbsp;the&nbsp;unknown&nbsp;file&nbsp;handler</c><br /><n style="padding-left:16px;">&nbsp;</n><c>'to&nbsp;the&nbsp;catch&nbsp;block&nbsp;for&nbsp;attempted&nbsp;Model&nbsp;load</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;LoadAllContent()<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'(existing&nbsp;code&nbsp;omitted)</c><br /><br /><n style="padding-left:184px;">&nbsp;</n><k>Try</k><br /><br /><n style="padding-left:216px;">&nbsp;</n><c>'Effect?</c><br /><br /><n style="padding-left:216px;">&nbsp;</n><k>With</k>&nbsp;file.FullName.ToLower<br /><n style="padding-left:248px;">&nbsp;</n><k>Me</k>.Effects.Add(file.Name.Replace(file.Extension,&nbsp;<t>""</t>),&nbsp;<k>Me</k>.Load(<k>Of</k>&nbsp;Effect)(.Replace(<k>Me</k>.strExecutingFolder&nbsp;&&nbsp;<t>"\"</t>,&nbsp;<t>""</t>).Replace(file.Extension,&nbsp;<t>""</t>)))<br /><n style="padding-left:216px;">&nbsp;</n><k>End</k>&nbsp;<k>With</k><br /><br /><n style="padding-left:184px;">&nbsp;</n><c>'Instead&nbsp;of&nbsp;giving&nbsp;up&nbsp;at&nbsp;this&nbsp;point,&nbsp;catch&nbsp;this&nbsp;as&nbsp;notAnEffect,</c><br /><n style="padding-left:184px;">&nbsp;</n><c>'and&nbsp;add&nbsp;1&nbsp;more&nbsp;check&nbsp;to&nbsp;see&nbsp;if&nbsp;the&nbsp;file&nbsp;is&nbsp;a&nbsp;model...</c><br /><br /><n style="padding-left:184px;">&nbsp;</n><b><k>Catch</k>&nbsp;notAnEffect&nbsp;<k>As</k>&nbsp;Exception<br /><br /><n style="padding-left:216px;">&nbsp;</n><k>Try</k><br /><br /><n style="padding-left:248px;">&nbsp;</n><c>'Model?</c><br /><br /><n style="padding-left:248px;">&nbsp;</n><k>With</k>&nbsp;file.FullName.ToLower<br /><n style="padding-left:280px;">&nbsp;</n><k>Me</k>.Models.Add(file.Name.Replace(file.Extension,&nbsp;<t>""</t>),&nbsp;<k>Me</k>.Load(<k>Of</k>&nbsp;Model)(.Replace(<k>Me</k>.strExecutingFolder&nbsp;&&nbsp;<t>"\"</t>,&nbsp;<t>""</t>).Replace(file.Extension,&nbsp;<t>""</t>)))<br /><n style="padding-left:248px;">&nbsp;</n><k>End</k>&nbsp;<k>With</k></b><br /><br /><n style="padding-left:240px;">&nbsp;</n><c>'If&nbsp;that&nbsp;doesn't&nbsp;work,&nbsp;then&nbsp;I've&nbsp;run&nbsp;out&nbsp;of&nbsp;things</c><br /><n style="padding-left:240px;">&nbsp;</n><c>'to&nbsp;try,&nbsp;so...</c><br /><br /><n style="padding-left:216px;">&nbsp;</n><b><k>Catch</k>&nbsp;unknown&nbsp;<k>As</k>&nbsp;Exception<br /><n style="padding-left:248px;">&nbsp;</n>strUnknownFiles&nbsp;&=&nbsp;vbCrLf&nbsp;&&nbsp;file.FullName<br /><n style="padding-left:216px;">&nbsp;</n><k>End</k>&nbsp;<k>Try</k><br /><br /><n style="padding-left:184px;">&nbsp;</n><k>End</k>&nbsp;<k>Try</k></b><br /><br /><n style="padding-left:64px;">&nbsp;</n><c>'(existing&nbsp;code&nbsp;omitted)</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'In&nbsp;HarvestContent,&nbsp;add&nbsp;code&nbsp;to&nbsp;search&nbsp;for&nbsp;.fbx&nbsp;files.&nbsp;&nbsp;Also,</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'I'm&nbsp;going&nbsp;to&nbsp;add&nbsp;code&nbsp;to&nbsp;look&nbsp;for&nbsp;.tga&nbsp;texture&nbsp;files&nbsp;as&nbsp;well</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'in&nbsp;case&nbsp;I&nbsp;need&nbsp;those&nbsp;too.</c><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Sub</k>&nbsp;HarvestContent()<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'(existing&nbsp;code&nbsp;omitted)</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Add&nbsp;code&nbsp;to&nbsp;look&nbsp;for&nbsp;.tga&nbsp;files...</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><b><k>For</k>&nbsp;<k>Each</k>&nbsp;texture&nbsp;<k>As</k>&nbsp;System.IO.FileInfo&nbsp;<k>In</k>&nbsp;objRoot.GetFiles(<t>"*.tga"</t>,&nbsp;IO.SearchOption.AllDirectories)<br /><n style="padding-left:88px;">&nbsp;</n><k>If</k>&nbsp;<k>Me</k>.objContent.Contains(<k>New</k>&nbsp;ContentFile(ContentTypes.Textures,&nbsp;texture,&nbsp;texture.Name.Replace(texture.Extension,&nbsp;<t>""</t>)))&nbsp;<k>Then</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.intDupeTexture&nbsp;+=&nbsp;1<br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.objContent.Add(<k>New</k>&nbsp;ContentFile(ContentTypes.Textures,&nbsp;texture,&nbsp;texture.Name.Replace(texture.Extension,&nbsp;<t>""</t>)&nbsp;&&nbsp;<k>Me</k>.intDupeTexture.ToString))<br /><n style="padding-left:88px;">&nbsp;</n><k>Else</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.objContent.Add(<k>New</k>&nbsp;ContentFile(ContentTypes.Textures,&nbsp;texture,&nbsp;texture.Name.Replace(texture.Extension,&nbsp;<t>""</t>)))<br /><n style="padding-left:88px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><n style="padding-left:56px;">&nbsp;</n><k>Next</k></b><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Add&nbsp;code&nbsp;to&nbsp;look&nbsp;for&nbsp;.fbx&nbsp;files...&nbsp;these&nbsp;are&nbsp;similar</c><br /><n style="padding-left:56px;">&nbsp;</n><c>'code&nbsp;blocks,&nbsp;just&nbsp;change&nbsp;the&nbsp;ContentType&nbsp;enumeration&nbsp;and&nbsp;the</c><br /><n style="padding-left:56px;">&nbsp;</n><c>'extension&nbsp;we&nbsp;want&nbsp;to&nbsp;look&nbsp;for&nbsp;from&nbsp;all&nbsp;the&nbsp;existing&nbsp;ones&nbsp;in</c><br /><n style="padding-left:56px;">&nbsp;</n><c>'this&nbsp;sub</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><b><k>For</k>&nbsp;<k>Each</k>&nbsp;model&nbsp;<k>As</k>&nbsp;System.IO.FileInfo&nbsp;<k>In</k>&nbsp;objRoot.GetFiles(<t>"*.fbx"</t>,&nbsp;IO.SearchOption.AllDirectories)<br /><n style="padding-left:88px;">&nbsp;</n><k>If</k>&nbsp;<k>Me</k>.objContent.Contains(<k>New</k>&nbsp;ContentFile(ContentTypes.Models,&nbsp;model,&nbsp;model.Name.Replace(model.Extension,&nbsp;<t>""</t>)))&nbsp;<k>Then</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.intDupeModel&nbsp;+=&nbsp;1<br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.objContent.Add(<k>New</k>&nbsp;ContentFile(ContentTypes.Models,&nbsp;model,&nbsp;model.Name.Replace(model.Extension,&nbsp;<t>""</t>)&nbsp;&&nbsp;<k>Me</k>.intDupeModel.ToString))<br /><n style="padding-left:88px;">&nbsp;</n><k>Else</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Me</k>.objContent.Add(<k>New</k>&nbsp;ContentFile(ContentTypes.Models,&nbsp;model,&nbsp;model.Name.Replace(model.Extension,&nbsp;<t>""</t>)))<br /><n style="padding-left:88px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><n style="padding-left:56px;">&nbsp;</n><k>Next</k></b><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br /><br />The VBContentManager code should now be able to import .fbx files. Create a folder called Models in your Content folder in the solution explorer, and put .fbx files in there:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/models.png" /><br /><br />Here I copied all the models (and textures) from the SpaceWar example, and they now compile & load into the Models hashtable of the VBContentManager.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-6677260728488485447?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com1tag:blogger.com,1999:blog-8768285918186248163.post-71920920964135032152008-02-02T02:19:00.000-08:002008-06-20T02:55:46.713-07:002008-06-20T02:55:46.713-07:00SceneCameraI got a chance to get a little more camera work done over the past couple days. What I've made is a 2D camera which is a game object (as such it can be placed onto a Screen just like anything else). These cameras point at "Scenes", which are similar conceptually to Screens, but unlike Screens a Scene is not meant to "draw itself", it just sits in memory. You use a SceneCamera to view a subset of the Scene.<br /><br />In this demonstration a Scene exists in memory, it's a simple repeating tile grid w/ a user-controlled ship. Multiple SceneCameras are implemented to demonstrate the concept of viewing a Scene through a SceneCamera. All of the SceneCameras are pointing at the same ship in the same Scene. <br /><br />Pardon the obnoxious music, I was just having fun with <a href="http://www.adobe.com/products/premiere/" target="_new">Premiere</a>:<br /><br /><object width="425" height="355"><param name="movie" value="http://www.youtube.com/v/a6BveT9K_Wo&rel=1"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/a6BveT9K_Wo&rel=1" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"></embed></object><br /><a href="http://www.youtube.com/watch?v=a6BveT9K_Wo" target="_new">direct link</a><br /><br />It's still pretty beta-ish but it basically works, I'll do a write up after it becomes more properly solidified.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-7192092096413503215?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-90714065086157682602008-01-28T18:53:00.000-08:002008-06-20T02:55:55.780-07:002008-06-20T02:55:55.780-07:00Tell Microsoft you want VB support for GSMicrosoft is offering a <a href="http://forums.xna.com/thread/42852.aspx">survey</a> to XNA/Game Studio users for feedback on the XNA/Game Studio product. There are one or two questions on the survey which give you the opportunity to suggest the support of other, non-C# .NET languages. If you would like Game Studio to support VB, you should <a href="http://forums.xna.com/thread/42852.aspx">fill out the survey</a> and mention it.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-9071406508615768260?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-54814031027283665882008-01-16T11:39:00.001-08:002008-06-20T02:56:14.724-07:002008-06-20T02:56:14.724-07:00Screens & the Screen Manager<b>Screens</b><br /><br />Screens are what I've been using, prior to implementing Cameras. So they may change. Here's how it all works now, and while the names may change in the future, the basic concept will probably remain mostly the same.<br /><br />In <a href="http://xnamachine.blogspot.com/2007/11/tutorial-7-sprites-as-game-objects.html">Tutorial 7</a> we defined custom game objects as separate classes in our project. In doing so we abstracted a lot of detail out of our game class; not only the construction of the little ship & turret tree, but also the Update code that moved a ship back and forth across the screen. The primary focus on putting these things into a Ship class was to make them readily reusable - but did you notice it really helped tidy up the Game class? In part there is a philosophical benefit to abstraction in that it simply helps to keep things looking tidy, if you're into that sort of thing.<br /><br />In the previous article <a href="http://xnamachine.blogspot.com/2008/01/forest-for-your-trees.html">A forest for your trees</a>, we simulated this strategy somewhat in a hypothetical Game class that defined some make-believe game objects as members:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MyGameTutorial<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Game<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Main&nbsp;menu&nbsp;stuff</c><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objNewGame&nbsp;<k>As</k>&nbsp;NewGame<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objGraphics&nbsp;<k>As</k>&nbsp;Graphics<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objQuit&nbsp;<k>As</k>&nbsp;Quit<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objContinue&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Game&nbsp;stuff</c><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objShip1&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objShip2&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objPlayer&nbsp;<k>As</k>&nbsp;PlayerShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objHighScore&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Initialization,&nbsp;etc.</c><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />We ended up with 8 game objects or trees, like so:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess1.png" /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess2.png" /><br /><br />And in theory you would manage (update/draw) all 8 of them directly from within the game class. That's not too bad, but, half of these objects are meant for the main menu game state, and the other half belong to the actual "playing the game" game state. The only way to know which ones even belong to which state is to read the comments above the member declarations! I'm no coding guru, but even I know that's turning into a sketchy situation.<br /><br />If we take the time to define custom Screens, just like we defined custom game objects in Tutorial 7, we can perform yet another level of abstraction that will not only help organize us, and help us maintain game state, it will again make our game class a more tidy, simple thing to work with.<br /><br />Tutorial 7 also walked us through creating custom game objects by deriving new classes from Sprite. You do something like this...<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MenuObject<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;ChildOption1&nbsp;<k>As</k>&nbsp;Sprite<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;ChildOption2&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;<k>New</k>()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.ChildOption1&nbsp;=&nbsp;<k>New</k>&nbsp;Sprite()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.ChildOption2&nbsp;=&nbsp;<k>New</k>&nbsp;Sprite()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(ChildOption1)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(ChildOption2)<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Update,&nbsp;Draw&nbsp;etc.</c><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />We can do the very same thing to create custom Screen objects from our base Screen class. Let's build some hypothetical screens to abstract those 8 game objects into 2 screens representing their respective states. First here's a pseudo Main Menu Screen class:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MainMenuScreen<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Screen<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;NewGameChoice&nbsp;<k>As</k>&nbsp;MenuObject<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;ContinueChoice&nbsp;<k>As</k>&nbsp;Sprite<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;GraphicSettings&nbsp;<k>As</k>&nbsp;MenuObject<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;Quit&nbsp;<k>As</k>&nbsp;MenuObject<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;<k>New</k>()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.NewGameChoice&nbsp;=&nbsp;<k>New</k>&nbsp;MenuObject()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.ContinueChoice&nbsp;=&nbsp;<k>New</k>&nbsp;MenuObject()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.GraphicSettings&nbsp;=&nbsp;<k>New</k>&nbsp;MenuObject()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.Quit&nbsp;=&nbsp;<k>New</k>&nbsp;MenuObject()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(NewGameChoice)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(ContinueChoice)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(GraphicSettings)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(Quit)<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Update,&nbsp;Draw&nbsp;etc.</c><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />It looks like this:<br /><img src="http://i97.photobucket.com/albums/l223/emachine74/organized1.png" /><br /><br />Now how about a pseudo game screen class:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;TheGameItself<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Screen<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;Ship1&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;Ship2&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;Player&nbsp;<k>As</k>&nbsp;PlayerShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;HighScore&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Public</k>&nbsp;<k>Sub</k>&nbsp;<k>New</k>()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.Ship1&nbsp;=&nbsp;<k>New</k>&nbsp;BadGuyShip()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.Ship2&nbsp;=&nbsp;<k>New</k>&nbsp;BadGuyShip()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.Player&nbsp;=&nbsp;<k>New</k>&nbsp;PlayerShip()<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.HighScore&nbsp;=&nbsp;<k>New</k>&nbsp;Sprite()<br /><br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(Ship1)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(Ship2)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(Player)<br /><n style="padding-left:72px;">&nbsp;</n><k>Me</k>.AddChild(HighScore)<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Update,&nbsp;Draw&nbsp;etc.</c><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />There we go:<br /><img src="http://i97.photobucket.com/albums/l223/emachine74/organized2.png" /><br /><br />Nevermind the naming mis-match on the diagram; the "game" node represents the "TheGameItself".<br /><br />Now instead of adding 8 game objects to our game class and organizing them with code comments, we can use two Screens with names that convey their purpose:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MyGameTutorial<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Game<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;MainMenu&nbsp;<k>As</k>&nbsp;MainMenuScreen<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;TheGameItself&nbsp;<k>As</k>&nbsp;GameScreen<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Initialization,&nbsp;etc.</c><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />This gains us a few benefits. With the addition of a Screen node, we can now transform (move rotate scale etc) an entire Screen of "stuff", be it the main menu or the game itself, something we couldn't do easily prior to implementing Screens. This also gives us something of a "choke point" for state management - if we want to draw the Main Menu, we just tell the main menu screen to .Update or .Draw and thanks to the underlying Sprite class, the rest more or less runs itself. All our Game really has to worry about now is calling the Update & Draw methods of whichever Screen(s) are appropriate for the current Game State.<br /><br /><b>XNA.Framework.Game.Components and the ScreenManager</b><br /><br />This has all been very cute, but it seems that all we've done is shuffled the question of <span style="font-weight:bold;">which</span> Game Objects to draw to which Screens to draw. What we've done so far helps organize us, but it hasn't helped us answered that important question.<br /><br />You may be pleased to know we can eliminate the need to ask this question all together. We can simply update/draw whichever Screens happen to be Enabled and/or Visible at the moment. For this, we will borrow upon the <a href="http://msdn2.microsoft.com/en-us/library/microsoft.xna.framework.game.components.aspx" target="_new">Components member of the XNA.Framework.Game class</a>. <br /><br />In brief, XNA.Framework.Game has a built-in Components collection. This collection is meant to hold objects which derive from <a href="http://msdn2.microsoft.com/en-us/library/microsoft.xna.framework.gamecomponent.aspx" target="_new">GameComponent</a> and/or <a href="http://msdn2.microsoft.com/en-us/library/microsoft.xna.framework.drawablegamecomponent.aspx" target="_new">DrawableGameComponent</a>, but in truth can hold any object which implements the IGameComponent/IUpdateable/IDrawable interface(s). The Game.Components collection is handy because, similar to how our Sprite class was created to automatically draw & update it's children (if any), XNA.Framework.Game understands that if it finds anything in it's Components collection, it should Update and Draw whatever it finds in there. There are a few useful features of this which we're going to borrow upon:<ul><li>The IUpdateable and IDrawable interfaces have UpdateOrder and DrawOrder properties, respectively. When XNA.Framework.Game processes it's Components collection, it uses these values to determine which objects get Updated first and which ones get Drawn first</li><li>The IUpdateable interface has an Enabled property (Boolean). The Game class will only Update components where Enabled=True</li><li>Similar to Enabled, the IDrawable interface has a Visible property and only objects which are Visible will be Drawn</li></ul>This is a convenient little system to plug our Screens into.<br /><br />Now you may have noticed some Interfaces being included in the base Screen class in the previous article, let's get another look at that:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>MustInherit</k>&nbsp;<k>Class</k>&nbsp;Screen<br /><n style="padding-left:24px;">&nbsp;</n><k>Inherits</k>&nbsp;Sprite<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IGameComponent<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IDrawable<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IUpdateable<br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'etc</c><br /><br /><k>End</k>&nbsp;<k>Class</k><br /></div><!-- <br />END --><br />Implementing these interfaces allows our Screen class to be "registered" or added to the Game.Component collection. Alternatively, you could be dealing with classes that inherit from GameComponent or DrawableGameComponent, which Implement these interfaces for you. Since our Screen inherits from Sprite, this hasn't been done for us so I'll just have the Screen class implement the necessary interfaces.<br /><br />The Screen Manager class is going to be our middle-man between our game code and the Screens stored in the Game.Components collection. The Screen Manager is going to add our Screens to the Collection for us, set DrawOrder for us, set Enabled & Visible for us, all that stuff. We'll just use the "Add", "Load" and "Unload" methods of the Screen Manager, for the most part. Here's the class, which I'll post for your amusement. If I get around to making a video about this I can go into more detail:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:613px;height:613px;overflow-x:scroll;overflow-y:scroll;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;ScreenManager<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;Game&nbsp;<k>As</k>&nbsp;GameEngine<br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;Root&nbsp;<k>As</k>&nbsp;Root<br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Shared</k>&nbsp;NameToGameID&nbsp;<k>As</k>&nbsp;<k>New</k>&nbsp;Hashtable(<k>Of</k>&nbsp;<k>Integer</k>)<br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Shared</k>&nbsp;GameIDArrangement&nbsp;<k>As</k>&nbsp;<k>New</k>&nbsp;List(<k>Of</k>&nbsp;<k>Integer</k>)<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;<k>ReadOnly</k>&nbsp;<k>Property</k>&nbsp;GetScreen(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String)&nbsp;<k>As</k>&nbsp;Screen<br /><n style="padding-left:56px;">&nbsp;</n><k>Get</k><br /><n style="padding-left:88px;">&nbsp;</n><k>If</k>&nbsp;NameToGameID.Contains(ScreenName)&nbsp;<k>Then</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Return</k>&nbsp;<k>CType</k>(Game.Components(NameToGameID(ScreenName)),&nbsp;Screen)<br /><n style="padding-left:88px;">&nbsp;</n><k>Else</k><br /><n style="padding-left:120px;">&nbsp;</n><k>Return</k>&nbsp;<k>Nothing</k><br /><n style="padding-left:88px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>Get</k><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Property</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;<k>ReadOnly</k>&nbsp;<k>Property</k>&nbsp;CurrentScreen()&nbsp;<k>As</k>&nbsp;Screen<br /><n style="padding-left:56px;">&nbsp;</n><k>Get</k><br /><n style="padding-left:88px;">&nbsp;</n><k>Dim</k>&nbsp;objRet&nbsp;<k>As</k>&nbsp;Screen&nbsp;=&nbsp;<k>Nothing</k><br /><n style="padding-left:88px;">&nbsp;</n><k>For</k>&nbsp;intX&nbsp;<k>As</k>&nbsp;<k>Integer</k>&nbsp;=&nbsp;GameIDArrangement.Count&nbsp;-&nbsp;1&nbsp;<k>To</k>&nbsp;0&nbsp;Step&nbsp;-1<br /><n style="padding-left:120px;">&nbsp;</n><k>If</k>&nbsp;<k>CType</k>(Game.Components(GameIDArrangement(intX)),&nbsp;Screen).Enabled&nbsp;<k>Then</k><br /><n style="padding-left:152px;">&nbsp;</n>objRet&nbsp;=&nbsp;<k>CType</k>(Game.Components(GameIDArrangement(intX)),&nbsp;Screen)<br /><n style="padding-left:152px;">&nbsp;</n><k>Exit</k>&nbsp;<k>For</k><br /><n style="padding-left:120px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><n style="padding-left:88px;">&nbsp;</n><k>Next</k><br /><n style="padding-left:88px;">&nbsp;</n><k>Return</k>&nbsp;objRet<br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>Get</k><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Property</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;<k>ReadOnly</k>&nbsp;<k>Property</k>&nbsp;IsScreen(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String)&nbsp;<k>As</k>&nbsp;<k>Boolean</k><br /><n style="padding-left:56px;">&nbsp;</n><k>Get</k><br /><n style="padding-left:88px;">&nbsp;</n><k>Return</k>&nbsp;NameToGameID.Contains(ScreenName)<br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>Get</k><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Property</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;<k>Function</k>&nbsp;CreateRoot(<k>ByVal</k>&nbsp;YourGame&nbsp;<k>As</k>&nbsp;GameEngine,&nbsp;<k>ByVal</k>&nbsp;objRootSize&nbsp;<k>As</k>&nbsp;Vector2,&nbsp;<k>Optional</k>&nbsp;<k>ByVal</k>&nbsp;strTextureName&nbsp;<k>As</k>&nbsp;String&nbsp;=&nbsp;<t>""</t>,&nbsp;<k>Optional</k>&nbsp;<k>ByVal</k>&nbsp;strTextureNameItem&nbsp;<k>As</k>&nbsp;String&nbsp;=&nbsp;<t>""</t>)&nbsp;<k>As</k>&nbsp;Root<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Dim</k>&nbsp;objRetVal&nbsp;<k>As</k>&nbsp;Root<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'make&nbsp;a&nbsp;new&nbsp;root&nbsp;object&nbsp;of&nbsp;the&nbsp;specified&nbsp;size&nbsp;at&nbsp;0,0</c><br /><n style="padding-left:56px;">&nbsp;</n>objRetVal&nbsp;=&nbsp;<k>New</k>&nbsp;Root(YourGame,&nbsp;<k>Nothing</k>,&nbsp;<t>"root"</t>,&nbsp;<k>True</k>,&nbsp;<k>True</k>,&nbsp;strTextureName,&nbsp;strTextureNameItem)<br /><n style="padding-left:56px;">&nbsp;</n>objRetVal.Size&nbsp;=&nbsp;objRootSize<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Return</k>&nbsp;objRetVal<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Function</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Adds&nbsp;a&nbsp;screen&nbsp;to&nbsp;the&nbsp;screen&nbsp;manager.&nbsp;Adds&nbsp;to&nbsp;the&nbsp;top&nbsp;of&nbsp;the&nbsp;stack&nbsp;by&nbsp;default,&nbsp;but&nbsp;does&nbsp;not&nbsp;alter&nbsp;Enabled&nbsp;or&nbsp;Visible</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;param&nbsp;name="obj"&gt;&lt;/param&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;AddScreen(<k>ByVal</k>&nbsp;obj&nbsp;<k>As</k>&nbsp;Screen)<br /><br /><n style="padding-left:56px;">&nbsp;</n>Root.AddScreen(obj)<br /><n style="padding-left:56px;">&nbsp;</n>Game.Components.Add(obj)<br /><n style="padding-left:56px;">&nbsp;</n>NameToGameID.Add(obj.Name,&nbsp;Game.Components.Count&nbsp;-&nbsp;1)<br /><n style="padding-left:56px;">&nbsp;</n>GameIDArrangement.Add(Game.Components.Count&nbsp;-&nbsp;1)<br /><n style="padding-left:56px;">&nbsp;</n>obj.Initialize()<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Sends&nbsp;the&nbsp;specified&nbsp;screen&nbsp;to&nbsp;the&nbsp;bottom&nbsp;of&nbsp;the&nbsp;stack&nbsp;and&nbsp;excludes&nbsp;it&nbsp;from&nbsp;the&nbsp;update/draw&nbsp;loop</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;Unload(<k>ByVal</k>&nbsp;intScreenID&nbsp;<k>As</k>&nbsp;<k>Integer</k>)<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Remove&nbsp;this&nbsp;screenID&nbsp;the&nbsp;update/draw&nbsp;loop</c><br /><n style="padding-left:56px;">&nbsp;</n><k>With</k>&nbsp;<k>CType</k>(Game.Components(intScreenID),&nbsp;Screen)<br /><br /><n style="padding-left:88px;">&nbsp;</n>.SetVisiblity(<k>False</k>)<br /><n style="padding-left:88px;">&nbsp;</n>.SetEnabled(<k>False</k>)<br /><br /><n style="padding-left:88px;">&nbsp;</n>.IsModal&nbsp;=&nbsp;<k>False</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>With</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Brings&nbsp;the&nbsp;specified&nbsp;screen&nbsp;to&nbsp;the&nbsp;front&nbsp;of&nbsp;the&nbsp;stack&nbsp;and&nbsp;includes&nbsp;it&nbsp;in&nbsp;the&nbsp;update/draw&nbsp;loop</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;Load(<k>ByVal</k>&nbsp;intScreenID&nbsp;<k>As</k>&nbsp;<k>Integer</k>,&nbsp;<k>ByVal</k>&nbsp;Modal&nbsp;<k>As</k>&nbsp;<k>Boolean</k>,&nbsp;<k>Optional</k>&nbsp;<k>ByVal</k>&nbsp;TransitionIn&nbsp;<k>As</k>&nbsp;<k>Boolean</k>&nbsp;=&nbsp;<k>True</k>)<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>Dim</k>&nbsp;ModalScreen&nbsp;<k>As</k>&nbsp;Screen<br /><n style="padding-left:56px;">&nbsp;</n><k>Dim</k>&nbsp;tex&nbsp;<k>As</k>&nbsp;Texture<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Bring&nbsp;to&nbsp;the&nbsp;front&nbsp;of&nbsp;the&nbsp;stack</c><br /><n style="padding-left:56px;">&nbsp;</n>ScreenManager.BringToFront(intScreenID)<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'that&nbsp;changes&nbsp;the&nbsp;gameIDArrangement&nbsp;(this&nbsp;screenID&nbsp;is&nbsp;now&nbsp;at&nbsp;.Count-1&nbsp;and&nbsp;all&nbsp;others&nbsp;have&nbsp;been&nbsp;pushed&nbsp;-1)</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Include&nbsp;this&nbsp;screenID&nbsp;the&nbsp;update/draw&nbsp;loop</c><br /><br /><n style="padding-left:56px;">&nbsp;</n><k>With</k>&nbsp;<k>CType</k>(Game.Components(intScreenID),&nbsp;Screen)<br /><br /><n style="padding-left:88px;">&nbsp;</n>.SetVisiblity(<k>True</k>)<br /><n style="padding-left:88px;">&nbsp;</n>.SetEnabled(<k>True</k>)<br /><br /><n style="padding-left:88px;">&nbsp;</n><c>'Set&nbsp;modal&nbsp;state</c><br /><n style="padding-left:88px;">&nbsp;</n>.IsModal&nbsp;=&nbsp;Modal<br /><br /><n style="padding-left:88px;">&nbsp;</n><k>If</k>&nbsp;TransitionIn&nbsp;<k>Then</k><br /><n style="padding-left:120px;">&nbsp;</n>.TransitionIn()<br /><n style="padding-left:88px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>With</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Now&nbsp;if&nbsp;we&nbsp;have&nbsp;just&nbsp;shown&nbsp;something&nbsp;modal,&nbsp;we&nbsp;need&nbsp;to&nbsp;stay&nbsp;in&nbsp;a&nbsp;loop&nbsp;here&nbsp;so&nbsp;the&nbsp;calling&nbsp;code</c><br /><n style="padding-left:56px;">&nbsp;</n><c>'can&nbsp;resume&nbsp;when&nbsp;the&nbsp;screen&nbsp;gets&nbsp;hidden.&nbsp;&nbsp;For&nbsp;as&nbsp;long&nbsp;as&nbsp;we&nbsp;are&nbsp;modal,&nbsp;this&nbsp;loop&nbsp;continues</c><br /><br /><n style="padding-left:56px;">&nbsp;</n>ModalScreen&nbsp;=&nbsp;<k>CType</k>(Game.Components(GameIDArrangement(GameIDArrangement.Count&nbsp;-&nbsp;1)),&nbsp;Screen)<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>If</k>&nbsp;ModalScreen.IsModal&nbsp;<k>Then</k><br /><br /><n style="padding-left:88px;">&nbsp;</n><k>While</k>&nbsp;ModalScreen.IsModal<br /><n style="padding-left:120px;">&nbsp;</n>Game.Tick()<br /><n style="padding-left:88px;">&nbsp;</n><k>End</k>&nbsp;<k>While</k><br /><br /><n style="padding-left:88px;">&nbsp;</n>Game.GraphicsDevice.Textures(0)&nbsp;=&nbsp;tex<br /><br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Brings&nbsp;the&nbsp;screen&nbsp;to&nbsp;the&nbsp;top&nbsp;of&nbsp;the&nbsp;render&nbsp;stack.&nbsp;Does&nbsp;not&nbsp;change&nbsp;Enabled&nbsp;or&nbsp;Visible</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;BringToFront(<k>ByVal</k>&nbsp;intScreenID&nbsp;<k>As</k>&nbsp;<k>Integer</k>)<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'If&nbsp;this&nbsp;screen&nbsp;is&nbsp;already&nbsp;in&nbsp;the&nbsp;show&nbsp;stack,&nbsp;remove&nbsp;it</c><br /><n style="padding-left:56px;">&nbsp;</n><k>If</k>&nbsp;GameIDArrangement.Contains(intScreenID)&nbsp;<k>Then</k><br /><n style="padding-left:88px;">&nbsp;</n>GameIDArrangement.Remove(intScreenID)<br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Put&nbsp;this&nbsp;screen&nbsp;at&nbsp;the&nbsp;top&nbsp;of&nbsp;the&nbsp;show&nbsp;stack</c><br /><n style="padding-left:56px;">&nbsp;</n>GameIDArrangement.Add(intScreenID)<br /><br /><n style="padding-left:56px;">&nbsp;</n>ScreenManager.SetDrawOrders()<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Syncs&nbsp;the&nbsp;draw&nbsp;orders&nbsp;of&nbsp;the&nbsp;screens&nbsp;with&nbsp;their&nbsp;positions&nbsp;in&nbsp;the&nbsp;show&nbsp;stack</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Private</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;SetDrawOrders()<br /><n style="padding-left:56px;">&nbsp;</n><k>For</k>&nbsp;intX&nbsp;<k>As</k>&nbsp;<k>Integer</k>&nbsp;=&nbsp;0&nbsp;<k>To</k>&nbsp;GameIDArrangement.Count&nbsp;-&nbsp;1<br /><n style="padding-left:88px;">&nbsp;</n><k>CType</k>(Game.Components(GameIDArrangement(intX)),&nbsp;Screen).SetDrawOrder(intX)<br /><n style="padding-left:56px;">&nbsp;</n><k>Next</k><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;Sends&nbsp;the&nbsp;screen&nbsp;to&nbsp;the&nbsp;bottom&nbsp;of&nbsp;the&nbsp;render&nbsp;stack</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;/summary&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><c>'''&nbsp;&lt;remarks&gt;&lt;/remarks&gt;</c><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;SendToBack(<k>ByVal</k>&nbsp;intScreenID&nbsp;<k>As</k>&nbsp;<k>Integer</k>)<br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'If&nbsp;this&nbsp;screen&nbsp;is&nbsp;already&nbsp;in&nbsp;the&nbsp;show&nbsp;stack,&nbsp;remove&nbsp;it</c><br /><n style="padding-left:56px;">&nbsp;</n><k>If</k>&nbsp;GameIDArrangement.Contains(intScreenID)&nbsp;<k>Then</k><br /><n style="padding-left:88px;">&nbsp;</n>GameIDArrangement.Remove(intScreenID)<br /><n style="padding-left:56px;">&nbsp;</n><k>End</k>&nbsp;<k>If</k><br /><br /><n style="padding-left:56px;">&nbsp;</n><c>'Put&nbsp;this&nbsp;screen&nbsp;at&nbsp;the&nbsp;bottom&nbsp;of&nbsp;the&nbsp;show&nbsp;stack</c><br /><n style="padding-left:56px;">&nbsp;</n>GameIDArrangement.Insert(0,&nbsp;intScreenID)<br /><br /><n style="padding-left:56px;">&nbsp;</n>ScreenManager.SetDrawOrders()<br /><br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;Load(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String,&nbsp;<k>ByVal</k>&nbsp;Modal&nbsp;<k>As</k>&nbsp;<k>Boolean</k>,&nbsp;<k>Optional</k>&nbsp;<k>ByVal</k>&nbsp;TransitionIn&nbsp;<k>As</k>&nbsp;<k>Boolean</k>&nbsp;=&nbsp;<k>True</k>)<br /><n style="padding-left:56px;">&nbsp;</n>Load(NameToGameID(ScreenName),&nbsp;Modal,&nbsp;TransitionIn)<br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;Unload(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String)<br /><n style="padding-left:56px;">&nbsp;</n>Unload(NameToGameID(ScreenName))<br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;BringToFront(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String)<br /><n style="padding-left:56px;">&nbsp;</n>BringToFront(NameToGameID(ScreenName))<br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:24px;">&nbsp;</n><k>Public</k>&nbsp;<k>Overloads</k>&nbsp;<k>Shared</k>&nbsp;<k>Sub</k>&nbsp;SendToBack(<k>ByVal</k>&nbsp;ScreenName&nbsp;<k>As</k>&nbsp;String)<br /><n style="padding-left:56px;">&nbsp;</n>SendToBack(NameToGameID(ScreenName))<br /><n style="padding-left:24px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><k>End</k>&nbsp;<k>Class</k><br /><br /></div><!-- <br />END --><br />You'll see some funky oddities in there but it's not too scary.<br /><br />Let's sum this situation up here before I get carpel tunnel:<ul><li>Sprites are the building blocks - they contain the code to invoke the Matrix cascade and contain the code necessary to facilitate the creation of tree nodes.</li><br /><li>Game Objects are reusable custom Sprite classes, typically with their own custom Update code and usually contain other child sprites to create small trees.</li><br /><li>Screens are the building blocks of State management. They derive from Sprite but typically don't require a texture, per se. They mainly exist to act as parent nodes so that we can graft related Game Objects and Sprites together into a Game State context.</li><br /><li>We use the Screen Manager to Register screens with the Component collection. We use the "Load" and "Unload" methods of the Screen Manager to basically set the DrawOrder, Enabled and Visible properties of Screens, which in turn causes the Component collection to change their order, start/stop Updating, or start/stop Drawing them.</li><br /><li>Game State is defined by whatever the state of our Enabled/Visible properties are on the Screens in the component collection - when a game starts, it registers it's Screens through the Screen Manager and Loads the Main Menu Screen. The Game State simply remains on the Main Menu Screen until changed, leaving no real need to question Game State (we simply allow all Enabled/Visible components in the Game.Component collection to Update & Draw as per their Enabled/Visible properties).</li></ul><br />Once you've got this in place and you make a game, add some screens with the screen manager and Load one, you essentially Update and Draw your game by calling MyBase.Update and MyBase.Draw from your game class. This will trigger the underlying XNA.Framework.Game to Draw & Update, which will cause it to process it's Components collection where your Screens live. In the end, you remove all Update and Draw calls to custom objects & screens from your game class and just let the underlying XNA Game process it's Components collection, using the Screen Manager to control which Screens in the collection end up getting Updated & Drawn.<br /><br />My apologies for the verbosity, I guess it's a lot to cover. There's still more to come...<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-5481403102728366588?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0tag:blogger.com,1999:blog-8768285918186248163.post-90651035554608083602008-01-14T13:28:00.000-08:002008-06-20T02:56:14.725-07:002008-06-20T02:56:14.725-07:00A forest for your trees<b>Trees</b><br /><br />The "parent/child" relationship afforded by the Sprite class can be thought of as an unordered <a href="http://en.wikipedia.org/wiki/Tree_%28data_structure%29" target="_new">tree structure</a>:<br /><br /><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Binary_tree.svg/180px-Binary_tree.svg.png" /><br /><br />In Star Maze, I've got a game object called PlayerShip. Like all game objects it inherits from Sprite:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;PlayerShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Sprite<br /><n style="padding-left:32px;">&nbsp;</n><c>'Update,&nbsp;Draw,&nbsp;etc.</c><br /><k>End</k>&nbsp;<k>Class</k><br /></div><!-- <br />END --><br />Considering Sprites to be tree nodes, any class inheriting from Sprite (including PlayerShip) becomes a valid tree node and can be diagrammed as such:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/playership.png" /><br /><br />It's not much to look at on it's own so let's use the AddChild method of the PlayerShip to attach a Turret sprite:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;">PlayerShip.AddChild(Turret)<br /></div><!-- <br />END --><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/shiptree.png" /><br /><br />The thrusters for the player's ship are separate game objects. Like the PlayerShip (and like all game objects we'll make with this engine), a Thruster is another game object derived from Sprite:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;Thruster<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Sprite<br /><n style="padding-left:32px;">&nbsp;</n><c>'Update,&nbsp;Draw,&nbsp;etc.</c><br /><k>End</k>&nbsp;<k>Class</k><br /></div><!-- <br />END --><br />We'll add a couple afterburner sprites to the Thruster,<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;">Thruster.AddChild(YellowAfterburner)<br />Thruster.AddChild(BlueAfterburner)<br /></div><!-- <br />END --><br />and here's the resultant tree diagram for the Thruster object:<br /><img src="http://i97.photobucket.com/albums/l223/emachine74/thrustertree.png" /><br /><br />Now we've got two different game objects, both of which inherit from Sprite: "Ship" and "Thruster". You can also interpret this as having two sprite trees: the PlayerShip tree and the Thruster tree:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/shiptree.png" /><img src="http://i97.photobucket.com/albums/l223/emachine74/thrustertree.png" /><br /><br />So far in the tutorials we've been using the AddChild method to append simple "leaf" nodes to sprites, but we can also do something slightly more profound, and that is to graft one entire tree onto another:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;">PlayerShip.AddChild(Thruster)<br /></div><!-- <br />END --><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/playershiptree.png" /><br /><br />By adding the root node of the Thruster tree as a child of PlayerShip, the entire Thruster tree is grafted to the PlayerShip.<br /><br />Thinking about Sprites as tree nodes just gives us an alternate way to describe or illustrate the "parent/child" system Sprites employ.<br /><br /><b>Trees in a Forest</b><br /><br />The "games" created in the tutorials thus far are simple programs meant to demonstrate classes like Sprite and VBContentManager (et al). None of the tutorials have put much thought into organizational techniques yet. A typical tutorial is structured like this:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MyGameTutorial<br /><n style="padding-left:32px;">&nbsp;</n><k>Inherits</k>&nbsp;Game<br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;GameObject1&nbsp;<k>As</k>&nbsp;SpriteTree1<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;GameObject2&nbsp;<k>As</k>&nbsp;SpriteTree2<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'etc.</c><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Protected</k>&nbsp;<k>Overrides</k>&nbsp;<k>Sub</k>&nbsp;Update()<br /><br /><n style="padding-left:72px;">&nbsp;</n>GameObject1.Update()<br /><n style="padding-left:72px;">&nbsp;</n>GameObject2.Update()<br /><br /><n style="padding-left:72px;">&nbsp;</n><c>'etc.</c><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Protected</k>&nbsp;<k>Overrides</k>&nbsp;<k>Sub</k>&nbsp;Draw()<br /><br /><n style="padding-left:72px;">&nbsp;</n>GameObject1.Draw()<br /><n style="padding-left:72px;">&nbsp;</n>GameObject2.Draw()<br /><br /><n style="padding-left:72px;">&nbsp;</n><c>'etc.</c><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />There may be some game object classes defined somewhere, but the general structure so far has been to create instances of game objects as private members of the Game class, and then simply Update & Draw them from within the Game's Update and Draw methods. Which is fine for a quick tutorials dealing with a limited number of sprites and game objects. But when we eventually get to the point where we're creating an actual game, we're going to end up with a lot more than just "a limited number of sprites & game objects". Our lack of organization will turn into a problem.<br /><br />Consider a tiny, theoretical game with 1 player, 2 bad guys, and a high score. This game starts up on a "main menu" with various options (new game, continue, quit, graphic settings). You can assume game object types such as BadGuyShip and PlayerShip (etc) to be irrelevant, all that matters is they derived from Sprite and are therefore valid tree structures of some kind:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>Class</k>&nbsp;MyGameTutorial<br /><n>&nbsp;</n><k>Inherits</k>&nbsp;Game<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Main&nbsp;menu&nbsp;stuff</c><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objNewGame&nbsp;<k>As</k>&nbsp;NewGame<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objGraphics&nbsp;<k>As</k>&nbsp;Graphics<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objQuit&nbsp;<k>As</k>&nbsp;Quit<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objContinue&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Game&nbsp;stuff</c><br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objShip1&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objShip2&nbsp;<k>As</k>&nbsp;BadGuyShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objPlayer&nbsp;<k>As</k>&nbsp;PlayerShip<br /><n style="padding-left:32px;">&nbsp;</n><k>Private</k>&nbsp;objHighScore&nbsp;<k>As</k>&nbsp;Sprite<br /><br /><n style="padding-left:32px;">&nbsp;</n><c>'Initialization,&nbsp;etc.</c><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Protected</k>&nbsp;<k>Overrides</k>&nbsp;<k>Sub</k>&nbsp;Update()<br /><n style="padding-left:72px;">&nbsp;</n><c>'Which&nbsp;ones&nbsp;do&nbsp;I&nbsp;update?</c><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><n style="padding-left:32px;">&nbsp;</n><k>Protected</k>&nbsp;<k>Overrides</k>&nbsp;<k>Sub</k>&nbsp;Draw()<br /><n style="padding-left:72px;">&nbsp;</n><c>'Which&nbsp;ones&nbsp;do&nbsp;I&nbsp;draw?</c><br /><n style="padding-left:32px;">&nbsp;</n><k>End</k>&nbsp;<k>Sub</k><br /><br /><k>End</k>&nbsp;<k>Sub</k><br /></div><!-- <br />END --><br />Here's all of our game objects, in tree view.<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess1.png" /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess2.png" /><br /><br />When this game runs and the update cycle comes around... which game objects do we update? High Score? BadGuy #2? The "Quit" menu item? We're now having to ask ourselves a new question: "Where am I?".<br /><br />There's a few ways to answer this question; you could just define a string called WhereAmI and if WhereAmI = "MainMenu" then you update/draw main menu stuff, and if WhereAmI = "PlayingTheGame" you update/draw the game stuff. And that would work, but as you add new features and possible "places" you could potentially be in the program (main menu, the game, the inventory screen, the game over screen, any of those but paused, etc) it becomes apparent that there may be better approaches.<br /><br />First of all, we need to get organized. We need a better method of organization for our game objects. Knowing where we are, ergo what to update & draw, will tie closely into our organizational strategy.<br /><br />Ironically enough one path to organization can be found in adding more Sprites. Not just any Sprite though; a special game object which is a bit of a Sprite hack, to tell the truth. It's called a Screen:<br /><br /><!-- BEGIN <br />--><style type="text/css">k{color:#0000FF;}c{color:#008000;}t{color:#A31515;}n{}.o{border-left:solid 1px grey;border-top:solid 1px grey;font-family:Courier New;vertical-align:top;font-size:10pt;text-align:left;color:#000000;}</style><div class="o" style="width:auto;height:auto;overflow-x:visible;overflow-y:visible;"><k>Public</k>&nbsp;<k>MustInherit</k>&nbsp;<k>Class</k>&nbsp;Screen<br /><n style="padding-left:24px;">&nbsp;</n><k>Inherits</k>&nbsp;Sprite<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IGameComponent<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IDrawable<br /><n style="padding-left:24px;">&nbsp;</n><k>Implements</k>&nbsp;IUpdateable<br /><br /><n style="padding-left:24px;">&nbsp;</n><c>'etc</c><br /><br /><k>End</k>&nbsp;<k>Class</k><br /></div><!-- <br />END --><br />Screens are going to help us turn this:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess1.png" /><img src="http://i97.photobucket.com/albums/l223/emachine74/treemess2.png" /><br /><br />Into this:<br /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/organized1.png" /><br /><img src="http://i97.photobucket.com/albums/l223/emachine74/organized2.png" /><br /><br />Once you're comfortable with the idea of Sprites as tree nodes, this is pretty simple; you define a MainMenu Screen and then just graft all your main menu game objects via MainMenu.AddChild(). A Screen is basically a container, a bucket into which we can toss game objects; it's the forest of our trees.<br /><br />Now anything we do to the Screen node - position, rotation, scale - the matrix cascade is going to apply that to every single attached child as well. This is our secret weapon that will let us make fancy screen transitions without too much of a problem.<br /><br />Ok this is just the beginning of what I want to get into with this, but it's taken me 2 days just to get this much done so I'm going to publish this and start working on part 2 - the Screen Manager, which is what we'll use to show / hide / order screens, we can even use it to show screens & dialogs "modal", continuing to update and draw our game but without loosing our place in code. And then, the great paradigm shift: Cameras, which may change all of it.<div class="blogger-post-footer"><img width='1' height='1' src='http://res1.blogblog.com/tracker/8768285918186248163-9065103555460808360?l=www.xnamachine.com'/></div>emachine74noreply@blogger.com0