Breakout – Del 3

Vi har nu kommit så långt så det är dags för att rita ut lite rutor (tiles) som vi ska pricka med bollen. Därför måste vi även hantera kollisioner mellan bollen och rutorna.

Rita ut rutorna (tiles)

För att kunna rita ut en ruta så måste vi först skapa grafiken för den. Vi kommer här att göra det enkelt för oss genom att bara ha grafik för en (vit) ruta som vi sedan ändrar färg på med hjälp av blending. Vi skapar en enkel vit ruta med måtten 40x20 pixlar.

bild

Bilden är förstorad och med tillagd skugga för att illustrera rutan som vi skapat. Filen finns att ladda hem i tile.zip nedan.

Vi kopierar filen tile.jpg till content mappen och lägger till den i vårt projekt precis som med tidigare grafik.

Vi lägger till följande globala variabler:

Tillägg av variabler

        int game_speed;
        Texture2D tile_gfx;
        List<Vector2> tile_position = new List<Vector2>();
        List<int> tile_type = new List<int>();

Variabeln tile_gfx av typen Texture2D är inget nytt. Däremot så skapar vi två "listor", alltså av typen List som vi döper till tile_position samt tile_type. Vi skapar även variabeln game_speed som vi ska använda oss av längre fram för att öka hastigheten och därmed svårighetsgraden för spelet.

En List är som en array men betydligt mer användbara eftersom de är dynamiska och man kan enkelt lägga till (med .Add()) och ta bort element i listan. Syntaxen för att skapa en "lista" är:


List<typ> namn = new List<typ>();

Vår lista tile_position är alltså av typen Vector2 och vi kommer att använda oss av den för att spara alla rutornas positioner. Listan tile_type däremot är av typen int och den kommer vi att använda oss av för att enkelt kunna sätta en siffra beroende på vilken typ av ruta det är (vi planerar redan nu lite framåt nämligen).

Vi gör följande tillägg till LoadContent():

Tillägg till LoadContent()

            game_speed = 1;
            tile_gfx = Content.Load<Texture2D>("tile");

            for (int j = 0; j < 6; j++)
            {
                for (int i = 0; i < 11; i++)
                {
                    tile_position.Add(new Vector2((i * 40) + 200, (j * 20) + 100));
                    tile_type.Add(j);
                }
            }

Vi laddar in grafiken till vår "tile". Sedan passar vi på att med hjälp av en nästlad for-loop rita ut en rektangek med 11 x 6 rutor (ytterloopen rullar 6 gånger och innerloopen rullar 11 gånger). Mer information om nästlade loopar hittar ni här. Vi lägger till en ny Vector2 för varje ruta och ger den en position. Vi börjar rita ut rutorna 200 pixlar från vänsterkanten (i X-led) och 100 pixlar från ovankanten (i Y-led) sedan måste varje position anpassas efter rutornas storlek, därav multiplikationen av 40 resp 20. Vi passar även på att lägga till typen till listan tile_type med tile_type.Add(j);. Typen blir alltså detsamma som radnumret i detta fall.

Vi gör även följande tillägg till Draw() (OBS mellan spriteBatch.Begin och spriteBatch.End):

Tillägg till Draw()

            for (int i = 0; i < tile_position.Count; i++)
            {
                spriteBatch.Draw(tile_gfx, tile_position[i], Color.White);
            }

Med en enkel for-loop så stegar vi igenom alla rutorna i vår lista. Listans längd får vi med tile.position.Count som är väldigt smidig (särskilt om vi börjar plocka bort rutor senare). I loopen så ritas sedan resp. ruta ut.

Har vi gjort detta så borde resultatet se ut såhär när vi provkör vårt projekt.

bild

Eftersom alla rutor ritas upp intill varandra och med samma färg så ser det bara ut som en stor rektangel nu.

Kollisionshantering med tiles

För att kolla ifall bollen kolliderar med en tile samt hur den kolliderar och för att vidtaga lämplig åtgärd så lägger vi till följande till Update() (OBS inne i if-satsen som kollar ifall game_state==1):

Tillägg till Update()

for (int i = 0; i < tile_position.Count; i++)
{
Rectangle tileBox = new Rectangle((int)tile_position[i].X, (int)tile_position[i].Y, 40, 20);
if (tileBox.Intersects(ballBox))
{
	//Vi har en kollision här!

	ball_position -= ball_speed * game_speed; //backa bollen

	if (ball_position.X + 6 >= tile_position[i].X && ball_position.X + 6 <= tile_position[i].X + 40)
	{
		ball_speed.Y = -ball_speed.Y; //kolision uppifrån eller nerifrån
	}
	else
	{
		ball_speed.X = -ball_speed.X; //kollisions från sidan
	}
	tile_position.RemoveAt(i);
    tile_type.RemoveAt(i);
	i--;
	break;
}
}

Tillägget måste ske efter det att vi kollar kollisionen mellan bollen och paddeln eftersom vi kommer att återanvända ballBox som är den rektangel runt bollen som vi skapade i förra delen.

Vi börjar med en for-sats som stegar igenom alla rutor som vi har i vår lista. I varje steg så ritar vi en rektangel kring aktuell ruta, tileBox, och kollar med en if-sats ifall bollens och rutans rektangel kolliderar (korsar) med varandra. Stämmer detta så börjar vi med att "backa" bollen en uppdatering (ball_position -= ball_speed * game_speed;) eftersom om bollen rör sig väldigt snabbt så kan den kollidera med flera rutor samtidigt och då kan det bli lite fel ifall vi inte gör detta.

Sedan kontrollerar vi ifall kollisionen sker i Y-led eller i X-led och ändrar vektorn för bollen därefter. Sist men inte minst så tar vi bort rutan som bollen kolliderar med, tile_position.RemoveAt(i); och tar bort tile-typen ur listan tile_type samt även minska vår räknare (i) med ett.

Provkör vi projektet så har vi nästan ett fungerande spel!

bild

Lämna ett svar

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

Scroll to top