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.