Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

FormSprite: I Love You Jessica!

4.67/5 (11 votes)
18 Oct 2010CPOL11 min read 64.4K   756  
A Jessica Rabbit desktop pet created using FormSprite
jessica_screen_capture.PNG

What's a Form Sprite?

Remember that sheep that used to prattle around your screen? Or that helpful paper-clip some Windows applications have to motivate you when you look like you don't know what you're doing? Well, now you too can create your own cute crawling critters to coddle your screen using a new sub-class, the classSprite called classSpriteForm. You don't need to download Sprite-Editor in order to run this application, but if you want to create your own sprites then that's just what you'll have to do. See the reference below.

Bitmap Regions

Have you ever heard of "Bitmap Regions"? They're used to mold your buttons and forms into the shapes of images you have on bitmaps by creating transparent regions where your bitmap has a transparent color (determined by the color of the bit at 0,0). That article was written in 2004 but is still pretty cool and the bitmapRegions.cs can be easily made into a .dll so that you can just reference it whenever you like. This project makes use of it to convert an animated character image into the shape of the form which moves around the screen as the character sprite struts about flaunting it and looking dirty mean.

Update : PerPixelForm

There isn't much of an article Per Pixel Alpha Blend in C# but its author left behind some code which does an even better job than the Bitmap Regions which, I am told, are fossil-like and can probably now properly take their place among the ancient artefacts of coding-history. A comment below from another reader pointed me this way and so I had a look and plugged this solution into my new classSpriteForm and Jessica is looking a lot better. Plus, as an added bonus, I fixed a bug that had her sore feet keeping her standing on the same spot but not any more, now she's prancing about parading her piece.

FormSprite

If you haven't read my Sprite Editor for .NET article, you should have a look at it because that's what you'll need in order to create your own sprite. Essentially the classSprite and its accompanying Sprite Editor allows you to take still images you've chopped up into severed limbs and reattach these limbs at pivot points I call hinges. With a sprite character joined together in this way, the Editor is then used to animate the characters as if they were paper dolls with rotating limbs pinned at the joints. You record the sequences of images you've created into configurations and can then summon any image in any configuration and throw it onto the screen.

For any of you who are using the classSprite already, you may be interested to note that the source code in this project includes an upgrade to the class. What's new here is the ability to request an image without drawing it onto an existing bitmap by calling the function :

C#
public Bitmap getConfigurationImage(int intConfigurationIndex, 
                                    int intConfigurationStep, 
                                    enuMirror mirror, 
                                    bool bolForceDraw)

The old classSprite forced you to work a bit to do this.

As for the new class FormSprite (which is introduced in this article and was not part of the original sprite project), it makes use of the perPixelAlphaBlend mentioned above to allow the programmer to drop animated characters onto the computer screen outside of any form. The class inherits System.Windows.Forms.Form, it is made borderless and images from the sprite loaded into it during runtime are used to mold the form around it so that the entire form disappears except for the character you're animating on the screen.

Its main functions are:

C#
public FormSprite()
{
  FormBorderStyle = FormBorderStyle.None;
  ShowInTaskbar = false;
  TopMost = true;
}
void drawMe()
public void drawMe(int ConfigurationIndex, 
                   int ConfigurationStep, 
                   Point ptLoc, 
                   enuMirror Mirror, 
                   double angle, 
                   double size)
public void loadSprite(string strFilename)

The class also includes several properties that control angle, size, location, mirror, configuration and configuration step. Whenever you change one of these properties independently, the class will automatically call the drawMe() function which will update the sprite's appearance automatically. You can avoid this by calling the overloaded function drawMe() directly and include all the variable changes:

C#
drawMe(configIndex, configStep, ptNew, eMyMirror, 0, 1.0);

This function sets the values tied to the properties, then calls the function drawMe() which uses those values to reset the form's appearance.

Because your sprite will likely have images of varying sizes as they walk around, dance and fight on the screen, you don't want the top-left corner of your sprite's image to be placed at the location ptNew in the example above. So the value of _pos in the FormSprite is adjusted to fit the size of the image. The way this works is with the help of the udtBmpAndPtTLInfo structure which tells you the distance from the sprite image's center location to the top-left corner of the bitmap. By "sprite image's center", I don't mean the center of the bitmap but the location of some specific part of the sprite's anatomy, like the heart, chest or pelvis, on that particular bitmap. This is done in the private function PlaceMe() shown below so you don't need to worry about it, only know that the Pos property of your classSpriteForm refers to the position on the screen where the sprite's master-block will be placed.

C#
void PlaceMe()
{
udtBmpAndPtTLInfo udrBmpTLInfo = cSprite.Configurations[intConfiguration].steps
	[intStep].BmpCacheByMirrorType[(int)eMirror].dir[intDir];
int intGrabLimbIndex = 0;
if (cStationaryLimb != null)
{
    for (int intLimbCounter = 0; 
	intLimbCounter < udrBmpTLInfo.limbPositions.Length; intLimbCounter++)
    {
        if (cStationaryLimb == udrBmpTLInfo.limbPositions[intLimbCounter].limb)
        {
            intGrabLimbIndex = intLimbCounter;
            break;
        }
    }
    Left = _pos.X + udrBmpTLInfo.ptTL.X - 
	udrBmpTLInfo.limbPositions[intGrabLimbIndex].pt.X;
    Top = _pos.Y + udrBmpTLInfo.ptTL.Y - 
	udrBmpTLInfo.limbPositions[intGrabLimbIndex].pt.Y;
    return;
}

Left = _pos.X + udrBmpTLInfo.ptTL.X;
Top = _pos.Y + udrBmpTLInfo.ptTL.Y;
}

Notice here the limStationary in the FormSprite. In some circumstances, you may want to center all the action your FormSprite does about a limb other than the MasterLimb and when you do, you merely have to find the limb you want and point to it via the cStationaryLimb pointer. In the case of Jessica, this sample code uses the stationary-limb to implement the "grab" routines so that if you click and hold Jessica's toe then she will dangle, writhe, kick and complain while her toe remains under your mouse cursor wherever it goes. In this way, you can pick Jessica up and move her to some other part of the screen.

Here's a snapshot of Jessica when she's been grabbed by her foot.

grab_far_foot.PNG

You'll notice that her far-foot is stuck to the mouse cursor and will follow the cursor wherever you move it on the screen.

Force Outline Color

Another comment I got suggested that the sprite's image could be drawn on a black-matte background so that when made transparent the bordering edge could have that specified color. When I tried it, I found that Jessica's reddish tint and bay beauty was marred by the darkened edge which this suggestion added. However, the idea is still a good one, but not versatile enough. As a consequence to this experiment, I added another property to the sprite class called ColorOutline shown below.

C#
SolidBrush brBack;
/// <summary>
/// set the color of sprite's outline.  default = white;
/// </summary>
public Color ColorOutline
{
  get
  {
    if (brBack != null)
      return brBack.Color;
    else
      return Color.White;
  }
  set
  {
    brBack = new SolidBrush(value);
  }
}

Along with the optional fillRectangle() call in classSprite's function getImageOnBitmap():

C#
udrRetVal.bmp = new Bitmap(ptBR.X - udrRetVal.ptTL.X, ptBR.Y - udrRetVal.ptTL.Y);
using (Graphics g = Graphics.FromImage(udrRetVal.bmp))
{
if (brBack != null)
  g.FillRectangle(brBack, 
	new Rectangle(0, 0, udrRetVal.bmp.Width, udrRetVal.bmp.Height));

So if you neglect to set the specific color you want the only time lost in execution is in the testing the existence of your specified color and you can still tinker with the girl's glow. If you know what I mean, nudge-nudge, wink-wink.

classJessica

Ok, so I said I was bored but not to the point where I'd bother drawing up an entirely new sprite. I've been busy and just put this project together with previously existing bits here and there thinking I'd try stuff out. It's welfare-day on a friday here in the slums and I didn't feel like sewing together a new sprite just for the occasion so I grabbed my favourite one and think she still looks pretty enough to show her moves (though I may teach her a dance move or two later!).

In a previous article, I did a similar thing using a screen captured image of the computer screen behind the form and then drawing it onto the form to fool the user into believing that there was no form there. But since there was too much flickering because the image needed to be removed before the screen could be captured again, I then resolved to capture a fine strip of the screen where the sprite was moving to and tacking that onto the image stored in memory and moving around like that. It was a bit more complicated than it needed to be and a much greater headache to write than this version which makes use of the Per Pixel Alpha Blend code and does away with all the fuss because FormSprite really does become transparent and your sprite can walk across video like an episode of the Gilmore Girls without smudging Rory's pretty grin. Panscake!

A double variable is used to calculate a dblSizeFactor depending on Jessica's position on the screen and though the number of steps required to move from A to B is calculated from the start location, each step is adjusted by this dblSizeFactor so that it's a good thing Jessica doesn't really need to be anywhere because if she starts at the top and moves down the small step-sizes she was taking at the top mean that by the time she gets to the bottom she'll go past the spot she'd intended to go to and if she starts at the bottom to work her way up she'll fall short of her target that way. But, like I said, I was just bored and she really doesn't have anywhere better to go anyway, so I don't care if she don't care and I ain't about to ask her.

Ninja Style Flip-action Toss

You can grab Jessica with your mouse and move her wherever you like and you can even toss her wherever. When you throw her, she will do a few flips and land on her feet ninja-style right on the edge of her Play-pen in whatever direction you tossed her.

Right-Clicking Jessica

If you right-click Jessica, a menu will appear similar to the right-click menu that appears whenever you right click any other form or your desktop. These are Jessica's options. There are only two for the moment:

  1. kill-jessica, which may be unfortunate but sometimes necessary and since she is a highly trained professional who understands the risks inherent in the life of a desktop-pet, Jessica is prepared for this eventuality and you need not concern yourself with her plight but rest assured that she willingly goes to her fate knowing that it is all for a better cause.
  2. define play-pen. This lets you tell Jessica to stay in a certain area of the screen.

Here's a screen shot of the right-click menu:

Right_Click.PNG

To define Jessica's play pen, the program sets up a form that contains a picturebox full docked and the form's size and position covers the entire screen. Before this form is thrown onto the screen, a screenshot is captured and stored in a global bitmap and is loaded into the picturebox. MouseDown, MouseUp and MouseMove events keep track of where the mouse button was pressed and where it was released and a timer tripped by the mouseMove event updates the image to show the yellow rectangle which the user has dragged across the screen. When the mouse is released, the form disappears, Jessica's move-timer is re-enabled and her "Play-Pen" is redefined so she redeploys to wherever you sent her and stays out of your way while you do all that important stuff that your boss thinks you do.

define_PlayPen.PNG

Adding Writhing Wriggles

If you have a copy of Sprite-Editor up and running, you can load Jessica's sprite and add "Writhe" functions to the sprite without need to edit the project. You'll have to be certain to keep all the configurations in order so that the quirky stuff like "scratch near arm" or "flip far hair" are all BEFORE "stand still" and "walk" and then you can add your "writhe" configurations remembering to follow the exact naming convention already established.

Speaking of "Naming conventions", there were a few comments about variable and class naming conventions in this and another article, I rebutted any attempt to sway me towards following Microsoft-standards but have conceded on the name of the class which I changed from classSpriteForm to the more apt FormSprite. Also, in the same light, I went and changed the name of the limb variable which allows Jessica to wriggle about a point other than the Master-Block which was something like cStationaryLimb instead of what I'm calling it now limStationary, which may be easier to read for anyone who has worked with the classSprite form where all limb variables have a "lim" prefix.

And you can just look at her go...

History

  • A few weeks ago, I put this together for the hell of it.
  • Then yesterday (Oct. 12, 2010), I got to thinking and made it better using PerPixel Alpha Blend.
  • And tonight(Oct. 13, 2010), I was tinkering with the code while she was wandering about and got in my way and I decided to make a way to grab her and move her around.
  • (Oct. 14, 2010) Added ColorOutline property
  • (Oct. 18, 2010) Added throw, new menu & play-pen

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)