Posts
67
Comments
120
Trackbacks
0
August 2010 Entries
WCF and XNA on WP7 – Hack Free

Update 1 (Sept. 17, 2010):

While the part below about using SLSvcUtil.exe to generate the service reference undoubtedly still works, it is no longer necessary. The RTM version of the Windows Phone Developer Tools now properly supports right-clicking on your game project's "References" item in the Solution Explorer and adding a Service Reference that way. ChannelFactory and related methods remain unsupported. For more details see http://msdn.microsoft.com/en-us/library/ff637320(VS.95).aspx (Networking in Silverlight for Windows Phone), which appears to have been updated since I first wrote this post.

------------------------

There’s been much ado about how to get a WCF service working in XNA on WP7. Most solutions involve some form of creating a Silverlight something or other and slinging WCF in from there. Those days are over. Here’s what you need: the Windows Phone Developer Tools and Visual Web Developer 2010 Express (both are free; I’ve also tested this with Visual Studio 2010 Ultimate which, not surprisingly, handled it just fine :) ).

First, let’s create a web service. Open up Visual Web Developer 2010 Express. Go to “File->New Project”. It may have you in Visual Basic. This would probably work fine, but I work much more in C# so I switched to Visual C# under Installed Templates and then chose “WCF Service Application”. I set a name (I chose WcfXnaTestService) and hit “OK”. Like magic, my web service sprang forth. The default service doesn’t do much – it takes in an int and spits back a string – but who cares! Go to the “Build” menu and choose “Build WcfXnaTestService” (or whatever you named your service). Then go to “Debug->Start Debugging”. This should start up a local webserver and open up a copy of either Internet Explorer or something called WCF Test Client.

If you get IE, click on the file that says “Service1.svc”. This will open a webpage telling you about your service. At the top will be a line that looks something like: “svcutil.exe http://localhost:64152/Service1.svc?wsdl”. Right click on the link, and choose “Copy Shortcut”.

If you get WCF Test Client, on the left you will see “My Service Projects”. If it has a little + next to it, click it to expand it. You will then see something that looks something like “http://localhost:64152/Service1.svc”. (Note: 64152 is just the port it happened to assign me on this run, you could get any high number). Right click on the localhost address and choose “Copy Address”.

Now don’t close any of that, whichever one you got. Just leave it be. We want that server to keep running until we’re completely and totally done with this entire tutorial. So minimize it or whatever, but leave it all running (including Visual Web Developer too, of course).

At this point you need to open a command prompt. Yep, land of dragons and wizards. Press the Windows Key + R to open up a Run dialog box (this works in both Windows 7 and Windows Vista; alternately, go to the Start menu and choose Run). Type “cmd” and hit enter. This will bring up a command prompt with you in your user directory. Assuming you have the tools installed in the default location, if you have a 32-bit version of Windows installed, you will run the following command:

C:\Program Files\Microsoft SDKs\Windows Phone\v7.0\Tools\SlSvcUtil.exe http://localhost:64152/Service1.svc?wsdl

if you have 64-bit Windows, you will instead run:

C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Tools\SlSvcUtil.exe http://localhost:64152/Service1.svc?wsdl

You’ll notice that we’re using that address we copied earlier. You can simply right click in the command prompt window and choose “Paste” to insert the correct one for you (if you copied the command from this tutorial, you just need to change the port (that 64152 number) to whichever one your computer decided to open for you). Note that if you do paste from earlier and you got WCF Test Client, you will need to add “?wsdl” to the end of the url just as it appears above.

Hit enter. It’ll tell you something like “Attempting to download metadata from ‘http://localhost…. etc. etc. DISCO (seriously) … Generating files… (file names).

It should give you Service1.cs and ServiceReferences.ClientConfig assuming you didn’t change anything.

Now, open up Visual Studio 2010 Express for Windows Phone and make a new XNA game. I’m going to assume you know how to do this. Once you have your XNA game, add those files it generated to your game project (you can drag and drop or add them by right clicking on the game project in the Solution Explorer and choosing “Add->Existing Item…” or however you want). Note: not the content project, the game project. Once they are all in, you must do two things. First, right click on ServiceReferences.ClientConfig and set choose “Properties”. Set the “Build Action” to “Content” and set “Copy to Output Directory” to “Copy if newer”. I’m not sure that you have to set it to “Content” but you must set it to copy into the output directory to make sure it’s included in the XAP file. Next, switch back to the Solution Explorer and right click on “References” in your game project. You will need to add references (if you don’t already have them) to System.Net, System.Runtime.Serialization, and System.Servicemodel. Try compiling your game. It should compile. If not, add more references (make educated guesses based on the error messages – but those were all I had to add). Then you can add an instance of the WCF service to your game. I used the stock game that comes with the Windows Phone Developer Tools just to keep things simple. I added a SpriteFont to my content project, added the following field to my Game1 class:

        SpriteFont spriteFont;
        string result = "";
        Service1Client service1Client;
        bool hasSentRequest;
 

 

then in LoadContent I loaded my sprite font and instantiated my Service1Client using the parameter-less constructor:

service1Client = new Service1Client();

 

I then typed in,

service1Client.GetDataCompleted += 

 

at which point auto completion shows up and says to hit TAB to add in the rest and then to hit TAB again to generate the callback method. Two tabs produced:

service1Client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(service1Client_GetDataCompleted);

 

with a new method:

void service1Client_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
    // It started with the below line, which I eliminated and replaced with the rest of what appears
    // throw new NotImplementedException();
    result = e.Result;
}
 

 

I added lines in Draw to draw the string called result and then in update I added:

            if (gameTime.TotalGameTime.TotalSeconds > 15 && !hasSentRequest)
            {
                hasSentRequest = true;
                service1Client.GetDataAsync(30);
            }
 

 

This waits for 15 seconds then starts the process of getting data from the WCF service (which is still running, right? You didn’t close it accidentally?). This will return quite quickly, setting what was our empty result string to “You entered: 30” which will appear right on screen courtesy of SpriteBatch which has been drawing the empty string this whole time and now draws the newly populated one.

So there you have it. I won’t really go into detail about all the ways I tried that didn’t work. I finally got to a point where I was trying to implement a ChannelFactory(TChannel) which gave me a NotSupportedException when I did implement it by hand (because it wasn’t implementing itself) and that triggered a memory that led me to MSDN and this page: http://msdn.microsoft.com/en-us/library/ff637320(VS.95).aspx (“Networking for Windows Phone”, specifically the “Implementation Differences” heading) which in turn led me to this page: http://msdn.microsoft.com/en-us/library/cc197958(VS.95).aspx (“Using SLsvcUtil.exe to Access a Service”). Once I found that, it was done. A quick search on my computer found the location of SLsvcUtil.exe (which you saw earlier in this post). A quick run showed me it was a command line utility. And then a quick foray into the command prompt showed me how it worked.

One final note: unless and until you are hosting this on a “real” website (or at least on a fixed port on your computer rather than one that will most likely change each time you close Visual Web Developer and then restart it later), you’ll need to remember to open up ServiceReferences.ClientConfig and change the port number to whatever port you happened to get on the new run. It’s in the address in the “endpoint” tag – it’s a small file so you’ll see it right away. Sorry it’s been a while. I’ve been working on something “big” that I hope to have done and ready to go soon. Until next time, good luck!

Posted On Monday, August 30, 2010 7:36 PM | Comments (8)
Properly Exiting Silverlight-based WP7 Games

Anyone who has read the Windows Phone 7 Application Certification Requirements (PDF) knows that a WP7 game can never be more than two taps of the Back button away from quitting the game. In XNA this is easy since the Game class provides a method called Exit that can be called with relative ease. In Silverlight nothing quite so easy exists. One clever hack that’s been making the rounds is to create a new instance of the XNA Game class and call its Exit method. However this solution, clever though it is, is still a hack and probably not the best way to approach things. So what else can the aspiring Silverlight WP7 game developer do?

Well one solution is to create a “V”-based solution. I’ve implemented a simple, yet full-featured one and made it available under the Microsoft Public License here: http://bouncesample.codeplex.com/. A “V”-based solution is one in which the start page (MainPage.xaml) acts as a routing page. When the user starts the game, it routes them to a MainMenu.xaml page. When they choose an option from that menu, rather than navigate directly to that option, a public static state variable is set in MainMenu.xaml’s code-behind and NavigationService.GoBack() is invoked instead. When the program returns to MainPage.xaml, the override of OnNavigatedTo triggers, checks the pre-set state variables, and routes to the appropriate page (e.g. OptionsMenu.xaml or GamePlay.xaml). In GamePlay.xaml, when the user presses Back, the OnBackKeyPressed override triggers and the state variable is set to redirect the user to the PauseMenu.xaml page. This page gives the user the option to resume the game, return to the main menu, go to the options menu, or quit the game entirely. As per the App Cert reqs, a press of Back here exits the game (again through an override of OnBackKeyPressed which sets the appropriate state). Choosing Options sets a special public static bool within the OptionsMenu.xaml code that tells it to both change the text on the return button to reflect that it will be returning to the pause menu and to return to the pause menu on a press of that button (and on a Back key press too).

Anyway, the code should be easy enough to follow – the interesting bits are all in the code behind files. It’s up to you to make it pretty and create a real game for it, of course. I suspect I’ll be returning back to XNA with my next post. But who knows. Until then, good luck!

Posted On Sunday, August 22, 2010 4:38 PM | Comments (0)
XNA and WP7 Load Times

This is going to be more of a high-level post. But it’s important, especially for my fellow XBLIG developers who are now targeting WP7, since it highlights some things you need to really think about before writing your game (or while porting it).

In order to provide a great user experience, all programs (games and apps alike) must display their first screen within 5 seconds of launch and must accept user input within 20 seconds of launch. (See: Windows Phone 7 Application Certification Requirements (PDF)). The recommended practice to meet the 5 second mark is to include a file in your game (in the game project itself, not your content project) that draws right on startup. In Silverlight, this can be done by adding an image called SplashScreenImage.jpg (yes, a JPG) which should be 480x800. Unfortunately, this doesn’t work in XNA games. Recommended solutions for XNA games can be found in this thread in the Windows Phone 7 forums. They aren’t quite as easy as just adding an image but they aren’t all that hard either.

Then there’s the 20 second input mark. If you’re frontloading using your main game class’s LoadContent method (or the threading based solution for drawing a splash screen given in the link above) to load all your content you could wind up accidentally blasting by this. It’s one of the reasons I like the GameStateManagement sample – you only load content where needed and as needed such that your main menu should easily come up in under 20 seconds (and quite possibly in under 5, though you should still implement a splash screen to be safe). Regardless of where you load content, though, your game cannot appear to “hang” or prevent the user from, e.g., taking a phone call. So any non-trivial amount of content loading should be threaded with a loading screen and an input handler that checks for a back button press at a minimum.

Speaking of which, go read the App Cert Reqs about how the Back button should behave in your game. I’ve decided to adopt the “return to menu with option to resume” for my simple puzzle game but will likely be adopting the “in-game pause screen” method for my next, more “action-y” game since that’ll fit the style of that better. Since the player can just decide to leave your game at any point (and can get interrupted by a phone call or can just hit the Start button to bring up their phone’s menu (thereby “tombstoning” your game) in order to go do something or check something, it’s really important to plan out the objects that represent the current state of your game and make them serializable/deserializable so that if the player decides to leave for whatever reason, you can write them out using, e.g., the XmlSerializer class and then (if/when they return) present them with the option of resuming their last game.

Lots of things to consider. They’re all easy enough to handle – but you must know that you need to handle them. So, like Cato with his Carthago delenda est, expect me to regularly include a link to the App Cert Reqs. They’re important in many ways beyond just what I’ve laid out in this post. They also contain all the prohibited content rules, for example. These are more restrictive than the XBLIG rules (and logically so since people don’t lug an Xbox and TV with them to play games while on a commuter train, for example). Short post (for me anyway). Good luck!

Posted On Friday, August 20, 2010 9:42 PM | Comments (1)
Update on App Submission - NO FEES! :)

As promised, a new post with the updated info on app submission. The following link is the official policy: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2010/06/07/new-policies-for-next-gen-windows-phone-marketplace.aspx

In relevant part,

  • Annual registration fee of $99
  • No limit to the number of paid apps submitted 5 free apps per registration, $19.99 each after that

I am sorry if I alarmed anyone (I was alarmed, myself). My other criticisms stand, but I am very glad that the other link contained bum information. I'd like to suggest to whoever is in charge of developer.windowsphone.com that a revamp of the website to clearly distinguish between policies applicable to WM 6.x and WP7 be undertaken ASAP. New developers login excitedly once their accounts are approved and start clicking through those links on the left and it can be very confusing very quickly. For now, simply changing the title of that box to "WM 6.X SUPPORT" from "SUPPORT" would remove much of the confusion since I'm sure that's where the writer of the post with the bad info got it from. Anyone who quickly reads through those links will see "$99 per app certification" and, being on the developer.windowsphone.com website could easily say "ok, that's the policy".

Again, I'm very glad that it's $99 per year and that there are no limits on paid apps and that there are 5 free apps (with a fee for additional free apps beyond 5 as stated above) included, too. I really do love WP7 and want to see it succeed more than I've wanted to see something succeed in a very long time. So if you're listening, please do try to do more for indie XNA developers. The XNA development team @ Microsoft has been wonderful with their helpful blog posts and incredible dedication to answering questions in the Creators Club forums on their own time. But if you want to see us create really great games, you've gotta get some phones into the hands of our XNA MVPs at the very least so that they can test things for us and answer questions that the documentation is still unclear about.

More tutorials coming soon. Until then, good luck!

Posted On Thursday, August 19, 2010 2:45 AM | Comments (0)
How to pin your game or app's tile in the emulator (and I still love you WP7!)

I came across this by accident the other day and thought it was really neat. So I wanted to share it to help karma balance my last post (more on that at the end). As I’m sure you know, Silverlight apps install automatically in the application menu. For XNA games, you need to use this handy workaround. Once you do, after you’ve deployed your app or game, it stays installed for the duration of the emulator session.

“Yes, yes, we all know that,” you say.

Ah, but did you know you could tile your app? Click the arrow in the circle on the screen that has the IE tile on it to bring up the apps menu (if anyone knows the official terms for these, post in the comments with a link to an official(ish) source and I’ll gladly correct it and credit you!). Then, left click and hold on your app to bring up the special menu that lets you pin your app (or delete it without restarting the emulator!). Choose the “pin to start” option. And voilà! Your app with its tile! See the screen shots below for a visual example:

emuimage1 emuimage2 emuimage3

For those of you wondering what a tile is and how you’re supposed to make one, this is a great time to go read the Windows Phone 7 Application Certification Requirements (PDF)and the UI Design and Interaction Guide for Windows Phone 7 (PDF). They’ll both tell you that you need a 173x173 png file (UI design says it can be a PNG or a JPG which is wrong – PNG only) but only the second one will tell you that your tile image should be using 256 dpi, which gives you a good idea as to how big it will appear in real life (but don’t ignore the first one – the content restrictions are in there as are the specifics about what you need to do if building apps using certain features like location or integration into one of the hubs or running under a locked screen, and it also tells you all the things you need to be ready for when the time comes for you to submit your app).

As to how to set the tile image:

  • For XNA games, in Visual Studio, in the Solution Explorer, right click on your project and choose “Properties…”. Go to the “XNA Game Studio” tab and under “Tile Options” set the name of your game in the “Tile title:” textbox (which gets automatically written on top of your tile image at the bottom – my tile image doesn’t have the word “Slide Tiles” in it, that’s put their by the phone) and set the image file using the “Tile image: drop down menu and file selector dialog.
  • For Silverlight apps, in Visual Studio, first right click on your project and choose “Add->Existing Item…”. Browse to your tile image, select it, and hit “Add”. Then, in the Solution Explorer, right click on your tile image and choose “Properties…”. Change the “Build Action” to “Content”. Then, in the Solution Explorer again, right click on your project and choose “Properties…”. Go to the “Application” tab and under “Tile Options” set the name of your app in the “Title:” textbox and set the image file using the “Background image:” drop-down menu (not that there’s no file browser here like there was for XNA hence the extra steps.

If you have any problems, check to make sure that your tile image’s “Build Action” (in its Properties) is set to “Content”. Note that you’ll have to actually clear your app from the emulator and deploy it fresh if it wasn’t set to Content the first time and you tiled it. But that’s easy enough since you can just go to the menu, click and hold, and choose “uninstall”!

-----------

A quick note on my last post. It was a little rough, but after a good nap I realized that I still love Windows Phone 7 and I will still be developing for it even if I don’t get a phone until a month after other developers: that should simply motivate me to be an even better developer and to create even better, more irresistible games and apps. I’m also very hopeful that the $99 certification fee thing was simply a matter of people getting confused by reading the cert docs for Windows Mobile, which, if you become a registered developer and login to your marketplace account, show up as links that it seems like you should read:

marketplace

Those links all seem like they’re for the marketplace as a whole, but as you read them more closely, you’ll see that they keep mentioning Windows Mobile (not Windows Phone) and make other statements that fit perfectly within the Windows Mobile model and don’t fit in with the Windows Phone 7 model at all. I know some people who “know people” and they’re looking into it so I’ll post updates both to the original post and as a new post if/when I hear anything back on it. I could understand if Microsoft wanted to put in a cert fee in a year or two when the platform is well-established, but it’d just be silly to do that now when you want people creating and submitting as many great apps and games as possible. Anyway, until next time, good luck!

Posted On Wednesday, August 18, 2010 11:15 PM | Comments (2)
WP7 Questions, Concerns, and Disappointments

Update 2:

Excellent news! The $99 per app was wrong. See: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2010/06/07/new-policies-for-next-gen-windows-phone-marketplace.aspx  Thank you, Fred!

Update 1:

It's possible that somebody who didn't read closely looked at the old WM 6.X app cert docs, saw the $99 per app fee there, and thought it applied to WP7. I know people who "know people" and are investigating it. I'm very hopeful it's a mistake - see the end of my next post for how someone might have gotten confused. The rest of the stuff still irks me, but it beats that fruit company's products any day of the week! I'll post a further update if/when I get any more info.

-------------------------------------

I’ve been working on more helpful posts, but it seems that I keep breaking my nose on these walls I run into. I wanted to have a helpful post tomorrow or even later today. But instead I feel that it’s incumbent upon me to post about some things that are not good.

I love Windows Phone 7. I love everything I’ve seen about it. I love the sandboxing model they’re using. After I figured out tombstoning, I found that I love it too (especially (being primarily an XNA dev) not having to worry about stuff in the background eating resources and making your game look bad. I love writing code for it. I love the development tools. I love the look of Metro.

So what in tarnation is the title of this post all about then? Simple. On multiple occasions over the past two weeks, I’ve come up against problems that either didn’t have answers or that it took me hours of searching to find an answer for (one of the things you really learn how to do well in law school is efficiently research problems in multiple forms of media and it’s something I was really good at before law school so that’s a bad sign). Let me give you a few examples.

This here is the MSDN page on the "Hardware Specifications for Windows Phone". It lists a whole bunch of things. Two things it doesn’t list are the fact that the display is only required to be at least 16-bit (5 red 6 green 5 blue) and the fact that all Windows Phone 7 devices are also required to have FM radios. To find those out, you merely have to switch to the Programming Guide and go to the Design Resources for Windows Phone page (it’s one of only 20 topics so you’re bound to trip across it eventually) and then download the UI Design and Interaction Guide for Windows Phone 7 (PDF) and then make your way to page 67 of the PDF (pages 132-133 by the internal numbering) for the display depth answer and page 68 of the PDF (pages 134-135) to find out that all phones are required to have a radio (and that no control for it is being provided so if you want to use it you have to make your own). Now I knew the display thing from earlier since I spent an hour researching it to answer a question in the XNA Community Forums a few weeks ago. But several smart, knowledgeable, and resourceful people had no clue about this. What a surprise, right? It’s not like it’s buried on page 67 (133 by internal numbering) of some PDF file four layers deep on MSDN which is titled “UI Design and Interaction Guide for Windows Phone 7” – a name which in no way shape or form could lead anyone do believe that it has a single thing to do with hardware specifications. I also knew that there was a radio. But I didn’t know until I started writing this that the FM radio requirement wasn’t listed on the “Hardware Specification for Windows Phone” page but was also only buried in the unassumingly named “UI Design and Interaction Guide for Windows Phone 7” that is buried deep in MSDN rather than displayed prominently on the front page of the http://developer.windowsphone.com/ site with blinking arrows and a glowing neon “this is important and contains a lot of stuff you don’t know but that you absolutely should know if you expect to design anything that isn’t terrible” sign. Though to be fair, there’s also no blinking arrows or glowing neon signs for the Windows Phone 7 Application Certification Requirements (PDF), which is also absolutely mandatory reading BEFORE you start developing anything since it also contains all the content restrictions and special requirements for apps that want to do things like photo editing or be in the video and music hub. (There are hubs? Sure wouldn’t know that from using the intentionally borked emulator that hides everything about the phone’s interface such that you’ll have no idea how your app or game actually integrates into the user experience in any way shape or form).

But those aren’t even the problems I faced. After all, I participated in the jump start sessions and have been reading various MS employees’ blogs and all sorts of other media accounts of the WP7 for months now and I generally have a very good memory. No the questions I had were much more difficult. Take the FM Radio. There’s a whole FMRadio class for controlling the thing. And if you check out its Frequency property you’ll see that the property is a double, that its gettable and settable, and that if you assign a value outside the allowed set of values for the current region (there’s an enum property called CurrentRegion which is one of the values in the RadioRegion enumeration) that it’ll throw an InvalidArgumentException. Woe unto you who search for what the allowed values are. They don’t exist. There is no listing. If you examine the assembly with Reflector you’ll find that it only seems to check to make sure that the value is between 0 and 1000.0, which can’t be right at all since that range represents everything from shortwave radio to microwave ovens. And then there’s the fact that RadioRegion consists of three enumerators: UnitedStates, Europe, and Japan. Live in Australia or New Zealand? Sorry. Canadians? Too bad (though Canada and the U.S. actually use the same allocations for all relevant frequencies due to the large common border and historical cooperation in such areas during WWII and the Cold War). Any other country that doesn’t fit into those three regions? Who knows? So all Windows Phones are required to have an FM radio for which there is no Silverlight control, which doesn’t necessarily even have a control within the phone itself, and for which no useable programming interface exists (guessing what frequencies are ok based on what Wikipedia tells you about how frequencies are allocated in various regions isn’t a programming interface, sorry). So after six hours of research on that, I said forget it (though I used much stronger language) and gave up on the idea of building a Radio control “screen” to integrate into the Game State Management sample for the time-being (that was one of the posts I had planned to do – a how-to on the radio along with a screen any XNA dev could plug in to the GSM and add to their options menu and pause menu to let players who didn’t want to listen to the game’s music listen to the radio instead if they’d like). I also couldn’t determine from the documentation whether the region would be set properly (in which case you wouldn’t have to pester to the user to set it) based on location or if the region would always be, e.g., UnitedStates, and you’d have to get the user to set it (or do some sort of GPS location-based thing to locate the user and then parse that data to determine their location). But that’s moot anyway since there’s no way to just jump up/down a frequency gap (in the U.S., FM radio runs from the upper 80s to the upper 100s in .2 MHz increments using odd number – e.g. 101.5 MHz)  and there’s no information on what frequencies (other than negative numbers) will actually throw an exception or about what are acceptable frequencies in the various regions or even an ability to set a region for NZ or Aus.

Next up trial mode. I could provide you all sort of links again, but suffice it to say, the documentation is confusing and the application programming interface is more of an application guessing interface. In XNA, all sorts of new features have been added to the GamerServices namespace to support LIVE on WP7. This is great. Only if you aren’t a registered developer with a top-secret decoder ring, you can’t actually use many of these features. That’s fine too – LIVE is a crown jewel of sorts and they only really want top-notch games to have access to achievements and leaderboards. They’ve also removed access to Avatars from the Indies. XBLIG Avatar games have been a combination of great and terrible so I guess I can understand why they did that (though Avatars are still available to Indies making XBLIG games – just not WP7 games). They’ve also shut off access to many of the other things Indies have access to on the Xbox, even things as simple as a player’s gamertag. But the documentation of what’s still accessible and what isn’t is spotty at best. Some classes have warnings that you need to be a Registered developer to use them, but others don’t. And calling Guide.ShowMarketplace (which, it turns out, is the way XNA devs should bring up the WP7 marketplace for their games) brings up a message on the emulator talking about the LIVE marketplace. Well all good XNA devs who are targeting WP7 know that all LIVE functionality is off-limits unless you’re part of the special program that lets you make games that use LIVE functionality. So any good developer might have questioned it (and questioned whether Guide.IsTrialMode would be valid as well, especially since the description page oozes Xbox360 and LIVE and contains el zilcho mentions of WP7) and instead delved into the Creating Trial Applications for Windows Phone page found in the Windows Phone Development Programming Guide section. Of course this is a nightmare in and of itself. It recommends that you use MarketplaceLauncher to show your program’s Marketplace details page. Click on that link, though, and you’ll see that MarketplaceLauncher is already a deprecated API. ??? And its Show method contains two overloads. You’d need to use the one that takes a string context and provide your program’s GUID to bring its marketplace page up. And how do you get the GUID? Simple, you spend 3 hours searching the internet in order to find out that there’s no recommended programmatic way for doing this such that you should just copy it from AssemblyInfo.cs in the Properties folder of the Visual Studio project for your program. Might MS change the GUID as part of its whole disassembly-reassembly-signing process it goes through on submission? You’d better hope not because you really don’t want your “Runner’s Buddy” app bringing up the marketplace page for “Fart Monster 9000” or displaying some sort of “the programmer who wrote this app is obviously stupid since they sent you to a non-existent marketplace page – are you sure you really want to buy something from him or her?” page. Maybe you could spend several hours figuring out how to query it with reflection, though it could turn out to be that there’s some security attribute set on that property or find out that it doesn’t actually work because the reflection thing wasn’t picked up by the magic automated tests that are done to determine what functionality your app uses so your app wasn’t authorized to do it. Oh, and that hardcode the GUID and pray thing? It’s also the way to do it if you use the new MarketplaceDetailTask class that you should be using instead of the deprecated even though the product hasn’t been released yet and there doesn’t seem to be anything at all wrong with it MarketplaceLauncher class.

I’m used to using XACT for audio. It’s a great API. But it’s also not a lightweight thing so I can understand why it wasn’t included on the phone. Instead XNA devs have to use the MediaPlayer class (along with the MediaLibrary class and all the other Media___ classes) in order to play background music. Great. So how do I know if the user’s playing music already? XACT just takes care of that for you as long as you set the categories appropriately. After an hour of reading about how to integrate with the Music and Video hub and how none of the functionality for any of these things will actually tell you if the user is already playing music using some other application (even though the system presumably knows such things), I threw up my hands and asked another question on the XNA forums. Thankfully one of the XNA team members saw my post and provided an answer. It turns out I’d overlooked the MediaPlayer.GameHasControl property, which is what tells you whether or not you are allowed to play your game’s music. If I’d used that API before, I would’ve known it, but I didn’t and none of the XNA Game Studio 4.0 docs that I came across mentioned it, let alone explained it or provided an example of its use.

Another major problem has been the fact that, as best I can tell, none of the “usual suspects” (i.e. people who regularly answer questions) in the XNA forums has an actual developer phone. None of the MVPs who are active in the forums (and one of them is writing a book on WP7 programming) and none of the regular active members. Let’s say I had a phone. That FM radio thing I was talking about earlier? Within 2 hours I could have written a program to test it out thoroughly and provided a lot of helpful information in a blog post about it. Instead I spent 5-6 hours all told searching every forums I know of for information only to find out that the FM radio is one of many red-headed stepchildren that exist already in WP7. Nobody discusses it. Nobody knows much about it. And, as best I can determine, anyone who has gotten an actual phone device is so busy excitedly gloating discussing it that they aren’t testing out all the actual functionality and providing help to those who don’t have one. Earlier I answered a basic knowledge question posted by someone who has an actual device. Basic. Knowledge. The kind of question where if you had never come across it before, it could only be because you never did anything with XNA before in your life. Yet… has a phone. XNA MVPs with reputation scores and post counts well into the thousands? No phones. Journalists who can easily fly around to various events to test out phones and who Microsoft could easily go to in order to demonstrate the phone? Phones. Well known and respected XBLIG developers who have already ported games to WP7 and created really impressive videos of the results? No phones. (And with the emu reportedly much better at graphics than actual phone devices, this is potentially a huge problem).

And then just a few days ago I read that it's going to cost $99 per app for certification. At the jumpstart sessions earlier this year, this all came up and what seemed to have been said was that developers who registered for the marketplace could submit unlimited paid apps and 8 free apps (after which they’d pay a fee of $20 per for the free apps). I’m sorry, but those two things don’t jive. I was led to believe that app certification (at least initially) was all rolled up in the fees for signing up for the marketplace. Indeed, if there was going to be a certification fee, what would be the point of limiting free apps to 8 after which you’d need to pay $20 per additional app? Isn’t the $99 certification fee a limiter on free apps in and of itself? And if not, does another $20 really make any difference? Microsoft wants lots of apps to make WP7 a success. Yet they want to charge developers $99 just for the privilege of saying they’re registered on the marketplace and want to bilk charge us an addition $99 per app that we submit for certification to this marketplace of unknown size for an as-yet unreleased product the success of which is still very much up in the air? And to top it off, phones won’t be available in the US until a month after they’re available in Europe (thereby putting all US developers a month behind their peers in Europe)? Well that’s just a crummy deal. First XNA devs are told that our Creators Club memberships mean nothing in regards to WP7 and that we have to sign up separately for that marketplace. Then we’re told that our signup fee means nothing and that we have to pay separately to certify each game we submit despite the fact that many of us were led to believe that there weren’t going to be any such fees initially. And finally those of us in the US are told that we’re being knee-capped set at a month’s disadvantage to developers on the other side of the Atlantic who will be able to buy actual phones, test their apps and games to ensure that they work right, and submit them before we even have a chance to buy a phone? And that the most helpful MVPs in the XNA forums don’t get phones but random people who don’t even know what they’re doing do?

I’m really disappointed. I mean that. I feel hurt and let down and mislead. I already signed up for the marketplace. I have one game virtually complete (though nothing other than the arms-tied-behind-its-back emulator to test it on) and two apps planned. I don’t even know if it’s worth the bother of writing them at this point. I get the privilege of spending $300-500 on a phone a month after it’s released so I can test apps that may not sell at all if someone with a month’s head start has already locked up the market and for which I have to spend $100 each to vet (is it an additional $100 if it fails certification because something in the documentation was unclear or misleading or does that fee cover retests? Who knows…) after spending $100 so some company could grab a credit report, ask me a handful of questions, and accidentally send me an instant rejection letter leaving me anxious and upset for more than a week until everything finally went through and that message with a big red-circled X telling me my account was “suspended” (making me feel like some sort of criminal) went away.

Between my expressions of disappointment there’s actually some useful information for WP7 developer in here. If you aren’t as disgusted as I am and are still gung-ho and excited to make apps and games, take it and use it. As for me, I’m going to spend the next several days re-evaluating my plans and hoping that someone at Microsoft reads this, realizes that XNA developers are feeling pissed on, and takes some steps to rectify it. Or you could just let the marketing folk keep spreading stories about how developers are the “secret sauce” while flipping off US developers by delaying our access to devices by a month and crippling game developers in general by failing to make even a single device available to one of the people who answers the questions we have and which we raise in the XNA forums just like the Windows Phone Developer site tells us to.

(I really would have loved to have a radio tutorial & sample and a trial mode in XNA and Silverlight tutorial with samples for y’all, but my nose is simply jammed too far into my face with all the walls I’ve been running in to. I’ll try to figure out something I can actually test well enough to write about and work it up for this weekend. No guarantees, but I’ll do what I can…)

Posted On Wednesday, August 18, 2010 2:18 PM | Comments (3)
IndieNerds.com and Xbox LIVE Indie Games: a Great Combination!

One of the toughest things for an Indie game developer is getting exposure. For many Xbox Live Indie Games (“XBLIGs”), the time they spend on the “New Releases” list is critical to their success (or failure). Most games can expect a couple of days to a week before dropping off the end of the list. At that point, if they haven’t jumped up to either the “Top Downloads” or the “Top Rated” list, they will often fall off the radar of many gamers.

Recently, though, a very fine gentleman from Germany, Mr. Gerald Terveen, has undertaken a mission to highlight some of the best and brightest games. His website, http://www.indienerds.com/, is a treasure trove of previews and reviews. He’s created over 30 top-notch video reviews of XBLIG titles so far. One I particularly like (for a game I really enjoy) is the AvaGlide review:

I have yet to find an Indie game with a better implementation of distributed high scores and I keep coming back and playing the game when I find I just need a nice relaxing break from a particularly arduous coding session. I’m working on more WP7 content and should have several new tutorials up soon. But XBLIG is a wonderful place that too many people either ignore or simply don’t know about. IndieNerds does a great job of highlighting some of the great games you can find there, so I highly recommend checking it out.

If you’ve never been to the XBLIG Marketplace, there are two ways to get there. From your Xbox dashboard, go to “Game Marketplace” and choose “Explore Game Content”. Then scroll up (or down) until you reach Indie Games. From here you can choose “New Arrivals”, “Top Downloads”, “Top Rated”, or “Browse” (to see all games sorted in various ways). You can also go directly to http://www.xbox.com/ and choose “Indie Games” from the “Games” menu. You can purchase games right from the Xbox.com site and they will queue up and begin downloading the next time you login to LIVE on your Xbox.

There are over 1000 Indie games to choose from ranging in price from 80 MSP to 240 MSP to 400 MSP (with one or two still at 800 MSP having been grandfathered in from the former pricing system). With 80 MSP being approximately $1 US, you can find many amazing (and many not-so-amazing) games for less than the price of a cheese danish in a vending machine. And unlike that snack, you’ll have these games forever! The XBLIG Marketplace is not available in all regions, unfortunately. But if you live in the United States, Canada, the United Kingdom, France, Italy, Germany, Japan, Sweden, Singapore or Spain, go check it out! Like any open market, there are good titles, mediocre titles, and even some down-right awful titles. So go check out IndieNerds.com and other sites like it to help you find some of the hidden gems. That’s all for now. Next week at least two new WP7 articles. Until then, good luck!

Posted On Saturday, August 14, 2010 2:22 AM | Comments (0)
Size Apps and Games Properly Using the WP7 Emulator

For Windows Phone 7 developers, one of the more important pages on the MSDN website is the Design Resources for Windows Phone page. In particular, the UI Design and Interaction Guide for Windows Phone 7 (PDF) found there is something that every developer should read (and sooner rather than later unless you look forward to major redevelopment work at the end of your product’s development cycle). If you read it a while ago, check it out again as it’s now at Version 2.0 (updated and expanded quite a bit around the same time as the Beta of the Developer Tools was released).

In particular, page 24 of that PDF (page 47 using the internal labeling), which covers “Tiles and tile notification” specifies that, “Tile images should be 173 pixels by 173 pixels at 256 dpi and in JPEG or PNG formats.” What interests me about that line (beyond knowing that I need a tile image) is the 256 dpi specification. We know that the hardware specs require an 800 x 480 screen. So with a target dpi of 256 (with some minor variance from manufacturer to manufacturer, in practice) and a resolution of 800 x 480, we can run the numbers and determine the approximate size of a “standard” WP7 display.

  • 800 / 256 = 3.125 in. = 7,9375 cm
  • 480 / 256 = 1.875 in. = 4,7625 cm

There are two ways this is important. First, at 256 dpi, your applications will look nice a crisp. Second, you can use this to size the emulator appropriately when testing to get an idea of the approximate size of things you expect the user to press. Fire up the emulator (the easiest way is to launch an application or game you’re working on), then once it’s ready, exit your application. Yep, exit (by pressing the “hardware” back button – the point is to keep the emulator open). You should be on a screen with just the Internet Explorer tile and an arrow in a circle that would be pointing right if you were in portrait mode (probably best to switch to portrait mode temporarily if you happen to be in landscape mode for whatever reason). Press the arrow in a circle and you should see a menu of things. At a minimum it will include “Internet Explorer” and “Settings”. It might include one or more of your applications. (If you’re developing an XNA game and don’t see your game, go read this great post from Michael Klucher explaining why that is and what you can do to make your game appear). Press “Settings”. Under the “System” panel, you will see “theme”. Press that. Then press “Background” and switch from “dark” to “light”. All of this wasn’t strictly necessary, of course, but it gives you a chance to see the system’s theming (particularly important for you Silverlight developers out there) and it makes it more important to determine the exact boundaries of the screen in the emulator.

When you hover your mouse over the emulator, a toolbar appears on the right. It’s the one that lets you rotate between landscape and portrait, amongst other things. On that toolbar is a little wrench icon. Click it and you’ll see a “Settings” window pop up. At present, the only setting available in the window is “Zoom level”. But that’s exactly what we need.

Start by switching down to 33%. Press “OK” and watch the emulator magically shrink. Now take a ruler (the kind that measures things – you can print one out from the internet if you find you don’t have one) and measure the area that comprises the emulator’s screen. You’re aiming for the long end to be about 3.125 inches (3 and 1/8 inches) which equates to 7,9375 cm (yes I used a comma as a decimal separator – for many who would be measuring in cms, a comma is their decimal separator, no matter how odd I think it looks :) ). Depending on the size of your monitor and the dpi of your monitor, you will want to either go up or go down (unless it’s perfect as is, in which case you’re done). Click the wrench icon again, and this time use the “Custom” textbox to enter a suitable guess. Click “OK”, measure, and repeat until it’s basically right. I ended up with 36% being about right based on my monitor size, resolution, and dpi. Once you’ve got the correct value, write it down somewhere so you don’t need to keep doing this (remember that it will change on different monitors, on the same monitor at a different resolution, on the same monitor at a different dpi, etc.). At this point, you can switch back to the “dark” theme if you’d like.

Now go back and start up your application/game again. Before you balk at the graphics being fuzzy or the text being hard to read, remember that the fuzzy, hard-to-read text is a result of you running the image scaled way down (most computers tend to run at 96 dpi, which means 9216 dots in a square inch, whereas the phone will run at ~256 dpi, which means 65536 dots in a square inch, more than 7 times as many pixels in the same area!). So this doesn’t tell you a thing about how your app or game will look on a phone. What it does do is allow you to poke your finger at your monitor and have a good idea as to whether or not the sizes you have assigned to elements you expect the user to touch and interact with are sufficiently large to be usable. The UI Design and Interaction Guide (linked to above) provides a lot of great information on minimum sizes (especially for you Silverlight designers out there), but being able to actually poke your finger and see how the area you’ve provided for someone to poke at compares to the size of an actual finger tip is very nice, especially for XNA developers who aren’t working in minimum pt sizes but instead in raw pixels or even in the more fluid world of 3D (where everything is measured in units and sizes are all merely relative to one another).

Of course none of this compares to the experience of having an actual Windows Phone 7 device to work with, but until more devices make their way out there, this is a pretty good reality check for those of us plugging away at developing games and apps with the emulator. You needn’t always run the emulator at that size, and it probably doesn’t make much sense to do so since the scaling will distort graphics and text as a by-product. But every now and then it’ll be a good idea to switch back down to that size and poke your finger at the screen some so that you can make any sizing adjustments on an on-going basis rather than having to suddenly do some major redesign work towards what you thought was the end of your development cycle. Good luck!

Posted On Friday, August 6, 2010 2:28 AM | Comments (0)
Tutorial: Turning a Solution into a Template for XNA 4.0 (and Visual Studio 2010 In General!)

One of the new features of XNA 4.0 is the Content Project. Those of us who’ve worked in XNA 3.1 and earlier are familiar with the old Content folder, which was simply a folder inside your game project into which you’d place your raw content and from which your game would load the compiled XNB files. In theory it could be named anything since one of the things you would do in the constructor for your game is specify the name of the Content folder. XNA 4.0 has moved content (i.e. game assets like music, sound effects, sprite images, 3D models, shader effects) into a separate, special type of project called the Content Project.

With the new Content Project as part of a normal XNA Game Solution, its a sure thing that almost every game will be a multi-project solution. First things first, let’s just go over a bit of terminology. If you already know this, great. If not, this might help clear it up. In Visual Studio, there are two container types: solutions and projects. A solution holds one or more projects and can also hold items not specific to any particular project called solution items. I’ve never seen solution items used in XNA projects, (it would require quite a bit of fiddling and futzing without any real benefit from an XNA perspective) so I’d recommend avoiding them when creating XNA games (certainly when starting out). So for our purposes, a solution holds projects.  A project holds individual items that make up the components that are put together to make your game. In a basic XNA 4.0 game, you would have two projects: a Content project, and a “Game” project. The Content project, as mentioned above, is where your content goes in order to be processed by the content pipeline and turned into XNB files (efficiently compressed binary files designed to be quickly loaded into their appropriate XNA Framework class by the ContentManager class). The “Game” project is your game itself. It’s where all the magic happens. Of course over time you’ll probably develop one or more “library” projects that contain classes and extension methods you find yourself needed in game after game. You’ll also probably discover that you’re constantly writing the same code and including the same basic assets (e.g. sprite fonts, splash screen) just to get to the “starting point” from which you begin building a unique game. And this is where we get to the purpose of this post.

One of the great features of Visual Studio is the ability to create your own custom templates. This way, if you write a “base” level project that you like, you can turn it into a template and then create new instances of it for as many different projects as you want. Visual Studio includes the ability to export templates automatically (“hooray”), but it can only export a single project or individual items from a project (“boo”). I’m kidding about the “boo” – the ability to take any arbitrary project or even just one or more individual class files (using the “Item Template” option) and just turn it into a template that you can then use again and again is incredible. Besides, if it could export an entire solution as a template, then I wouldn’t be writing this exciting tutorial to tell you how to do it! Yes, that is the goal, and we’re going to do it using the Windows Phone Game State Management sample as a starting point. You will need to agree to the terms of the Microsoft Permissive License, a copy of which can be obtained from the same webpage as the sample, in order to download and use it. It’s up to you whether you agree or not, but if not, then the rest of this will be quite hard to follow along with. I’ll be including some XML in this example. It too is subject to the Microsoft Permissive License and so if you agree to those terms, please continue reading and if not, you should probably stop now.

So first things first, download the sample, unzip it, and load it up in Visual Studio. For reference, this is all done entirely with the free Microsoft Visual Studio 2010 Express for Windows Phone (which I’ll be calling “Visual Studio” for short from here on out) which comes with the free XNA Game Studio 4.0 Beta, also called the Windows Phone Developer Tools Beta (but it’s the full XNA for all platforms, don’t worry!). It is all also possible in Visual Studio 2010 Pro, Premium, Ultimate, etc., of course (using the same steps). But I’ve made sure to do it with the free tools so that anyone at all can make use of it. unblockAs a tip, once you’ve downloaded the zip file, but before you’ve extracted it, go to the folder where you downloaded it to, right click on it, select “Properties”, and then in the “General” tab press the “Unblock” button if you see it (see the picture to the left) and then press Ok.   In Windows 7 (and I believe in Vista, too, though I’m not positive), when you download a file from the internet, the operating system tags it as being something it might want to keep track of, just to make sure it doesn’t do bad things to the computer. If you just unzip it, all the files that are unzipped that could potentially be hazardous are also tagged with this special security marker. But if you unblock the zip file first and then unzip it, the special “danger” tag isn’t applied to the unzipped files. This gets rid of that “This project comes from an untrusted source, etc. etc.” message you might come across sometimes when loading samples. So anyway, if you do that and make sure the zip file is unblocked before unzipping it first, you shouldn’t get that message or have any problems. (Always scan all files you download from the internet with a virus scanner that is using the latest definitions. Even from trustworthy sites like the Creators Club. It only takes a couple of seconds and if you make good habits like always virus scanning everything you download part of your routine, you may just save yourself from a catastrophe someday).

Ok. So, file unblocked and unzipped to the location of your choice. Go ahead and open up Visual Studio, and open up the solution using “File”->”Open Project…”. It should load up and be all set. At this point, you could make any changes you wanted to it or compile it first and deploy it just to make sure it works. If you do wish to do that you can, so go ahead and make any changes you want (or don’t as the case may be).

Ok. Let’s proceed. So at this point you’re going to want to go to the “File” menu and choose “Export Template…”. exporttemplate

 

 

 

 

It may pop up a dialog asking you if you wish to save. If so, just click Yes. You’ll then be presented with a dialog asking you to choose a template type. By default it should have “Project template” selected, which is nice because that’s just what we want. You’ll also see a drop-down box asking you “From which project would you like to create a template?”. Assuming you haven’t changed anything, it should have “GameStateManagementSample (Phone)” selected. We’ll stick with this for now (if you renamed it, go select the game project – we’re doing Content second because… well, ‘cause that’s how we roll.) When you’re ready, click “Next”.

At this point you’ll be presented with the “Select Template Options” section.  This part is important! So pay close attention. First, we don’t want to stick with the default really long name. It’s quite long and that length can cause problems. So we’re going to shorten it. In this case, I’ve chosen to shorten it to “PhoneGame”. Nice and short but still reasonably descriptive.

templateoptions

Then we want to write a good “Template description”. Let’s say you become a game-making machine and start cranking out a game a month. Maybe you end up teaming up with a couple of people and together you have several projects going at any one time and have a new game ready every two weeks! The more games you make, the more refined the starting points will get. You might make a platformer and while writing it decide that you’ll want to write a sequel or perhaps a different platformer. So maybe you’ll want to create a platformer starting point. And maybe a puzzle game starting point. And maybe one for XBLIG titles (with all your special saving and loading code built in). So the description can be quite important. Then there’s the Icon Image and Preview Image. These help you identify the project when you’re browsing templates in Visual Studio. You’ll probably want to create a special one at some point (most templates use a .ICO icon file that’s 64x64 pixels, 24bpp, 1-bit alpha, no palette, and not a compressed PNG), but you needn’t worry, you can just use the GameThumbnail.png file that comes with the sample for now if you’d like. Make a note of the Output location. Then make sure that “Automatically import the template into Visual Studio” is NOT checked. We’ll put it into Visual Studio later when we’re ready. So it should look something like the image above when all’s said and done. If you’d like, you can check the “Display an explorer window on the output files folder” checkbox. This will save you a bit of time navigating to it yourself. Then click “Finish”. If you did put that check in, the Explorer window will open and show you your brand new template zip file. It’s nice, but we’re not done yet! Switch back to Visual Studio.

Time to repeat the process. Go to the “File” menu and choose “Export Template…”. Once again it’s a “Project Template” but this time, we must go to the drop-down menu and change the project to “Content”. Click “Next”, and we get to the “Select Template Options” window. This time you probably want to leave the name alone (“Content” is fine). Add a “Template Description” (e.g. “Content Project for PhoneGame”), select an Icon Image and a Preview Image, make sure that “Automatically import the template into Visual Studio” is not checked, and click “Finish”.

At this point, it’s time to leave Visual Studio. If you had it open a folder for you automatically, switch to that folder now. Otherwise, open up Explorer and make your way to the appropriate folder (usually “C:\Users\username\Documents\Visual Studio 2010\My Exported Templates\” where “username” is your user name on your system). Once there, select both the game and the content project templates (if you followed my naming scheme above, they should be “PhoneGame.zip” and “Content.zip”). In another Explorer window create a new directory (the name is not important). You could also use an existing directory, but it should be completely empty – for this reason, it’s better just to make a new directory which you can delete later if you’d like. Switch back to the window with the two zip files. Select them both, then choose “Cut” from the “Edit” menu. Switch back to the window with your empty directory, make sure you are inside the empty directory, and then choose “Edit”->”Paste” to paste the zip files into the empty directory.

And now you’ll want to unzip these. In Windows 7, simply right click on the zip file and choose “Extract All…”. Make sure to extract them into their own directories (i.e. Content.zip should extract into a folder called “Content” and PhoneGame.zip should extract into a folder called “PhoneGame”). Like so: directories

Now, delete the zip files. Or move them to some other directory. Do something with them such that they are no longer in this directory or any subdirectory of this directory. Done? Good. Now if you look in each of the directories, you should see that they have all of their content from when they were Visual Studio projects plus three additional files:

  • “__PreviewImage.png”,
  • “__TemplateIcon.png”, and
  • “MyTemplate.vstemplate”.

The first two might have .ICO extensions if you decided to create “proper” icons for them and if you didn’t set one or either when exporting your templates, then they simply won’t exist, which is no problem.

Now, return back to the directory that has those two directories in it. This is where the “magic” happens. First, we will want an icon for the project. The easiest thing to do is go into one of the subdirectories, copy its “__TemplateIcon.png” file, and paste it here in the base directory. Make a note of the name of the file you choose. I’m going to assume you are using “__TemplateIcon.png”, so replace that with the name of your file if you use something different.

You should now have a directory with two subdirectories, “Content” and “PhoneGame”, and an image file called “__TemplateIcon.png”. Right click in the empty space in this base folder area and choose “New”-> “Text Document”. Name this file whatever you like. I am going to assume that you will have named it “PhoneGame.txt”. Open this file up in your favorite plain text editor (we don’t want any RTF or anything else creeping in – Notepad is suitable). And then paste the following in:

<VSTemplate Version="3.0.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
  <TemplateData>
    <Name>PhoneGame</Name>
    <Description>A Windows Phone 7 game template for XNA 4.0</Description>
    <Icon>__TemplateIcon.png</Icon>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>12000</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>
    <DefaultName>MyPhoneGame</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
    <PromptForSaveOnCreation>true</PromptForSaveOnCreation>
  </TemplateData>
  <TemplateContent>
    <ProjectCollection>
      <ProjectTemplateLink ProjectName="PhoneGame">
        PhoneGame\MyTemplate.vstemplate
      </ProjectTemplateLink>
      <ProjectTemplateLink ProjectName="Content">
        Content\MyTemplate.vstemplate
      </ProjectTemplateLink>
    </ProjectCollection>
  </TemplateContent>
</VSTemplate>

 

As you may have noticed, this is a bunch of XML code. If you are interested, you can view the schema here. Things you can change: the “Name” tag’s value can be something other than “PhoneGame”; the “DefaultName” tag’s value can be something  other than “MyPhoneGame” (keep it short though – this is what gets used by default when you create a new one of these, e.g. “MyPhoneGame1”, and if it’s too long it will cause problems and you’ll have to come back and shorten it); if you used names other than “PhoneGame” and “Content” you should modify these in the “ProjectTemplateLink” tags as appropriate; if you have a different “Icon” file name, change it to the proper value. Now save this file with an extension of “.vstemplate”. If you used “PhoneGame.txt”, for instance, you will now save it as “PhoneGame.vstemplate”. Then close the file.

If you still have a “PhoneGame.txt” file hanging around, delete it. Your folder contents should now consist of two folders, “Content” and “PhoneGame”, an image file “__TemplateIcon.png”, and the “PhoneGame.vstemplate” file we just created. Select all of these, and add them to a zip file called “PhoneGame.zip”. In Windows 7 (and perhaps Vista too), select all files, right click, and choose “Send to”->”Compressed (zipped) folder”. You should now have a file named “PhoneGame.zip”. Select this file, go to “Edit”->“Copy”. Now go to your “Visual Studio 2010” directory where your projects and other VS2010 files and folders are located. This should be something like “C:\Users\username\Documents\Visual Studio 2010\”. Go to the “Templates” directory in there. Then go to the “ProjectTemplates” subdirectory. You may see various subdirectories in here. Ignore them. Go to “Edit”->”Paste”. You should see “PhoneGame.zip” appear.

Now, go back to Visual Studio. Close the current solution (if you didn’t do so earlier). Then go to “File”->”New Project…”. If you’ve done everything correctly, you should be greeted with this:

victory

Choose a name, press OK, and voila, your very own XNA game template! As a quick final note, there is nothing about this that makes it specific only to XNA games. You can use this for any multi-project solution that you’d like to turn in to a template. And you are not limited to only two projects, either. To add more projects, simply export them, unzip them as you did the others, and then add an additional well-formed “ProjectTemplateLink” tag to the .vstemplate file. Just as I finished writing this, I discovered the How to: Create Multi-Project Templates page on MSDN. Since I’ve already written this out (and taken all these nice screenshots), I figure I will go ahead and post it. But it is a good object lesson in something I’ve learned over time: if you search hard enough, you will find almost everything you could ever imagine explained on MSDN (at least as far as computing and Microsoft products goes :) ). Until next time, good luck!

Posted On Thursday, August 5, 2010 5:36 AM | Comments (4)
Making a MenuEntry in XNA 4.0’s GSM Sample Look Like a Button

In this post, I’m working with the GameStateManagement Sample for Windows Phone 7. All source code not a part of that sample is Copyright 2010 Michael B. McLaughlin. If you wish to use it, you may do so provided that you agree to the terms of the Microsoft Permissive License, available here. If you do not agree to those terms, you may not use this source code (and should probably stop reading now). Since this code is built upon the GameStateManagement Sample, you will also need that and thus will also need to agree to the license for it (which, not surprisingly, is also the Microsoft Permissive License, a copy of which can be downloaded at the link to the GameStateManagement Sample above).

My original purpose in writing this code was to display as a texture a solid colored background with an optional border. This is a sample image I am using:

border

To make use of the code, you will need to know the width in pixels of the border. If there is no border or if the image itself is smaller than the border, it will simply scale accordingly (for example, this lets you use 1x1 textures – I use a 1x1 black texture with the alpha set to 80% as a “drop shadow” style texture – more on this later). Anyway, first we need to add a new class to the project. Here it is:

    /// <summary>
    /// A class for drawing a solid colored texture with a border where you do not wish the width and height (in pixels) of the border to change.
    /// </summary>
    public class BorderedTextureBox
    {
        /// <summary>
        /// The texture to be drawn.
        /// </summary>
        Texture2D texture;
        
        /// <summary>
        /// The width in pixels of the border along the width (i.e. horizontal sides) of the texture.
        /// </summary>
        int borderWidth;
        
        /// <summary>
        /// The width in pixels of the border along the height (i.e. vertical sides) of the texture.
        /// </summary>
        int borderHeight;
        
        /// <summary>
        /// The Rectangles used to construct the properly scaled texture. These deal with unscaled corners, scaled verticals along the left and right
        /// borders (sampled from the middle of each, respectively), scaled horizontals along the top and bottom (same sampling method), and a
        /// scaled middle, sampled from the middle of the image and scaled to fill in all areas not covered by the border.
        /// </summary>
        Rectangle upperLeftSource, upperRightSource, upperStretchSource, lowerLeftSource, lowerRightSource, lowerStretchSource, leftStretchSource, rightStretchSource, middleSource;
 
        /// <summary>
        /// Used to avoid recalculating the other Rectangles every frame if the destination passed is the same as it was last frame.
        /// </summary>
        Rectangle previousDestination;
        
        /// <summary>
        /// The span, in pixels, between the end of the border on the left and the beginning of the border on the right. Used as a scaling factor.
        /// </summary>
        float horizontalGapWidth;
 
        /// <summary>
        /// The span, in pixels, between the end of the border on the top and the beginning of the border on the bottom. Used as a scaling factor.
        /// </summary>
        float verticalGapWidth;
 
        /// <summary>
        /// Create a new BorderedTextureBox using the specified parameters.
        /// </summary>
        /// <param name="texture">The texture to use.</param>
        /// <param name="borderSize">The width in pixels of the border around the texture.</param>
        public BorderedTextureBox(Texture2D texture, int borderSize)
            : this(texture, borderSize, borderSize)
        {
        }
 
        /// <summary>
        /// Create a new BorderedTextureBox using the specified parameters.
        /// </summary>
        /// <param name="texture">The texture to use.</param>
        /// <param name="borderWidth">The width in pixels of the border along the width (i.e. horizontal sides) of the texture.</param>
        /// <param name="borderHeight">The width in pixels of the border along the height (i.e. vertical sides) of the texture.</param>
        public BorderedTextureBox(Texture2D texture, int borderWidth, int borderHeight)
        {
            this.texture = texture;
            this.borderWidth = borderWidth;
            this.borderHeight = borderHeight;
        }
 
        /// <summary>
        /// Draws the texture.
        /// </summary>
        /// <param name="spriteBatch">The SpriteBatch with which to draw the texture. The calling method is responsible for calling Begin and End for the SpriteBatch.</param>
        /// <param name="destination">A rectangle specifying the destination. This will be used to appropriately scale the image.</param>
        /// <param name="color">The color to tint the texture. Use Color.White for no tinting. (Same as SpriteBatch.Draw).</param>
        /// <param name="layerDepth">The layerDepth to be passed to SpriteBatch.Draw. Must be between 0.0f and 1.0f, inclusive.</param>
        public void Draw(SpriteBatch spriteBatch, Rectangle destination, Color color, float layerDepth)
        {
            if (texture == null)
            {
#if DEBUG
                // In debug configuration, we want to know about the problem, so throw an exception.
                throw new InvalidOperationException("Texture is null.");
#else
                // In release configuration, we don't want the game to crash over a missing texture, so return silently.
                return;
#endif
            }
            // If both borderWidth and borderHeight are 0, or if the either of the destination dimensions are less than the value of the combined corners,
            // or if the provided texture's dimensions are less than the borderWidth or borderHeight, 
            // then just draw it auto-scaled to the size requested
            if ((borderWidth == 0 && borderHeight == 0) || destination.Width < borderWidth * 2 || destination.Height < borderHeight * 2 || texture.Width <= borderWidth * 2 || texture.Height <= borderHeight * 2)
            {
                spriteBatch.Draw(texture, destination, color);
                return;
            }
 
            if (destination.X != previousDestination.X || destination.Y != previousDestination.Y || destination.Width != previousDestination.Width || destination.Height != previousDestination.Height)
            {
                int halfWidth = texture.Width / 2;
                int halfHeight = texture.Height / 2;
 
                // Upper corners
                upperLeftSource = new Rectangle(0, 0, borderWidth, borderHeight);
                upperRightSource = new Rectangle(texture.Width - borderWidth, 0, borderWidth, borderHeight);
 
                // Lower corners
                lowerLeftSource = new Rectangle(0, texture.Height - borderWidth, borderWidth, borderHeight);
                lowerRightSource = new Rectangle(texture.Width - borderWidth, texture.Height - borderHeight, borderWidth, borderHeight);
 
                // Horizontal borders
                upperStretchSource = new Rectangle(halfWidth, 0, 1, borderHeight);
                lowerStretchSource = new Rectangle(halfWidth, texture.Height - borderHeight, 1, borderHeight);
 
                // Vertical borders
                leftStretchSource = new Rectangle(0, halfHeight, borderWidth, 1);
                rightStretchSource = new Rectangle(texture.Width - borderWidth, halfHeight, borderWidth, 1);
 
                // Middle (grab from the very middle to avoid sampling issues)
                middleSource = new Rectangle(halfWidth, halfHeight, 1, 1);
 
                // Determine the span between corners
                horizontalGapWidth = destination.Width - (borderWidth * 2);
                verticalGapWidth = destination.Height - (borderHeight * 2);
 
                // Assign previousDestination to avoid having to do these calculations whenever this doesn't change.
                previousDestination = destination;
            }
 
            // First draw the corners since they require no scaling
            // Draw upperLeft
            spriteBatch.Draw(texture,
                new Vector2(destination.X, destination.Y),
                upperLeftSource,
                color,
                0.0f,
                Vector2.Zero,
                Vector2.One,
                SpriteEffects.None,
                layerDepth);
 
            // Draw upperRight
            spriteBatch.Draw(texture,
                new Vector2(destination.X + destination.Width - borderWidth, destination.Y),
                upperRightSource,
                color,
                0.0f,
                Vector2.Zero,
                Vector2.One,
                SpriteEffects.None,
                layerDepth);
 
            // Draw lowerLeft
            spriteBatch.Draw(texture,
                new Vector2(destination.X, destination.Y + destination.Height - borderHeight),
                lowerLeftSource,
                color,
                0.0f,
                Vector2.Zero,
                Vector2.One,
                SpriteEffects.None,
                layerDepth);
 
            // Draw lowerRight
            spriteBatch.Draw(texture,
                new Vector2(destination.X + destination.Width - borderWidth, destination.Y + destination.Height - borderHeight),
                lowerRightSource,
                color,
                0.0f,
                Vector2.Zero,
                Vector2.One,
                SpriteEffects.None,
                layerDepth);
 
            // Draw the horizontals
            // Draw upperStretch
            spriteBatch.Draw(texture,
                new Vector2(destination.X + borderWidth, destination.Y),
                upperStretchSource,
                color,
                0.0f,
                Vector2.Zero,
                new Vector2(horizontalGapWidth, 1.0f),
                SpriteEffects.None,
                layerDepth);
 
            // Draw lowerStretch
            spriteBatch.Draw(texture,
                new Vector2(destination.X + borderWidth, destination.Y + destination.Height - borderHeight),
                lowerStretchSource,
                color,
                0.0f,
                Vector2.Zero,
                new Vector2(horizontalGapWidth, 1.0f),
                SpriteEffects.None,
                layerDepth);
 
            // Draw the verticals
            // Draw leftStretch
            spriteBatch.Draw(texture,
                new Vector2(destination.X, destination.Y + borderHeight),
                leftStretchSource,
                color,
                0.0f,
                Vector2.Zero,
                new Vector2(1.0f, verticalGapWidth),
                SpriteEffects.None,
                layerDepth);
 
            // Draw rightStretch
            spriteBatch.Draw(texture,
                new Vector2(destination.X + destination.Width - borderWidth, destination.Y + borderHeight),
                rightStretchSource,
                color,
                0.0f,
                Vector2.Zero,
                new Vector2(1.0f, verticalGapWidth),
                SpriteEffects.None,
                layerDepth);
 
            // Draw middle
            spriteBatch.Draw(texture,
                new Vector2(destination.X + borderWidth, destination.Y + borderHeight),
                middleSource,
                color,
                0.0f,
                Vector2.Zero,
                new Vector2(horizontalGapWidth, verticalGapWidth),
                SpriteEffects.None,
                layerDepth);
        }
    }

Once we’ve got that class added, we can then proceed to our modifications. MenuEntry is primarily focused with drawing a string. We need to modify it so that it will draw a texture underneath the text. But sometimes you want a fancier look – perhaps a drop shadow effect like I was mentioning up above. You could do such a thing with a custom shader, only Windows Phone 7 currently doesn’t support custom shaders. I’d much rather do it with a 1x1 semi-transparent texture. Here’s the image I use (scaled up so you can see it):

shadow

So like I was saying, we need to modify MenuEntry.cs – so open that file up. The file is divided up very nicely using the #region - #endregion “tags”. First, to the “Fields” region, we must add a few class-level fields:

        /// <summary>
        /// The text color for a selected menu item (unused on WP7).
        /// </summary>
        private Color textSelectedColor;
 
        /// <summary>
        /// The text color for an unselected menu item (used for all menu items on WP7).
        /// </summary>
        private Color textColor;
 
        /// <summary>
        /// The texture, if any, located underneath the menuEntryBackground.
        /// </summary>
        private BorderedTextureBox menuEntrySubBackground;
 
        /// <summary>
        /// The texture, if any, located underneath the menu entry text.
        /// </summary>
        private BorderedTextureBox menuEntryBackground;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuEntryBackground texture.
        /// </summary>
        private int menuEntryBackgroundXPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuEntryBackground texture.
        /// </summary>
        private int menuEntryBackgroundYPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuEntrySubBackground texture.
        /// </summary>
        private int menuEntrySubBackgroundXPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuEntrySubBackground texture.
        /// </summary>
        private int menuEntrySubBackgroundYPadding;
 
        /// <summary>
        /// The distance in pixels which the menuEntrySubBackground image will be offset along the X axis from menuEntryBackground.
        /// Positive for right, negative for left.
        /// </summary>
        private int menuEntrySubBackgroundXOffset;
 
        /// <summary>
        /// The distance in pixels which the menuEntrySubBackground image will be offset along the X axis from menuEntryBackground.
        /// Positive for down, negative for up.
        /// </summary>
        private int menuEntrySubBackgroundYOffset;
 

In the original MenuEntry.cs, the MenuEntry colors are hard coded. I wanted to be able to specify what color I wanted my menu entries to be, so I made those parameters class level fields and added them to the constructor. Speaking of which, here’s the new constructors (which should completely replace the old constructors:

        /// <summary>
        /// Constructs a new menu entry with the specified text and default colors of yellow (selected) and white (unselected).
        /// </summary>
        /// <param name="text">The text for the MenuEntry item.</param>
        public MenuEntry(string text)
            : this(text, Color.Yellow, Color.White, null, 0, 0, 0, 0, null, 0, 0, 0, 0, 0, 0)
        {
        }
 
        /// <summary>
        /// Constructs a new menu entry with the specified text.
        /// </summary>
        /// <param name="text">The text for the MenuEntry item.</param>
        /// <param name="textSelectedColor">The color when the menu entry is the selected menu item (not used on WP7).</param>
        /// <param name="textColor">The text color when the menu entry is not the selected item (used for all items on WP7).</param>
        /// <param name="menuEntryBackground">The image, if any, to be drawn behind the MenuEntry text. Use null if not desired.</param>
        /// <param name="menuEntrySubBackground">The image, if any, to be drawn behind the menuEntryBackground image. Use null if not desired.</param>
        /// <param name="menuEntryBackgroundBorderWidth">The width in pixels of the border along the width (i.e. horizontal sides) of the menuEntryBackground image. Use 0 for no border.</param>
        /// <param name="menuEntryBackgroundBorderHeight">The width in pixels of the border along the height (i.e. vertical sides) of the menuEntryBackground image. Use 0 for no border.</param>
        /// <param name="menuEntryBackgroundXPadding">The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuEntryBackground texture.</param>
        /// <param name="menuEntryBackgroundYPadding">The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuEntryBackground texture.</param>
        /// <param name="menuEntrySubBackgroundBorderWidth">The width in pixels of the border along the width (i.e. horizontal sides) of the menuEntrySubBackground image. Use 0 for no border.</param>
        /// <param name="menuEntrySubBackgroundBorderHeight">The width in pixels of the border along the height (i.e. vertical sides) of the menuEntrySubBackground image. Use 0 for no border.</param>
        /// <param name="menuEntrySubBackgroundXPadding">The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuEntrySubBackground texture.</param>
        /// <param name="menuEntrySubBackgroundYPadding">The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuEntrySubBackground texture.</param>
        /// <param name="menuEntrySubBackgroundXOffset">The distance in pixels which the menuEntrySubBackground image will be offset along the X axis from menuEntryBackground. Positive for right, negative for left.</param>
        /// <param name="menuEntrySubBackgroundYOffset">The distance in pixels which the menuEntrySubBackground image will be offset along the X axis from menuEntryBackground. Positive for down, negative for up.</param>
        public MenuEntry(string text, Color textSelectedColor, Color textColor, Texture2D menuEntryBackground, int menuEntryBackgroundBorderWidth, int menuEntryBackgroundBorderHeight, int menuEntryBackgroundXPadding, int menuEntryBackgroundYPadding, Texture2D menuEntrySubBackground, int menuEntrySubBackgroundBorderWidth, int menuEntrySubBackgroundBorderHeight, int menuEntrySubBackgroundXPadding, int menuEntrySubBackgroundYPadding, int menuEntrySubBackgroundXOffset, int menuEntrySubBackgroundYOffset)
        {
            this.text = text;
 
            this.textSelectedColor = textSelectedColor;
            this.textColor = textColor;
 
            this.menuEntryBackgroundXPadding = menuEntryBackgroundXPadding;
            this.menuEntryBackgroundYPadding = menuEntryBackgroundYPadding;
 
            this.menuEntrySubBackgroundXPadding = menuEntrySubBackgroundXPadding;
            this.menuEntrySubBackgroundYPadding = menuEntrySubBackgroundYPadding;
 
            this.menuEntrySubBackgroundXOffset = menuEntrySubBackgroundXOffset;
            this.menuEntrySubBackgroundYOffset = menuEntrySubBackgroundYOffset;
 
            if (menuEntryBackground != null)
            {
                this.menuEntryBackground = new BorderedTextureBox(menuEntryBackground, menuEntryBackgroundBorderWidth, menuEntryBackgroundBorderHeight);
            }
            else
            {
                this.menuEntryBackground = null;
            }
            if (menuEntrySubBackground != null)
            {
                this.menuEntrySubBackground = new BorderedTextureBox(menuEntrySubBackground, menuEntrySubBackgroundBorderWidth, menuEntrySubBackgroundBorderHeight);
            }
            else
            {
                this.menuEntrySubBackground = null;
            }
        }
 

Finally, we need to modify our Draw method. First up in draw, we must replace the line that creates and sets the “Color color” variable with this:

            Color color = isSelected ? textSelectedColor : textColor;

 

As you can see, we’re no longer using the hard-coded yellow and white but instead are using our custom values. Then, we need to add some drawing code. At the bottom of the Draw method is a line that draws the string itself. We modify this line ever so slightly as well so we’ll just overwrite it. As such, the following code should be inserted after the line that creates and sets “Vector2 origin”:

            // Measure the string for computational purposes.
            Vector2 measurement = font.MeasureString(text);
 
            // Determine the proper "upper left corner" of the menuEntryBackground image.
            Vector2 menuEntryBackgroundPosition = new Vector2(position.X - menuEntryBackgroundXPadding, position.Y - origin.Y - menuEntryBackgroundYPadding);
 
            // Determine the proper width and height of the menuEntryBackground image, assigning the width to the X field and the height to the Y field.
            Vector2 menuEntryBackgroundDimensions = new Vector2(measurement.X + (menuEntryBackgroundXPadding * 2), measurement.Y + (menuEntryBackgroundYPadding * 2));
 
            // Determine the proper "upper left corner" of the menuEntrySubBackground image.
            Vector2 menuEntrySubBackgroundPosition = new Vector2(position.X - menuEntrySubBackgroundXPadding + menuEntrySubBackgroundXOffset, position.Y - origin.Y - menuEntrySubBackgroundYPadding + menuEntrySubBackgroundYOffset);
 
            // Determine the proper width and height of the menuEntrySubBackground image, assigning the width to the X field and the height to the Y field.
            Vector2 menuEntrySubBackgroundDimensions = new Vector2(measurement.X + (menuEntrySubBackgroundXPadding * 2), measurement.Y + (menuEntrySubBackgroundYPadding * 2));
 
            // If menuEntrySubBackground exists, draw it first. Notice how we create a Rectangle and use the offsets here.
            if (menuEntrySubBackground != null)
            {
                menuEntrySubBackground.Draw(spriteBatch,
                    new Rectangle((int)menuEntrySubBackgroundPosition.X, (int)menuEntrySubBackgroundPosition.Y, (int)menuEntrySubBackgroundDimensions.X, (int)menuEntrySubBackgroundDimensions.Y),
                    new Color(255, 255,255) * screen.TransitionAlpha,
                    0.0f);
            }
 
            // If menuEntryBackground exists, draw it second.
            if (menuEntryBackground != null)
            {
                menuEntryBackground.Draw(spriteBatch,
                    new Rectangle((int)menuEntryBackgroundPosition.X, (int)menuEntryBackgroundPosition.Y, (int)menuEntryBackgroundDimensions.X, (int)menuEntryBackgroundDimensions.Y),
                    new Color(255, 255, 255) * screen.TransitionAlpha,
                    0.0f);
            }
 
            // Lastly, draw the menu entry text. Note we change the original 0 of the layerDepth to 0.0f. This is just for consistency and shouldn't change anything.
            spriteBatch.DrawString(font, text, position, color, 0, origin, scale, SpriteEffects.None, 0.0f);

After the above code insertion should be the } brace that closes the Draw method. And voila. We now have a menu entry that can display a properly scaled background image with an optional drop shadow, and that image can have a border of a constant size (though with that, we lose the ability to have the anything other than a solid color taken from the middle pixel of the image as the background for our “button” – the primary reason for this has to do with texture sampling when scaling) or can have a borderless image (which will use the entire image, not just a single pixel, scaled up to size).

So how do we implement this? We aren’t quite ready yet. You see, while this will allow us to draw an image behind a menu entry, it still leaves our menu screen’s title both imageless and with the default color. And so we must modify further. So, open up MenuScreen.cs. This is the class that all menu screens inherit from. Once again we need to first add some fields to the “Fields” region. So go ahead and add these lines:

        /// <summary>
        /// The color of the menuTitle text.
        /// </summary>
        Color menuTitleColor;
 
        /// <summary>
        /// The texture, if any, located underneath the menuTitleSubBackground.
        /// </summary>
        private BorderedTextureBox menuTitleSubBackground;
 
        /// <summary>
        /// The texture, if any, located underneath the menu title text.
        /// </summary>
        private BorderedTextureBox menuTitleBackground;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuTitleBackground texture.
        /// </summary>
        private int menuTitleBackgroundXPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuTitleBackground texture.
        /// </summary>
        private int menuTitleBackgroundYPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuTitleSubBackground texture.
        /// </summary>
        private int menuTitleSubBackgroundXPadding;
 
        /// <summary>
        /// The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuTitleSubBackground texture.
        /// </summary>
        private int menuTitleSubBackgroundYPadding;
 
        /// <summary>
        /// The distance in pixels which the menuTitleSubBackground image will be offset along the X axis from menuTitleBackground.
        /// Positive for right, negative for left.
        /// </summary>
        private int menuTitleSubBackgroundXOffset;
 
        /// <summary>
        /// The distance in pixels which the menuTitleSubBackground image will be offset along the X axis from menuTitleBackground.
        /// Positive for down, negative for up.
        /// </summary>
        private int menuTitleSubBackgroundYOffset;
 

 

Next up we change the existing constructor, add a new overload, and add two parameter setting methods. So delete the existing constructor and in its place, add this:

        /// <summary>
        /// Constructor for the base class that menus inherit from. Uses a default light grey color from the sample.
        /// </summary>
        /// <param name="menuTitle">The text to display as a title.</param>
        public MenuScreen(string menuTitle)
            : this(menuTitle, new Color(192, 192, 192))
        {
        }
 
 
        /// <summary>
        /// Constructor for the base class that menus inherit from.
        /// </summary>
        /// <param name="menuTitle">The text to display as a title.</param>
        /// <param name="menuTitleColor">The color to display the text in.</param>
        public MenuScreen(string menuTitle, Color menuTitleColor)
        {
            // menus generally only need Tap for menu selection
            EnabledGestures = GestureType.Tap;
 
            this.menuTitle = menuTitle;
            this.menuTitleColor = menuTitleColor;
 
            menuTitleBackground = null;
            menuTitleSubBackground = null;
 
            TransitionOnTime = TimeSpan.FromSeconds(0.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
        }
 
        /// <summary>
        /// Sets a background image that will be displayed beneath the menu title text.
        /// </summary>
        /// <param name="menuTitleBackgroundImage">The image.</param>
        /// <param name="menuTitleBackgroundBorderWidth">The width in pixels of the border along the width (i.e. horizontal sides) of the menuTitleBackground image. Use 0 for no border.</param>
        /// <param name="menuTitleBackgroundBorderHeight">The width in pixels of the border along the height (i.e. vertical sides) of the menuTitleBackground image. Use 0 for no border.</param>
        /// <param name="menuTitleBackgroundXPadding">The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuTitleBackground texture.</param>
        /// <param name="menuTitleBackgroundYPadding">The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuTitleBackground texture.</param>
        public void SetMenuTitleBackground(Texture2D menuTitleBackgroundImage, int menuTitleBackgroundBorderWidth, int menuTitleBackgroundBorderHeight, int menuTitleBackgroundXPadding, int menuTitleBackgroundYPadding)
        {
            this.menuTitleBackground = new BorderedTextureBox(menuTitleBackgroundImage, menuTitleBackgroundBorderWidth, menuTitleBackgroundBorderHeight);
            this.menuTitleBackgroundXPadding = menuTitleBackgroundXPadding;
            this.menuTitleBackgroundYPadding = menuTitleBackgroundYPadding;
        }
 
        /// <summary>
        /// Sets a sub-background image that will be displayed beneath the background image (if any) that appears behind the menu title text.
        /// </summary>
        /// <param name="menuTitleSubBackgroundImage">The image.</param>
        /// <param name="menuTitleSubBackGroundBorderWidth">The width in pixels of the border along the width (i.e. horizontal sides) of the menuTitleSubBackground image. Use 0 for no border.</param>
        /// <param name="menuTitleSubBackgroundBorderHeight">The width in pixels of the border along the height (i.e. vertical sides) of the menuTitleSubBackground image. Use 0 for no border.</param>
        /// <param name="menuTitleSubBackgroundXPadding">The amount of "padding" in pixels to add between the left and right of the string and the edge of the menuTitleSubBackground texture.</param>
        /// <param name="menuTitleSubBackgroundYPadding">The amount of "padding" in pixels to add between the top and bottom of the string and the edge of the menuTitleSubBackground texture.</param>
        /// <param name="menuTitleSubBackgroundXOffset">The distance in pixels which the menuTitleSubBackground image will be offset along the X axis from menuTitleBackground. Positive for right, negative for left.</param>
        /// <param name="menuTitleSubBackgroundYOffset">The distance in pixels which the menuTitleSubBackground image will be offset along the X axis from menuTitleBackground. Positive for down, negative for up.</param>
        public void SetMenuTitleSubBackground(Texture2D menuTitleSubBackgroundImage, int menuTitleSubBackGroundBorderWidth, int menuTitleSubBackgroundBorderHeight, int menuTitleSubBackgroundXPadding, int menuTitleSubBackgroundYPadding, int menuTitleSubBackgroundXOffset, int menuTitleSubBackgroundYOffset)
        {
            this.menuTitleSubBackground = new BorderedTextureBox(menuTitleSubBackgroundImage, menuTitleSubBackGroundBorderWidth, menuTitleSubBackgroundBorderHeight);
            this.menuTitleSubBackgroundXPadding = menuTitleSubBackgroundXPadding;
            this.menuTitleSubBackgroundYPadding = menuTitleSubBackgroundYPadding;
            this.menuTitleSubBackgroundXOffset = menuTitleSubBackgroundXOffset;
            this.menuTitleSubBackgroundYOffset = menuTitleSubBackgroundYOffset;
        }

 

Lastly, we change the Draw method. Starting at the line that sets the titleColor (including that line), we’re going to replace everything in the method from there down to the end. So highlight that line and everything that follows it inside the Draw method, delete it, and replace it with this:

            // Use the menuTitleColor multiplied by the TransitionAlpha for transition effects to look good and proper.
            Color titleColor = menuTitleColor * TransitionAlpha;
 
            // Unchanged from the original sample.
            float titleScale = 1.25f;
 
            // Unchanged from the original sample.
            titlePosition.Y -= transitionOffset * 100;
 
            // Measure the string for computational purposes.
            Vector2 measurement = font.MeasureString(menuTitle);
 
            // Determine the proper "upper left corner" of the menuTitleBackground image. Notice how we need to take titleScale into account.
            Vector2 menuTitleBackgroundPosition = new Vector2(titlePosition.X - (titleOrigin.X * titleScale) - menuTitleBackgroundXPadding, titlePosition.Y - (titleOrigin.Y * titleScale) - menuTitleBackgroundYPadding);
 
            // Determine the proper width and height of the menuEntryBackground image, assigning the width to the X field and the height to the Y field.
            Vector2 menuTitleBackgroundDimensions = new Vector2((measurement.X * titleScale) + (menuTitleBackgroundXPadding * 2), (measurement.Y * titleScale) + (menuTitleBackgroundYPadding * 2));
 
            // Determine the proper "upper left corner" of the menuTitleBackground image. Notice how we need to take titleScale into account.
            Vector2 menuTitleSubBackgroundPosition = new Vector2(titlePosition.X - (titleOrigin.X * titleScale) - menuTitleSubBackgroundXPadding + menuTitleSubBackgroundXOffset, titlePosition.Y - (titleOrigin.Y * titleScale) - menuTitleSubBackgroundYPadding + menuTitleSubBackgroundYOffset);
 
            // Determine the proper width and height of the menuEntryBackground image, assigning the width to the X field and the height to the Y field.
            Vector2 menuTitleSubBackgroundDimensions = new Vector2((measurement.X * titleScale) + (menuTitleSubBackgroundXPadding * 2), (measurement.Y * titleScale) + (menuTitleSubBackgroundYPadding * 2));
 
            // If menuTitleSubBackground exists, draw it first. Notice how we create a Rectangle and use the offsets here.
            if (menuTitleSubBackground != null)
            {
                menuTitleSubBackground.Draw(spriteBatch,
                    new Rectangle((int)menuTitleSubBackgroundPosition.X, (int)menuTitleSubBackgroundPosition.Y, (int)menuTitleSubBackgroundDimensions.X, (int)menuTitleSubBackgroundDimensions.Y),
                    new Color(255, 255, 255) * TransitionAlpha,
                    0.0f);
            }
 
            // If menuEntryBackground exists, draw it second.
            if (menuTitleBackground != null)
            {
                menuTitleBackground.Draw(spriteBatch,
                    new Rectangle((int)menuTitleBackgroundPosition.X, (int)menuTitleBackgroundPosition.Y, (int)menuTitleBackgroundDimensions.X, (int)menuTitleBackgroundDimensions.Y),
                    new Color(255, 255, 255) * TransitionAlpha,
                    0.0f);
            }
 
            // Lastly, draw the menu entry text. Note we change the original 0 of the layerDepth to 0.0f. This is just for consistency and shouldn't change anything.
            spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
                                   titleOrigin, titleScale, SpriteEffects.None, 0.0f);
 
            spriteBatch.End();

 

As you can see, much the same as the MenuEntry Draw method changes. With those changes complete, we can now have custom colors for both menu titles and menu entries, and we can also have background textures (with optional borders) and textures underneath those for producing drop shadow effects (or whatever else you might want, but drop shadows seem like the most obvious use).

So how do we use all this new found power? Open up MainMenuScreen.cs and we’ll get right to it. These changes may seem a little more drastic, but they aren’t all that bad. First up, we’re adding a field to this class. Since it doesn’t have any fields, we’re also going to add the proper region tag for consistency. So add these lines at the top of the class:

        #region Fields
        
        /// <summary>
        /// Our ContentManager.
        /// </summary>
        ContentManager content;
 
        #endregion
 

 

Once again we’re replacing the existing constructor. This time we’re also adding two methods immediately after it. Here’s the new constructor with new methods:

        /// <summary>
        /// Constructor sets the menu title and menu title color.
        /// </summary>
        public MainMenuScreen()
            : base("My Magic Game", Color.DarkRed)
        {
        }
 
        /// <summary>
        /// Loads the assets used for this menu screen.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");
 
            SetMenuTitleSubBackground(content.Load<Texture2D>("shadow"), 0, 0, 30, 4, 4, 4);
            SetMenuTitleBackground(content.Load<Texture2D>("border"), 5, 5, 30, 4);
 
            // Create our menu entries.
            MenuEntry playGameMenuEntry = new MenuEntry("Play Game", Color.RoyalBlue, Color.RoyalBlue, content.Load<Texture2D>("blank"), 0, 0, 18, 0, content.Load<Texture2D>("shadow"), 0, 0, 18, 0, 4, 4);
            MenuEntry optionsMenuEntry = new MenuEntry("Options", Color.RoyalBlue, Color.RoyalBlue, content.Load<Texture2D>("blank"), 0, 0, 18, 0, content.Load<Texture2D>("shadow"), 0, 0, 18, 0, 4, 4);
 
            // Hook up menu event handlers.
            playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
 
            // Add entries to the menu.
            MenuEntries.Add(playGameMenuEntry);
            MenuEntries.Add(optionsMenuEntry);
        }
 
        /// <summary>
        /// Unloads the assets for this menu screen.
        /// </summary>
        public override void UnloadContent()
        {
            if (content != null)
            {
                content.Unload();
            }
        }
 

 

And that’s all there is to it. As you can see, what we did was move the creation of the MenuEntry items out of the constructor and into the LoadContent method. This allows us to initialize them with textures. We’re also setting the textures for the menuTitle which were inherited from the MenuScreen base class. Now this code will only compile and work if you have textures named shadow, border, and blank in the main folder of your Content project. If not it won’t. The texture named blank should already be in there, and I’ve supplied you above with the two other textures. If you save border.png, you’ll see that the border is 5 pixels wide on all sides. You can, of course, use your own textures with your own names and your own border widths (which need neither be 5px nor even be the same width horizontally and vertically).

If you’re curious, here’s an example of the result:

borderedmenustest

Good luck!

Posted On Monday, August 2, 2010 9:27 AM | Comments (0)
Bob Taco Industries is an ISV focused on game and app development for Microsoft platforms headed up by Michael B. McLaughlin. Mike is a Microsoft Visual C++ MVP (previously an XNA/DirectX MVP from 2011-2013), a developer, a writer, a consultant, and a retired lawyer. If you're a developer who is just getting started, consider checking out the BTI website's section for developers for links to code samples and other helpful sites.
Tag Cloud