Kollisionserkennung
-
Also ich habe eine Blocks-variante programmiert, und habe allerdings bei der Kollisionserkennung ein Problem. Wenn der Ball nämlich von unten her den Block trifft, und der Winkel relativ flach ist, dann wird die Richtung nicht geändert.
Ich habe folgenden Code geschrieben, wobei die Variable daweg ein bool-Array ist, indem gespeichert ist, ob ein Block vorhanden ist oder nicht. BlocksAnzahl ist die Anzahl vorhandener Blöcke, StepX die Bewegung in X-Richtung und StepY die Bewegung in Y-Richtung, Block_Height ist die Höhe eines Blockes und die funktionen RY() und RX() geben die Koordinaten des Sprites zurück und TestCollide(Sprite* Sprite) gibt zurück ob eine Kollision aufgetreten ist:int relx; int rely; float xkoor; float m; float t; int y; for(int i=0; i<BlocksAnzahl; i++) { if(daweg[i]) { if(Ball->TestCollide(&Blocks[i])) { relx = Ball->RX() + Ball_Durchmesser - Blocks[i].RX(); y = (Ball->StepY < 0) ? Block_Height+Ball_Durchmesser : 0; rely = Blocks[i].RY() - (Ball->RY() + Ball_Durchmesser); //Formel: y = mx + t if(Ball->StepX != 0) { m = (float(Ball->StepY / Ball->StepX)); t = rely - m*relx; xkoor = (y - t) / m; } else { xkoor = (float)relx; } if(Ball->StepY < 0) { //Ball kommt von unten if((xkoor <= Block_Width+Ball_Durchmesser) && (xkoor >= 0)) { //Ball hat unten getroffen Ball->StepY = abs(Ball->StepY); } else if(Ball->StepX < 0) { //Ball hat rechts getroffen Ball->StepX = abs(Ball->StepX); } else { //Ball hat links getroffen Ball->StepX = -abs(Ball->StepX); } } else { //Ball kommt von oben if((xkoor <= Block_Width+Ball_Durchmesser) && (xkoor >= 0)) { //Ball hat oben getroffen Ball->StepY = -abs(Ball->StepY); } else if(Ball->StepX < 0) { //Ball hat rechts getroffen Ball->StepX = abs(Ball->StepX); } else { //Ball hat links getroffen Ball->StepX = -abs(Ball->StepX); } } daweg[i] = false; return; } } }
Ich hoffe, das ihr mir helfen könnt.
Ich bedanke mich schonmal im Vorraus.
-
Hab eigentlich grad keine Lust mir den ganzen Code durchzulesen. Aber Du kannst den Code auf jeden Fall vereinfachen. Vielleicht siehst Du dann den Fehler leichter.
else if(Ball->StepX < 0) { //Ball hat rechts getroffen Ball->StepX = abs(Ball->StepX); } else { //Ball hat links getroffen Ball->StepX = -abs(Ball->StepX); } Du machst extra ne Fallunterscheidung, ob > oder < 0, dann nimmst Du den Absolutwert und dann ein Minus dazu, falls es vorher >0 war. das geht auch einfacher! Ball->StepX = -Ball->StepX; erledigt die Aufgabe der obigen Codezeilen. denn: wenn newx < 0: newx = abs(x) = -x, da x ja kleiner 0 war. wenn newx >= 0: newx = -abs(x) = -x, da x=abs(x) bei x>=0 also berechnest Du in Wirklichkeit nur newx = -x;
Wenn ich das recht gesehen habe, dann kannst Du auf diese Art und Weise Deinen Code signifikant verkürzen. Mach das doch einfach mal, vielleicht wird's dann übersichtlicher und Du findest den Fehler ganz allein, ansonsten meld Dich noch mal und poste den verkürzrten Code.
MfG Jester
-
Ich seh' da int und float Werte...
Tust Du die ineinander casten!?!
Nich' daß es an einfachen Rundungsfehlern liegt...
-
Hallo,
Also tut mir leid, dass ich nicht eher zurückschreibe, aber ich hatte viel zu tun. Also hier die Neue gekürzte Version der Kollisionserkennung:void Block::Collision(Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken) { float y; float x; float m; for(int i=0; i<BlocksAnzahl; i++) { if(daweg[i]) { //Formel: y=mx+t //für die linke Seite x = (float)Blocks[i].RX(); if(Ball->StepX==0) { return; } m = (float(Ball->StepY / Ball->StepX)); y = m*x; if((y==(float(Ball->RY()+Ball_Durchmesser/2))) && (x==(float(Ball->RX()+Ball_Durchmesser/2)))) { //er hat links getroffen Ball->StepX = -Ball->StepX; daweg[i] = false; continue; } //für die rechte Seite x = (float)Blocks[i].RX()+Block_Width; y = m*x; if((y==(float(Ball->RY()+Ball_Durchmesser/2))) && (x==(float(Ball->RX()+Ball_Durchmesser/2)))) { //er hat rechts getroffen Ball->StepX = -Ball->StepX; daweg[i] = false; continue; } //für die obere Seite y = (float)Blocks[i].RY(); x = y/m; if((y==(float(Ball->RY()+Ball_Durchmesser/2))) && (x==(float(Ball->RX()+Ball_Durchmesser/2)))) { //er hat oben getroffen Ball->StepY = -Ball->StepY; daweg[i] = false; continue; } //für die untere Seite y = (float)Blocks[i].RY()+Block_Height; x = y/m; if((y==(float(Ball->RY()+Ball_Durchmesser/2))) && (x==(float(Ball->RX()+Ball_Durchmesser/2)))) { //er hat unten getroffen Ball->StepY = -Ball->StepY; daweg[i] = false; continue; } } } }
-
Oh entschuldigung, das war nur so ein Versuch von mir,
aber hier ist jetzt der richtige Code:void Block::Collision(Sprite* Ball, PointManager *PM, DirectXAudio *DXAudio, Sprite*Funken) { float relx; float rely; float xkoor; float m; float t; float y; for(int i=0; i<BlocksAnzahl; i++) { if(daweg[i]) { if(Ball->TestCollide(&Blocks[i])) { relx = (float(Ball->RX() + Ball_Durchmesser - Blocks[i].RX())); y = (Ball->StepY < 0) ? (float(Block_Height+Ball_Durchmesser)) : (float)0; rely = (float(Blocks[i].RY() - (Ball->RY() + Ball_Durchmesser))); //Formel: y = mx + t if(Ball->StepX != 0) { m = (float(Ball->StepY / Ball->StepX)); t = rely - m*relx; xkoor = (y - t) / m; } else { xkoor = (float)relx; } if(Ball->StepY < 0) { //Ball kommt von unten if((xkoor <= (float(Block_Width+Ball_Durchmesser))) && (xkoor >= 0)) { //Ball hat unten getroffen Ball->StepY = abs(Ball->StepY); } else { //Ball hat rechts getroffen Ball->StepX = -Ball->StepX; } } else { //Ball kommt von oben if((xkoor <= (float(Block_Width+Ball_Durchmesser))) && (xkoor >= 0)) { //Ball hat oben getroffen Ball->StepY = -abs(Ball->StepY); } else { //Ball hat rechts getroffen Ball->StepX = -Ball->StepX; } } daweg[i] = false; PM->AddPunkte(10); DXAudio->PlaySoundEffect(".\\audio\\treffer.wav"); Funken->SetXY(Blocks[i].RX() + Block_Width/2 - Funken->GetWidth()/2, Blocks[i].RY() + Block_Height/2 - Funken->GetHeight()/2); Funken->ResetCurrentFrame(); return; } } } }
-
Du hast ja immer noch ein paar unnötige abs drin!
Vielleicht liegt es daran, dass die Schrittweite so groß ist, dass der Ball in Frame1 dem Block ist und in Frame2 schon dahinter
-
hi, ich hab mich auch mal dem problemchen angenommen und bin auf folgende lösung gekommen:
//////////////////////////////////////////////////////// //Prüfen auf Kollision //////////////////////////////////////////////////////// BOOL Object::TestCollision(Object* Object,int distanceX,int distanceY) { //Prüfen, ob das Objekt innerhalb eines bestimmten Bereiches liegt, in // dem Kollisionen auftreten können. Bereich ist das Rechteck // distanceX*distanceY if(((abs(CenterX-Object->CenterX))<=distanceX) && (abs(CenterY-Object->CenterY)<=distanceY)) { //Objekt trifft auf eine Ecke: rechts unten if(((Object->x==x2)&&(Object->y==y2)) &&(Object-StepX<0) &&(Object->StepY<0)) { CollisionPosition = CORNER_COLLISION; return TRUE; } //Objekt trifft auf eine Ecke: rechts oben else if(((Object->x==x2)&&(Object->y2==y)) && (Object->StepX<0) &&(Object->StepY>0)) { CollisionPosition = CORNER_COLLISION; return TRUE; } //Objekt trifft auf eine Ecke: links oben else if(((Object->x2==x)&&(Object->y2==y)) && (Object->StepX>0) &&(Object->StepY>0)) { CollisionPosition = CORNER_COLLISION; return TRUE; } //Objekt trifft auf eine Ecke: links unten else if(((Object->x2==x)&&(Object->y==y2)) && (Object->StepX>0) &&(Object->StepY<0)) { CollisionPosition = CORNER_COLLISION; return TRUE; } // Objekt kommt von oben else if(((Object->CenterY+Object->FrameHeight/2)== (CenterY-FrameHeight/2)) && (Object->CenterY<(CenterY-FrameHeight/2)) && ( ((Object->x2>=x)&&(Object->x2<=x2)) || ((Object->x<=x2)&&(Object->x>=x)))) { Object->CollisionPosition=VERTICAL_COLLISION; Beep(500,30); //Nur zum Test return TRUE; } // Objekt kommt von unten else if((((Object->CenterY-Object->FrameHeight/2)== (CenterY+FrameHeight/2)) && (Object->CenterY>(CenterY+FrameHeight/2)) && (((Object->x2>=x)&&(Object->x2<=x2)) || ((Object->x<=x2)&&(Object->x>=x))))) { Object->CollisionPosition=VERTICAL_COLLISION; Beep(500,30); return TRUE; } //Wenn Objekt nicht von Oben oder Unten und auch keine Ecke //trifft, es aber trotzdem // im Kollisionsbereich liegt, muss es entweder von links oder //rechts treffen else { Object->CollisionPosition = HORIZONTAL_COLLISION; Beep(800,30); return TRUE; } } //wenn es nicht imKollisionsbereich liegt, keine Kollision (logisch) else { return FALSE; } return FALSE; }
Ich reagiere auf die Kollision, indem ich einer int-Variable CollisionPosition
verschieden Werte für vertikale(von oben oder unten), horizontale oder Eck-Kollisionen zuweise. Je nachdem, welchen Wert sie hat, wird in der Funktion für die Bewegung des Balls entweder bei
a) vertikaler Kollision: der "y-Geschwindigkeit"- des Balls ihr Gegenwert zugewiesen
b) horizontaler Kollision: der "x-Geschwindigkeit"- des Balls ihr Gegenwert zugewiesen
c) Eck-Kollisionen: beiden Geschwindigkeiten ihr jeweiliger Gegenwert zugewiesen.So müsste das eigentlich klappen....
andere Möglichkeit is, den Einfallswinkel zu berechnen.
vielleicht kannst du damit ja was anfangen.
Gruß vom Konstantin
-
aso, nochwas:
CenterX is der X-Wert des Sprite-Mittelpunktes
CenterY is der Y-Wert " " "x2/y2 is x+FrameWidth bzw y+FrameHeight.
so, das wars glaub ich
tschüss