My first game

I just submitted my first game to the Windows Phone Marketplace. I decided on the name Cloud Catcher.

It’s a fairly simple game, cards with clouds on them drift across the screen from right to left and you have to tap on the two that match in each column before they reach the gold line.

I’m interested to see if I can get any sales on this game. I think not having a trial is going to hurt me because its difficult to judge this game without playing it. Finding the matching clouds seems easy when they aren’t marching off the screen.

If I decide to do a version 2.0 of this game, I would like to figure out some good way to do a trial. Maybe it only allows X matches per game or something. (Time based trials don’t work well on WP because users can uninstall and reinstall after the trial is up and there is nothing the developer can do about it.) I would also add sound effects and music. I usually play games with the music and sounds muted, but I know it can add a lot to a game’s feel. And maybe another mechanic, like birds would block some of the clouds and you’d have to tap them first before you could see where the match was.

But for now I’m pretty happy with my first real video game. It’s always been a goal of mine to be able to make video games, and while I will probably not make any money off it, at least I’ve got a completed project. (Depending on how well or poorly  it sells, I may start posting code from the game and talking about it here, but I don’t want to just post the whole project and have someone completely rip it off, even though I know there are sites that just rip XAP files from the marketplace already.)

Be a Windows Phone developer in 30 days

Why would anyone want to develop for Windows Phone? How many people do you even know with one of these things? So the reviews are good and that’s great, but really shouldn’t I just build for the iPhone so I can make some money with my app?

Yep, probably.

BUT…

Microsoft is trying really hard to not fail with Windows Phone. They know its important for their future to have a mobile platform and are spending boat-loads of money to get developers on board with it. They have started a contest today that will allow you to get up to speed with building apps for free.

The tools are free, the $99 annual subscription to submit apps is free, and a you can get a free Nokia Lumia 800 for testing (and it’s yours to keep).

If you already publish apps for the iPhone or Android, there’s little reason not to spend a few hours this month porting one over to Windows Phone. If you are a .NET web or Windows developer, you’ll find it very easy to get started building simple apps.

Get signed up at http://www.30tolaunch.com/ and take advantage of Microsoft’s generosity. And maybe you’ll make something really amazing and win one of FOUR $3000 home entertainment packages.

(And yes I realize this whole post makes me sound like a MS fanboi, but really, I like making apps for WP, especially games using XNA, so it’s in my best interest that the platform is successful. Plus, a little more competition between platforms is good for everyone.)

Quaking code

After reviewing other earthquake map applications in the Windows Phone marketplace, I decided there wasn’t much for me to do as far as making a better earthquake map. Most of the complaints users have with the existing apps have to do with data, and there’s not much I can do about that.

That said, here’s the bad code I came up with to make my map work.

<my:Map Margin="0,0,0,0" Name="map1" CredentialsProvider="secretfrombing" ZoomBarVisibility="Visible" />

That’s all my XAML. Generally, in a Silverlight phone app, you’ll want more than that. Your view should contain everything needed to drive the view with databinding and your code behind should just be pulling data.

I didn’t realize you could databind pins in a map until I was trying to figure out how to make the pins clickable links (or I guess on the phone they are tappable).

        public MainPage()
        {
            InitializeComponent();
            LoadFeed();
        }

        private void LoadFeed()
        {
            WebClient webClient1 = new WebClient();
            webClient1.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient1_DownloadStringCompleted);
            webClient1.DownloadStringAsync(new System.Uri("http://earthquake.usgs.gov/earthquakes/catalogs/eqs7day-M2.5.xml"));
        }

        private void webClient1_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =&gt;
                {
                    MessageBox.Show("Failed to load basic feed.");
                });
            }
            else
            {
                this.State["feed1"] = e.Result;
                UpdateFeedList1(e.Result);
            }
        }

        private void UpdateFeedList1(string feedXML)
        {
            XElement xmlFeed = XElement.Parse(feedXML);

            XNamespace geo = "http://www.w3.org/2003/01/geo/wgs84_pos#";
            XNamespace dc = "http://purl.org/dc/elements/1.1/";

            var quakes = from item in xmlFeed.Descendants("channel").Descendants("item")
                         select new SyndicatedItem
                         {
                             Magnitude = double.Parse(item.Element("title").Value.Substring(2, 3)),
                             Lat = double.Parse(item.Element(geo + "lat").Value),
                             Long = double.Parse(item.Element(geo + "long").Value),
                             PublishDate = DateTime.Parse(item.Element("pubDate").Value),
                             Link = item.Element("link").Value
                         };

            foreach (var quake in quakes)
            {
                MapPin(quake.Lat, quake.Long, quake.Magnitude, quake.Magnitude.ToString(), quake.Link);
            }
        }

        public void MapPin(double lat, double lon, double mag, string content, string link)
        {
            var pin = new Pushpin();
            pin.Location = new GeoCoordinate(lat, lon);
            Color magColor;

            switch ((int)(Math.Floor(mag)))
            {
                case 0:
                    magColor = Colors.Gray;
                    break;
                case 1:
                    magColor = Colors.DarkGray;
                    break;
                case 2:
                    magColor = Colors.Magenta;
                    break;
                case 3:
                    magColor = Colors.Purple;
                    break;
                case 4:
                    magColor = Colors.Blue;
                    break;
                case 5:
                    magColor = Colors.Green;
                    break;
                case 6:
                    magColor = Colors.Yellow;
                    break;
                case 7:
                    magColor = Colors.Orange;
                    break;
                case 8:
                    magColor = Colors.Red;
                    break;
                case 9:
                    magColor = Colors.Black;
                    break;
                default:
                    magColor = Colors.Gray;
                    break;
            }

            pin.Background = new SolidColorBrush(magColor);
            pin.Content = content;
            pin.Tag = link;
            pin.Tap += new EventHandler&lt;GestureEventArgs&gt;(pin_Tap);
            map1.Children.Add(pin);
        }

        void pin_Tap(object sender, GestureEventArgs e)
        {
            var pin = sender as Pushpin;

            WebBrowserTask webBrowserTask = new WebBrowserTask();
            webBrowserTask.Uri = new Uri(pin.Tag.ToString());
            webBrowserTask.Show();
        }

And my SyndicatedItem class:

    public class SyndicatedItem
    {
        public DateTime PublishDate { get; set; }
        public double Magnitude { get; set; }
        public double Lat { get; set; }
        public double Long { get; set; }
        public string Link { get; set; }
    }

I create a new webclient and get the RSS xml asynchronously, when it comes back, I used LinqToXML to parse the file. Pay attention to how namespaces are handled. If you don’t declare the namespace as an object and concatenate it, it will not find the node you are looking for. I added a function that would add a pin to the screen so I could for-each over the result set to display the data on the map.

Each pin gets an event handler added to it for the tap event so the pin can link back to the web page with more details on the earthquake and I’m storing the URL in the tag property of the pin. If I was databinding the pins, I would simply refer back to the datamodel to get the URL.

Now that I’ve shown you the wrong way, here’s a blog post that shows a little better way to work with the map using databinding: http://igrali.wordpress.com/2012/01/07/show-a-tooltip-for-tapped-pushpin-on-windows-phone/.

Earthquakes!

Was trying to come up with a fun Windows Phone app idea when I found some RSS feeds of recent earthquakes published by the US government (you can check them out here: http://earthquake.usgs.gov/earthquakes/catalogs/). I don’t know how useful such an app would be, but it seems like an interesting way to learn about the map controls.

Naming my project “Earthquakes” has an amusing side effect, when I send a new build to the emulator, I get to “Deploy Earthquakes.”

Update (one hour later)!

I’ve got a map parsing live data from the RSS feed and popping color-coded pins (based on magnitude) all over the map.