Multiplayer Tanks – Del 5

Fortsättning från del 4...

SelectingRole

Vi fortsätter med implementationen för nästa gameState, SelectingRole. I detta skede så skall vi som spelare välja ifall vi vill spela som host eller som player. Nu kommer vi in lite på datorkommunikation och som nämnts tidigare så behövs inga djupare kunskaper om nätverk och protokoll för att realisera vårt projekt men det skadar ju inte direkt heller. Vi kommer som hastigast att förklara vissa grundläggande begrepp som man bör känna till när man sysslar med nätverk.

Vi börjar med ordet host som oftast översätts till värd på svenska. Egentligen så räknas alla enheter som kan kommunicera på ett nätverk som en host men ofta så menas en enhet/dator som agerar server och som tillhandahåller någon form av nätverkstjänst. En av de vanligaste sätten att kommunicera via nätverk är med client/server-teknik. Detta innebär att en part agerar server och en annan part agerar klient. Ett exempel på detta är webben där vi med ansluter till en webbserver med klientprogramvara (vår webbläsare). Motsatsen till client/server är peer-2-peer vilket innebär att alla enheter som kommunicerar med varandra är "jämbördiga" och agerar både server och klient.

I vårt projekt så kommer vi ha ha olika roller. Antigen spelar man som host eller som player (vilket är detsamma som klient). Den som spelar som host kommer att agera server och hantera alla händelser i spelet, såsom kollisionshantering mm. Spelar man som player skickas bara information om vad spelaren gör (förflyttning, skjuter etc) till servern som tar ställning till eventuella konsekvenser och skickar tillbaka den informationen. På så sätt blir det enkelt att lägga till fler klienter även om vi till en början skapar ett spel enbart för 2 spelare.

Det är viktigt att tänka på att det alltid är en viss fördröjning vad gäller utbytet av information via nätverket. Därför är det viktigt att endast en spelare (servern) håller koll på viktiga händelser och synkroniserar allt annars kan problem uppstå.

Till en början behövs en skärmbild som informerar spelaren om valmöjligheterna. Vi skapar något i stil med detta:

bild

Ladda hem zip-filen och packa upp den i content-mappen och lägg till bilden precis som vanligt.

Vi gör följande tillägg till Game1.cs: (OBS vi gör tillägg på flera ställen, se kommentarerna)

Tillägg till Game1.cs

//Tillägg av globala variabler
Texture2D selectingRoleGfx;
NetworkSession session;

//Skapa följande två metoder:
void hostSession_GamerJoined(object sender, GamerJoinedEventArgs e)
{
	if (session.RemoteGamers.Count == 1)
	{
		state = GameState.PlayingAsHost;
	}
}
void hostSession_GamerLeft(object sender, GamerLeftEventArgs e)
{
	session.Dispose();
	state = GameState.TitleScreen;
}

//Tillägg till LoadContent()
selectingRoleGfx = Content.Load<Texture2D>("selectingRole_screen");
NetworkSession session;

//Tillägg till Update()
case GameState.SelectingRole:
	if (key.IsKeyDown(Keys.F1))
	{
	//Välj rollen HOST och skapa sessionen
	session = NetworkSession.Create(NetworkSessionType.SystemLink, 1, 2);

	session.GamerJoined += new EventHandler<GamerJoinedEventArgs>(hostSession_GamerJoined);
	session.GamerLeft += new EventHandler<GamerLeftEventArgs>(hostSession_GamerLeft);

	state = GameState.WaitingAsHost;
	}
	if (key.IsKeyDown(Keys.F2))
	{
		state = GameState.WaitingAsPlayer;
	}
        break;

//Tillägg till Draw()
case GameState.SelectingRole:
	spriteBatch.Draw(selectingRoleGfx, new Vector2(0, 0), Color.White);
	break;

Nu har vi lagt till en hel del på en gång men lugn, lugn, vi kommer att förklara allt.

Vi deklarerar ett objekt av typen NetworkSession med namnet session som när vi väljer rollen host genom att trycka på F1 så skapas en session av typen SystemLink. Detta görs med metoden Create som förutom typen av session även tar 2 heltal som inparameter. Dessa anger i ordning antalet lokala spelare som tillåts samt max antal spelare som spelet kommer att stödja. I detta fall 1 lokal spelare per maskin och maximalt 2 spelare allt som allt (detta måste vi ändra senare ifall vi vill ha stöd för fler spelare än 2).

När sessionen skapats så ska vi som host vänta på att spelare ansluter till vår session så att vi kan börja spela. För att göra detta så måste vi använda events som talar om för vårt program när fler spelare ansluter (eller ifall en spelare försvinner).

Med hjälp av events så kan vi låta vårt program reagera på saker som händer, när de händer, istället för att "vänta" på att saken ska hända. Så istället för att vänta på att en spelare skall ansluta så vill vi fortsätta att köra Update() och Draw() så därför talar vi om för sessionen vad som skall hända när en spelare ansluter genom att binda (eng. bind) en metod (i vårt fall hostSession_GamerJoined) till det event (i vårt fall session.GamerJoined) som vi vill att vårt program skall reagera på.

För att detta ska fungera så behöver programmet ett sätt att skapa en referens till en metod. En referens till en metod kallas för delegate (sv. delegat). En delegat är ett objekt som refererar till en speciell metod i en klass. Vi kommer inte att fördjupa oss i events och delegates men den intresserade bör göra ett besök på MSDNs hemsida. Där hittar ni mer information och exempel på events och delegates.

I vår kod skapar vi metoden hostSession_GamerJoined och kopplar den till "eventet" GamerJoined som är en property för vårt session objekt. Delegaten är inställd på att referera till en EventHandler metod som accepterar GamerJoinedEventArgs. Metoden hostSession_GamerJoined måste kolla ifall det finns tillräckligt med anslutna spelare och om så är fallet så skall spelet startas och gameState ändras till PlayingAsHost.

Vår metod hostSession_GamerJoined tillåter vem som helst att ansluta utan någon direkt koll. Så fort en spelare ansluter så startar vi ett spel med honom. Vill vi så kan vi utveckla denna så att vi kan kolla namnet på anslutna spelare och bara starta spelet med vissa spelare eller göra så att vi i nästa gameState WaitingAsHost har möjlighet att välja vilken spelare vi ska spela med ifall flera spelare är anslutna.

På liknande sätt skapar vi metoden hostSession_GamerLeft och kopplar den till "eventet" GamerLeft. I denna metod avslutar vi spelet genom att återgå till gameState TitleScreen ifall en spelare har lämnat vår session.

WaitingAsHost

I gameState WaitingAsHost har vi skapat en session och inväntar en spelare, dvs någon som kör vårt spel och väljer rollen "player". Under tiden vi väntar kan det vara trevligt att få en lista på anslutna spelare. Eftersom vårt spel kommer att starta så fort en annan spelare ansluter så kommer vi bara att se vår egen spelare som är inloggad medan vi väntar. Detta är dock bättre än inget.

Vill man så kan man utöka WaitingAsHost (och senare WaitingAsPlayer) så att man får en riktig Lobby där spelare kan se flera servrar (om de finns) och de som agerar host kan välja vilka spelare som man ska spela med mm.

Vi skapar en skärmbild som vi kan ha nytta av i framtiden ifall vi ska förbättra "Lobbyn".

bild

Den riktiga bilden finns tillgänglig som zip-fil via länken nedan.

Vi packar upp och lägger till bilden som vanligt till vårt projekt. Därefter gör vi följande ändringar till Game1.cs: (OBS - ändringar på flera ställen se kommentarer)

Tillägg till Game1.cs

//Tillägg av globala variabler
Texture2D waitingAsHostGfx;

//Tillägg till LoadContent()
waitingAsHostGfx = Content.Load<Texture2D>("WaitingAsHost_screen");

//Tillägg till Update()
case GameState.WaitingAsHost:
    displayMessage = "";
    foreach (Gamer g in session.AllGamers)
    {
       displayMessage += g.Gamertag + "\n";
    }
    session.Update();
    break;

//Tillägg till Draw()
case GameState.WaitingAsHost:
	spriteBatch.Draw(waitingAsHostGfx, new Vector2(0, 0), Color.White);
	spriteBatch.DrawString(font, displayMessage, new Vector2(616, 435), Color.Black);
	spriteBatch.DrawString(font, displayMessage, new Vector2(615, 436), Color.White);
	break;

Befinner sig spelet i WaitingAsHost så lägger vi till namnet (Gamertag) för alla anslutna spelare i variabeln displayMessage som vi sedan skriver ut i Draw() metoden. Värt att notera är session.Update(); som uppdatera vår session och detta är något som vi inte får glömma när vi skapat en session annars tappar vi kontakten med sessionen.

WaitingASPlayer

Har vi valt rollen player så väntar vi på att en session skall skapas som vi kan ansluta.

Vi gör följande ändringar till Game1.cs: (OBS - ändringar på flera ställen se kommentarer)

Tillägg till Game1.cs

//Skapa följande metod
void playerSession_GamerLeft(object sender, GamerLeftEventArgs e)
{
	session.Dispose();
	state = GameState.TitleScreen;
}

//Tillägg till Update()
case GameState.WaitingAsPlayer:
	AvailableNetworkSessionCollection sessions = NetworkSession.Find(NetworkSessionType.SystemLink, 1, null);
	if (sessions.Count > 0)
	{
		AvailableNetworkSession mySession = sessions[0];
		session = NetworkSession.Join(mySession);
		session.GamerLeft += new EventHandler<GamerLeftEventArgs>(playerSession_GamerLeft);
		state = GameState.PlayingAsPlayer;
	}
	break;

Vi kommer hela tiden att leta efter en tillgänglig session. Hittar vi det så kommer vi att ansluta till den direkt. Som nämnts tidigare så kan man utöka detta till en riktig "Lobby" där vi som spelare kan välja vilken session som vi vill ansluta till.

Find metoden för klassen NetworkSession anropas som letar efter tillgängliga sessioner och returnerar en samling av dem som hittas. Vi kan välja vilken typ av session vi vill leta efter tillsammans med antalet lokala spelare och andra filtreringsalternativ. Vårt anrop letar efter sessioner av typen SystemLink som bara tillåter 1 lokal spelare per maskin. Hittar vi en passande session så ansluter vi till den. Vi lägger även till metoden playerSession_GamerLeft till eventet GamerLeft så att spelet blir informerad om ifall spelaren lämnar sessionen. Metoden playerSession_GamerLeft avslutar sessionen och återgår till gameState TitleScreen. Har vi anslutit till en session så ändras gameState till PlayingAsPlayer.

Fortsättning följer i del 6, to be continued...

Lämna ett svar

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

Scroll to top