// Busca los 4 vecinos de una casilla, en horizontal y vertical public List <Casilla> Get4Neighbours(Casilla cell) { List <Casilla> neighbours = new List <Casilla>(); for (int r = -1; r <= 1; r++) { for (int c = -1; c <= 1; c++) { if (r == 0 && c == 0) { continue; } if (r == 0 || c == 0) //Para no coger las diagonales { int checkCol = Mathf.RoundToInt(cell.pos.GetColumn() + r); int checkRow = Mathf.RoundToInt(cell.pos.GetRow() + c); if (checkCol >= 0 && checkCol < casillas_.GetLength(0) && checkRow >= 0 && checkRow < casillas_.GetLength(1)) { neighbours.Add(casillas_[checkRow, checkCol]); } } } } return(neighbours); }
// Te enseña el camino hasta el candy public void showPath() { for (int i = 0; i < path_.Count - 1; i++) { Vector3 holi = path_[i].transform.position - path_[i + 1].transform.position; Casilla cel = Instantiate(flechaPrefab, new Vector3(path_[i].transform.position.x, (float)0.3, path_[i].transform.position.z), Quaternion.identity.normalized); if (holi.x == 0 && holi.z > 0) { cel.transform.Rotate(new Vector3(0, 0, 0), Space.Self); } else if (holi.x < 0 && holi.z == 0) { cel.transform.Rotate(new Vector3(0, -90, 0), Space.Self); } else if (holi.x == 0 && holi.z < 0) { cel.transform.Rotate(new Vector3(0, 180, 0), Space.Self); } else if (holi.x > 0 && holi.z == 0) { cel.transform.Rotate(new Vector3(0, 90, 0), Space.Self); } cel.Init(this, 7); flechas_.Add(cel); } }
//Calculo original del coste de movimiento private int GetDistance(Casilla a, Casilla b) { int dstX = Mathf.RoundToInt(Mathf.Abs(a.pos.GetRow() - b.pos.GetRow())); int dstY = Mathf.RoundToInt(Mathf.Abs(a.pos.GetColumn() - b.pos.GetColumn())); if (dstX > dstY) { return(14 * dstY + 10 * (dstX - dstY)); } else { return(14 * dstX + 10 * (dstY - dstX)); } }
// Te devuelve el camino que deves seguir dando la vuelta a la lista de casillas que se ha ido guardando private void RetracePath(Casilla startCasilla, Casilla endCasilla) { List <Casilla> path = new List <Casilla>(); Casilla currentCasilla = endCasilla; while (currentCasilla != startCasilla) { cost += currentCasilla.penalty; path.Add(currentCasilla); currentCasilla = currentCasilla.parent; } path.Reverse(); tablero_.setPath(path); }
private int PenaltyDistance(Casilla a, Casilla b) { int dstX = Mathf.RoundToInt(Mathf.Abs(a.pos.GetRow() - b.pos.GetRow())); int dstY = Mathf.RoundToInt(Mathf.Abs(a.pos.GetColumn() - b.pos.GetColumn())); int penalty = b.penalty; if (dstX > dstY) { return(10 * dstY + 10 * (dstX - dstY) + 10 * penalty); } else { return(10 * dstX + 10 * (dstY - dstX) + 10 * penalty); } }
// Realiza la busqueda private void search() { if (Input.GetButtonDown("Jump")) { this.setWorking(true); mode_ = tablero_.getTank().getMode(); seeker = tablero_.getTank(); target = tablero_.getCandy(); if (seeker != null && target != null && working) { selectMode(); tablero_.getTank().setSteps(0); } } }
private void Move() { towardsTarget = targetPosition - transform.position; if (towardsTarget.magnitude < 0.25f) //Si llega al destino obtener la siguiente posicion { steps_++; Casilla nextObj = board_.MoveTank(steps_); if (nextObj != null) { RecalculateTargetPosition(nextObj); } } transform.position += towardsTarget.normalized * movementSpeed * Time.deltaTime; pos = tPos; }
//Elimina el candy actual y lo sustituye por una casilla pisable cualquiera private void deleteLastCandy() { Casilla cel = null; switch (UnityEngine.Random.Range(0, 3)) { case 0: //Creamos un suelo cel = Instantiate(sueloPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + candy_.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - candy_.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = candy_.pos; cel.Init(this, 0); break; case 1: //Creamos un agua cel = Instantiate(aguaPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + candy_.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - candy_.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = candy_.pos; cel.Init(this, 1); break; case 2: //Creamos un barro cel = Instantiate(barroPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + candy_.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - candy_.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = candy_.pos; cel.Init(this, 2); break; default: break; } if (cel != null) { Destroy(casillas_[candy_.pos.GetRow(), candy_.pos.GetColumn()].gameObject); casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; } }
//--------------------------------Privates-------------------- //Crea un candy en la posicion de la casilla que le pasas private void createCandy(Casilla c) { if (candy_ != null) { deleteLastCandy(); } Casilla cel = Instantiate(banderaPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - c.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity.normalized); cel.transform.Rotate(0, 180, 0, Space.Self); cel.pos = c.pos; cel.Init(this, 4); cel.candy_ = true; setCandy(cel); Destroy(casillas_[c.pos.GetRow(), c.pos.GetColumn()].gameObject); //Borramos la casilla a cambiar casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; //Asignamos la nueva casilla }
//Seleccionar una casilla con Candy (Solo se usa en el init de game manager para colocar la primera casilla) public void GiveCandy(uint r, uint c) { Casilla cell = casillas_[r, c]; if (cell != null) { cell = null; Destroy(casillas_[r, c].gameObject); } cell = Instantiate(candyPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); cell.candy_ = true; Position pos = new Position(r, c); cell.pos = pos; cell.Init(this, 4); setCandy(cell); casillas_[r, c] = cell; }
//Calculo del coste de movimiento por ManhattanDistance private int ManhattanDistance(Casilla a, Casilla b) { return(10 * Mathf.RoundToInt(Mathf.Abs(a.pos.GetRow() - b.pos.GetRow()) + Mathf.Abs(a.pos.GetColumn() - b.pos.GetColumn()))); }
// Implementa el mismo A* que el metodo de arriba, solo que en vez de usar Heap usa Listas, lo que incrementa el coste de ejecucion private void SlowerFindingPath(Position iPos, Position fPos) { Stopwatch sw = new Stopwatch(); sw.Start(); Casilla startCasilla = tablero_.GetBlock(iPos); Casilla targetCasilla = tablero_.GetBlock(fPos); List <Casilla> openSet = new List <Casilla>(); HashSet <Casilla> closedSet = new HashSet <Casilla>(); openSet.Add(startCasilla); while (openSet.Count > 0) { Casilla currentCasilla = openSet[0]; for (int i = 1; i < openSet.Count; i++) { // Coomparamos los fCost de las casillas if (openSet[i].fCost < currentCasilla.fCost || openSet[i].fCost == currentCasilla.fCost && openSet[i].hCost < currentCasilla.hCost) { currentCasilla = openSet[i]; } } openSet.Remove(currentCasilla); closedSet.Add(currentCasilla); //En caso de que lleguemos al caramelo if (currentCasilla == targetCasilla) { sw.Stop(); UnityEngine.Debug.Log(ToString() + "Path found: " + sw.ElapsedMilliseconds + " ms"); TiempoText_.text = "Tiempo: " + sw.ElapsedMilliseconds + "ms"; RetracePath(startCasilla, targetCasilla); return; } foreach (Casilla neighbour in tablero_.Get4Neighbours(currentCasilla)) { //Si es muro o default pasamos la iteracion if (neighbour.type_ == 3 || neighbour.type_ == 6 || closedSet.Contains(neighbour) || neighbour == null) { continue; } // A partir de aqui se calcula el coste de movimiento (hCost, gCost y penalty) int newMovementCostToNeighbour = currentCasilla.gCost + GetDistance(currentCasilla, neighbour) + neighbour.penalty; if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newMovementCostToNeighbour; neighbour.hCost = GetDistance(neighbour, targetCasilla); neighbour.parent = currentCasilla; if (!openSet.Contains(neighbour)) { // Si ya lo tiene esque ha cambiado el valor de esa casilla openSet.Add(neighbour); } } } } }
public void setCandy(Casilla c) { candy_ = c; }
// Generador de casillas private void GenerateCasillas(Map m) { if (m == null) { throw new ArgumentNullException(nameof(m)); } var rows = casillas_.GetLength(0); var cols = casillas_.GetLength(1); for (var r = 0u; r < rows; r++) { for (var c = 0u; c < cols; c++) { //Creamos la casilla en una row y col del vector de casillas Casilla cel = casillas_[r, c]; // Le damos esa row y col como posicion Position pos = new Position(r, c); uint value = m.GetValue(pos); if (cel == null) { switch (value) { //Suelo case 0: cel = Instantiate(sueloPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); break; //Agua case 1: cel = Instantiate(aguaPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); break; //Barro case 2: cel = Instantiate(barroPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); break; //Muro case 3: cel = Instantiate(muroPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); break; //Default default: cel = Instantiate(sueloPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - r * POSITION_FACTOR_R), Quaternion.identity); break; } cel.pos = pos; cel.Init(this, value); casillas_[r, c] = cel; } } } }
//-------------------------------------------------Interacciones con el tablero--------------------------------- // Cambia un tipo de casilla por otra public void changeCasilla(Casilla c) { Casilla cel; if (!tank_.selected) { if (tank_.pos != c.pos) // Si la posicion de la casilla y el tanque no es la misma { switch (c.type_) { case 0: // El suelo pasa a ser agua cel = Instantiate(aguaPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - c.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = c.pos; cel.Init(this, 1); Destroy(casillas_[c.pos.GetRow(), c.pos.GetColumn()].gameObject); //Borramos la casilla a cambiar casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; //Asignamos la nueva casilla break; case 1: // El agua pasa a ser barro cel = Instantiate(barroPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - c.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = c.pos; cel.Init(this, 2); Destroy(casillas_[c.pos.GetRow(), c.pos.GetColumn()].gameObject); //Borramos la casilla a cambiar casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; //Asignamos la nueva casilla break; case 2: // El barro pasa a ser muro cel = Instantiate(muroPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - c.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = c.pos; cel.Init(this, 3); Destroy(casillas_[c.pos.GetRow(), c.pos.GetColumn()].gameObject); //Borramos la casilla a cambiar casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; //Asignamos la nueva casilla break; case 3: // El muro pasa a ser suelo cel = Instantiate(sueloPrefab, new Vector3(-((casillas_.GetLength(1) / 2.0f) * POSITION_FACTOR_C - (POSITION_FACTOR_C / 2.0f)) + c.pos.GetColumn() * POSITION_FACTOR_C, 0, (casillas_.GetLength(0) / 2.0f) * POSITION_FACTOR_R - (POSITION_FACTOR_R / 2.0f) - c.pos.GetRow() * POSITION_FACTOR_R), Quaternion.identity); cel.pos = c.pos; cel.Init(this, 0); Destroy(casillas_[c.pos.GetRow(), c.pos.GetColumn()].gameObject); //Borramos la casilla a cambiar casillas_[cel.pos.GetRow(), cel.pos.GetColumn()] = cel; //Asignamos la nueva casilla break; default: break; } } } else { resetPath(); switch (c.type_) { case 0: // Nuevo candy! createCandy(c); break; case 1: // Nuevo candy! createCandy(c); break; case 2: // Nuevo candy! createCandy(c); break; default: break; } } }
// Busqueda por el algoritmo de A* busca en las 4-8 posiciones adjuntas a la posicion de busqueda // "los 4-8 vecinos" y calcula por cual de ellos es mas rapido llegar a la meta, si es que se puede // pasar por ellos claro. // Dependiendo del modo activado tiene en cuenta el coste de avanzar por una casilla o no private void FindPath(Position initPos, Position targetPos) { Stopwatch sw = new Stopwatch(); sw.Start(); Casilla startCasilla = tablero_.GetBlock(initPos); Casilla targetCasilla = tablero_.GetBlock(targetPos); //List<Casilla> openSet = new List<Casilla>(); Heap <Casilla> openSet = new Heap <Casilla>(tablero_.MaxSize); HashSet <Casilla> closedSet = new HashSet <Casilla>(); openSet.Add(startCasilla); while (openSet.Count > 0) { Casilla currentCasilla = openSet.RemoveFirst(); closedSet.Add(currentCasilla); //En caso de que lleguemos al caramelo if (currentCasilla == targetCasilla) { sw.Stop(); UnityEngine.Debug.Log(ToString() + "Path found: " + sw.ElapsedMilliseconds + " ms"); TiempoText_.text = "Tiempo: " + sw.ElapsedMilliseconds + "ms"; RetracePath(startCasilla, targetCasilla); return; } //Mirar los vecinos de cada Casilla que exploramos //foreach(Casilla neighbour in tablero_.Get8Neighbours(currentCasilla)) { // Para 8 vecinos foreach (Casilla neighbour in tablero_.Get4Neighbours(currentCasilla)) // Para 4 vecinos //Si es muro o default pasamos la iteracion { if (neighbour.type_ == 3 || neighbour.type_ == 6 || closedSet.Contains(neighbour) || neighbour == null) { continue; } // A partir de aqui se calcula el coste de movimiento (hCost, gCost) int newMovementCostToNeighbour; //--------------------Modo 0------------------- if (!mode2) { newMovementCostToNeighbour = currentCasilla.gCost + PenaltyDistance(currentCasilla, neighbour); if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newMovementCostToNeighbour; neighbour.hCost = PenaltyDistance(neighbour, targetCasilla); neighbour.parent = currentCasilla; if (!openSet.Contains(neighbour)) { // Si ya lo tiene esque ha cambiado el valor de esa casilla openSet.Add(neighbour); } else // Actualizamos la casilla { openSet.UpdateItem(neighbour); } } } //---------------------Modo 2------------------ else { newMovementCostToNeighbour = currentCasilla.gCost + GetDistance(currentCasilla, neighbour) + neighbour.penalty; if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { //cost += newMovementCostToNeighbour; neighbour.gCost = newMovementCostToNeighbour; neighbour.hCost = GetDistance(neighbour, targetCasilla); neighbour.parent = currentCasilla; if (!openSet.Contains(neighbour)) { // Si ya lo tiene esque ha cambiado el valor de esa casilla openSet.Add(neighbour); } else // Actualizamos la casilla { openSet.UpdateItem(neighbour); } } } } } }
public void RecalculateTargetPosition(Casilla p) { targetPosition = p.transform.position; targetPosition.y = 1; tPos = p.pos; }