/// <summary> /// Method that checks if there is a collision with any collider /// </summary> /// <returns>True if a collision is detected</returns> public virtual bool CollidesAny() { // Check if we could be colliding var colliders = BoxOverlapAny(this); if (colliders.Count() != 0) { // Run through all colliders that might collide and check if we do foreach (var col in colliders) { if (Collides(col)) { return(true); } } } // Tilemap collision var circle = this as CircleCollider; if (circle != null) { return(CircleTilemapOverlap(circle, MonoGearGame.GetCurrentLevel())); } else { return(BoxTilemapOverlap(this, MonoGearGame.GetCurrentLevel())); } }
/// <summary> /// Method that checks if there is a collision with any collider /// </summary> /// <param name="other">Returns the other collider</param> /// <param name="hitTilemap">Returns if the collision is with the tilemap</param> /// <returns>True if a collision is detected</returns> public virtual bool CollidesAny(out Collider other, out bool hitTilemap) { // Check if we could be colliding var colliders = BoxOverlapAny(this); if (colliders.Count() != 0) { // Run through all colliders that might collide and check if we do foreach (var col in colliders) { if (Collides(col)) { other = col; hitTilemap = false; return(true); } } } other = null; // Tilemap collision var circle = this as CircleCollider; if (circle != null) { hitTilemap = CircleTilemapOverlap(circle, MonoGearGame.GetCurrentLevel()); } else { hitTilemap = BoxTilemapOverlap(this, MonoGearGame.GetCurrentLevel()); } return(hitTilemap); }
/// <summary> /// Adds a node to the list if it's walkable. /// </summary> /// <param name="x">Tile x</param> /// <param name="y">Tile y</param> /// <param name="map">map of tiles</param> /// <param name="list">list to add to</param> static void GetNodeIfWalkable(int x, int y, Tile[] map, List <Node> list) { var level = MonoGearGame.GetCurrentLevel(); if (x < level.Width && x >= 0 && (y) < level.Height && y >= 0 && (map[x + y * level.Width]?.Walkable) == true) { list.Add(nodes[x + y * level.Width]); } }
/// <summary> /// Updates the internal tilemap and nodes based on the active level /// </summary> private void UpdateInternalMap() { var level = MonoGearGame.GetCurrentLevel(); if (level != null) { map = level.Tiles; nodes = new Node[map.Length]; // New nodes for (int y = 0; y < level.Height; y++) { for (int x = 0; x < level.Width; x++) { nodes[x + y * level.Width] = new Node(); nodes[x + y * level.Width].Location.X = x; nodes[x + y * level.Width].Location.Y = y; } } } }
/// <summary> /// Does actual A* pathfinding. /// </summary> /// <param name="from">Start point</param> /// <param name="to">End point</param> /// <returns>List of Vector2 or null if no path can be found</returns> private static List <Vector2> FindPathImpl(Vector2 from, Vector2 to) { // Set up start and end nodes Node current = null; var level = MonoGearGame.GetCurrentLevel(); var start = nodes[(int)(from.X / level.TileWidth) + (int)(from.Y / level.TileHeight) * level.Width]; var target = nodes[(int)(to.X / level.TileWidth) + (int)(to.Y / level.TileHeight) * level.Width]; // Check for unreachable targets if (!unreachableTargets.Contains(target.Location)) { foreach (var node in nodes) { node.F = 0; node.G = 0; node.H = 0; node.closed = false; node.Parent = null; } var openList = new HashSet <Node>(); int g = 0; // start by adding the original position to the open list openList.Add(start); bool pathFound = false; while (openList.Count > 0) { // get the tile with the lowest F score var lowest = openList.Min(l => l.F); current = openList.First(l => l.F == lowest); // add the current square to the closed list current.closed = true; // remove it from the open list openList.Remove(current); // if we added the destination to the closed list, we've found a path if (target.closed) { pathFound = true; break; } var adjacentTiles = GetWalkableAdjacentTiles(current.Location, map); g++; foreach (var adjacentTile in adjacentTiles) { // if this adjacent square is already in the closed list, ignore it if (adjacentTile.closed) { continue; } // if it's not in the open list... if (!openList.Contains(adjacentTile)) { // compute its score, set the parent adjacentTile.G = g; adjacentTile.H = ComputeHScore(adjacentTile.Location, target.Location); adjacentTile.F = adjacentTile.G + adjacentTile.H; adjacentTile.Parent = current; // and add it to the open list openList.Add(adjacentTile); } else { // test if using the current G score makes the adjacent square's F score // lower, if yes update the parent because it means it's a better path if (g + adjacentTile.H < adjacentTile.F) { adjacentTile.G = g; adjacentTile.F = adjacentTile.G + adjacentTile.H; adjacentTile.Parent = current; } } } } if (pathFound) { List <Vector2> path = new List <Vector2>(); // Build path by backtracing while (current != null) { path.Add(new Vector2(current.Location.X, current.Location.Y) * level.TileHeight + Vector2.One * level.TileHeight / 2); current = current.Parent; } // Reverse it for the correct order path.Reverse(); return(path); } else { // Assume point is not reachable for anyone since the playable area should always be one zone unreachableTargets.Add(target.Location); } } // Couldn't find anything return(null); }
/// <summary> /// Called once per frame /// </summary> /// <param name="input">input</param> /// <param name="gameTime">gametime</param> public override void Update(Input input, GameTime gameTime) { // Animation done by parent class base.Update(input, gameTime); // Movement delta var dx = 0.0f; var dy = 0.0f; // Use analog sticks or keyboard for movement depending on if we have a gamepad connected if (input.PadConnected()) { var sticks = input.GetGamepadState().ThumbSticks; dx += sticks.Left.X * Speed; dy += sticks.Left.Y * Speed; } else { if (input.IsButtonDown(Input.Button.Left)) { dx -= Speed; } if (input.IsButtonDown(Input.Button.Right)) { dx += Speed; } if (input.IsButtonDown(Input.Button.Up)) { dy -= Speed; } if (input.IsButtonDown(Input.Button.Down)) { dy += Speed; } } // Sneak mode if (input.IsButtonDown(Input.Button.Sneak)) { SneakMode = true; Speed = 50; } else { SneakMode = false; Speed = 100; } // Clamp movement speed var delta = new Vector2(dx, dy); if (delta.LengthSquared() > Speed * Speed) { delta.Normalize(); delta *= Speed; } // Get correct tile sound var tilevalue = MonoGearGame.GetCurrentLevel().GetTile(Position)?.Sound; SoundEffectInstance tilesound; switch (tilevalue) { case Tile.TileSound.Grass: tilesound = walkingSoundGrass; break; case Tile.TileSound.Water: tilesound = walkingSoundWater; break; case Tile.TileSound.Concrete: tilesound = walkingSoundStone; break; default: tilesound = walkingSoundStone; break; } if (tilesound != null && tilesound != walkingSound) { // stop old sound walkingSound.Stop(); walkingSound = tilesound; } if (delta.LengthSquared() > 0) { // Moving Rotation = MathExtensions.VectorToAngle(delta); AnimationRunning = true; walkingSound.Play(); } else { // Standing still SneakMode = true; AnimationRunning = false; AnimationCurrentFrame = 1; walkingSound.Stop(); } // Sneaking is silent if (SneakMode) { walkingSound.Stop(); } // Reduce delay per frame if (ThrowingDelay > 0) { ThrowingDelay -= 1; } // Throw rock if (input.IsButtonPressed(Input.Button.Throw)) { if (ThrowingDelay <= 0) { // Spawn rock and play sound var rock = new Rock(MonoGearGame.FindEntitiesOfType <Player>()[0].Collider); rock.Position = Position; rock.Rotation = Rotation; MonoGearGame.SpawnLevelEntity(rock); ThrowingDelay = 45; var sound = MonoGearGame.GetResource <SoundEffect>("Audio/AudioFX/StoneTrow_sound").CreateInstance(); sound.Volume = 1 * SettingsPage.Volume * SettingsPage.EffectVolume; sound.Play(); } } // Shoot sleep dart if (input.IsButtonPressed(Input.Button.Shoot)) { if (DartCount > 0) { // Spawn dart and play sound var sleepDart = new SleepDart(MonoGearGame.FindEntitiesOfType <Player>()[0].Collider); sleepDart.Position = Position; sleepDart.Rotation = Rotation; MonoGearGame.SpawnLevelEntity(sleepDart); DartCount--; var sound = MonoGearGame.GetResource <SoundEffect>("Audio/AudioFX/Blowgun").CreateInstance(); sound.Volume = 1 * SettingsPage.Volume * SettingsPage.EffectVolume; sound.Play(); } } // Check collisions if (input.IsKeyDown(Keys.N)) { // Noclip mode for debugging Position += delta * (float)gameTime.ElapsedGameTime.TotalSeconds * 10; } else { // Check collisions per axis var prevPos = Position; var deltaX = new Vector2(delta.X, 0); var deltaY = new Vector2(0, delta.Y); Position += deltaX * (float)gameTime.ElapsedGameTime.TotalSeconds; if (Collider.CollidesAny()) { // Reset if we hit anything Position = prevPos; } prevPos = Position; Position += deltaY * (float)gameTime.ElapsedGameTime.TotalSeconds; if (Collider.CollidesAny()) { // Reset if we hit anything Position = prevPos; } } // Camera tracks player Camera.main.Position = new Vector2(Position.X, Position.Y); }