<h1><p style="text-align:center; padding-top:14px;">Hexagonal Chess</p></h1>
<h3><p style="text-align:center; padding-top:14px;">In my interpretation of hexagonal chess, I used Gliński’s version of the game’s mechanics.
This game is played on a hexagonal board, each side being 6 cells long. In total, the board has 91 hex cells of three different colors.</p></h3>
<br>
<br>
<div style="position:relative; padding-bottom:56.25%; padding-left:30px; height:0; overflow:hidden; align:center;">
<iframe width="725" height="344" src="https://www.youtube.com/embed/qUuExQmTvo0" frameborder="0" allowfullscreen
style="position:absolute; width:97%; height:100%; align:center;">
</iframe>
</div><br>
<br><h3><p style="text-align:center; padding-top:14px;">
I am going to show several scripts to present main concepts of how this game is made.
<br><br><br>
The board is made from scratch. Everything starts with a vertice, then weaved into triangle, which are finally connected...</p></h3>
<br><br><br>
<desc>HexMesh.cs</desc>
<br>
<pre><code>
using UnityEngine;
using System.Collections.Generic;
using Controller.Board;
using Model.Cell;
namespace Render {
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class HexMesh : MonoBehaviour {
private Mesh _hexMesh;
private List<Vector3> _vertices;
private List<int> _triangles;
private List<Color> _colors;
private MeshCollider _meshCollider;
void Awake() {
GetComponent<MeshFilter>().mesh = _hexMesh = new Mesh();
_meshCollider = gameObject.AddComponent<MeshCollider>();
_hexMesh.name = "Hex Mesh";
_vertices = new List<Vector3>();
_colors = new List<Color>();
_triangles = new List<int>();
}
void Triangulate(HexCell cell) {
Vector3 center = cell.transform.localPosition;
for (int i = 0; i < 6; i++) {
AddTriangle(
center,
center + HexMetrics.corners[i],
center + HexMetrics.corners[i + 1]
);
AddTriangleColor(cell.GetCurrentColor());
}
}
public void Triangulate(HexCell[] cells) {
_hexMesh.Clear();
_vertices.Clear();
_triangles.Clear();
_colors.Clear();
for (int i = 0; i < HexGrid.cellsCount; i++) {
if (cells[i] != null)
Triangulate(cells[i]);
}
_hexMesh.vertices = _vertices.ToArray();
_hexMesh.triangles = _triangles.ToArray();
_hexMesh.colors = _colors.ToArray();
_hexMesh.RecalculateNormals();
_meshCollider.sharedMesh = _hexMesh;
}
void AddTriangleColor (Color color) {
_colors.Add(color);
_colors.Add(color);
_colors.Add(color);
}
void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3) {
int vertexIndex = _vertices.Count;
_vertices.Add(v1);
_vertices.Add(v2);
_vertices.Add(v3);
_triangles.Add(vertexIndex);
_triangles.Add(vertexIndex + 1);
_triangles.Add(vertexIndex + 2);
}
}
}</pre></code>
<br><br><br>
<h3><p style="text-align:center; padding-top:14px;">Next step is to control every cell.</p></h3>
<br><br><br>
<desc>HexCell.cs</desc>
<br>
<pre><code>namespace Model.Cell {
public class HexCell : MonoBehaviour {
public float yOffset;
public Color currentColor;
public Color nativeColor;
public List<HexAbility> hexAbilities = new List<HexAbility>();
private HexCoordinates _coordinates;
private Vector3 _offsetCoordinates = Vector3.zero;
private GameObject _currentChessman;
public static int sizeLimit;
private int _x;
public int X {
get { return _x + HexGrid.boardSize - 1; }
set { _x = value; }
}
private int _y;
public int Y {
get { return _y + HexGrid.boardSize - 1; }
set { _y = value; }
}
private int _z;
public int Z {
get { return _z + HexGrid.boardSize - 1; }
set { _z = value; }
}
public Vector3 ReturnCoords() {
return new Vector3(X, Y, Z);
}
private Vector3 GetOffsetCoords() {
if (_offsetCoordinates == Vector3.zero) _offsetCoordinates = new Vector3(0f, -yOffset, 0f);
return _offsetCoordinates;
}
public void AddAbility(HexAbility ability, List<Player> playerExceptions) {
ability.SetPlayersExceptionsList(playerExceptions);
hexAbilities.Add(ability);
}
public void SetNativeColor(Color color) {
nativeColor = color;
currentColor = nativeColor;
}
public void SetSelectionColor(Color color) {
currentColor = color;
}
public void SetCurrentChessman(GameObject chessman) {
_currentChessman = chessman;
}
public void UndoSelection() {
currentColor = nativeColor;
}
public Color GetCurrentColor() {
return currentColor;
}
public void SpawnChessman(GameObject chessman, Player player) {
_currentChessman = Instantiate(chessman) as GameObject;
_currentChessman.transform.SetParent(transform);
_currentChessman.transform.localPosition = GetOffsetCoords();
_currentChessman.transform.rotation = chessman.transform.rotation;
_currentChessman.GetComponentInChildren<Chessman>().ChessPlayer = player;
}
public void RemoveChessman() {
if (_currentChessman != null) Destroy(_currentChessman);
else {
Debug.LogError("You're trying to remove a chessman, but this cell doesn't have one!");
}
}
public bool HasChessman() {
return (_currentChessman != null);
}
public GameObject GetChessman()
{
return _currentChessman;
}
public Dictionary<HexCell, HexGrid.MoveType> GetChessmanMoves() {
//function allows a player to see active chessman and shows him possible moves
//we'll return possible moves (cells)
if (_currentChessman == null) {
Debug.Log("There is any chessmen on those cell!");
return null;
}
Movable[] movableScripts = _currentChessman.GetComponentsInChildren<Movable>();
if (movableScripts == null) {
Debug.LogError("Your chessmans don't have movable scripts!");
return null;
}
var board = BoardManager.Instance;
var possibleMoves = new Dictionary<HexCell, HexGrid.MoveType>();
sizeLimit = (HexGrid.boardSize - 1) * 2;
foreach (Movable script in movableScripts) {
Dictionary<HexCell, HexGrid.MoveType> currentPossibleMoves = script.GetPossibleMoves(X, Y, Z, sizeLimit, board.CurrentPlayer);
foreach (var pair in currentPossibleMoves) {
possibleMoves.Add(pair.Key, pair.Value);
}
}
return possibleMoves;
}
void Awake() {
hexAbilities.Add(new StandartMove());
}
}
}</code></pre>
<br><br><br><h3><p style="text-align:center; padding-top:14px;">
Every chessman inherits Movable class. Movable is the class with all basic rules for every chess piece on the board: setting position, coordinates, moving. So every chess piece just need to implement how it changes coordinates in order to move. Let Bishop be an example!</p></h3>
<br><br><br>
<desc>Bishop.cs</desc>
<br>
<pre><code>
namespace GameLogic {
public class Bishop : Movable {
// This function returns the dictionary with coordinates of all the moves that the active chess piece can make
public override Dictionary<HexCell, HexGrid.MoveType> GetPossibleMoves(
int currentX,
int currentY,
int currentZ,
int maxValue,
Player player)
{
var board = BoardManager.Instance;
int indexX = 0;
int indexY = 0;
int indexZ = 0;
var possibleMoves = new Dictionary<HexCell, HexGrid.MoveType>();
HexCell cell;
CheckMoves checkMoves = changeCoords => {
//current position of the chess piece.
indexX = currentX;
indexY = currentY;
indexZ = currentZ;
while (true) {
// we'll go out from the cycle after findind another chessman or the limit of the board
//the function that we recieve as a parameter. it's getting coordinates to the neighbour cell of one of the possible directions for movement
changeCoords(); // for example move to the cell which has position with indexY++; indexZ--;
// are we trying to exceed the limit of the board ?
if (indexX > maxValue || indexX < 0 || indexY > maxValue || indexY < 0 || indexZ < 0 || indexZ > maxValue)
break;
cell = board.GetCellByCoords(indexX, indexY, indexZ);
if (cell != null) {
//if cell has other's player chessman - we can ATTACK
if (cell.HasChessman()) {
if (cell.GetChessman().GetComponent<Chessman>().ChessPlayer != player) {
possibleMoves.Add(cell, HexGrid.MoveType.Kill);
}
break;
}
//if the cell is empty - you can move
else
possibleMoves.Add(cell, HexGrid.MoveType.Move);
}
};
};
// we have 3 directions, and we need to check all of them
// each direction is a line, so it turns in 2 ways
//some calculations. that's how hexagonal bishop moves.
checkMoves(() => { indexX += 2; indexY--; indexZ--; });
checkMoves(() => { indexZ += 2; indexY--; indexX--; });
checkMoves(() => { indexY += 2; indexX--; indexZ--; });
checkMoves(() => { indexX -= 2; indexY++; indexZ++; });
checkMoves(() => { indexZ -= 2; indexY++; indexX++; });
checkMoves(() => { indexY -= 2; indexX++; indexZ++; });
return possibleMoves;
}
}
}
</pre></code>
<br><br><br><h3><p style="text-align:center; padding-top:14px;">
The hardest thing is to get all pieces together and to make this work. That's the duty of the next script.</p></h3>
<br><br><br>
<desc>HexGrid.cs</desc>
<br>
<pre><code>
namespace Controller.Board {
public class HexGrid : MonoBehaviour {
#region StaticVariables
public static HexGrid Instance { get; set; }
public static HexCell[] cells;
public static int cellsCount;
public static int coordsLength;
public static int boardSize;
#endregion
#region Prefabs
public HexCell cellPrefab;
public GameObject highlinePrefab;
//there are 3 types of islands.
//is classical chess types are 2 colors of cells, but there is one more direction, so 3 colors
public GameObject firstIsland;
public GameObject secondIsland;
public GameObject thirdIsland;
public Text cellLabelPrefab;
[SerializeField]
private GameObject messageWindow;
#endregion
#region Colors
public Color attackColor;
public Color attackColor2;
public Color moveColor;
public Color moveColor2;
public Color arrowColor;
public Color arrowColor2;
public Color activeColor;
public Color activeColor2;
#endregion
#region Islands
private GameObject _islandsParent;
[SerializeField]
private IslandShaker _shiverIsland;
public GameObject[] islands;
#endregion
#region Components
//to not mess the scene
private GameObject _cellsParent;
private BoardManager board;
private MessageWindow _messageWindow;
private Canvas _gridCanvas;
private HexMesh _hexMesh;
#endregion
//Arrow type is for future modifications
public enum MoveType { Kill, Move, Arrow };
private Dictionary<HexCell, MoveType> _possibleMoves;
private Dictionary<MoveType, Color> _moveColors1;
private Dictionary<MoveType, Color> _moveColors2;
private Dictionary<Vector3, Vector3> _enpassanDict;
private Dictionary<HexCell, Highline> _highlines;
private HexCell _activeCell = null;
private List<CellCoords> cellCoordsList = new List<CellCoords>();
private Coroutine _shiverCoroutine;
class CellCoords {
int cellIndex;
Vector3 position;
public CellCoords(int index, Vector3 coords) {
cellIndex = index;
position = coords;
}
//helps to compare real physical coods to board coords
public bool IsInside(Vector3 coords) {
float xPos = position.x;
float yPos = position.y;
float zPos = position.z;
float r = HexMetrics.innerRadius;
if (Vector3.Distance(position, coords) < = r )
return true;
return false;
}
}
public void SetEnpassanMove(Vector3 point1, Vector3 point2)
{
_enpassanDict.Add(point1, point2);
}
void DrawBoard(int size) {
boardSize = size;
coordsLength = size - 1;
cellsCount = 6 * (size / 2) * (size - 1) + 1;
_cellsParent = new GameObject();
_cellsParent.name = "Cells";
_cellsParent.transform.parent = this.transform;
_gridCanvas = GetComponentInChildren< Canvas>();
_hexMesh = GetComponentInChildren<HexMesh >();
_hexMesh.transform.position += Vector3.up * 1f;
cells = new HexCell[cellsCount];
_highlines = new Dictionary<HexCell, Highline>();
islands = new GameObject[cellsCount];
int currentEl = 0;
int rowsCount = 1 + 4 * (size - 1); //for example rows from 0 to 19 for size = 6
int currentRow = rowsCount;
int topRowsCount = size - 1;
int bottomRowsCount = topRowsCount;
int middleRowsCount = size * 2 - 1;
DrawTopRows(topRowsCount, ref currentEl, ref currentRow);
DrawMiddleRows(middleRowsCount, ref currentEl, ref currentRow);
DrawBottomRows(bottomRowsCount, ref currentEl, ref currentRow);
}
void DrawTopRows(int rowsCount, ref int currentEl, ref int currentRow) {
int startZ = 0;
int startX = -coordsLength;
int startY = coordsLength;
for (int rowWidth = 0; rowWidth < rowsCount; rowWidth++) {
int currentX = startX;
int currentZ = startZ;
int currentY = startY;
for (int currentWidth = 0; currentWidth <= rowWidth; currentWidth++) {
CreateCell(currentX, currentY, currentZ, currentEl++, currentRow);
currentZ -= 2;
currentX++;
currentY++;
}
currentRow--;
startY--;
startZ++;
}
}
void DrawMiddleRows(int rowsCount, ref int currentEl, ref int currentRow) {
int startZ = coordsLength;
int startY = 0;
int startX = -coordsLength;
for (int rowNum = 0; rowNum < rowsCount; rowNum += 2) {
int currentX = startX;
int currentZ = startZ;
int currentY = startY;
for (int currentWidth = 0; currentWidth < boardSize; currentWidth++) {
CreateCell(currentX++, currentY++, currentZ, currentEl++, currentRow);
currentZ -= 2;
}
currentRow--;
currentZ = startZ - 1;
currentX = startX + 1;
currentY = startY;
for (int currentWidth = 0; currentWidth < boardSize - 1; currentWidth++) {
CreateCell(currentX++, currentY++, currentZ, currentEl++, currentRow);
currentZ -= 2;
}
currentRow--;
startY--;
startX++;
}
}
void DrawBottomRows(int rowsCount, ref int currentEl, ref int currentRow) {
int startZ = coordsLength - 2;
int startY = -coordsLength;
int startX = 2;
for (int rowNum = 0; rowNum < rowsCount; rowNum++) {
int currentX = startX;
int currentZ = startZ;
int currentY = startY;
for (int currentWidth = 0; currentWidth < rowsCount - rowNum - 1; currentWidth++) {
CreateCell(currentX, currentY, currentZ, currentEl++, currentRow);
currentZ -= 2;
currentX++;
currentY++;
}
currentRow--;
startZ--;
startX++;
}
}
void Awake() {
_messageWindow = messageWindow.GetComponent<MessageWindow>();
Instance = this;
_enpassanDict = new Dictionary<Vector3, Vector3>();
_moveColors1 = new Dictionary<MoveType, Color> {
{ MoveType.Kill, attackColor },
{ MoveType.Move, moveColor},
{ MoveType.Arrow, arrowColor}
};
_moveColors2 = new Dictionary<MoveType, Color> {
{ MoveType.Kill, attackColor2 },
{ MoveType.Move, moveColor2},
{ MoveType.Arrow, arrowColor2}
};
_possibleMoves = null;
_islandsParent = new GameObject();
_islandsParent.name = "Islands";
_islandsParent.transform.parent = transform;
DrawBoard(6);
BoardManager.Instance.SetCenterPoint(CenterCoords());
}
void Start() {
_hexMesh.Triangulate(cells);
board = BoardManager.Instance;
}
public void DiscardSelection() {
if (_shiverIsland!= null) _shiverIsland.StopShivering();
_shiverIsland = null;
if (_highlines.Count != 0)
foreach(var highline in _highlines) {
highline.Value.Fade();
}
if (_possibleMoves != null) {
if (_activeCell != null) { _activeCell.UndoSelection(); _activeCell = null; }
if (_possibleMoves.Count > 0) {
foreach (var selectedCell in _possibleMoves) {
selectedCell.Key.UndoSelection();
_highlines[selectedCell.Key].Fade();
}
};
_possibleMoves = null;
_hexMesh.Triangulate(cells);
}
}
//update board after player's move
public void ActivateChessman(MoveType action, Vector3 startCoords, Vector3 finalCoords) {
var board = BoardManager.Instance;
HexCell startCell = board.GetCellByCoords((int)startCoords.x, (int)startCoords.y, (int)startCoords.z);
HexCell finalCell = board.GetCellByCoords((int)finalCoords.x, (int)finalCoords.y, (int)finalCoords.z);
Player owner;
GameObject chessman = startCell.GetChessman();
if(chessman == null) {
return;
}
Movable[] movableScripts = chessman.GetComponentsInChildren<Movable>();
foreach (var script in movableScripts) {
script.SignFirstMove();
}
switch (action) {
case MoveType.Move:
Chessman chessmanScript = chessman.GetComponentInChildren<Chessman>();
owner = chessmanScript.ChessPlayer;
if (board.IsKing(chessmanScript)) board.kings[owner] = finalCell;
finalCell.SpawnChessman(startCell.GetChessman(), owner);
startCell.RemoveChessman();
break;
case MoveType.Kill:
//CHECK: if final cell has a king - current player wins!
foreach (var entry in board.kings)
if (entry.Value == finalCell) Debug.Log("WIN");
owner = startCell.GetChessman().GetComponentInChildren<Chessman>().ChessPlayer;
finalCell.RemoveChessman();
finalCell.SpawnChessman(startCell.GetChessman(), owner);
startCell.RemoveChessman();
break;
default:
break;
}
board.NextPlayerTurn();
}
public StandartMove TryMove(MoveType action, HexCell startCell, HexCell finalCell)
{
Player owner;
//1. to take a chess piece from the cell
GameObject chessman = startCell.GetChessman();
//2. to get all moves that are available
Movable[] movableScripts = chessman.GetComponentsInChildren<Movable>();
foreach (var script in movableScripts)
{
script.SignFirstMove();
}
StandartMove moveAction = null;
Player enemy = GetEnemy();
foreach (var hexAbility in finalCell.hexAbilities)
{
if (hexAbility.GetType() == typeof(StandartMove))
{
moveAction = hexAbility as StandartMove;
}
}
Chessman chessmanScript = chessman.GetComponentInChildren<Chessman>();
owner = chessmanScript.ChessPlayer;
switch (action)
{
//if it's movement
case MoveType.Move:
//where is enemy's king?
if (board.IsKing(chessmanScript)) board.kings[owner] = finalCell;
//change the parent of a chess piece logically, but not visually, 'cause the move isnt done yet
if (moveAction != null)
moveAction.ChangeParent(startCell, finalCell.transform);
else
Debug.Log("moveaction = null");
break;
case MoveType.Kill:
//CHECK: if final cell has a king - current player wins!
foreach (var entry in board.kings)
if (entry.Value == finalCell) Debug.Log("WIN");
if (board.IsKing(chessmanScript)) board.kings[owner] = finalCell;
if (moveAction != null)
moveAction.ChangeParent(startCell, finalCell.transform);
else
Debug.Log("moveaction = null");
break;
case MoveType.Arrow:
foreach (var entry in board.kings)
if (entry.Value == finalCell) Debug.Log("WIN");
owner = startCell.GetChessman().GetComponentInChildren<Chessman>().ChessPlayer;
finalCell.RemoveChessman();
GameManager.Instance.AbilityIsReady();
break;
default:
break;
}
return moveAction;
}
//checks if a player can make the move
public void TryActivateChessman(MoveType action, HexCell startCell, HexCell finalCell) {
GameObject chessman = startCell.GetChessman();
StandartMove moveAction = TryMove(action, startCell, finalCell);
GameManager gm = GameManager.Instance;
bool moved = false;
int fullAbilitiesCount = 0;
if (IsCheck(GetEnemy(), GetAlly())) {
gm.RevertTurn();
gm.SetActiveAbilities(0);
moveAction.ReturnParent();
_messageWindow.ShowChangeTurn();
Chessman chessmanScript = chessman.GetComponentInChildren<Chessman>();
if (board.IsKing(chessmanScript)) board.kings[chessmanScript.ChessPlayer] = startCell;
return;
}
//how much actions we need?
foreach (var hexAbility in finalCell.hexAbilities) {
if (hexAbility.WorksFor(chessman)) fullAbilitiesCount++;
};
gm.SetActiveAbilities(fullAbilitiesCount);
moveAction.ReturnParent();
if (action == MoveType.Kill) finalCell.RemoveChessman();
foreach (var hexAbility in finalCell.hexAbilities) {
if (hexAbility.WorksFor(chessman)) hexAbility.Move(startCell, chessman.GetComponentInChildren<Chessman>(), finalCell);
}
if (moved) GameManager.Instance.AbilityIsReady();
int moveType = (int) action;
Vector3 start = new Vector3(startCell.X, startCell.Y, startCell.Z);
Vector3 final = new Vector3(finalCell.X, finalCell.Y, finalCell.Z);
}
public Player GetEnemy() {
if (board.CurrentPlayer == board.GetFirstPlayer()) {
return board.GetSecondPlayer();
}
else {
return board.GetFirstPlayer();
}
}
public Player GetAlly() {
if (board.CurrentPlayer == board.GetFirstPlayer()) {
return board.GetFirstPlayer();
}
else {
return board.GetSecondPlayer();
}
}
public void ActivateHighLine(HexCell cell, Color color1, Color color2, bool isActive) {
Highline highline = _highlines[cell];
highline.ChangeColors(color1, color2);
highline.Appear();
}
public void ClickCell(Vector3 position) {
HexCell cell = FindCell(position);
if (cell == null) return;
//if position corresponds to one of selected cell (one of possible moves) - do something (move/kill/etc) with the chessman!
//and then discard selection
if (_possibleMoves != null && _activeCell != null)
foreach (var currentCell in _possibleMoves) {
HexCell cellCoords = currentCell.Key;
if (cell.X == cellCoords.X && cell.Y == cellCoords.Y && cell.Z == cellCoords.Z) {
TryActivateChessman(currentCell.Value, _activeCell, cell);
DiscardSelection();
return;
}
}
//clear previous selection
_enpassanDict = new Dictionary<Vector3, Vector3>();
DiscardSelection();
if (cell.HasChessman()) {
if (cell.GetChessman().GetComponentInChildren<Chessman>().ChessPlayer != BoardManager.Instance.CurrentPlayer) return;
_activeCell = cell;
cell.SetSelectionColor(activeColor);
ActivateHighLine(cell, activeColor, activeColor2, true);
ShakeIsland(_highlines[cell].transform.parent.gameObject, cell.GetChessman());
}
else return;
_possibleMoves = cell.GetChessmanMoves();
//if we have at least one move for those chessman
if (_possibleMoves != null && _possibleMoves.Count > 0)
//each Cell to select and type of type of movement
foreach (var possibleCell in _possibleMoves) {
Color selectionColor1;
Color selectionColor2;
//we want to find color of this type of movement to select our cell
bool found1 = _moveColors1.TryGetValue(possibleCell.Value, out selectionColor1);
bool found2 = _moveColors2.TryGetValue(possibleCell.Value, out selectionColor2);
if (found1 && found2)
{
possibleCell.Key.SetSelectionColor(selectionColor1);
ActivateHighLine(possibleCell.Key, selectionColor1, selectionColor2, false);
}
else
Debug.LogError("You haven't set color for " + possibleCell.Value);
}
_hexMesh.Triangulate(cells);
}
public HexCell FindCell(Vector3 currentCoords) {
for (int i = 0; i < cellsCount; i++) {
if (cellCoordsList[i].IsInside(currentCoords))
return cells[i];
}
return null;
}
public HexCell FindCell(int x, int y, int z) {
for (int i=0; i < cells.Length; i++) {
HexCell curentCell = cells[i];
if ((curentCell.X == x) && (curentCell.Y == y) && (curentCell.Z == z)) {
return curentCell;
}
}
return null;
}
private Vector3 CenterCoords() {
int middleIndex = boardSize - 1;
HexCell centerCell = FindCell(middleIndex, middleIndex, middleIndex);
Transform placement = centerCell.transform;
return new Vector3(placement.position.x, placement.position.y, placement.position.z);
}
private void CreateCell(int x, int y, int z, int i, int currentHeight) {
Vector3 position;
position.y = 0f;
position.z = HexMetrics.innerRadius * currentHeight;
position.x = - z * 1.5f * HexMetrics.outerRadius;
cells[i] = Instantiate<HexCell>(cellPrefab);
HexCell cell = cells[i];
cell.transform.SetParent(_cellsParent.transform, false);
cell.transform.localPosition = position;
cellCoordsList.Add(new CellCoords(i, cell.transform.position));
GameObject island;
if (currentHeight % 3 == 0) {
//cell.SetNativeColor(firstColor);
island = Instantiate<GameObject>(firstIsland);
}
else if (currentHeight % 3 == 1) {
//cell.SetNativeColor(secondColor);
island = Instantiate<GameObject>(secondIsland);
}
else {
//cell.SetNativeColor(thirdColor);
island = Instantiate<GameObject>(thirdIsland);
}
cell.X = x;
cell.Y = y;
cell.Z = z;
island.AddComponent<IslandShaker>();
GameObject highline;
highline = Instantiate(highlinePrefab);
highline.transform.position = position + new Vector3(0f, -8f, 0f);
float yOffset = UnityEngine.Random.Range(0f, 6f);
cell.yOffset = yOffset;
island.transform.position = position - new Vector3(0f, 8f + yOffset, 0f);
highline.transform.position -= new Vector3(0f, yOffset, 0f);
island.transform.SetParent(_islandsParent.transform);
highline.transform.SetParent(island.transform);
islands[i] = island;
Highline _highlinescript = highline.GetComponent<Highline>();
_highlines.Add(cell, _highlinescript);
Text label = Instantiate<Text>(cellLabelPrefab);
label.rectTransform.SetParent(_gridCanvas.transform, false);
label.rectTransform.anchoredPosition =
new Vector3(position.x, position.y, position.z);
label.text = (cell.X.ToString() + " " + cell.Y.ToString() + " " + cell.Z.ToString());
label.transform.position = position;
}
public void AfterTurn() {
Player ally;
Player enemy;
if (board.CurrentPlayer == board.GetFirstPlayer()) {
ally = board.GetFirstPlayer();
enemy = board.GetSecondPlayer();
}
else {
ally = board.GetSecondPlayer();
enemy = board.GetFirstPlayer();
}
bool isCheck = IsCheck(enemy, ally);
if (isCheck)
{
//Debug.Log("there is a in check");
if (isMate()) Debug.LogError("MATE!!!");
else
{
_messageWindow.ShowKingAttacked();
}
}
}
public void ShakeIsland(GameObject island, GameObject chessman) {
if (_shiverIsland != null) _shiverIsland.StopShivering();
_shiverIsland = island.GetComponent<IslandShaker>();
_shiverIsland.StartShivering(chessman);
}
//in order to understand if there is a possible check we need to take all enemy's chess pieces and check if they can attack current player's king
public bool IsCheck(Player enemy, Player ally) {
var currentPlayerCell = board.kings[ally];
foreach (var cell in cells) {
//we're trying to take a chessman from every cell
GameObject chessmanGO = cell.GetChessman();
//then, if there is a chessman
if (chessmanGO != null) {
//we check it
Chessman chessman = chessmanGO.GetComponentInChildren<Chessman>();
if (chessman.ChessPlayer == enemy) //it's the enemy!
{
//what moves are possible for this enemy's chessman?
Movable[] moves = chessmanGO.GetComponentsInChildren<Movable>();
foreach (var move in moves) {
if (chessman.ChessPlayer == enemy) //it's the enemy!
foreach (var possibleCell in move.GetPossibleMoves(cell.X, cell.Y, cell.Z, HexCell.sizeLimit, enemy)) {
if (possibleCell.Value == MoveType.Kill) {
if (currentPlayerCell.X == possibleCell.Key.X && currentPlayerCell.Y == possibleCell.Key.Y && currentPlayerCell.Z == possibleCell.Key.Z) {
return true;
}
}
}
}
}
}
}
return false;
}
//is it chessmate?
//first of all, we check, if there is check
//they, if tehre is any move that can protect the king, it's mate
public bool isMate()
{
GameManager gm = GameManager.Instance;
bool noMate = false;
HexCell currentCell = board.kings[board.CurrentPlayer];
King ourKing = currentCell.GetChessman().GetComponentInChildren<Movable>() as King;
//maybe king can just ran away?
foreach (var possibleMove in ourKing.GetPossibleMoves(currentCell.X, currentCell.Y, currentCell.Z, HexCell.sizeLimit, board.CurrentPlayer))
{
StandartMove moveAction = TryMove(possibleMove.Value, currentCell, possibleMove.Key);
noMate = (!IsCheck(GetEnemy(), GetAlly()));
gm.RevertTurn();
gm.SetActiveAbilities(0);
moveAction.ReturnParent();
if (board.IsKing(currentCell.GetChessman().GetComponentInChildren<Chessman>())) board.kings[currentCell.GetChessman().GetComponentInChildren<Chessman>().ChessPlayer] = currentCell;
if (noMate) return false;
}
//if not, can someone protect him?
foreach(HexCell cell in cells) {
GameObject chessman = cell.GetChessman();
if (chessman != null) {
Movable[] moves = chessman.GetComponentsInChildren<Movable>();
//we are trying to move only attacked player's chessmen
if (cell.GetChessman().GetComponentInChildren<Chessman>().ChessPlayer == board.CurrentPlayer)
foreach (var movableScript in moves)
foreach (var possibleMove in movableScript.GetPossibleMoves(currentCell.X, currentCell.Y, currentCell.Z, HexCell.sizeLimit, board.CurrentPlayer)) {
StandartMove moveAction = TryMove(possibleMove.Value, currentCell, possibleMove.Key);
noMate = (!IsCheck(GetEnemy(), GetAlly()));
gm.RevertTurn();
gm.SetActiveAbilities(0);
moveAction.ReturnParent();
if (noMate) return false;
}
}
}
Debug.LogError("oh my god! that's mate!");
return true;
}
}
}</pre></code>
<br><br><br><h3><p style="text-align:center; padding-top:14px;">
Don't forget about good-looking! This script brings those animated shine thank appears when a player clicks on a board alive.</p></h3>
<br><br><br>
<desc>Highline.cs</desc>
<br>
<pre><code>
namespace View {
public class Highline : MonoBehaviour {
//min and max transparasy of the Highline
[SerializeField]
private float minAlpha = 0f;
[SerializeField]
private float maxAlpha = 0.2f;
//time spent of the changing minAlpha and maxAlpha
[SerializeField]
private float autoSeconds = 0.25f;
private float _alphaPerSecond;
private bool _appears;
private bool _fades;
private Renderer _renderer;
//every Highline is made of two materials
private Material mat1;
private Material mat2;
private Color _color;
private bool _isActive = false;
private Vector2 startTextureOffset1 = new Vector2(0.3f, 0.05f);
private Vector2 startTextureOffset2 = new Vector2(0.9f, 0.05f);
void Awake() {
//Highline has to have a child with two HexGlows
_renderer = GetComponentInChildren<Renderer>();
mat1 = _renderer.materials[0];
mat2 = _renderer.materials[1];
//different offsets make interesting visual effect
mat1.SetTextureOffset("_MainTex", startTextureOffset1);
mat2.SetTextureOffset("_MainTex", startTextureOffset2);
}
void Update () {
if (_fades) {
_color = mat1.GetColor("_TintColor");//Tint Color
_color.a -= _alphaPerSecond * Time.deltaTime;
mat1.SetColor("_TintColor", _color);
_color = mat2.GetColor("_TintColor");//Tint Color
_color.a -= _alphaPerSecond * Time.deltaTime;
mat2.SetColor("_TintColor", _color);
}
if (_appears) {
_color = mat1.GetColor("_TintColor");
_color.a += _alphaPerSecond * Time.deltaTime;
mat1.SetColor("_TintColor", _color);
_color = mat2.GetColor("_TintColor");
_color.a += _alphaPerSecond * Time.deltaTime;
mat2.SetColor("_TintColor", _color);
}
if (_isActive) {
Vector2 tiling = mat1.GetTextureOffset("_MainTex");
if (tiling.x >= 1f) mat1.SetTextureOffset("_MainTex", startTextureOffset1);
else {
tiling.x += 0.001f;
mat1.SetTextureOffset("_MainTex", tiling);
}
tiling = mat2.GetTextureOffset("_MainTex");
if (tiling.x <= 0f) mat2.SetTextureOffset("_MainTex", startTextureOffset2);
else {
tiling.x -= 0.001f;
mat2.SetTextureOffset("_MainTex", tiling);
}
}
}
public void Fade() {
//we don't need to fade twice
if (!_renderer.enabled) return;
Fade(autoSeconds);
}
public void Appear() {
Appear(autoSeconds);
}
public void Fade(float seconds) {
_alphaPerSecond = (maxAlpha - minAlpha) / seconds;
_fades = true;
StartCoroutine(StartFade(seconds));
}
public void Appear(float seconds) {
_isActive = true;
_alphaPerSecond = (maxAlpha - minAlpha) / seconds;
_color = mat1.GetColor("_TintColor");
_color.a = minAlpha;
mat1.SetColor("_TintColor", _color);
_color = mat2.GetColor("_TintColor");
_color.a = minAlpha;
mat2.SetColor("_TintColor", _color);
_appears = true;
_renderer.enabled = true;
StartCoroutine(StartAppear(seconds));
}
private IEnumerator StartFade(float seconds) {
_appears = false;
yield return new WaitForSeconds(seconds);
_fades = false;
_isActive = false;
if (!_appears) {
_renderer.enabled = false;
}
yield break;
}
//active means with active chessman
private IEnumerator StartAppear(float seconds) {
_fades = false;
yield return new WaitForSeconds(seconds);
_appears = false;
yield break;
}
public void ChangeColors(Color color1, Color color2)
{
Color alpha = color1;
mat1.SetColor("_TintColor", color1);
alpha = color2;
mat2.SetColor("_TintColor", color2);
}
}
}
</pre></code>
.container {
height: 400px;
width: 330px;
margin: 20px auto;
}
.portfolio-thesis-img {
max-width: 800px;
max-height: 400px;
box-shadow: 15px 15px 15px 15px rgba(0,0,0,0.45);
}
desc {
font-size: x-large;
padding-left: 58px;
}
pre, code {
font-family: monospace, monospace;
font-size: large;
}
pre {
overflow: auto;
}
pre > code {
display: block;
padding: 1rem;
word-wrap: normal;
}
pre{
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
margin-bottom: 10px;
overflow: auto;
width: auto;
padding-left: 45px;
background-color: #eee;
width: 450px!ie7;
padding-bottom: 20px!ie7;
max-height: 600px;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.