Tiles Plaatsen¶
Doelstelling¶
Om het spel te spelen moet de speler natuurlijk tiles kunnen plaatsen, wat ook gedeeld moet worden met alle andere spelers in het spel.
Toegepaste oplossing¶
Client¶
In de client word de tile gepositioneerd onder de muis. Maar alleen als een tile geselecteerd is om te plaatsen. Voor nu is dit altijd zo, om het te testen.
public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
base.Draw(gameTime, spriteBatch);
if (tileToPlace != null)
{
tileToPlace.pos = tileGrid.MousePos;
// temporarily illegally set parent, otherwise positioning won't work
tileToPlace.Parent = tileGrid;
tileToPlace.Draw(gameTime, spriteBatch);
tileToPlace.Parent = null;
}
}
Dan kan de speler ook deze tile draaien en uiteindelijk plaatsen.
public override void HandleInput(InputHelper inputHelper)
{
base.HandleInput(inputHelper);
if (tileToPlace != null)
{
if (inputHelper.KeyPressed(Keys.R))
tileToPlace.Rotation +=
inputHelper.IsKeyDown(Keys.LeftShift) || inputHelper.IsKeyDown(Keys.RightShift)
? 1
: -1;
if (inputHelper.MouseLeftButtonReleased)
{
tileToPlace.pos = tileGrid.MousePos;
var placed = AttemptToPlaceTile(tileToPlace);
App.AssetManager.AudioManager.PlaySoundEffect(placed ? "button_agree" : "button_cancel");
//TODO niet een willekeurige test tile creëren
if (placed) tileToPlace = new Tile(new HexVector(), TileEdgeHolder.FromRandom3(App.Random), 0);
}
}
}
Hier word hier uiteindelijk geprobeerd om een tile te plaatsen. Het checkt of de tile binnen het speelveld is, dan of er niet al een tile op die plek staat en als laatste gaat het langs elke zijkant en kijkt het of het tenminste één overeenkomende zijkant heeft. Dan word deze informatie doorgegeven aan de server.
protected bool AttemptToPlaceTile(Tile tile)
{
var pos = tile.pos;
if (pos.HexMagnitude() > tileGrid.radius)
return false;
if (tileGrid.Get(pos) != null)
return false;
var matches = 0;
for (var i = 0; i < HexVector.DirectionVectors.All.Length; i++)
{
var neighbourPos = pos + HexVector.DirectionVectors.All[i];
if (!tileGrid.IsInRange(neighbourPos))
continue;
var neighbour = tileGrid.Get(neighbourPos);
if (neighbour == null)
continue;
if (tile.edges.GetEdge(i) == neighbour.edges.GetEdge(i + 3))
matches ++;
}
if (matches <= 0)
return false;
SocketClient.Instance.SendDataPacket(new PlaceTilePacket()
{
tile = new PublicTile()
{
pos = tile.pos,
edges = tile.edges,
rotation = tile.Rotation
}
});
tileGrid.Add(tile);
return true;
}
Dan nadat de server zijn eigen checks heeft gedaan word het weer teruggestuurd naar de andere spelers. En worden hun lokale borden ook geüpdatet zodat zij ook de geplaatste tile hebben.
private void OnTileUpdate(TileUpdatePacket update)
{
switch (update.change)
{
case TileUpdateType.Removed:
tileGrid.Remove(update.tile.pos);
break;
case TileUpdateType.Placed:
if (tileGrid.Get(update.tile.pos) != null)
{
App.Logger.LogWarning("Server said to place tile at {pos}, while there is already a tile there", update.tile.pos);
tileGrid.Remove(update.tile.pos);
}
tileGrid.Add(new Tile(update.tile.pos, update.tile.edges, update.tile.rotation));
break;
}
}
Server¶
Hier op de server worden eigenlijk dezelfde checks gedaan zoals in AttemptToPlaceTile()
,
maar op de server om valsspelers tegen te gaan.
Daarna word naast de server grid updaten het ook door verstuurd naar alle andere spelers.
Hier worden ook voor het eerst de nieuwe hub filter barriers gebruikt, die het herhalende checken van of bepaalde requirements voldoen gemakkelijk maakt.
[PlayerBarrier]
[RoomBarrier]
[GameBarrier(checkForTurn = true)]
public Task TilePlaced(PlaceTilePacket place)
{
place.tile.edges.offset = place.tile.rotation;
if (place.tile.pos.HexMagnitude() > Game.range)
throw new UserFriendlyException("Not on bord", "You tried to place a tile outside the range of the playing field");
if (Game.tileGrid.ElementAt(place.tile.pos) != null)
throw new UserFriendlyException("Filled spot", "You tried to place a tile where another is already placed");
var matches = 0;
for (var i = 0; i < HexVector.DirectionVectors.All.Length; i++)
{
var neighbourPos = place.tile.pos + HexVector.DirectionVectors.All[i];
if (place.tile.pos.HexMagnitude() > Game.range)
continue;
var neighbour = Game.tileGrid.ElementAt(neighbourPos);
if (neighbour == null)
continue;
if (place.tile.edges.GetEdge(i) == neighbour.edges.GetEdge(i + 3))
matches ++;
}
if (matches <= 0)
throw new UserFriendlyException("No connections", "You tried to place a tile without connecting at least 1 side");
Game.tileGrid.Add(place.tile.pos, new Tile(place.tile.pos, place.tile.edges, place.tile.rotation));
return Clients.OthersInGroup(Room.id).SendPacketAsync(new TileUpdatePacket()
{
change = TileUpdateType.Placed,
tile = place.tile
});
}
Packets¶
Als laatste zijn hier de packets die gebruikt worden om het bovenstaande te doen.
Eerst de PlaceTilePacket
naar de server met informatie over de tile die de speler wilt plaatsen.
Wat later niet een gehele tile moet zijn,
zodat de speler niet kan liegen en een nieuwe tile niet in hun hand kan verzinnen.
Daarna de TileUpdatePacket
om alle spelers te updaten.
Ook met de optie om een tile te verwijderen,
voor in de toekomst wanneer dit met kaarten kan.
public class PlaceTilePacket() : BasePacket("TilePlaced", PT.ToServer)
{
public PublicTile tile { get; set; }
}
public enum TileUpdateType
{ Placed, Removed }
public class TileUpdatePacket() : BasePacket("TileUpdate", PT.ToClient)
{
public TileUpdateType change { get; set; }
public PublicTile tile { get; set; }
}
Sequentiediagram¶
Bronnen¶
- C# documentation. (z.d.) learn.microsoft.com.
Laatst geraadpleegd op 8 maart 2024, van https://learn.microsoft.com/en-uk/dotnet/csharp/
Gecreëerd: March 11, 2024