Mega Code Archive

 
Categories / Delphi / Hardware
 

How to detect simple collision and transparency

Title: How to detect simple collision and transparency function CheckBoundryCollision( R1, R2 : TRect; pR : PRect = nil; OffSetY : LongInt = 0; OffSetX : LongInt = 0): boolean; {overload;} // Simple collision test based on rectangles. begin // Rectangle R1 can be the rectangle of the character (player) // Rectangle R2 can be the rectangle of the enemy if( pR nil ) then begin // Simple collision test based on rectangles. We use here the // API function IntersectRect() which returns the intersection rectangle. with( R1 ) do R1:=Rect( Left+OffSetX, Top+OffSetY, Right-(OffSetX * 2), Bottom-(OffSetY * 2)); with( R2 ) do R2:=Rect( Left+OffSetX, Top+OffSetY, Right-(OffSetX * 2), Bottom-(OffSetY * 2)); Result:=IntersectRect( pR^, R1, R2 ); end else begin // Simple collision test based on rectangles. We can also use the // API function IntersectRect() but i believe this is much faster. Result:=( NOT ((R1.Bottom - (OffSetY * 2) .Top + OffSetY) or(R1.Top + OffSetY R2.Bottom - (OffSetY * 2)) or( R1.Right - (OffSetX * 2) .Left + OffSetX) or( R1.Left + OffSetX R2.Right - (OffSetX * 2)))); end; end; (* Also you can make an overloaded function with the same name based on handles (to make it easier). *) function CheckBoundryCollision( h1, h2 : Hwnd; pR : PRect = nil; OffSetY : LongInt = 0; OffSetX : LongInt = 0): boolean; {overload;} var R1 : TRect; R2 : TRect; begin FillChar( R1, SizeOf( R1 ), 0 ); FillChar( R2, SizeOf( R2 ), 0 ); // Note: You will get here the REAL screen coordinates. // It doesn't matter if a control is placed inside a panel or something. GetWindowRect( h1, R1 ); GetWindowRect( h2, R2 ); Result:=CheckBoundryCollision( R1, R2, pR, OffsetY, OffSetX ); end; As an example, the above function may be used in a (threated timer) loop like this to check the player against multiple enemies: for i := 0 to Length( Enemies ) - 1 do if( CheckCollision( Player.Handle, Enemies[i].Handle, nil, 4, 4 )) then GameOver; (* Bottom line is that usually it's enough if it "looks" real, and less important that it "is" real. It's a balance between how exact we want it, and how fast we need it to perform. I have used this methods in a simple game and it just works fine. Let's talk about transparency. You can use Regions to create special shaped Wincontrols. For example: A TPanel with a TImage on it (Panel1 and Image1). You can shape the panel to the image using the following examples: *) function CreateRegion( Bitmap : TBitmap; Rect : TRect; TransColor : TColor ) : hRgn; var i1, Count : LongInt; x,y : LongInt; c1 : Cardinal; c,t : TColor; Msg : TMsg; begin Result:=0; if( NOT Assigned( Bitmap )) then Exit; Count :=0; {if( TransColor = clAutomatic ) then }t:=Bitmap.Canvas.Pixels[ Rect.Left, Rect.Top ] {else t:=TransColor}; with( Bitmap.canvas ) do for y := Rect.Top to Rect.Bottom do begin // Sort of the same like Application.ProcessMessages but doesn't require // the bulky Forms unit. PeekMessage( Msg, 0, 0, 0, PM_REMOVE ); x:=Rect.Left; while( x = Rect.Right ) do begin C:=Pixels[ x, y ]; if( C t ) then begin i1 := x; while( C t ) do begin Inc(i1); C:=Pixels[ i1, y ]; if( i1 = Rect.Right ) then Break; end; c1:=CreateRectRgn( x-Rect.Left,y-Rect.Top, i1-Rect.Left, (y-Rect.Top) + 1 ); if( count = 0 ) then Result:=c1 else begin CombineRgn( Result, Result, c1, RGN_OR ); DeleteObject( c1 ); end; Inc( Count ); x := i1; Continue; end; Inc(x); end; end; if( Count = 0 ) and ( Result 0 ) then begin DeleteObject( Result ); Result:=0; end; end; // Put this somewhere in your code (for example in the FormCreate event) begin Image1.Left:=0; Image1.Top:=0; Image1.AutoSize:=TRUE; Panel1.BevelInner:=bvNone; Panel1.BevelOuter:=bvNone; Panel1.Width:=Image1.Width; Panel1.Height:=Image1.Height; // Create a region of the bitmap that is inside the image and set the region for the window. // Note: In this example the transparent color is black. Make sure the transparent (background) color is // black! To be sure that you using the right color, you can also try this: Image1.Picture.Bitmap.Pixels[ 0, 0 ] with( Panel1 ) do SetWindowRgn( Handle, CreateRegion( Image1.Picture.Bitmap, Rect( 1, 1, Width, Height ), clBlack )); end; (* Note: When you move the panel it is possible that the bitmap starts to flicker. You can create an Message handler for the message WN_EREASEBKGND to avoid the flicker. See the help of Delphi how to create new components and/or message handlers if do not know. Here is an example: procedure TMyPanel.WMEraseBkgnd(var Message: TWmEraseBkgnd); begin if( csDesigning in ComponentState ) then inherited else Message.Result:=1; // Fake erase end; *) (* NOTE: Absolutly no warranty to this code. This is just an example. It is possible that you need to make some improvements to the code to give it a more proffesional behaviour. This article was aimed towards beginners wanting get into game programming, I??ll hope it was helpful. Regards and good luck, Erwin Haantjes. *)