views:

502

answers:

6

I have a Chat program written in Delphi7. As a special "effect" we display a few bullet holes when a particular gunshot sound file is played. We did this by drawing a new form in the shape of the bmp image file of the bullet hole, with a timed delay of a few secs for it to be visible and then fade away.

All of this works, however, while the bullet hole images are onscreen, the program is effectively locked up... returning focus back to the user when the last of the images has faded away.

My programmer isn't real well versed in graphics and believes this is just the price you have to pay to get this effect, but I'm hoping that's not quite true... any suggestions of a better way to randomly display bullet hole images across the screen?

+3  A: 

It's hard to say without seeing code and knowing exactly how your opening/loading the second form, but it sounds like you are opening them with ShowModal which will lock the parent form until the modal form returns a result.

If that is the case then you can simple open it with the Show method and then set the focus back to the main form like so.

procedure TForm1.Button1Click(Sender: TObject);
   var obj:TForm2;
begin
    obj := TForm2.Create(nil);
    try
        obj.FormStyle := fsStayOnTop;
        obj.show;
        Self.SetFocus; //set focus back to the form1
    except
        FreeAndNil(obj);
    end;
end;

The above also amuses that you are creating the form dynamically at runtime and that the second form is responsible for freeing itself.

Re0sless
I have not tried this, but won't Form2 end up hidden by Form1 with this approach?
Argalatyr
not if you set it to be always on top. (Not sure if D7 can do that, but later versions can.)
Mason Wheeler
+1 then, but someone (ReOsless or someone with high rep) should edit the code with that property setting.
Argalatyr
The VCL's PopupMode and PopupParent properties can be used to fix a Form right in front of the MainForm. The BulletHole Form wouldn't be transparent for the Mouse though.
Henk Holterman
I was thinking that as it was a chat client it would not take up the whole screen, and the bullet holes would be else were one the screen (ie not over the chat window), but your right about stayOnTop so i have added that to the code.
Re0sless
You can also just manipulate the z-order of the bullet hole form so it is just above the main form and not on top of all forms.
Jim McKeeth
A: 

I believe what is needed would be to use the SetWinRegion method to draw an outline around the "bullet hole". That way they can be clicked thru, but the holes themselves would be floating on top (provided the previous answer of fsStayOnTop is used).

Even a flash image would require a form of some type to act as the container for the image. It all comes down to creating a window and blasting the image to that window. It doesn't have to "lock" the screen, if you set it to always on top and just "SHOW" it (not ShowModal) it should appear and float. I would then use a timer to control when it is destroyed. The advantage of the SetWinRegion, is you can create a window that has an irregular shape, and the only place the user would not be able to click would be the portion of your window which contained the actual form (inside the region).

skamradt
Thanks for the responses everyone... I was hoping for a possibly different approach? like a separate thread or a different way to show images than to draw a form, Like displaying a flash image of the bullethole or something? suggestions/ideas would be most appreciated... there are other effects I wold like to implement but can't if it's going to lock up the screen.. again thanks to everyone.. this is my first time using this and it's amazing you all take the time and effort to help out like this.. Mark Gundywww.magchat.com
MarkGundy
+1  A: 

Opening a second form really is not the way to do this. Perhaps a picture control with a partially transparent image sized to just be bigger than your bullet hole that you could place anywhere on the form. If you can't do a transparent control, you could take a snapshot of that section of the form and place the bullet hole on top.

  1. When the bullet should appear, you make the picture visible and bring to front
  2. Start a timer for two seconds and tell the picture control to refresh
  3. When the timer goes off, hide the bullet picture

If you want to animate the fade out, just replace the picture with different ones at different times using the timer going off at different times

  • At 0 seconds make picture visible
  • At 1.5 seconds replace image with a faded version of the picture
  • At 2 seconds hide the picture

Using this, there is no need for another form, and the app remains responsive.

Jeff Cuscutis
A: 

Here's a solution that works. You'd want to wrap the firing process into a timer or some other event driven process.

Form Definition:

  TForm1 = class(TForm)
    btnTurnOn: TButton;
    ListBox1: TListBox;
    btnTurnOff: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnTurnOnClick(Sender: TObject);
    procedure btnTurnOffClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    F:TForm;
    Bullet:TBitmap;
    x,y:integer;

    procedure SubFormPaint(Sender: TObject);
  end;

Code:

procedure TForm1.btnTurnOnClick(Sender: TObject);
begin
if F=nil then
   begin
   F:=TForm.Create(self);
   F.Parent:=self;
   F.FormStyle:=fsStayOnTop;
   F.BorderStyle:=bsNone;
   F.Left:=x;
   F.Top:=y;
   F.Width:=Bullet.Width;
   F.Height:=Bullet.Height;
   F.OnPaint:=Self.SubFormPaint;
   F.Show;
   end
else
   begin
   inc(x,5);
   inc(y,5);
   F.Left:=x;
   F.Top:=y;
   F.Invalidate;
   end;
end;

procedure TForm1.btnTurnOffClick(Sender: TObject);
begin
F.Free;
F:=nil;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
F:=nil;
Bullet:=TBitmap.Create;
Bullet.LoadFromFile('C:\source\glyfx\glyfx\Emoticon\BMP\16x16\wink_16.bmp');
x:=1;
y:=1;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Bullet.Free;
end;

procedure TForm1.SubFormPaint(Sender: TObject);
begin
if F<>nil then
   F.Canvas.Draw(1,1,Bullet);
end;
Marshall Fryman
A: 

If the animations of bullet holes are written as a loop that executes until done then that might be the issue. If so, then try inserting Application.ProcessMessages somewhere in the loop to allow other things to happen while the animation is occurring.

dwc
A: 

See http://msdn.microsoft.com/en-us/library/dd183353(VS.85).aspx, it has a good example of the API calls for alpha blending bitmaps together. For each frame of an animation, you'd probably need to grab a copy of the image that represents your main screen, and then blend in your glyphs with this at the appropriate transparency level.

As previously posted, you could do Application.ProcessMessages repeatedly, although I prefer doing it backwards and using PostMessage to trigger the next frame of the animation.

You'd use ProcessMessages in a loop, but that call could cause other things to occur while you're in that loop - window moving, minimizing, closing, or triggering your animation event again - before the first animation is finished. This is all fine as long as you're mindful that you're not always necessarily operating within the scope of just your loop.

For that reason I prefer having each frame of animation do a PostMessage to trigger the next frame. It just helps me keep things in perspective.

Kroden