Thursday, December 27, 2007

Making Process Results Public

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/making_process_results_public.htm]

Enterprise applications require a lot of supporting processes - continuous builds, feature tracking, monitoring production, etc... One sign of a mature department is that the non-secure output from those processes is publicly available to the entire team, as opposed to "hidden" in a personal file on some manager's computer. This keeps everyone on the same page, encourages team feedback, and reduces bureaucracy.

 

For example, anyone on your team should be able to tell you the release dates, their current expected feature list, the amount of open issues they have, and if their code patch broke any unit tests on the server. Those should not be "special" things that only a manager has access to view.

 

There are several processes that any development department should have, and these processes should all have publicly accessible results (such as via a web dashboard or shared network drive).

  • Issue Tracking System (we use a web-based solution)

  • Continuous build with unit test results (we prefer CruiseControl)

  • Wiki (we started with FlexWiki to just get started, but now use SharePoint)

  • Production performance results (you should be able to go to a web dashboard, not pester your IT department for the raw IIS log files)

  • Available project plan (even an Excel sheet on a shared network drive)

There's no doubt more to this list, but it's a good start.

Wednesday, December 26, 2007

Book: Getting Started in Consulting

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/book_getting_started_in_consulting.htm]

I just finished a very good book - Getting Started in Consulting. As its title suggests, it explains how to start up your own consulting practice. While I am personally entrenched in industry at Paylocity and not heading towards consulting, the book is still very applicable. (I used to work in consulting at Deloitte and CSC). Essentially, the developers who really stand out do a lot of the same things that successful consultants do - they take initiative to add value in a way that supports the client.

 

As someone now in industry, my "client" is usually my boss, or the customers who purchase our product. My "fees" are usually how I get to spend my time - i.e. I need to justify to my boss that project X is worth the time investment, and they should therefore allot schedule resources for it. The chapters on sales and marketing are good for any developer to know because they remind you that ultimately the project needs to generate revenue. It's easy to think that money grows on trees for these corporate giants, but for many of us, good projects often come down to a series of small "sales". Alan does a great job of showing how to incrementally add value and handle scope creep.

 

He also has very applicable chapters on "Moving to the Next Level" and "What do you do with Success?" For developers, if you've been in the field for 10 years, are you just doing more same old features, or are you still professionally growing and contributing back to the community via mentoring, publications, user groups, or other extra curricular activities?

 

It's an easy read, a well done book, and stimulates your mind to think about how to add value for your current project in industry.

Monday, December 24, 2007

Merry Christmas

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/merry_christmas_1.htm]

Merry Christmas for 2007, and a happy new year coming up in 2008!

 

Wednesday, December 19, 2007

Generic Dictionary in C#

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/generic_dictionary_in_c.htm]

I love the generic Dictionary class. It's located in the System.Collections.Generic namespace. This class is like a strongly-typed hash table (which lets you reference objects as key-value pairs). You can specify the type for the key and another type for the value. This code snippet below shows how to create, add, and retrieve data from a Dictionary. It spares the developer from writing a bunch of extra conversion wrapping code.

    [TestMethod]
    public void Dictionary_1()
    {
      Dictionary<string, DateTime> group = new Dictionary<string, DateTime>();

      //Add based on separate variables
      string strKey1 = "abc";
      DateTime dt1 = DateTime.Now;
      group.Add(strKey1, dt1);

      //Add as single line
      group.Add("def", new DateTime(2005, 12, 25));

      //Should now have 2 items
      Assert.AreEqual(2, group.Count);

      //Get value given key
      DateTime dt2 = group["abc"];
      Assert.AreEqual(dt1, dt2);
    }

Generics are great. You can also use them for strongly-typed lists, setting the return type of a method, and many other techniques that simplify code.

Tuesday, December 18, 2007

Tips to overcome problems with learning

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/tips_to_overcome_problems_with_learning.htm]

Although it's common knowledge that you need to invest time and effort to learn, the question is how to maximize that investment. Certainly you will encounter problems at every step of the way, such as:
  • Staying motivated.
  • Not feeling overwhelmed.
  • Finding time to learn despite being busy.
  • Learning the right stuff.
  • Retaining what you do learn.
  • Getting to use your new hard-earned knowledge.

Here are some random tips to dealing with those problems. Obviously a lot more could be said about each one; this is just a partial brain dump - and isn't that what blogs are for?

ProblemIdeas for Solutions
Staying motivated
  • Work on what you personally care about
  • Reflect on your accomplishments
Not feeling overwhelmed
  • Make reasonable goals
  • Focus on smaller chunks
Retaining what you do learn
  • Summarize your new knowledge
  • Put it in easy-to-find places
  • Practice it hands-on
Learning the right stuff
  • Continually try at everything, and eventually you’ll hit the jackpot (This will also increase your learning ability such that you’ll be ready when you get the chance).
  • Invest time outside of your required learning to explore the topics you want.
Using your new hard-earned knowledge
  • Pick topics that assist with your personal goals, so regardless of what happens at work you'll still get to use it.
  • Hang out in communities that value the same knowledge.
     
Finding time to learn despite being so busy.
  • Use the dead parts of the day (like driving or waiting in line) to mull over ideas.
  • Use smaller chunks of time instead of waiting for an entire free day.
  • Even in seasons when you’re just trying to survive (and hence are not doing any new studying), you can still learn better character like patience and determination.

 

Monday, December 17, 2007

Twenty excuses to avoid learning

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/twenty_excuses_to_avoid_learning.htm]

I was thinking the other day of all the excuses that I've heard for my software developers avoid trying to learn new things. I'm a huge advocate of developers doing continuing education, so I always find the excuses interesting. Here's a random list of the top twenty:
  1. I’m too old / I’m too young.
  2. I’m not smart enough.
  3. I don’t need anything new / I don’t care.
  4. But my memory is too bad…
  5. Even if I learn something new, it won’t help me.
  6. I don’t have the technical job yet. First I’ll get the job, and then I’ll learn about it.
  7. The technology will just be obsolete by the time I learn it.
  8. I’ll just wait until some teaches me.
  9. Learning is punishment for being bad - i.e. the grade-school mentality when you're teach assigns extra homework to "punish" you.
  10. The instructor dislikes me, therefore I won’t try.
  11. The techniques/resources aren’t available.
  12. My environment is unfair, so why put myself on the line when it’s setup against me.
  13. Training is too expensive
  14. It’s too overwhelming
  15. It’s my boss’s responsibility to train me
  16. I don’t even like my job, why would I want to learn more about it? (Rebuttal: then learn about another job, the job you want).
  17. I don’t want to waste effort learning something unless I absolutely need it.
  18. If I learn some niche tech, then I’ll get stuck maintaining that obsolete system.
  19. I worked really hard before, and nothing good came from it.
  20. It’s not worth it.

Are there other ones that you've heard? At some point, I'd like to offer explicit rebuttals to each of these.

Sunday, December 16, 2007

Resources for learning software development

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/resources_for_learning_software_development.htm]

I was thinking the other day how many different resources are out there to help someone learn software development. There's something to appeal to almost every style:

  1. Books
  2. Reference docs
  3. Articles
  4. Webcasts
  5. Podcasts
  6. Virtual labs
  7. Newsgroups, discussion forums
  8. Live Events
  9. Professional Groups
  10. Blogs
  11. Training (class room setting)
  12. Sample code - quickstarts
  13. Sample code - real projects
  14. Tutorials & Walkthroughs
  15. Local class at community college
  16. Bachelors or Master’s degree
  17. Teaching others (forces you to learn it)
  18. Write your own blog
  19. Personal pet projects
  20. Open source projects
  21. Your coworkers. A physically present person, who can look at your machine environment, can be the best way to learn.
  22. Private tutor – perhaps for introductory topics where such tutors are much more abundant.
  23. Certification

Any others that you've personally found helpful?
 

Thursday, December 13, 2007

Architecture: Keeping it non-exponential

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/architecture_keeping_it_nonexponential.htm]

Systems have a tendency to get more disordered. Architecturally, it's easy to always take shortcuts such that your application code becomes one big mess of tightly coupled objects and duplicated code. It starts innocent enough -you have only a few initial components, and each one has a dependency on the others. So that's still only a few dependencies.

But then, a few emergencies later (and what isn't an emergency?), your code has ballooned, and the dependencies have exponentially exploded into a mess. The project has accrued so much technical debt that the schedule is hopeless, and it's no longer fun. Every object is intertwined with every other object:

A much more sustainable model is to demand cleaner code at each step of the way, and end up with something maintainable, which the number of dependencies increases linearly with the number of new components. This lets you add, remove, and modify the code much more easily:

I see this as one of the basic issues of architecture: keeping the ever-growing code base in a maintainable state. In application development, this often boils down to conscious decisions where everyone "knows" the right way, but instead write sloppy code for ("we don't have time", "we'll fix it later", "this one won't hurt", "it works", "the maintenance team will fix it", etc...). Often, it seems like the team just needs a strong and credible voice to encourage everyone to do the right thing. Especially for long-term, enterprise projects, this seems like the optimal way to go. It sure beats having 12 months of garbled code.

Wednesday, December 12, 2007

SSN is not a secure password

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/ssn_is_not_a_secure_password.htm]

I had a conversation the other day where SSN was considered viable to replace using a password. Yes, it simplifies single sign-in, but it has major consequences (and there are other ways to do single sign-in). Using SSN as a password is a bad idea, the kind of thing that blows up in your face with security and functionality problems.

  1. You can't change your SSN (unlike your password)

  2. SSN is too limited. It's merely a nine digit number, so it can't even pass strong password tests. Furthermore, the last 4 digits are often available (so down to 5 digits left), and there are rules that an SSN must adhere too (they aren't just any random 9-digit numbers). With less than 50,000 possibilities, it's fair game for a brute force attack.

  3. SSN is publicly available in many cases. For example, HR and managers can often see your SSN (but not your password).

  4. SSN has direct real life significance too, and is therefore immediately dangerous if lost. For example, if you lose a password, that's bad, but it's only dangerous if the hacker (A) knows the context - i.e. the system  to apply it to, and (B) the password hasn't changed yet. But the context for an SSN is immediately known (like any financial or government institution), and SSNs can't change.

What's your favorite Domain Specific Language?

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/whats_your_favorite_domain_specific_language.htm]

I love domain specific languages (DSL). The idea is that instead of programming at a low level (i.e. tedious and error-prone), you program at a higher level of abstraction. This means coding with a language (or even class library) that closely maps to the problem domain. For example, here are several domain specific languages. While you could try to do without, it just becomes so much easier with them:

 

DSLPurposeExampleManually doing without:
Regular ExpressionsFind and replace patterns in textFind all numeric decimals in a fileYou could use core string methods like SubString and IndexOf
SQLManage database dataSelect all employees that meet a certain criteriaYou could do selects by getting the entire dataset and cycling through the object model
XPathQuery xml dataGet the custom order xml nodes where price is less than $100You could step through the xml with a reader, or loop through an XmlDocument
MSBuildMicrosoft's build engine to automate your processesCompile your application on a build server, run unit tests, and then produce MSI outputsYou could use System.Diagnostics to manually run a bunch of commands, and keep track of error conditions and logging output yourself.
String Format ExpressionsFormat a string using var.ToString("myPattern")Format the number 12.3456 to only two decimal placesYou could use core string methods, and pick apart the variable, and re-assemble it.

 

The point is that while someone could get by without knowing the appropriate domain language, it's just not practical to tackle the domain without it. Each of these has tons of tutorials and quickstarts, so there's no reason to avoid them. An application developer should probably be comfortable with most of these.

 

So, what's your favorite domain specific language?

Tuesday, December 11, 2007

Silverlight Image Utilities - clipping and shrinking an image to fit

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_image_utilities__clipping_and_shrinking_an_imag.htm]

For my TruckWars game, I needed to get the profile shot for an image. For example, when you select an object, it displays an image of that object in the dashboard. The two problems are:

  1. What if the image is bigger than the dashboard's profile size? (i..e image is 96x96, but the dashboard only allows 48x48).

  2. What if the image has multiple frames for animation (i.e. image displays as 48x48, but has two frames, and therefore actual size is 96x48).

I wanted a way that given any image, it would clip the image to a single frame, and then shrink it to fit within the allowed-profile size. You can do this if you know the actual image size. In this case, the size of the profile image in the dashboard is 48 pixels. Because I make the animation frames be horizontal (i.e. something with two frames is twice as wide), I scale the image based on height. Then I clip to just the first frame.

 

    public static void MakeProfileImage(ref Image image1, Size szActual)
    {

      //Scale back the img to fit to the view size
      const double profileHeight = 48;
      double dblScale = szActual.Height / profileHeight;
      image1.Width = szActual.Width / dblScale;
      image1.Height = profileHeight;

      //Always clip the view size (in case there were multiple frames for animation)
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(0, 0, profileHeight, profileHeight);
      image1.Clip = r;
    }

 

I added this to a ImageUtilities class for reuse later.

 

Monday, December 10, 2007

Silverlight and Globalization: System.FormatException from NumberFormatInfo

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_and_globalization_systemformatexception_from_n.htm]

Through recent error-logging that I added to TruckWars, I found out the sometimes this line would fail:

 double x = Convert.ToDouble("12.5");

 

The input string was static (not user input) as it came from an xml config file. The line threw this exception:

System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options,
    NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
   at System.Double.Parse(String s, NumberStyles style, NumberFormatInfo info)
   at System.Convert.ToDouble(String value)

At first I thought "of course '12.5' is a valid double." And of course it worked on my machine, and all the servers I was checking. But, the logs still showed this occasional error. Then I thought - what if it's a globalization problem? In other words, for the "en-US" culture, "12.5" is a valid number, but not for other cultures. For example, this would fail for someone in France, where "12,5" is used for a decimal place (note the comma instead of the period).

 

So, I set up a unit test to check for a different culture:

[TestMethod]
public void ParseFromString_Global_Decimal()    
{      
  System.Globalization.CultureInfo culture =
  System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR");
  System.Threading.Thread.CurrentThread.CurrentCulture = culture;

  //run my parsing method here

  //ensure that culture wasn't overridden:
  Assert.AreEqual(culture.Name,
   System.Threading.Thread.CurrentThread.CurrentCulture.Name);
}

And the test failed with the exact error that I expected. I could then fix it by passing in a specific culture (like "en-US") NumberFormat:

private static System.Globalization.NumberFormatInfo _formatNumber =
System.Globalization.CultureInfo.CreateSpecificCulture("en-US").NumberFormat;

//essentially fixed by this:
double x = Double.Parse("12.5", _formatNumber);

I don't normally get this error because for our ASP.Net apps I use a set of utilities that already handled this, and I didn't need to worry about international users for the  windows forms because I only make those for internal development tools. However, it's another reminder why it's nice to have logging for even simple apps.

Sunday, December 9, 2007

Becoming a credible developer

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/becoming_a_credible_developer.htm]

Every developer wants to be taken seriously. We want to be credible. But what makes someone credible?

 

I've seen a lot of devs who think that being credible means:

  • Sending out a link to a guidelines document

  • Keep referring to your  last project (that no-one else on your current team was on) as that perfect project

  • Throwing out buzzwords

  • Never admitting that you're wrong

The problem is that all of these require little effort and don't really help anyone else. They don't distinguish the speaker - anyone can send out links, bluff about a former project, or throw out buzzwords.

 

I think a much better way to see if some is credible is if they:

  • Have experience in the problem domain

  • Have work products they can point to (i.e "I made this website", "I wrote this tool")

  • Can accurately predict what will happen (not "try this", but rather" do this and you'll get that")

  • Have a reputation of being correct (including other credible people who will back them up - i.e. "good references")

  • Can point to the official source (not "I heard on some blog that...", but rather "The MSDN reference spec for C# 2.0 says ...")

  • Are willing to invest their own resources into the approach or product (i.e. dogfood it). If someone won't even put their own resources in, then they probably don't believe in the approach.

You can't easily bluff these things. For example, your prediction either comes true or it doesn't (if it's ambiguous, then it's a bad engineering prediction); you either have a concrete product you can show people, or you don't. I think every industry leader you find does these things - they have tons of experience and products, make accurate predictions, have a reputation that precedes them, they write the official source, and they dedicate their lives to their cause. Now that's being credible.

Thursday, December 6, 2007

Create an object dynamically with CreateInstance using Reflection

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/create_an_object_dynamically_with_createinstance_using_refle.htm]

You can instantiate an object dynamically using Reflection. For example, the boards in TruckWars are stored in Xml files. Each board has a list of creatures:

 

  <Creatures>
    <Creature type="TankUnit" boardPosition="1.5, 7.5" team="Hero" />
    <Creature type="PickupTruck" boardPosition="1.5, 2" team="Hero" />
    <Creature type="PushButtonStayDown" boardPosition="13.5, 1.5" />
    <Creature type="TankUnitEnemy" boardPosition="14.2, 7.5" team="Enemy1" />
  Creatures
>

 

You could use an xml reader to cycle through this, and at each creature node, dynamically create a creature object. The "trick" is to have a base type (like "CreatureBase") that all your objects inherit from. You then specify the type in the xml file, and use the CreateInstance() method to dynamically create an object (to my knowledge, this requires that the base type at least have an empty constructor):

 

        CreatureBase c = (CreatureBase)Assembly.GetExecutingAssembly().CreateInstance(CreatureNamespace + "." + strType);
        c.Position = strPositionSerializedFromXmlAttribute;
        c.Team = (Team)Enum.Parse(typeof(Team), strTeam, true);    //makes an enum
        c.Name = strName;

 

You can then serialize the xmlNode's attributes and use them to set properties on that object. Thanks to polymorphism, the object will act as the derived type (for example, it will call the derived type's overridden methods).

 

This technique is often used in enterprise architecture for extensibility. The core system creates the base class, but then you can override it and set some xml config file to use your derived type.

 

Wednesday, December 5, 2007

Deploying Silverlight Apps

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/deploying_silverlight_apps.htm]

Silverlight is awesome to deploy. As this forum thread describes, you basically just need your web server to setup the right mime type ("Extension should be .xaml and the Content Type should be application/xaml+xml". I also had ".dll --> application/x-msdownload" because some other blogs recommended it).

 

You can then just ftp the files from your silverlight project (ClientBin, files in the root folder, and any other directories), to your server.

 

After dealing with Winform, ASP.Net, and XNA deployment problems, I was floored that it just actually worked.

 

Because Silverlight runs on the client machine, it is the client's responsibility to have the right stuff installed. However, silverlight makes that really easy by rendering as a single-click download link if the client doesn't have it installed yet.

 

Also note that Silverlight is not a server technology like SQL2005 or ASP.Net. You don't even need an ASP.Net server to run Silverlight. (This is especially practical if you're a hobbyist on a budget, and your host company charges extra to asp-enable your site.).

Tuesday, December 4, 2007

Using Path.Combine instead of string concatenation

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_pathcombine_instead_of_string_concatenation.htm]

I often need to work with filePaths, such as getting an absolute file name given a directory and a fileName. One way for developers to do this is simply string concatenation. A lot of devs do this because it appears so simple. But the problem is that when passing in the directory as a string, you often don't know if it will end with a final slash or not - i.e "C:\Temp" or "C:\Temp\". Merely doing string concat will screw it up, you could get a non-existent file like "C:\Temp\\MyFile.txt".

 

A much better approach is to use System.IO.Path.Combine. This allows you to have a directory (with or without the slash), and the combine method if smart enough to handle it:


    public static string GetFileName(string strDir, string strName)
    {
      //return strDir + @"\" + strName;   //BAD
      return System.IO.Path.Combine(strDir, strName); //GOOD
    }

 

What's funny is that both options are just 1 line of code and take essentially the exact same amount of time to write. However, the first is very brittle, and could likely cost you tons of time tracking down an error because someone passed in a directory with or without that extra slash. The second just works.

Monday, December 3, 2007

Silverlight 1.1 global page error handler

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_11_global_page_error_handler.htm]

When writing Silverlight TruckWars, I was trying to figure out how to create a global error handler for a page. Eventually I saw the WebApplication.Current.ApplicationUnhandledException event, which I could hook up like so:

 

    public void Page_Loaded(object o, EventArgs e)
    {
        //....
        WebApplication.Current.ApplicationUnhandledException +=
            new EventHandler
            (Current_ApplicationUnhandledException);
    }

 

    void Current_ApplicationUnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
      HandleError(e);
    }

 

Note that Silverlight handles errors differently than ASP.Net. In ASP, you get the yellow error page with a detail exception message (by default). However, in Silverlight, it just stops executing your code and returns to the caller, leading to weird behavior.

Sunday, December 2, 2007

10 Rules that Age of Empires Teaches about Development

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/10_rules_that_age_of_empires_teaches_about_development.htm]

I think that Age of Empires II (AoE) is arguably the best computer game of all time. A close second would be Civilization IV. I'm not much of a computer game player, so the best way for me to justify playing a game is that it's somehow educational. Smile Here are 10 rules, that Age of Empires clearly demonstrates, which also apply to software engineering:

  1. You need to invest in research: This means the occasional big investment, as well as many frequent upgrades as resources allow.  If you neglect your research, it won't matter if you have a hundred units - they'll all be obsolete and practically useless. It may even backfire because you'll still have to maintain them (via population limits or distracting your "medics" to get healed), which will prevent you from focusing on the new, important units that can actually win the game.

  2. You need the right tools: Don't even try taking down a castle with just stone-age sling shots. Often you need a combination of complementary units - i.e. you need to coordinate siege, cavalry, medics, etc... Likewise, software engineering needs the right tools - good luck with team development if you don't at least have a good source control system and a build server.

  3. Strike a problem while it's still small: Like in real life, problems grow in AoE. An enemy team gets bigger and stronger, resources dwindle, or the clock runs outs. This is why a common strategy is to rush your opponents while they're still small. (You may also be small at the moment, but you're still big enough to beat them now). In development, problems grow too - bad code propagates. Knock it out early before it comes back to bite you.

  4. Protect your home base: Don't rush out on some adventure and leave you home base defenseless. Likewise, in development, your core application is your home base, and it should be protected by a suite of automated unit tests. So if you get distracted with some other "adventure", at least your code still has some defense against bad additions that would break it.

  5. Not all units are equal: Although a foot-solder and a horse-archer are each just a single unit, one horse-archer could beat ten foot-solders. Furthermore, the horse-archer, who can shoot at range (like across a river), could accomplish things that a million foot-soldiers could never do. Likewise, in development, there are real "stars" - a star dev will produce not just more than 10x an average dev, they'll create things that an average dev won't even comprehend. Granted, sometimes a company's technical needs are simple enough that they don't need stars, but it's good to at least be aware of the discrepancy.

  6. Your plan can be limited by mental energy: You may have 50 units, but it doesn't matter if it's impossible to manage them. AoE is an arcade game, and you may make dumb decisions simply because there wasn't enough time to think out the perfect solution. I personally prefer a handful of powerful units that are easy to manage, as opposed to an army of weak units that just get plucked off because they're too hard to coordinate. Same applies to software engineering, but even more so. Most of what you do is limited by mental energy (code being hard to maintain, a purist algorithm taking too much thinking to figure out, etc...). Mental energy is as real a resource as gold.

  7. Don't rely on cheat codes: AoE has cheat codes. However,  don't depend on them because they could be disabled or make you miss the bigger picture. In software engineering, the equivalent is to use hacks - bad code that solves the immediate problem now, only to break tens times as much later.

  8. Expect the unexpected. In AoE, there are other teams actively working against you. The enemy may not attack your straight on (where all your defenses are), but may instead be creative and attack from the side, or ambush behind, or siege you, or something else. Likewise, in software engineering, there are constant business changes, miscommunications, developer bugs, new technologies, all causing unexpected things to happen.

  9. Recognize the common patterns and solutions: AoE has perhaps hundreds of units. With many different civs (each with their own unique unit), it's a lot to have a special plan for every single unit that you may encounter.  However, there are comparatively only a few categories for them - like infantry, ranged, cavalry, siege, naval units, etc... By looking at the common patterns (ranged units usually beat infantry, cavalry usually beats siege), you can abstract out the details and make a flexible plan. Same thing in software engineering - there are common design patterns and ways to abstract out complexity via domain-specific-languages. Be prepared to look at the high-level abstract issues, then drill down into the details.

  10. It should be fun: AoE is a good, old-fashioned-fun kind of computer game. It has huge replay value. Likewise, development should be fun, especially if you have the right process and schedule in place.
     

Wednesday, November 28, 2007

Have a child trigger a method in its parent

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/have_a_child_trigger_a_method_in_its_parent.htm]

Often you'll want to have a child object trigger some method in its parent. For example, you'll click a user control, but want that to change something in the parent control.  .Net 2.0 makes it very easy to do this. In .Net 1.1 (at least to my knowledge), this required a bunch of steps. Now, you can just add two lines in the object (declare a delegate, and then create an instance of that), and then use just one line in that object's consumer to specify the method to-be-called. In this case, we create an object "MyObject", with an event to be

namespace AddEvent
{
  public class Program
  {
    static void Main(string[] args)
    {
      MyOject o = new MyOject();
      o.GotClickedHandler += ParentGotClickedHandler;
      o.DoStuff();  //Trigger the object for some reason (this could come from a UI event like clicking it)
    }

    public static bool ParentGotClickedHandler(int i)
    {
      //I'm in the parent, but was triggered from the child object.
      i = i * 2;
      Console.WriteLine(i);
      return true;
    }

  }

  public class MyOject
  {

    public delegate bool GotClickedDelegate(int i);
    public GotClickedDelegate GotClickedHandler;


    public void DoStuff()
    {
      //trigger event:
      //trigger an event, passing that data
      int i = DateTime.Now.Second;

      if (this.GotClickedHandler != null)
      {
        GotClickedHandler(i);
      }
    }
  }
}

Tuesday, November 27, 2007

Displaying different colored text to the console (just like MSBuild)

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/displaying_different_colored_text_to_the_console_just_like_.htm]

I really like how MSBuild displays different colored text to the console - red for errors, yellow for warnings, green for success, etc... It's just a nice perk.

You can easily do this too using Console.ForegroundColor. For example, the following code has a master method "_ConsoleWrite" that takes the text and a ConsoleColor. It then keeps track of the original color, writes the text in the new color, and restores the original. It's simple, but adds a nice touch to a console app.


    private static void _ConsoleWrite(string strText, ConsoleColor cWrite)
    {
      ConsoleColor cOriginal = Console.ForegroundColor;

      Console.ForegroundColor = cWrite;
      Console.WriteLine(strText);

      Console.ForegroundColor = cOriginal;
    }

 

    public static void ConsoleWriteNormal(string strText)
    {
      _ConsoleWrite(strText, ConsoleColor.Gray);
    }

    public static void ConsoleWriteError(string strText)
    {
      _ConsoleWrite(strText, ConsoleColor.Red);
    }

    public static void ConsoleWriteWarning(string strText)
    {
      _ConsoleWrite(strText, ConsoleColor.Yellow);
    }

    public static void ConsoleWriteSuccess(string strText)
    {
      _ConsoleWrite(strText, ConsoleColor.Green);
    }

Monday, November 26, 2007

Creating a transparent, animating, and opaque image in Silverlight 1.1

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/creating_a_transparent_animating_and_opaque_image_in_silve.htm]

Every sprite-based game eventually needs two things from images: transparency and animation. In TruckWars, I use this all over the place.

 

Transparent - You can make an image be transparent by just using a transparent png (I don't think Silverlight supports transparent gifs yet), which you can easily create in Paint.Net.

Opaque - You can also make it opaque by just setting the built-in Silverlight opacity property on an image.

 

Animation is a litter harder. There are different takes on this. Two main categories of approaches are (A) have a single large image which contains all the frames, and then clip that image every x milliseconds, or (B) have a separate image for each animation frame, and then update the image source every x milliseconds. I prefer option A because it's much easier to manage, but requires a little extra coding to do the clipping.

 

About option A, for example, the smoke cloud consists of four separate frames:

 

Smoke

 

You then animate it by displaying one frame every x milliseconds. (Note that I used sprites for a smoke effect instead of a particle system because the sprite is much faster). The question becomes now - how do I clip this image? Ultimately I'll use the Silverlight clipping property - but how do you set what the clip should be?

 

Some people would just use the built-in storyboard technique with key-frames to update the clip region. My problem with that is (1) I didn't know how to manage that for multiple objects such that I could pause-on-demand and have the animation freeze, and (2) that's Silverlight-specific, so learning it wouldn't help me in other scenarios, like XNA, (3) I just didn't have the motivation to learn the new technique yet. So, instead I decided to build code to manage my own clip updating.

 

You can see the code for this in the TruckWars download: Tank\Tank.Test\Tank.Core\SpriteBase.cs, however, here's a quick overview. The code runs on the same gameClock than runs everything else. It keeps track of the current frame, and has two animation options: Repeat or RunOnce. For example, the water repeats its animation, but the smoke only runs once and then gets removed from the game. At every cycle of the gameloop, it checks if enough time has passed (in this case, 200 milliseconds), and then potentially draws the next frame.

 

To actually display the new animation requires two things: (1) clip the image to the size of a single frame (48 x 48), but then also offset the images left position such that that frame appears in the same place

 

      //set clip
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(intFrameIndex * this.Size.Width, 0, this.Size.Width, this.Size.Height);

      MainImage.Clip = r;

      //apply offset
      this.MainImage.SetValue<double>(Canvas.LeftProperty, 0 - intFrameIndex * this.Size.Width);

 

 

Here's more complete code:


    #region Animation

    protected int _intFrameIndex = -1;  //Default to -1 because Update runs first, and increments it to 0 right away
    private const int AnimationFrameCount = 4;

    private int _TimeToWaitForAnimation = 200;
    public int TimeToWaitForAnimation
    {
      get
      {
        return _TimeToWaitForAnimation; //wait x milliseconds between animation frames
      }
      set
      {
        _TimeToWaitForAnimation = value;
      }
    }

    protected double _dblTimeOfLastAnimation = 0;


    public bool FinishedAnimationCycle
    {
      get
      {
        return ((_intFrameIndex) >= AnimationFrameCount);
      }
    }


    private AnimationCycle _AnimationCycle = AnimationCycle.Repeat;
    public AnimationCycle AnimationCycle
    {
      get
      {
        return _AnimationCycle;
      }
      set
      {
        _AnimationCycle = value;
      }
    }

    protected void ResetAnimation()
    {
      _dblTimeOfLastAnimation = 0;
      _intFrameIndex = -1;
    }

    public void DrawAnimation(double dblElapsedGameTimeMS)
    {
      bool blnShouldUpdate = ShouldUpdate(dblElapsedGameTimeMS);
      if (!blnShouldUpdate)
        return;

      //-----------
      _dblTimeOfLastAnimation = dblElapsedGameTimeMS;

      _intFrameIndex++;

      if (this.AnimationCycle == AnimationCycle.Repeat)
      {
        if (_intFrameIndex + 1 > AnimationFrameCount)
          _intFrameIndex = 0;
      }
      //-------------

      DrawFrame(_intFrameIndex);
    }

    private bool ShouldUpdate(double dblElapsedGameTimeMS)
    {
      if (Convert.ToInt32(dblElapsedGameTimeMS - _dblTimeOfLastAnimation) < TimeToWaitForAnimation)
        return false;
      else
        return true;
    }

    ///


    /// Animation is done by giving a single image with multiple frames, and then displaying one frame at a time.
    /// For performance reasons, this only redraws the image if the FrameIndex has changed. Therefore repeatedly calling this
    /// method in a loop should not degrade performance, as it will just cancel out once the image is set (and until the image is changed).
    ///

    ///
    public void DrawFrame(int intFrameIndex)
    {
      if (intFrameIndex == _intLastDrawnFrame)
        return; //Already at the correct frame, don't waste resources re-drawing.

      //set clip
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(intFrameIndex * this.Size.Width, 0, this.Size.Width, this.Size.Height);

      MainImage.Clip = r;

      //apply offset
      this.MainImage.SetValue<double>(Canvas.LeftProperty, 0 - intFrameIndex * this.Size.Width);

      _intLastDrawnFrame = intFrameIndex;

    }

    private int _intLastDrawnFrame = -1;

    private Image MainImage
    {
      get
      {
        return (this.root.FindName("Image1") as Image);
      }
    }

    #endregion

 

 

Monday, November 19, 2007

Benefits of Farseer 2D Physics Engine

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/benefits_of_farseer_2d_physics_engine.htm]

I recently released Silverlight TruckWars v1.3, whose big enhancement was to incorporate the Farseer 2D Physics Engine. There are several benefits I like about Farseer:

  1. It just works - The collision algorithms just work. Before I had some code to do polygon detection. It was good code that I found online. And my use of it was okay when comparing two simple bodies, but it broke down with bigger things - like 5 units all pushing on each other. Because TruckWars is intensive on objects interacting with each other, this was important.

  2. Automatic pushing of objects - Before I had special code for the crate object (that you can push), but now, pushing objects is automatically handled.

  3. Collision categories - Not every object collides with every other object. For example, a fireball hovers over water, but a tank is blocked by it.

  4. Static objects - Farseer recognizes that sometimes, something is just impenetrable and unmovable - like the walls of a game board. Whereas in the real world, enough force will eventually push through something, I didn't want to allow that in the game world.

  5. Circular geometries - Farseer implements circular geometries via polygons, and lets you specify the number of sides for precision. My previous collision code only handled rectangles.

  6. Collision callback - You can add an extra method that fires on any collision. That method can then return true (to continue normal collision behavior) or false (to ignore collisions for that specific case).

  7. Body and Geometry have object tags, so I can associate them with a creature.

  8. API - The API is just clean, especially if you have any physics background. It both maps to standard physics knowledge, as well as provides desired method calls that you'd want to actually program something.

It took some extra effort, but it was well worth it. The engine feels much more solid now.

 

It's also worth noting, that Farseer almost makes you to do it correctly - by applying forces instead of directly setting positions. That's how the real phsycial world works. This also makes you appreciate what's involved in seemingly simple things - moving at a constant rate, having zero-turning radius, stopping a unit on demand (perfect breaks). You can achieve each of these by setting the correct properties. While it's tempting to just set the positions because then you can control everything, that becomes a nightmare with complex collisions.

 

I see a similar parallel to Enterprise development. It's tempting to just whip out some hack, but then as you scale up, that hack always comes back to haunt you.

 

I've open-sourced TruckWars on CodePlex - so you can see all the places Farseer is used. One place is from the MoveTowardTarget
 method in Tank\Tank.Test\Tank.Core\MovementStrategies.cs:

//Always clear angular velocity
c.Body.AngularVelocity = 0f;

//update direction and position
double dist = MovementUtilities.GetDistance(c.Position, c.TargetPosition);
if (dist > c.Size.Width / 2)
{
  ReleaseBreaks(c);

  Vector2 force = new Vector2();
  force.X = (float)(c.TargetPosition.X - c.Position.X);
  force.Y = (float)((c.TargetPosition.Y - c.Position.Y));
  float forceAmount = c.Thrust * PhysicsHelper.MovementForce;
  force = force.Normalize() * forceAmount;

  //Prevent sheering --> make LinearVelocity always be in direction of force
  c.Body.LinearVelocity = force.Normalize() * c.Body.LinearVelocity.Length();
  c.Body.ApplyForce(force);


  c.UpdateDirection();
}
else
{
  //You've reached your target, so apply the breaks
  ApplyBreaks(c);
}

This clears the angular velocity to prevent spinning, normalizes the force to push you in  a straight direction, and incorporates applying & releasing breaks to allow for quick stopping. Here are the two break methods:

public static void ApplyBreaks(CreatureBase c)
{
  //Need to "release breaks" when moving again
  c.Body.ClearForce();
  c.Body.LinearDragCoefficient = 4 * PhysicsHelper.LinearDragBreaks;
  c.Body.AngularVelocity = 0f;
  c.Body.ClearTorque();
}

private static void ReleaseBreaks(CreatureBase c)
{
  c.Body.LinearDragCoefficient = (float)(c.LinearDrag * PhysicsHelper.LinearDragMovement);
}

Sunday, November 18, 2007

Releases Silverlight TruckWars v1.3 - OpenSourced on CodePlex

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/releases_silverlight_truckwars_v13__opensourced_on_codeple.htm]

Silverlight TruckWars is a real-time strategy game built entirely in Silverlight. I recently releases version 1.3. The biggest thing is that I've now open-sourced it on CodePlex.

 

Check it out:

I've also added several enhancements

  • Added hold-able objects, like a key

  • Gameplay enhancmeents.

    • The biggest one is that selecting any creature now provides help text.

    • Destroying an enemy unit now can optionally provide a "treasure", like a powerup or key.

  • Lots more powerups:

    • Bounce - projectile bounces off walls

    • Thrust - doubles your thrust

    • Clone - clones the creature that got the powerup

    • Health - plus 50 health points

  • New creatures & levels

    • LandMine

    • Giant Tank

Thursday, November 8, 2007

Don't let me hurt myself

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/dont_let_me_hurt_myself.htm]

In the real world - apart from computers - it was always understood that you could do dumb things to hurt yourself. Therefore a level of discipline was always required. You could hold a sharp knife wrong and cut yourself. You could say something dumb to a friend and ruin the relationship. A sufficiently out-of-luck person could basically mess up anything they came in contact with.

 

What I find ironic is that with software, there seems more and more a demand to reverse all that - to almost make it impossible for a user to hurt themselves:

  • Through intuitive UI and instant validation, software hinders you from doing something wrong. It won't let you type bad input, it prompts you with warnings if your intent is unclear ("Do you really want to delete this account?"), and it lets you undo your action ("Please restore that account I just deleted"). Unlike a knife, good software won't let you get cut, no matter how incorrectly you hold it.

  • Every other product seems to have a long list of disclaimers on how not to use that product. "Don't do X, or you could get hurt, and our company is not responsible."

  • If you send a bad communication that blows something up, good software lets you undo it, edit it, or roll it back.

  • Even when building software, because you can instantly back up all your work (with source control), any mistake can be reverted. Imagine baking a cake and accidentally putting in one too many eggs - you can't just "undo" it.

While it's great that software keeps improving, I find this un-paralleled expectation very interesting.

 

Wednesday, November 7, 2007

Releases Silverlight TruckWars v1.2 - uses Farseer Physics Engine

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/releases_silverlight_truckwars_v12__uses_farseer_physics_e.htm]

Silverlight TruckWars is a real-time strategy game built entirely in Silverlight. I recently releases version 1.2, with the big feature being that it uses the Farseer Physics engine. I also simplified the game play, and added some new creatures. I'm trying to move the game from "technical gimmick" to "fun".

 

Ultimately I'll try to open-source it so others could create their own creatures and levels.

 

Check it out:

 

About the game play - I had feedback that the previous physics engine (some scrappy code I had whipped up) just wasn't powerful enough to do what I wanted. I want the game to be about interacting systems - like trucks colliding with each other, growing objects (that change their size), pushing around crates, bouncing bricks that can crush a unit if they overlap, etc... All of this requires a powerful physics engine, which is why I took the effort to migrate to Farseer.

Thursday, November 1, 2007

Comparing XNA and Silverlight for 2D game development

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/comparing_xna_and_silverlight_for_2d_game_development.htm]

Most developers are interested in writing games, if even just as hobbyists. That's why there's all this buzz around about two (relatively) new technologies - XNA and Silverlight. I wrote a simple real-time strategy on each platform, and I found the differences very educational. There's a lot of overlap. For example, the powerful Farseer Physics Engine is being marketed to both groups. The best blog I've seen so far on this has been MVP Bill Reiss's stuff - he's written good tutorials for game development on both platforms.

 

 XNASilverlight 1.1
SummaryA platform designed explicitly for gamer development. XNA is obviously more powerful, as it's a client download, requires DirectX, and is optimized specifically for games and high-speed (including 3D) rendering.A new plug-in that hosts xaml, letting you write thick-client applications in the browser. Silverlight lets you write many things, of which games are just one.
Why care?Solves rendering and gameLooping such that anyone can now make a simple game.Allows you to write simple games over the web, so you can share with anyone.
Age of technology?Came out about a year agoStill in alpha
RenderingThe Game class has a built-in gameLoop, which fires both an Update and Draw. The Update method updates your models, positions, and logic, whole the Draw method then uses those models to draw images to the screen via the SpriteBatch. If you want to add a new object, simply display its sprite. If you want to remove that object, stop rendering its sprite.

For 2D games, XNA just displays sprites - it's not vector based (as far as I could tell).

You add objects using Xaml, and then the objects stay rendered until you explicitly remove them from the root canvas.

Xaml is vector based, so you can draw shapes as well as sprites.

 

AnimationDeveloper manually handles by updating the position and what images to show in the code.You can use Xaml storyboards for simple animation sequences. You can also set the position, rotate, and clip of a sprite.

SEE: how to make an animating sprite

Transparent ImagesHandled by setting all pixels to magentaHandled by making a transparent png image.
Killer Features
  • Awesome performance
  • 3D games
  • Deploys over the web, on all major browsers, using just FTP (you don't even need ASP.Net on the server)
  • Integrates with Html and Ajax, so you can also use web controls (for example, TruckWars uses an html dropdown to let you select levels, and an html button to both reset levels, and provide a confirm box)
Big Problem

 

Slow performance compared to XNA, but good enough for classic 2D games.
Benefit to learningGood way for a hobbyist to get started.I personally think that the skills needed to make Silverlight games are much more marketable because they can also be applied to enterprise and business development. For example, if you can make Pacman in Silverlight, you can make graphs and input controls that the business world cares about. While XNA can make graphs too, there's a much bigger market for web development than on the XNA platform.
TutorialBill Reiss on XNABill Reiss on Silverlight
TruckWars sampleTruckWars in XNA (you need to download a bunch of stuff)TruckWars in Silverlight (runs right in the browser)

 

Silverlight and XNA are both easy to get started, especially with the active communities evolving around each of them. Even if you're a full-time .Net developer, it's still fun to toy around with these technologies on the side.

 

Personally, I started with XNA, but quickly moved to Silverlight. For me, the deciding factor is that (1) The Silverlight skill set is more applicable to my job (enterprise development), (2) Silverlight is so much easier to deploy, so I can share the results with others.

 

Wednesday, October 31, 2007

Why try if you can't be the best?

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/why_try_if_you_cant_be_the_best.htm]

In a competitive world of information overload, with millions of other developers, there will always be someone better than you. Even if you pick a niche, chances are there will still be someone even more talented than you in that niche. "There's always a bigger fish in the sea". Furthermore, learning any new field always requires hard work and making mistakes. So, a common question is "Why should I try if I can't be the best?" For example, why learn how to build web applications, or computer games, or any programming, when there will always be someone so much better?

 

It's a good question, one that unfortunately intimidates a lot of people from trying in the first place. There are several reasons:

  • You don't need to be the best. You can still get a job, contribute to the field, and have fun.

  • You can learn something just for the enjoyment of learning it. For example, you may enjoy making a Silverlight or XNA game just to have fun - it doesn't have to be the next Super Mario or Age of Empires. Similarly, I go running just for my own enjoyment - even though I make a turtle look fast.

  • The act of trying will improve your character, and still teach you other skills.

  • Often the "best" isn't available (they're already locked up in another job or project), so one's moderate skills are still needed.

  • You can still add value, even if you're not the best. Often star developers want to coordinate other people to help build a project bigger than what any one person can create by themselves.

  • Even if you're not the best, you can still probably help other people more junior than yourself. I.e., you're probably still an expert to someone - even if it's just a young student, intern, or someone changing careers.

  • Everyone has to start someplace. You may even become that super-expert someday.

Someone who constantly passes up the opportunity to learn something new, simply because they can't be the expert at it, is selling themselves short. As a father pointed out in the movie Facing the Giants, in a scene about a soccer kid hesitant to try out for the football team because he may not make the cuts, "You aren't on the team right now. You can't be any less on the team than you are right now." Likewise in development, if you don't know a technology and you're hesitant to learn because you're not an expert, you can't be any less of an expert than you are right now. You might as well take a stab at something you enjoy, have fun along the way, and become good enough to do what you want to do. I'll never be Michael Jordan, but it's still fun to shoot a few hoops.

Tuesday, October 30, 2007

Silverlight: Unit Testing

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_unit_testing.htm]

Silverlight 1.1 lets you use C# code, which is awesome. However, the next natural question for a developer is "How will I test that code?" For example, any silverlight game, like the TruckWars game that I've been blogging about, has lots of backend logic for collision detection, movement algorithms, interactions between units, checking for victory conditions, etc... Ideally, you could follow the standard pattern of having a core assembly with all the logic (a Silverlight class library), exposed with just a light GUI wrapper (a Silverlight project). Then you could add that class library to a test harness (like MSTest) and do Test-Driven-Development.

 

The problem is that Silverlight doesn't let you mix Silverlight and non-Silverlight assemblies. So, you can't add your SL class library to a test harness (like MSTest), nor can you even add an assembly to your Silverlight library (like NUnit). [If there's a way to do this, I haven't seen it yet, and I'm all ears].

 

So, there is a work-around. What you could do is create an MSTest project, and then place your Silverlight class library in a child folder of that MSTest project. Then each project has references to all the same physical files.

 

For example, have a Test solution at:

    FolderSilverlight\Tank\Tank.Test\Tank.Test.csproj

 

And then your silverlight class library, called "SilverlightUtilities", as a child:

    FolderSilverlight\Tank\Tank.Test\SilverlightUtilities\SilverlightUtilities.csproj

 

Then, say you have a silverlight file to test, like "MathHelper.cs", located in the SilverlightUtilities:

    FolderSilverlight\Tank\Tank.Test\SilverlightUtilities\MathHelper.cs

   

Both projects, "Tank.Test" and "SilverlightUtilities" can have references to the same physical file, "MathHelper.cs" (there is only one copy of this file, but it gets referenced by two projects). Essentially, this means that the Tank.Test Project includes both the source code, and the test code.

 

Note, you'll need to give the Test project references to several Silverlight prerequisites:

  • agclr - C:\Program Files\Microsoft Silverlight\agclr.dll

  • System.Core - C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll

  • System.Silverlight - C:\Program Files\Microsoft Silverlight\System.Silverlight.dll

  • System.Xml.Core - C:\Program Files\Microsoft Silverlight\System.Xml.Core.dll

This does mean that as you add each new C# file, you have to build two projects, but an automated script should make that easy.

 

Also, I still haven't found a way to test code that uses Xaml (like a Silverlight Control). I keep getting the error:

 

    Project file must include the .NET Framework assembly 'WindowsBase, PresentationCore, PresentationFramework' in the reference list.

 

I'm sure there's a way to deal with this, but I haven't found it yet.

 

I hope that because Silverlight is still a beta, that Microsoft will eventually make it easier to unit test. In the meantime, it's good to be able to continue writing tests, even on Silverlight.

 

Monday, October 29, 2007

Silverlight: Using isolated storage to save client data

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_using_isolated_storage_to_save_client_data.htm]

Silverlight allows developers to persist data to a file on the client's machine using Isolated Storage. This is just like writing a normal file, except that it handles a bunch of extra grunt work to write that file to unique spot for the user and application. So, instead of writing a file to "C:\Temp\myStuff.txt", that all users and applications can see, it writes it to something essentially like "FolderRoot\UserName\Application\myStuff.txt" (although, the real directory structure is a little more optimized and less plain-text).

 

What's also interesting about Silverlight isolated storage is that unlike other browser client-side repositories, like a cookie, (A) it is shared across browsers, and (B) it's not cleared by deleting temp files.

 

For example, in my Silverlight arcade TruckWars game, it uses isolated storage to determine what's the highest level you've reached, effectively saving your progress. The code has a class, IsolatedStorageHelper, that provides static utility methods to SetData, GetData, and Delete a file:

 

using System.IO.IsolatedStorage; using System.IO;  namespace SilverlightUtilities {   public class IsolatedStorageHelper   {     ///      /// Writes the given data to the given fileName in isolated storage.     /// If the file already exists, it is overridden.     /// If the file does not exist, it is created.     ///      ///      ///      ///      public static void SetData(string strData, string strFileName)     {       using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())       {         using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(strFileName, FileMode.Create, isoStore))         {           using (StreamWriter writer = new StreamWriter(isoStream))           {             writer.Write(strData);           }         }       }     }      ///      /// Gets the data from the fiven file.     /// Returns null if the file does not exist.     ///      ///      ///      public static string GetData(string strFileName)     {       using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())       {         if (isoStore.CurrentSize == 0)           return null;          using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(strFileName, FileMode.Open, isoStore))         {           using (StreamReader reader = new StreamReader(isoStream))           {             // Read the first line from the file.             string s = reader.ReadLine();             return s;           }         }       }     }      public static void DeleteFile(string strFileName)     {       using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())       {         if (isoStore.CurrentSize == 0)           return;          isoStore.DeleteFile(strFileName);       }     }   } }

Sunday, October 28, 2007

Releases Silverlight TruckWars v1.1

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/releases_silverlight_truckwars_v11.htm]

Silverlight TruckWars is a real-time strategy game built entirely in Silverlight. I recently releases version 1.1, which as some new features:

  • More creatures:

    • Ambulance that heals

    • Growing forests

    • Moving bricks

    • SignPost to display helpful messages

  • New levels, including tutorials and new graphics

  • Better game management - saves your progress, and an HTML toolbar that lets you jump to previously solved levels, restart the game

  • A help page

These really help round out the game.

You canplay the game online here. Being Silverlight, it essentially runs within a blog post, so feel free to leave comments on that (or any) post.

 

I'll blog more about these in the future. Definitely there are more enhancements I'd like to make. I'd group these into improving the engine, and adding new creatures to better demonstrate that engine. In particular, new features I'd like:

  • Incorporate Farseer Physics Engine for better collision detection

  • Add a game-difficult setting

  • Improve movement algorithms - i.e. a unit should be smart enough to move around a concave polygon.

  • Add a "building" type object that can create new units.

  • Something with railroad tracks - for my son :)
     



Monday, October 22, 2007

Paylocity is hiring for a .Net/C# Programmer (in Chicago)

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/paylocity_is_hiring_for_a_netc_programmer_in_chicago.htm]

Paylocity, a Chicago-based payroll company, is hiring for a .Net / C# Programmer. We have a good team culture, a growing company, and are consistently voted one of the best places to work for. The position is in Internal Development, and takes a hybrid of business and technology skills. This would be an excellent opportunity for a junior or mid-career programmer, as it provides tons of learning potential.

While it isn't my direct department (I'm in software engineering), I constantly hear good things about them.

 

If you're interested, feel free to either apply online, or contact me directly (just include your contact info if you do so that I can reply).