Juegos, un mundo global en varios idiomas

En el juego que estamos desarrollando es primordial llegar al máximo público posible. Al ser un juego de estrategia es necesario explicar a través de un tutorial explicar las opciones que en cada momento tiene el jugador. Para ello es importante que le jugador pueda entender bien las instrucciones con lo cual llegar a través de un idioma conocido es importante.


En este proyecto utilizo el asset de Unity creado por Inter Illusion: I2 Localization. Este asset nos permite cambiar, entre muchas otras cosas, dinámicamente el idoma del proyecto y localizar el juego para cada jugador. L2 puede utilizar el traductor de Google para traducir el juego a varios idiomas de forma dinámica y gestionar varias fuentes para lenguas no latinas.  Todo ello a través de la interfaz integrada en el propio editor de Unity para máxima comodidad.



En nuestro caso, más allá de las herramientas elementales del producto queremos que en tiempo de ejecución todos los idiomas disponibles y que cree una scroll list dinámica a través de un elemento prefab. De este modo podremos añadir nuevos idiomas en el futuro sin tener que modificar una línea de código.


Para ello es necesario leer el número de idiomas disponibles y crear un elemento de la lista a través de un prefab para cada idioma. Al pulsar sobre el idioma llamaremos a la función que modifica el idioma actual.

 using System.Collections.Generic;  
 using UnityEngine;  
 using UnityEngine.UI;  
 public class Languages : MonoBehaviour  
 {  
   public GameObject langPrefab;  
   public GameObject itemsParent;  
   // Start is called before the first frame update  
   void Start()  
   {  
     I2.Loc.LocalizationManager.CurrentLanguageCode = GlobalInfo.language;  
     List<string> langs = new List<string>();  
     langs = I2.Loc.LocalizationManager.GetAllLanguagesCode();  
     List<string> langsName = new List<string>();  
     langsName = I2.Loc.LocalizationManager.GetAllLanguages();  
     for (int i = 0; i < langs.Count ; i++)  
     {  
       //Edit Prefab before Instantiate  
       Transform langTitle = langPrefab.GetComponentInChildren<Transform>().Find("Name");        
       langTitle.GetComponent<Text>().text = I2.Loc.LocalizationManager.GetTermTranslation("Language", true, 0, true, false, null, langsName[i]);  
       Transform langCode = langPrefab.GetComponentInChildren<Transform>().Find("Code");  
       langCode.GetComponent<Text>().text = langs[i].ToUpper();  
       Instantiate(langPrefab, new Vector3(0, 0, 0), Quaternion.identity, itemsParent.transform);  
     }      
   }  
 }  

Para ello es necesario leer el número de idiomas disponibles y crear un elemento de la lista a través de un prefab para cada idioma. Al pulsar sobre el idioma llamaremos a la función que modifica el idioma actual.


 using UnityEngine;  
 using UnityEngine.UI;  
 public class Language : MonoBehaviour  
 {  
   public Text langCode;  
   public void LoadNewLanguage()  
   {  
     I2.Loc.LocalizationManager.CurrentLanguageCode = langCode.text;  
     GlobalInfo.language = langCode.text;  
     PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>(GlobalInfo.configFile, "txt");  
     loadedData.language = GlobalInfo.language;  
     DataSaver.saveData(loadedData, GlobalInfo.configFile, "txt");  
   }  
 }  

Después de seleccionar un nuevo idioma, es necesario actualizar la variable que define el idioma actual y guardar la información en el archivo de configuración para que la próxima partida cargué el idioma deseado automáticamente.

Attila. Condiciones de victoria y derrota

Una vez construida las mecánicas básicas del juego llego el momento de programar las condiciones de victoria y derrota. De hecho son aquellas condiciones que nos hacen progresar dentro del nivel de juego y avanzar si conseguimos superar el nivel y acceder al siguiente nivel.


Este enfoque necesita gestionar el movimiento del caballo en la escena del juego así como programar las mecánica del menú de niveles que hasta ahora era un composición de imágenes estáticas antes de entrar en el nivel de prueba.

Basándonos en la mecánica del juego:

  1. Recogemos las propiedades del ejército.
  2. El caballo de mueve a una casilla disponible.
  3. Destruimos la casilla de origen.
  4. Se produce un evento en función de la casilla destino.
  5. Actualizamos las propiedades del ejército.
  6. Mostramos las nuevas casillas disponibles.


Consideraciones:

  1. Si la casilla destino es un objetivo la descontamos.
  2. Si la casilla es final pero aún quedan casillas objetivo por destruir no podemos avanzar.
  3. Si no quedan casillas objetivo y estamos en una casilla final… ejecutamos el evento y si es superado terminados el nivel.

Sobre esta mecánica hay que tener en cuenta dos situaciones distintas. En cualquier casilla se debe poder ver cuál es el balance del movimiento del caballo sobre la casilla y como afecta a la composición del ejército. En las casillas donde se supone que hay una contienda entre romanos y hunos (pueblo, ciudad, objetivo, casilla de ejército) debemos mostrar, además, el resultado de la contienda.


Puede darse el caso que en cualquier batalla, si nuestros recursos no son suficientes, que seamos derrotados y debamos volver a iniciar el nivel desde la posición inicial. En caso de ganar la batalla, recibiremos la compensación de esa casilla y seguiremos avanzando.

Para gestionar estas condiciones trabajaremos en primer lugar con la clase MovePlayer donde dejamos preparada la función para gestionar los eventos. Ahora sólo es necesario distinguir en función del tipo la casilla destino del jugador y actuar en consecuencia. Si conseguimos terminar el nivel, será necesario, también, salvar la información en el archivo de configuración y avanzar de nivel.

 public void ProcessEvents()  
   {  
     //Update values  
     if (GameObject.Find(destination).GetComponent<GameCell>().objective == true)  
     {  
       Objective();  
     }  
     if (GameObject.Find(destination).GetComponent<GameCell>().final == true)  
     {  
       Final();  
     }  
     if ((GameObject.Find(destination).GetComponent<GameCell>().final == false) && (GameObject.Find(destination).GetComponent<GameCell>().objective == false))  
     {  
       int nType = GlobalInfo.gridStage[GameObject.Find(destination).GetComponent<GameCell>().num - 1].type;  
       // Is Town OR City OR Army  
       if (nType == 4 || nType == 5 || nType == 6 )  
       {  
         GameObject.Find("GameManager").GetComponent<GameManager>().ShowBattleResult();  
       }  
       Normal();  
     }  
     //Events  
     if (GlobalInfo.objectivesNum == 0)  
     {  
       if (GlobalInfo.finalNum == 0)  
       {  
         //Good job!  
         GlobalInfo.maxStageCompleted = GlobalInfo.actualStage;  
         PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>(GlobalInfo.configFile, "txt");  
         loadedData.actualStage = GlobalInfo.actualStage;  
         loadedData.maxStageCompleted = GlobalInfo.maxStageCompleted;  
         loadedData.troops = GlobalInfo.troops;  
         loadedData.weapons = GlobalInfo.weapons;  
         loadedData.water = GlobalInfo.water;  
         loadedData.food = GlobalInfo.food;  
         loadedData.gold = GlobalInfo.gold;  
         loadedData.score = GlobalInfo.gold;  
         DataSaver.saveData(loadedData, GlobalInfo.configFile, "txt");  
         if (GlobalInfo.maxStageCompleted == GlobalInfo.maxStagesGame)  
         {  
           //Game Finish  
           SceneManager.LoadScene("Winner");  
         }  
         else  
         {  
           //Next stage  
           StartCoroutine(NextLevel());            
         }          
       }  
     }  
     if (GlobalInfo.movementsNum == 0)  
     {  
       //Game over;  
     }  
     ShowInfo();  
     GlobalInfo.isPlayerMoving = false;  
   }  
   public void Objective()  
   {  
     GlobalInfo.score = GlobalInfo.score + 100;  
     GameObject.Find(destination).GetComponent<GameCell>().UpdateValues();  
     GlobalInfo.objectivesNum--;  
     GameObject.Find("GameManager").GetComponent<GameManager>().ShowBattleResult();  
   }  
   public void Final()  
   {  
     GlobalInfo.score = GlobalInfo.score + 1000;  
     if (GlobalInfo.objectivesNum == 0)  
     {  
       GameObject.Find(destination).GetComponent<GameCell>().UpdateValues();  
       GlobalInfo.finalNum--;  
       GameObject.Find("GameManager").GetComponent<GameManager>().ShowBattleResult();  
     }      
   }  
   public void Normal()  
   {  
     GameObject.Find(destination).GetComponent<GameCell>().UpdateValues();  
     GlobalInfo.score = GlobalInfo.score + 10;  
   }  
   IEnumerator NextLevel()  
   {  
     yield return new WaitUntil(() => GlobalInfo.isShowingInfo == false);  
     GlobalInfo.actualStage++;  
     SceneManager.LoadScene("Attila");  
   }  

Así mismo debemos controlar el número de objetivos por conseguir antes de ejecutar el evento de la casilla final. Esto nos permite además incluir otra pieza en la estrategia del jugador. Mientras no ataquemos la casilla final la podemos utilizar como pivote para movernos entre otras casillas ya que esta no será destruida al pasar por ella. También las casillas de tipo montaña nos permites hacer esta función ya que tampoco serán destruidas al pasar por ellas. Estas características especiales nos permitirán modular la estrategia para resolver cada nivel y graduar y a tener más piezas para modelar la dificultad de los diferentes niveles del juego.


La segunda parte del proceso consiste en mostrar las cajas de dialogo para mostrar la información cuando es requerida o al entrar en una casilla que produce una batalla. Para ello se ha creado unas cajas de dialogo en el canvas para mostrar la información.

Hama Beads: Knight Lore

Una de las cosas que me gusta hacer para relajarme es jugar con Hama beads. Estas pequeñas piezas de colores de plástico que sobre un platilla podemos construir dibujos en modo Pixel Art. Es una buena manera de distraerse y crear composiciones con los diseños y patrones que podemos encontrar en Internet.



Sobre la temática de juegos hay muchos patrones pero de juegos clásicos y retro ya cuesta más de encontrar, así que os pongo aquí algunos diseños de juegos clásicos que he he hecho yo mismo.



Espero que os gusten y paséis un rato entretenido haciendo y compartiendo vuestras creaciones. 

CABALLERO: KNIGHT LORE (1984)

Attila. Mecánica de juego

Una vez terminado el editor de niveles, podemos crear algunos y gravarlos en la carpeta resources para que al instalar el juego en un dispositivo móvil podamos seguir accediendo a los archivos individualmente.


El siguiente paso, más allá de crear las escenas intermedias (menú principal, menú de etapas) que más adelante implementaremos nos permiten llegar a la escena del juego. En esta escena queremos mostrar el tablero en formato isométrico para poder jugar con el nivel previamente diseñado.


En concreto yo utilizo Isometric Builder para para implementar las escenas isométricas ya que me permite, más adelante, tratar cada casilla como un objetos independiente y en este caso añadir clases para cada casilla independiente.


El primer paso es diseñar el tablero máximo para distribuir los elementos de juego (tablero HUD, controles) en el espacio. En este caso yo he utilizado un gráfico isométrico genérico para implementar las casillas vacías.


A partir de aquí la implementación del juego consiste en la interrelación de las clases compuestas por el GameManager, el Player, y las casillas del tablero.



El GameManager se encarga de cargar el tablero a partir del archivo en la carpeta resources y generar el tablero en pantalla. Así mismo debemos calcular los movimientos disponibles después de mover el jugador así como procesar los eventos que se generen al mover el caballo. También es su función estar permanentemente a la escucha para saber cuándo el jugador ha pulsado alguna casilla de destino.


GameManager.cs
 using System.Collections;  
 using System.Collections.Generic;  
 using UnityEngine;  
 using UnityEngine.SceneManagement;  
 using UnityEngine.UI;  
 public class GameManager : MonoBehaviour  
 {  
   public Text troops;  
   public Text weapons;  
   public Text water;  
   public Text food;  
   public Text gold;  
   public Text stageName;  
   public Sprite tundra;  
   public Sprite desert;  
   public Sprite woods;  
   public Sprite town;  
   public Sprite city;  
   public Sprite army;  
   public Sprite crops;  
   public Sprite lake;  
   public Sprite river;  
   public Sprite mine;  
   public Sprite objective;  
   public Sprite mountains;  
   public Sprite empty;  
   public Sprite horse;  
   private int troopsO;  
   private int weaponsO;  
   private int waterO;  
   private int foodO;  
   private int goldO;  
   private int scoreO;  
   // Start is called before the first frame update  
   void Start()  
   {  
     SaveOriginals();  
     GlobalInfo.isPlaying = false;  
     GlobalInfo.stagesCount++;  
     SetEnviroment();  
     StartPlay();  
   }  
   public void SetEnviroment()  
   {  
     troops.text = GlobalInfo.troops.ToString("#,#");  
     weapons.text = GlobalInfo.weapons.ToString("#,#");  
     water.text = GlobalInfo.water.ToString("#,#");  
     food.text = GlobalInfo.food.ToString("#,#");  
     gold.text = GlobalInfo.gold.ToString("#,#");  
     Levels.LoadLevel(GlobalInfo.actualStage);  
     GlobalInfo.objectivesNum = 0;  
     GlobalInfo.finalNum = 0;  
     stageName.text = GlobalInfo.stageName;  
     PaintStage();  
     PaintInfo();  
   }  
   public void SaveOriginals()  
   {  
     //Save original values  
     troopsO = GlobalInfo.troops;  
     weaponsO = GlobalInfo.weapons;  
     waterO = GlobalInfo.water;  
     foodO = GlobalInfo.food;  
     goldO = GlobalInfo.gold;  
     scoreO = GlobalInfo.score;  
   }  
   public void RestoreOriginals()  
   {  
     //Restore original values  
     GlobalInfo.troops = troopsO;  
     GlobalInfo.weapons = weaponsO;  
     GlobalInfo.water = waterO;  
     GlobalInfo.food = foodO;  
     GlobalInfo.gold = goldO;  
     GlobalInfo.score = scoreO;  
   }   
   private void StartPlay()  
   {  
     GlobalInfo.isPlaying = true;  
   }  
   private void PaintStage()  
   {  
     int xx = 0;  
     int yy = 0;  
     for (int i = 1; i <= 64; i++)  
     {  
       GameObject cell = GameObject.Find("Cell" + i.ToString());  
       GameObject regular = GetChildWithName(cell, "Regualr_Collider_Union");  
       GameObject spriteCell = GetChildWithName(regular, "Iso2DObject_Union");  
       // GameCell Class info  
       cell.GetComponent<GameCell>().num = i;  
       cell.GetComponent<GameCell>().x = xx;  
       cell.GetComponent<GameCell>().y = yy;  
       xx++;  
       if (xx == 8)  
       {  
         xx = 0;  
         yy++;  
       }  
       //Sprite  
       if (GlobalInfo.gridStage[i - 1].type > 0)  
       {  
         spriteCell.GetComponent<SpriteRenderer>().sprite = TypeSprite(GlobalInfo.gridStage[i - 1].type);  
         if (GlobalInfo.gridStage[i - 1].isObjective)  
         {            
           GameObject ObjFlag = GeneralUtils.FindObject(cell, "Flag");  
           ObjFlag.SetActive(true);  
           cell.GetComponent<GameCell>().objective = true;  
           GlobalInfo.objectivesNum++;            
         }  
         if (GlobalInfo.gridStage[i - 1].isFinal)  
         {           
           GameObject FinFlag = GeneralUtils.FindObject(cell, "FlagF");  
           FinFlag.SetActive(true);  
           cell.GetComponent<GameCell>().final = true;  
           GlobalInfo.finalNum++;  
         }  
       } else  
       {  
         Destroy(cell);  
       }  
       //Player info  
       if (GlobalInfo.gridStage[i - 1].isStart == true)  
       {  
         GlobalInfo.playerPos = i;  
       }        
     }  
     //Set player  
     GameObject player = GameObject.Find("Player");  
     player.transform.position = GameObject.Find("Cell" + GlobalInfo.playerPos.ToString()).GetComponent<Transform>().position;  
     //Set player cell  
     GameObject cellStart = GameObject.Find("Cell" + GlobalInfo.playerPos.ToString());  
     GameObject regularStart = GetChildWithName(cellStart, "Regualr_Collider_Union");  
     GameObject spriteCellStart = GetChildWithName(regularStart, "Iso2DObject_Union");  
     spriteCellStart.GetComponent<SpriteRenderer>().sprite = horse;  
     Invoke("CalculateMovementsAvaliable", 0.5f);  
   }  
   private void CalculateMovementsAvaliable()  
   {  
     //Calculate movements avaliable  
     GameObject[] cells = GameObject.FindGameObjectsWithTag("GameCell");  
     foreach (GameObject cell in cells)  
     {  
       cell.GetComponent<GameCell>().CalculateMovements();  
     }  
     GameObject cellStart = GameObject.Find("Cell" + GlobalInfo.playerPos.ToString());  
     cellStart.GetComponent<GameCell>().SetMoveables();  
     cellStart.GetComponent<GameCell>().ShowMoveables();  
   }  
   private GameObject GetChildWithName(GameObject obj, string name)  
   {  
     Transform trans = obj.transform;  
     Transform childTrans = trans.Find(name);  
     if (childTrans != null)  
     {  
       return childTrans.gameObject;  
     }  
     else  
     {  
       return null;  
     }  
   }  
   public void PaintInfo()  
   {  
     GameObject.Find("Player").GetComponent<MovePlayer>().ShowInfo();  
   }  
   public Sprite TypeSprite(int num)  
   {  
     if (num == 1) { return tundra; }  
     if (num == 2) { return desert; }  
     if (num == 3) { return woods; }  
     if (num == 4) { return town; }  
     if (num == 5) { return city; }  
     if (num == 6) { return army; }  
     if (num == 7) { return crops; }  
     if (num == 8) { return lake; }  
     if (num == 9) { return river; }  
     if (num == 10) { return mine; }  
     if (num == 11) { return objective; }  
     if (num == 12) { return mountains; }  
     return empty;  
   }  
   public void ToStageMenu()  
   {  
     SceneManager.LoadScene("StageSelector");  
   }  
   public void RestartLevel()  
   {  
     RestoreOriginals();  
     SceneManager.LoadScene("Attila");  
   }  
   public void MoveHorse(string origen, string final)  
   {  
     GameObject.Find("Player").GetComponent<MovePlayer>().Move(final);  
   }  
   private bool DestionationAvaliable(GameObject dest)  
   {      
     return dest.GetComponent<GameCell>().moveable;  
   }  
   // Update is called once per frame  
   void Update()  
   {  
     if (Input.GetMouseButtonDown(0))  
     {  
       Ray screenRay = Camera.main.ScreenPointToRay(Input.mousePosition);  
       RaycastHit hit;  
       if (Physics.Raycast(screenRay, out hit))  
       {  
         if (hit.collider != null)  
         {  
           if (hit.collider.gameObject.name == "Regualr_Collider_Union"   
             && GlobalInfo.isPlaying == true   
             && GlobalInfo.isPlayerMoving == false)  
           {  
             if (DestionationAvaliable(GameObject.Find(hit.collider.gameObject.transform.parent.name)))  
             {  
               MoveHorse("Cell" + GlobalInfo.playerPos.ToString(), hit.collider.gameObject.transform.parent.name);  
             }              
           }            
         }          
       }  
     }  
   }  
 }  

Hay que tener en cuenta que el caballo tarda un tiempo en moverse por lo cual debemos gestionar ese tiempo de movimiento y actuar al llegar a la casilla destino y no dejar que se produzca ningún evento mientras el caballo está en movimiento. La clase MovePlayer se encarga de este trabajo, mover el caballo utilizando el NavMesh que hemos creado encima del tablero. También será la encargada de gestionar los eventos al mover el jugador e interaccionar con una casilla.

MovePlayer.cs
 using System.Collections;  
 using UnityEngine;  
 using UnityEngine.AI;  
 using UnityEngine.UI;  
 public class MovePlayer : MonoBehaviour  
 {  
   public Text objectivesText;  
   public Text finalText;  
   private NavMeshAgent agent;  
   private string destination;  
   public void Start()  
   {  
     destination = "";  
     GlobalInfo.isOldCellDestroyed = true;  
   }  
   public void Move(string mCell)  
   {  
     GameObject dest = GameObject.Find(mCell);  
     if (dest != null)  
     {  
       //Don't move at the same position  
       if ("Cell" + GlobalInfo.playerPos.ToString() != mCell)  
       {  
         NavMeshAgent agent = GetComponent<NavMeshAgent>();  
         agent.destination = dest.transform.position;  
         GlobalInfo.isPlayerMoving = true;  
         GlobalInfo.isOldCellDestroyed = false;  
         destination = mCell;  
         dest.GetComponent<GameCell>().SetMoveables();  
         GameObject origin = GameObject.Find("Cell" + GlobalInfo.playerPos.ToString());  
         origin.GetComponent<GameCell>().HideMoveables();  
         StartCoroutine(DestroyCell(origin));  
       }        
     }      
   }  
   IEnumerator DestroyCell(GameObject cellToDestroy)  
   {  
     yield return new WaitForSeconds(1f);      
     Destroy(cellToDestroy);  
     yield return new WaitForSeconds(0.5f);  
     CalculateNewMovements();  
     GlobalInfo.isOldCellDestroyed = true;  
   }  
   private void CalculateNewMovements()  
   {  
     GameObject[] cells = GameObject.FindGameObjectsWithTag("GameCell");  
     foreach (GameObject cell in cells)  
     {  
       cell.GetComponent<GameCell>().CalculateMovements();  
     }  
   }  
   private bool PathComplete()  
   {  
     NavMeshAgent mNavMeshAgent = GetComponent<NavMeshAgent>();  
     if (!mNavMeshAgent.pathPending)  
     {  
       if (mNavMeshAgent.remainingDistance <= mNavMeshAgent.stoppingDistance)  
       {  
         if (!mNavMeshAgent.hasPath || mNavMeshAgent.velocity.sqrMagnitude == 0f)  
         {  
           return true;  
         }  
         return false;  
       }  
       return false;  
     }  
     return false;  
   }  
   public void ProcessEvents()  
   {  
     //Update values  
     if (GameObject.Find(destination).GetComponent<GameCell>().objective == true)  
     {  
       GlobalInfo.objectivesNum--;  
     }  
     if (GameObject.Find(destination).GetComponent<GameCell>().final == true)  
     {  
       GlobalInfo.finalNum--;  
     }  
     if (GlobalInfo.objectivesNum == 0)  
     {  
       if (GlobalInfo.finalNum == 0)  
       {  
         //Good job!  
       }  
     }  
     if (GlobalInfo.movementsNum == 0)  
     {  
       //Game over;  
     }  
     ShowInfo();  
     GlobalInfo.isPlayerMoving = false;  
   }  
   public void ShowInfo()  
   {  
     objectivesText.text = GlobalInfo.objectivesNum.ToString();      
   }  
   private void Update()  
   {  
     if (GlobalInfo.isPlayerMoving == true)  
     {  
       if (PathComplete() && GlobalInfo.isOldCellDestroyed)  
       {          
         GameObject.Find(destination).GetComponent<GameCell>().ShowMoveables();  
         GlobalInfo.playerPos = GameObject.Find(destination).GetComponent<GameCell>().num;  
         //Set destionation cell  
         GameObject cellStart = GameObject.Find("Cell" + GlobalInfo.playerPos.ToString());  
         GameObject regularStart = GetChildWithName(cellStart, "Regualr_Collider_Union");  
         GameObject spriteCellStart = GetChildWithName(regularStart, "Iso2DObject_Union");  
         spriteCellStart.GetComponent<SpriteRenderer>().sprite = GameObject.Find("GameManager").GetComponent<GameManager>().horse;  
         ProcessEvents();          
       }        
     }  
   }  
   private GameObject GetChildWithName(GameObject obj, string name)  
   {  
     Transform trans = obj.transform;  
     Transform childTrans = trans.Find(name);  
     if (childTrans != null)  
     {  
       return childTrans.gameObject;  
     }  
     else  
     {  
       return null;  
     }  
   }  
 }  

Y finalmente tenemos la clase GameCell que se encarga de calcular las posibilidades de movimiento teniendo en cuenta el movimiento del caballo y las casillas que nos quedan disponibles después de cada tirada, mostrar u ocultar las posibilidades en pantalla.


GameCell.cs
 using System.Collections;  
 using System.Collections.Generic;  
 using UnityEngine;  
 using System;  
 public class GameCell : MonoBehaviour  
 {  
   public int x;  
   public int y;  
   public int num;  
   public bool moveable;  
   public bool final;  
   public bool objective;  
   public int[] moves = new int[8] ;  
   // Start is called before the first frame update  
   void Start()  
   {  
     ResetMovements();      
   }  
   private void ResetMovements()  
   {  
     moves[0] = 0;  
     moves[1] = 0;  
     moves[2] = 0;  
     moves[3] = 0;  
     moves[4] = 0;  
     moves[5] = 0;  
     moves[6] = 0;  
     moves[7] = 0;  
   }  
   public void CalculateMovements()  
   {  
     ResetMovements();  
     if ((x + 1 < 8) && (y - 2 >= 0))  
     {  
       moves[0] = FindGameCell(x+1,y-2);  
     }  
     if ((x + 2 < 8) && (y + 1 < 8))  
     {  
       moves[1] = FindGameCell(x + 2, y + 1);  
     }  
     if ((x + 2 < 8) && (y - 1 >= 0))  
     {  
       moves[2] = FindGameCell(x + 2, y - 1);  
     }  
     if ((x + 1 < 8) && (y + 2 < 8))  
     {  
       moves[3] = FindGameCell(x + 1, y + 2);  
     }  
     if ((x - 1 >= 0) && (y + 2 < 8))  
     {  
       moves[4] = FindGameCell(x - 1, y + 2);  
     }  
     if ((x - 2 >= 0) && (y + 1 < 8))  
     {  
       moves[5] = FindGameCell(x - 2, y + 1);  
     }  
     if ((x - 2 >= 0) && (y - 1 >= 0))  
     {  
       moves[6] = FindGameCell(x - 2, y - 1);  
     }  
     if ((x - 1 >= 0) && (y - 2 >= 0))  
     {  
       moves[7] = FindGameCell(x - 1, y - 2);  
     }  
   }    
   private int FindGameCell(int xx, int yy)  
   {  
     GameObject[] cells = GameObject.FindGameObjectsWithTag("GameCell");  
     foreach (GameObject cell in cells)  
     {  
       if (cell.GetComponent<GameCell>().x == xx && cell.GetComponent<GameCell>().y == yy)  
       {  
         return cell.GetComponent<GameCell>().num;  
       }  
     }  
     return 0;  
   }  
   public void SetMoveables()  
   {  
     GameObject[] cells = GameObject.FindGameObjectsWithTag("GameCell");  
     foreach (GameObject cell in cells)  
     {  
       cell.GetComponent<GameCell>().moveable = false;  
       int pos = Array.IndexOf(moves, cell.GetComponent<GameCell>().num);  
       if (pos > -1)  
       {  
         cell.GetComponent<GameCell>().moveable = true;          
       }  
     }  
   }  
   public void ShowMoveables()  
   {  
     GlobalInfo.movementsNum = 0;  
     for (int i = 0; i < 8; i++)  
     {  
       if (moves[i]>0)  
       {  
         GameObject cell = GameObject.Find("Cell" + moves[i].ToString());  
         GameObject selector = GeneralUtils.FindObject(cell, "Selector");  
         selector.SetActive(true);  
         GlobalInfo.movementsNum++;  
       }  
     }  
   }  
   public int GetMoveables()  
   {  
     int num = 0;  
     for (int i = 0; i < 8; i++)  
     {  
       if (moves[i] > 0)  
       {  
         num++;  
       }  
     }  
     return num;  
   }  
   public void HideMoveables()  
   {  
     for (int i = 0; i < 8; i++)  
     {  
       if (moves[i] > 0)  
       {  
         GameObject cell = GameObject.Find("Cell" + moves[i].ToString());  
         GameObject selector = GeneralUtils.FindObject(cell, "Selector");  
         selector.SetActive(false);  
       }  
     }  
   }  
 }  

Como hacer copias de tu código de Unity con GitHub

Podriamos escribir un libro entero de las bondades de Git para el trabajo colaborativo y la gestión de versiones en un entorno como Unity. D...