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.