Entwickler-Ecke

Multimedia / Grafik - Canvas Kollisionsabfrage


somebody - So 10.01.10 01:01
Titel: Canvas Kollisionsabfrage
Hallo Leute, für die Schule sollen wir einen kleines Spiel Programmieren. Das ganze soll recht einfach sein, man hat eine Kanone, die man nach links und rechts bewegen kann. Außerdem kann diese auch Kugeln abschießen. Oben befindet sich ein "Raumschiff", welches man treffen kann.
Soweit auch kein problem. Zwei Bedingungen waren noch gegeben, das ganze soll Objektorientiert und mit Canvas gemacht werden. Auch noch kein Problem.

Jetzt ist nur die Frage, wie kann ich mit Canvas eine Kollisionsabfrage machen, also abfragen, ob die Kugel das "Raumschiff" berührt hat? Ich hatte schon probiert es so zu machen, dass ich gucke ob die y koordinaten der kugel und des Raumschiffes nahezu gleich sind, UND ob die x Koordinaten der Kugel und des Raumschiffes nahezu gleich sind, allerdings ist dieser Fall komischerweise nie eingetreten.
Die Kugel ist ein Canvas.Ellipse und das Raumschiff ein Canvas.Rectangle

Habt ihr eine Idde?

Gruß
somebody

P.S: Falls die Infos zu wenig waren, kann ich ja auch noch ein Stück Code posten.


MaPsTaR - So 10.01.10 01:09

Ich würde mir um das Raumschiff ein Rechteck oder anderes "denken" und dann anhand der Koordinaten des Geschosses berechnen, ob es sich in dem Rechteck befindet.

Aber wenn das bei dir nie eingetreten ist...

Am besten wäre du postest mal deinen Code.


somebody - So 10.01.10 01:18

Oki, also hiermit bewege ich die Kugel


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Canvas.Ellipse(Kugel.Bewegen_x(10,Kugel.x),Kugel.Bewegen_y(10,Kugel.y),20 + Kugel.Bewegen_x(10,Kugel.x),20 + Kugel.Bewegen_y(10,Kugel.y));

.
.
.

Function TKugel.Bewegen_Y(AVy: integer; AYalt: integer): Integer;
begin
     FYneu := AYalt + AVy;

     Result := FYNeu;
end;

function TKugel.Bewegen_X(AVx: integer; AXalt: integer): Integer;
begin
     FXneu := AXAlt + AVx;

     Result := FXneu;
end;


Und das Raumschiff bewegt sich so


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Canvas.Rectangle(Raumschiff.Bewegen_x(Lok_x),20,50 + Raumschiff.Bewegen_x(Lok_x), 40);  //Lok_x ist eine Lokale variable, die man erhöht, damit sich das Raumschiff bewegt

.
.
.

function TRaumschiff.Bewegen_X(AVx: integer): Integer;
begin
     FXneu := FXneu + AVx;

     if FXneu >= 800 then   //wenn das Raumschiff aus dem Bildschirm fliegt, dann fängt es vorne wieder an 
     begin
          FXneu := -20;
     end
     else
     begin
          Result := FXneu;
     end;
end;


Und dann hatte ich halt versucht, die Koordinaten zu vergleichen, das sah dann ungefähr so aus.


Delphi-Quelltext
1:
2:
3:
4:
if (Kugel.y <= 8and (Kugel.x = Raumschiff.Bewegen_x(Lok_speicher) then  //Lok_speicher hat den gleichen Wert wie Lok_x, mit dem Unterschied, dass si global ist
begin
   Raumschiff.Destroy;
end;


Ich hoffe es bringt etwas...

Moderiert von user profile iconmatze: Code- durch Delphi-Tags ersetzt


MaPsTaR - So 10.01.10 01:30

Zitat:

Delphi-Quelltext
1:
2:
3:
4:
if (Kugel.y <= 8and (Kugel.x = Raumschiff.Bewegen_x(Lok_speicher) then  //Lok_speicher hat den gleichen Wert wie Lok_x, mit dem Unterschied, dass si global ist
begin
   Raumschiff.Destroy;
end;


Warum bewegst du das Raumschiff beim Testen nochmal?

Welche Felder hat Raumschiff denn?
X- und Y-Koordinate? Width? ...


Delphi-Quelltext
1:
2:
3:
4:
if (Kugel.y <= 8and (Kugel.x > Raumschiff.x) and (Kugel.x < (Raumschiff.x + Raumschiff.width))  then  
begin
   Raumschiff.Destroy;
end;


ub60 - So 10.01.10 01:39

Die grobe Kollision gibt es mit IntersectRect (Schnittpunkt zweier Rechtecke), das "Finetuning" dann mit Masken.

ub60


somebody - So 10.01.10 01:40

Also ich bewege das Raumschiff nochmal, weil ich darüber die x Koordinate des Raumschiffes erhalte. Diese überprüfe ich dann... Man sieht die Bewegung aber nicht, da ich sie ja nicht zeichne.
Und das Raumschiff hat nur die Felder x und y.


MaPsTaR - So 10.01.10 01:53

Aber du müsstest die X-Koordinate doch auch in Lok_Speicher haben, oder?


Delphi-Quelltext
1:
2:
3:
4:
if (Kugel.y <= 8and (Kugel.x > Lok_speicher) and (Kugel.x < (Lok_speicher + 50))  then  
begin
   Raumschiff.Destroy;
end;


Wie kommst du eigentlich auf if (Kugel.y <= 8) ...

Müsste es nicht eher

Delphi-Quelltext
1:
if (Kugel.y <= Raumschiff.y) ...                    

heißen?


Bergmann89 - So 10.01.10 03:04

Hey,

ich würd das so machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
const
  TOLERANCE = 10;

if (Kugel.x + TOLERANCE > Raumschiff.x - TOLERANCE) and
   (Kugel.x - TOLERANCE < Raumschiff.x + TOLERANCE) and
   (Kugel.y + TOLERANCE > Raumschiff.y - TOLERANCE) and
   (Kugel.y - TOLERANCE < Raumschiff.y + TOLERANCE) then
  Raumschiff.Free;

skizze
das Bild zeigt jetzt die 2. überprüfung. Der Punkt sind die Koordinaten un das Viereck die Toleranz.

MfG Bergmann

Moderiert von user profile iconNarses: Bild als Anhang hochgeladen.


somebody - So 10.01.10 11:29

Oki, Danke für die schnellen Antworten. Ich habe es jetzt mit Bergmans Variante versucht und es geht fast ohne Fehler. Das einzige Problem ist, dass wenn ich das Raumschiff getroffen habe, es nicht zerstört wird, sondern noch weiterlebt aber von Anfang an wieder startet. Wenn ich es dann ein Zweites mal treffen will, dann kommt eine Zugriffsverletzung, aber auch nur wenn ich das Raumschiff getroffen habe.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
procedure TForm1.Timer1Timer(Sender: TObject);
const toleranz = 10;
begin
     Timer2.Enabled := false;
     Paintbox1.refresh;

     Canvas.Ellipse(Kugel.Bewegen_x(10,Kugel.x),Kugel.Bewegen_y(10,Kugel.y),20 + Kugel.Bewegen_x(10,Kugel.x),20 + Kugel.Bewegen_y(10,Kugel.y));

     Canvas.Rectangle(Lok_speicher,20,50 + Lok_speicher, 40);  // hier zeichne ich das Raumschiff immer wieder, da man es sonst durch das Refresh nicht sehen würde. Der Timer ist aber 
                                                               // aus(siehe oben), das heíßt das Raumschiff bewegt sich solange nicht(Das soll das treffen erleichtern)
     Kugel.Y := Kugel.Y - 10;

     if Kugel.y <= 8 then                                     // Das Raumschiff bewegt sich nur in x Richtung, und die Y-Koordinate des Raumschiffs(8 [auch gleichzeitig der Spielfeldrand]) bleibt 
     begin                                                    // immer gleich
          if (Kugel.x + toleranz > Raumschiff.x - toleranz) and
             (Kugel.x - toleranz < Raumschiff.x + toleranz) then
          begin
               Raumschiff.Free;                              // hier hab ich es auch schon mit .Destroy versucht, der Fehler bleibt aber
               Timer2.Enabled := false;
          end
          else
          begin
               Timer2.Enabled := true;
               PaintBox1.Refresh;
               Kugel.Y := 650;
               Timer1.Enabled := false;
          end;
     end;
end;


Ich finde den Fehler irgendwie nicht, könnt ihr mir da weiterhelfen?

EDIT: Ich erzeuge das Raumschiff einfach nochmal, nachdem es getroffen wurde, damit nach einmal schießen nicht gleich alles vorbei ist. Das alte wird natürlich auch zerstört um speicher zu sparen.
Danke für eure hilfe!

Moderiert von user profile iconmatze: Code- durch Delphi-Tags ersetzt


F34r0fTh3D4rk - So 10.01.10 12:12

user profile iconBergmann89 hat folgendes geschrieben Zum zitierten Posting springen:
Hey,

ich würd das so machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
const
  TOLERANCE = 10;

if (Kugel.x + TOLERANCE > Raumschiff.x - TOLERANCE) and
   (Kugel.x - TOLERANCE < Raumschiff.x + TOLERANCE) and
   (Kugel.y + TOLERANCE > Raumschiff.y - TOLERANCE) and
   (Kugel.y - TOLERANCE < Raumschiff.y + TOLERANCE) then
  Raumschiff.Free;

user defined image
das Bild zeigt jetzt die 2. überprüfung. Der Punkt sind die Koordinaten un das Viereck die Toleranz.

MfG Bergmann


ich würde die Toleranz auf der linken Seite weglassen. Sonst könnte man auch gleich schreiben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
const
  TOLERANCE = 10;

if (Kugel.x > Raumschiff.x - TOLERANCE*2and
   (Kugel.x < Raumschiff.x + TOLERANCE*2and
   (Kugel.y > Raumschiff.y - TOLERANCE*2and
   (Kugel.y < Raumschiff.y + TOLERANCE*2then

und dann lässt man das *2 gleich weg und nimmt lieber ne größere Toleranz, falls es nicht reicht.
Das Projektil würde ich mir eher als Punkt vorstellen und das Raumschiff als Rechteck (in diesem Falle wäre es ein Quadrat).
Dann hast du halt nur noch die Punkt-In-Quadrat Prüfung zu machen.

Also eher wie das hier:

Delphi-Quelltext
1:
2:
3:
4:
5:
if (Kugel.x > Raumschiff.x - Raumschiff.breite) and
   (Kugel.x < Raumschiff.x + Raumschiff.breite) and
   (Kugel.y > Raumschiff.y - Raumschiff.hoehe) and
   (Kugel.y < Raumschiff.y + Raumschiff.hoehe) then
  // KOLLISION

Und das mit der globalen Variable, in der du zusätzlich die Koordinate speicherst, ist nicht im Sinne der Objektorientierung.
In der Regel verzichtet man auf globale Variablen.
Ich würde das zu testende Projektil der Kollisions Funktion des Raumschiffs übergeben (oder umgekehrt):

Delphi-Quelltext
1:
TRaumschiff.pruefeKollisionMit(projektil: TProjektil): boolean;                    

oder:

Delphi-Quelltext
1:
TProjektil.pruefeKollisionMit(raumschiff: TRaumschiff): boolean;                    

Oder was hat es genau mit diesem Lok_Speicher auf sich?

mfg


Bergmann89 - So 10.01.10 15:29

Hey,

wie erzeugst du denn das Raumschiff, bzw wo? Weil das steht ja nich im Codeausschnitt.

MfG Bergmann.


somebody - Mo 11.01.10 12:20

Das Raumschiff erzeuge ich in formcreate. Danke zwar für die weiteren Fragen, aber das Problem hat sich schon erledigt. geht mittlerweile alles...
aber trotzdem danke für die hilfe ;)


Florian H. - Mo 11.01.10 18:35

http://forum.delphi-treff.de/showthread.php?p=204124#post204124

Delphi-Treff Ufo-Spiel