void UpdateGameplay(float fDeltaTime) { if (!IsPaused) { m_Camera.Update(fDeltaTime); if (GameState.Get().Speed != eGameSpeed.Stop) { float fAlteredTime = fDeltaTime * TimeFactor; // We have to make a temp copy in case the objects list changes LinkedList <GameObject> temp = new LinkedList <GameObject>(m_GameObjects); foreach (GameObject o in temp) { if (o.Enabled) { o.Update(fAlteredTime); } } m_Timer.Update(fAlteredTime); } // Use mouse picking to select the appropriate tile. Ray ray = InputManager.Get().CalculateMouseRay(); m_CurrentTile = m_Level.Intersects(ray); } }
// This selects a particular tile given a ray from the camera public Objects.Tile Intersects(Ray ray) { // It's possible that multiple tiles intersect, so we need to create a list // of potential tiles to select. LinkedList <Objects.Tile> possibles = new LinkedList <Objects.Tile>(); foreach (Objects.Tile t in m_Tiles) { if (t.AABB.Intersects(ray) != null) { possibles.AddLast(t); } } // Now select the tile that is the closest to the start position of the ray Objects.Tile retval = null; float fBestDist = 999999999.0f; foreach (Objects.Tile t in possibles) { float fDist = Vector3.DistanceSquared(t.Position, ray.Position); if (fDist < fBestDist) { retval = t; fBestDist = fDist; } } return(retval); }
public void SetupGameplay() { ClearGameObjects(); m_UIStack.Clear(); m_UIGameplay = new UI.UIGameplay(m_Game.Content); m_UIStack.Push(m_UIGameplay); m_bPaused = false; m_Speed = eGameSpeed.Normal; m_Camera.ResetCamera(); GraphicsManager.Get().ResetProjection(); m_SelectedTile = null; m_CurrentTile = null; m_Timer.RemoveAll(); Money = Balance.StartingMoney; Life = Balance.StartingLife; m_WaveNumber = 0; m_bWaveActive = false; SpawnWorld(); // Start the timer for the first wave m_Timer.AddTimer("StartWave", Balance.FirstWaveTime, StartWave, false); m_bCanPlayAlarm = true; }
// Helper function that creates the correct type of tile Objects.Tile CreateTile(Vector3 vPos, eTileType type = eTileType.Default) { Objects.Tile Tile = null; switch (type) { case (eTileType.Default): Tile = new Objects.Tile(m_Game); break; case (eTileType.Green): Tile = new Objects.TileGreen(m_Game); break; case (eTileType.Base): Tile = new Objects.TileGreen(m_Game); break; case (eTileType.Red): Tile = new Objects.TileRed(m_Game); break; case (eTileType.EnemySpawn): Tile = new Objects.TileRed(m_Game); break; } if (Tile != null) { Tile.Position = vPos; GameState.Get().SpawnGameObject(Tile); m_Tiles.AddLast(Tile); } return(Tile); }
// This function is called when a new tile is clicked on and selected public void SetSelected(Objects.Tile t) { if (m_SelectedTile != null) { m_SelectedTile.IsSelected = false; } m_SelectedTile = t; // Notify the UI that a new tile has been selected m_UIGameplay.NewSelectedTile(t); if (m_SelectedTile != null) { m_SelectedTile.IsSelected = true; } }
public override void Update(float fDeltaTime) { // Update the position between the two nodes if (m_LerpTo != null) { float fTotalDistance = Vector3.Distance(m_StartPos, m_EndPos); float fTotalTime = fTotalDistance / Balance.Enemies[m_Level - 1].Speed; float fSnareAmount = SnareFactor / 100.0f; m_fMoveTime += fDeltaTime * fSnareAmount; Position = Vector3.Lerp(m_StartPos, m_EndPos, m_fMoveTime / fTotalTime); // If we're at the target node, time to move on to the next one if (Vector3.Distance(Position, m_EndPos) < 0.05f) { m_LerpFrom = m_LerpTo; m_LerpTo = m_LerpFrom.parent; m_StartPos = m_LerpFrom.tile.Position; m_EndPos = m_LerpTo.tile.Position; // Update the current tile m_CurrentTile = m_LerpFrom.tile; if (m_LerpTo != null) { m_fMoveTime = 0.0f; SetDirection(); } } } // If we're at the goal node, deal damage and remove from the world if (Vector3.Distance(Position, Pathfinder.Get().GlobalGoalTile.Position) < 0.5f) { GameState.Get().DamageBase(Balance.Enemies[m_Level - 1].Damage); GameState.Get().RemoveEnemy(this); } base.Update(fDeltaTime); }
public void ResetPath(bool bRecalculated = false) { if (m_Path != null) { m_LerpFrom = m_Path; m_CurrentTile = m_LerpFrom.tile; m_LerpTo = m_LerpFrom.parent; // If this was recalculated (because something was built), // set the start pos to the current one, so it smoothly transitions to the new path. if (bRecalculated) { m_StartPos = Position; } else { m_StartPos = m_LerpFrom.tile.Position; } m_EndPos = m_LerpTo.tile.Position; SetDirection(); } else { // We have no path, so we're just stuck m_LerpFrom = null; m_LerpTo = null; } m_fMoveTime = 0.0f; }
public void AddNeighbor(Tile t) { m_Neighbors.AddLast(t); }
public virtual void LoadLevel(string sLevelName) { m_Tiles.Clear(); int iWidth = 10; int iHeight = 5; int[,] TileData = { {2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {2, 2, 0, 0, 0, 0, 0, 0, 0, 0}, {4, 0, 0, 0, 0, 0, 0, 0, 0, 3}, {2, 2, 0, 0, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }; // Generate tile array based on width/height // (used to setup neighbors) Objects.Tile[,] Tiles = new Objects.Tile[iHeight, iWidth]; float fPerColumn = 1.72f; float fPerRow = 1.5f; Objects.Tile BaseTile = null; Objects.Tile SpawnTile = null; int iColCount = 0; float fXOffset = -1.0f * (iWidth / 2) * fPerColumn; while (iColCount < iWidth) { int iRowCount = 0; float fZOffset = -1.0f * (iHeight / 2) * fPerRow; while (iRowCount < iHeight) { eTileType type = (eTileType)TileData[iRowCount, iColCount]; float fTileHeight = GlobalDefines.fTileHeight; Objects.Tile t; if (iRowCount % 2 == 0) { t = CreateTile(new Vector3(fXOffset, fTileHeight, fZOffset), type); } else { t = CreateTile(new Vector3(fXOffset - 0.875f, fTileHeight, fZOffset), type); } Tiles[iRowCount, iColCount] = t; if (type == eTileType.Base) { BaseTile = t; } else if (type == eTileType.EnemySpawn) { SpawnTile = t; } fZOffset += fPerRow; iRowCount++; } fXOffset += fPerColumn; iColCount++; } // Now loop through the array of tiles to assign neighbors. // Since these are hexagons, the row affects which hexagons are neighbors. for (int i = 0; i < iHeight; i++) { for (int j = 0; j < iWidth; j++) { // East/West are same regardless of row modulus // E if (j + 1 < iWidth) { Tiles[i, j].AddNeighbor(Tiles[i, j + 1]); } // W if (j - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i, j - 1]); } if (i % 2 == 0) { // NE if ((i - 1 >= 0) && (j + 1 < iWidth)) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j + 1]); } // SE if ((i + 1 < iHeight) && (j + 1 < iWidth)) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j + 1]); } // SW if (i + 1 < iHeight) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j]); } // NW if (i - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j]); } } else { // NE if (i - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j]); } // SE if (i + 1 < iHeight) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j]); } // SW if ((i + 1 < iHeight) && (j - 1 >= 0)) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j - 1]); } // NW if ((i - 1 >= 0) && (j - 1 >= 0)) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j - 1]); } } } } // These values let the camera know what the maximum scroll area should be. GameState.Get().Camera.LevelMin = Tiles[0, 0].Position; GameState.Get().Camera.LevelMax = Tiles[iHeight - 1, iWidth - 1].Position; // Create the player's base and initial path for enemies if (BaseTile != null && SpawnTile != null) { BaseTile.Build(new Objects.Base(m_Game)); Pathfinder.Get().GlobalStartTile = SpawnTile; Pathfinder.Get().GlobalGoalTile = BaseTile; Pathfinder.Get().ComputeAStar(); GameState.Get().SetSelected(BaseTile); } }
// Helper function that creates the correct type of tile Objects.Tile CreateTile(Vector3 vPos, eTileType type = eTileType.Default) { Objects.Tile Tile = null; switch (type) { case (eTileType.Default): Tile = new Objects.Tile(m_Game); break; case (eTileType.Green): Tile = new Objects.TileGreen(m_Game); break; case (eTileType.Base): Tile = new Objects.TileGreen(m_Game); break; case (eTileType.Red): Tile = new Objects.TileRed(m_Game); break; case (eTileType.EnemySpawn): Tile = new Objects.TileRed(m_Game); break; } if (Tile != null) { Tile.Position = vPos; GameState.Get().SpawnGameObject(Tile); m_Tiles.AddLast(Tile); } return Tile; }
// Computes the enemy path using A* // Returns true if there is a valid path, false if not. // If the optional parameter is NOT set, it sets the global path that all enemies use by default // otherwise, this function calculates the path only for the passed in Enemy. // This is to allow recalculation of paths for currently active Enemiess. public bool ComputeAStar(Objects.Enemy e = null) { // Clear out the open/closed set from previous paths m_OpenSet.Clear(); m_ClosedSet.Clear(); Objects.Tile StartTile = GlobalStartTile; // If we have a specific Enemy, the starting tile is the one the Enemy is currently at if (e != null) { StartTile = e.CurrentTile; } // We start at the goal node instead of the start node // so that the parent linked list does not need to be reversed once the A* is complete. PathNode node = new PathNode(); node.tile = GlobalGoalTile; node.g = 0.0f; node.h = 0.0f; node.f = 0.0f; // Initialize the A* algorithm PathNode CurrentNode = node; m_ClosedSet.Add(CurrentNode.tile, CurrentNode); do { // Loop through all the nodes adjacent to the current one foreach (Objects.Tile t in CurrentNode.tile.m_Neighbors) { // Is this tile already in the closed set? if (m_ClosedSet.ContainsKey(t)) { // If so, skip. continue; } else if (m_OpenSet.ContainsKey(t)) { // Check if node adoption should occur float test_g = CurrentNode.g + Vector3.Distance(t.Position, CurrentNode.tile.Position); // Is the new g(x) value lower than the current one? node = m_OpenSet[t]; if (test_g < node.g) { // If so, change parent node.parent = CurrentNode; node.g = test_g; node.f = node.g + node.h; } } // Tiles with a building are not passable, so should be ignored... // UNLESS it's the start node, which can happen if this is a Enemy repositioning else if (t.Tower == null || t == StartTile) { // Add a new PathNode to open set with CurrentNode as parent node = new PathNode(); node.parent = CurrentNode; node.tile = t; // Calculate h(x) using Euclidean distance node.h = Vector3.Distance(GlobalGoalTile.Position, t.Position); // Calculate g(x) node.g = node.parent.g + Vector3.Distance(t.Position, node.parent.tile.Position); // f(x) = g(x) + h(x) node.f = node.g + node.h; // Add to open set m_OpenSet.Add(t, node); } } // If the open set is empty, we failed to find a path if (m_OpenSet.Count == 0) { break; } // Search the open set for lowest f(x) cost float lowest_f = 10000000000.0f; foreach (PathNode p in m_OpenSet.Values) { if (p.f < lowest_f) { CurrentNode = p; lowest_f = p.f; } } // Move CurrentNode to closed set m_OpenSet.Remove(CurrentNode.tile); m_ClosedSet.Add(CurrentNode.tile, CurrentNode); }while (CurrentNode.tile != StartTile); // Check to see if we found a path if (CurrentNode.tile == StartTile) { // Set the start of the path to the current node // If there is no Enemy, this is the new global path if (e == null) { GlobalPath = CurrentNode; } else { e.m_Path = CurrentNode; e.ResetPath(true); } return(true); } else { // If there is no Enemy, this is the new global path if (e == null) { GlobalPath = null; } else { e.m_Path = null; e.ResetPath(true); } return(false); } }
public virtual void LoadLevel(string sLevelName) { m_Tiles.Clear(); int iWidth = 10; int iHeight = 5; int[,] TileData = { { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { 4, 0, 0, 0, 0, 0, 0, 0, 0, 3 }, { 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; // Generate tile array based on width/height // (used to setup neighbors) Objects.Tile[,] Tiles = new Objects.Tile[iHeight, iWidth]; float fPerColumn = 1.72f; float fPerRow = 1.5f; Objects.Tile BaseTile = null; Objects.Tile SpawnTile = null; int iColCount = 0; float fXOffset = -1.0f * (iWidth / 2) * fPerColumn; while (iColCount < iWidth) { int iRowCount = 0; float fZOffset = -1.0f * (iHeight / 2) * fPerRow; while (iRowCount < iHeight) { eTileType type = (eTileType)TileData[iRowCount, iColCount]; float fTileHeight = GlobalDefines.fTileHeight; Objects.Tile t; if (iRowCount % 2 == 0) { t = CreateTile(new Vector3(fXOffset, fTileHeight, fZOffset), type); } else { t = CreateTile(new Vector3(fXOffset - 0.875f, fTileHeight, fZOffset), type); } Tiles[iRowCount, iColCount] = t; if (type == eTileType.Base) { BaseTile = t; } else if (type == eTileType.EnemySpawn) { SpawnTile = t; } fZOffset += fPerRow; iRowCount++; } fXOffset += fPerColumn; iColCount++; } // Now loop through the array of tiles to assign neighbors. // Since these are hexagons, the row affects which hexagons are neighbors. for (int i = 0; i < iHeight; i++) { for (int j = 0; j < iWidth; j++) { // East/West are same regardless of row modulus // E if (j + 1 < iWidth) { Tiles[i, j].AddNeighbor(Tiles[i, j + 1]); } // W if (j - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i, j - 1]); } if (i % 2 == 0) { // NE if ((i - 1 >= 0) && (j + 1 < iWidth)) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j + 1]); } // SE if ((i + 1 < iHeight) && (j + 1 < iWidth)) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j + 1]); } // SW if (i + 1 < iHeight) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j]); } // NW if (i - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j]); } } else { // NE if (i - 1 >= 0) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j]); } // SE if (i + 1 < iHeight) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j]); } // SW if ((i + 1 < iHeight) && (j - 1 >= 0)) { Tiles[i, j].AddNeighbor(Tiles[i + 1, j - 1]); } // NW if ((i - 1 >= 0) && (j - 1 >= 0)) { Tiles[i, j].AddNeighbor(Tiles[i - 1, j - 1]); } } } } // These values let the camera know what the maximum scroll area should be. GameState.Get().Camera.LevelMin = Tiles[0, 0].Position; GameState.Get().Camera.LevelMax = Tiles[iHeight - 1, iWidth - 1].Position; // Create the player's base and initial path for enemies if (BaseTile != null && SpawnTile != null) { BaseTile.Build(new Objects.Base(m_Game)); Pathfinder.Get().GlobalStartTile = SpawnTile; Pathfinder.Get().GlobalGoalTile = BaseTile; Pathfinder.Get().ComputeAStar(); GameState.Get().SetSelected(BaseTile); } }