Kollisioner med rektanglar

Kollisionshantering och beräkningar är självklart en viktig del i spel. Den modell för kollisioner som presenteras i denna artikel lämpar sig såklart inte i alla situationer utan snarast i de allra enklaste av fall. Planerar du att göra spel som t.ex. Pong, Breakout eller Space Invaders däremot så fungerar modellen väl.

I spel så har vi oftast en massa föremål som ska ritas ut. Dessa föremål måste ha variabler/data som beskriver:

  • Var på skärmen är föremålet (X- och Y-koordinat i form av pixlar gärna av typen double för fin precision)
  • Hastighet som bestämmer åt vilket håll vi ska röra oss åt. Oftast två variabler även här i X- och Y-led (t.ex. SpeedX, SpeedY). Tänk på att hastigheten även kan vara negativ. Ex:
  • SpeedX = 20 och SpeedY = 20 flyttar föremålet snett ned till höger på skärmen.
  • Grafik som bestämmer hur föremålet ska se ut. Här använder vi datatypen Texture2D som kommer från XNA (DirectX).

Utöver detta så behöver vi också veta när två föremål kolliderar med varandra. Vi börjar med ett exempel på ett rymdskepp och en meteor. Vi utgår från att du redan kan hantera positioner, hastigheter och grafik och kan rita ut något liknande:

bild

Kollisioner

Hur ska vi nu kunna avgöra om dessa två föremål kolliderar eller ej? Till vår hjälp ska vi nu använda klassen Rectangle som du tidigare använt vid uppritning. Denna datatyp är perfekt för att skapa rektanglar runt våra föremål. Datatypen finns i namnutrymmet System.Drawing, men om du använder XNA så finns det redan en datatyp vid namn Rectangle. Problemet är bara att de fungerar lite olika men vi kommer att använda oss av XNA i detta exempel.

Okej, vi testar att skapa två variabler av typen Rectangle som ska "rama in" våra föremål.


Rectangle playerBox = new Rectangle( (int)playerX, (int)playerY, 48, 60);
Rectangle meteorBox = new Rectangle( (int)meteorX, (int)meteorY, 110, 110);

Vi anger fasta värden på bredd och höjd (48x60 för player osv.). Variablerna playerX, playerY, meteorX och meteorY har vi sedan tidigare för att hålla reda på var på skärmen föremålen är. Dessa är av typen double. Därför är vi tvungna att konvertera värdet på variablerna till int när vi skapar rektangeln.

Detta kallas typecasting!

Det går till som i exemplet ovan att man skriver (int) framför variabeln som man vill "kasta om" till en annan datatyp temporärt. Detta gör man främst med talvariabler såsom int, float, char, byte och double. Det går t.ex. inte med variabler av typen string.

Åter till våra boxar. Kommer vi att se dem på skärmen? Nej, självklart inte eftersom vi inte kommer att rita ut dem. Det är också hela poängen med dessa rektanglar, de kommer att användas för att avgöra om vi har kollisioner eller inte. Jag har ändå valt att rita ut rektanglarna så att ni får en bild av hur det fungerar.

bild

Hur avgör jag nu om de kolliderar?

Jo, det finns en inbyggd funktion i datatypen Rectangle som gör att en rektangel kan avgöra om den kolliderar med en annan rektangel. Funktionen kallas för Intersects och svarar ja eller nej. På dataspråk kallas det för en bool och har värdet true eller false.

Det kan se ut såhär:


if (playerBox.Intersects(meteorBox))
{
    //Vi har en kollision här!
}

På så sätt vet vi när vi kolliderar. Har du flera föremål, kanske i en List, så får du göra en loop för att sköta kollideringen mellan t.ex. player och alla andra föremål. Du måste då även ha alla rektanglar i en lista av rektanglar för att det ska fungera smidigt.

Uppdatering av rektanglar

Vi får inte glömma att flytta med rektanglarna varje gång vi flyttar våra föremål. Annars blir de ju genast värdelösa! Rektanglarna måste hela tiden spegla var föremålen finns. Om vi inte uppdaterar så kommer det att se ut såhär:

bild

Kommer vi att veta att vi har kolliderat? Nej, vi måste göra en uppdatering av rektanglarna hela tiden av typ:


playerBox.X = (int)playerX;
playerBox.Y = (int)playerY;
meteorBox.X = (int)meteorX;
meteorBox.Y = (int)meteorY;

Notera typecastingen igen.

Nu kommer det att se ut som:

bild

Den överlappande rektangeln har jag markerat med rött. Vi kan nu såklart avgöra att vi har kolliderat. Vad vi ska göra när vi väl kolliderar är en annan historia.

Överlappande rektanglar

Tyvärr finns det ingen funktion i XNA för att räkna fram den överlappande rektangeln när vi väl kolliderar, som den röda rektangeln i bilderna. En funktion som räknar ut den överlappande rektangeln skulle kunna se ut såhär:

Funktion: Räkna ut överlappande rektangel

public static Rectangle Intersection(Rectangle r1, Rectangle r2)
{
    int x1 = Math.Max(r1.Left, r2.Left);
    int y1 = Math.Max(r1.Top, r2.Top);
    int x2 = Math.Min(r1.Right, r2.Right);
    int y2 = Math.Min(r1.Bottom, r2.Bottom);

    if ((x2 >= x1) && (y2 >= y1))
    {
        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    }
    return Rectangle.Empty;
}

DEMO

Koden för att räkna ut den överlappande rektangeln tillsammans används i detta demo. Grafiken som används är:

  • ship.bmp. Grafiken till skeppet.
  • meteor.bmp. Grafiken till meteoriten.
  • red.bmp. Grafiken för det röda överlappande området. Alpha-värdet är sänkt något på denna bild för att skapa den genomskinliga effekten.

bild

Skeppet följer muspositionerna och om det finns en överlappande rektangel så ritas det ut ett rött område.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *

Scroll to top