sfml enemy follow path in csharp

To make the enemy follow a path in SFML using C# language, we need to write a code that finds the shortest path from the enemy's current position to the target position using a pathfinding algorithm. Once the path is obtained, we can move the enemy along this path by updating its position each frame.

Here's an example code that shows how to make an enemy follow a path using A* pathfinding algorithm and SFML in C#:

main.cs
using SFML.Graphics;
using SFML.System;
using SFML.Window;
using System;
using System.Collections.Generic;

namespace Game
{
    class Enemy
    {
        private readonly List<Vector2f> _path;
        private int _nextNodeIndex;

        public Vector2f Position { get; set; }

        public Enemy(Vector2f position, Vector2f target)
        {
            Position = position;
            _path = FindPath(position, target);
            _nextNodeIndex = 0;
        }

        public void Update(float deltaTime, Vector2f target)
        {
            // get the direction vector towards the next path node
            var direction = _path[_nextNodeIndex] - Position;
            var distanceToNode = direction.Length;

            // if we're close enough to the next node, select the next node in the path
            if (distanceToNode < 5.0f)
            {
                _nextNodeIndex++;
                if (_nextNodeIndex >= _path.Count)
                {
                    // if we've reached the end of the path, generate a new path to the target
                    _path.Clear();
                    _path.AddRange(FindPath(Position, target));
                    _nextNodeIndex = 0;
                }
            }

            direction /= distanceToNode;

            // move the enemy in the direction of the next node with a constant speed
            const float speed = 150.0f;
            Position += direction * speed * deltaTime;
        }

        private List<Vector2f> FindPath(Vector2f start, Vector2f end)
        {
            var nodes = new List<Node>();
            var openSet = new List<Node>();
            var closedSet = new List<Node>();

            // create a 2D grid of nodes
            const int gridSize = 32;
            var gridWidth = MathF.FloorToInt(Program.Window.Size.X / gridSize);
            var gridHeight = MathF.FloorToInt(Program.Window.Size.Y / gridSize);

            for (int y = 0; y < gridHeight; y++)
            {
                for (int x = 0; x < gridWidth; x++)
                {
                    var position = new Vector2f(x * gridSize, y * gridSize);
                    nodes.Add(new Node(position));
                }
            }

            // find the start and end nodes
            var startNode = nodes.Find(n => n.Position == start);
            var endNode = nodes.Find(n => n.Position == end);

            // add the start node to the open set
            startNode.G = 0.0f;
            startNode.H = Heuristic(startNode, endNode);
            startNode.F = startNode.H;
            openSet.Add(startNode);

            while (openSet.Count > 0)
            {
                // get the node with the lowest cost
                var currentNode = openSet[0];
                foreach (var node in openSet)
                {
                    if (node.F < currentNode.F)
                    {
                        currentNode = node;
                    }
                }

                // remove the current node from the open set and add it to the closed set
                openSet.Remove(currentNode);
                closedSet.Add(currentNode);

                // if we've reached the end node, generate the path
                if (currentNode == endNode)
                {
                    var path = new List<Vector2f>();
                    var node = endNode;
                    while (node != startNode)
                    {
                        path.Add(node.Position);
                        node = node.Parent;
                    }
                    path.Add(startNode.Position);
                    path.Reverse();
                    return path;
                }

                // explore the neighbors of the current node
                foreach (var neighbor in GetNeighbors(nodes, currentNode))
                {
                    if (closedSet.Contains(neighbor))
                    {
                        continue;
                    }

                    var tentativeG = currentNode.G + (neighbor.Position - currentNode.Position).Length;
                    if (!openSet.Contains(neighbor))
                    {
                        neighbor.H = Heuristic(neighbor, endNode);
                        openSet.Add(neighbor);
                    }
                    else if (tentativeG >= neighbor.G)
                    {
                        continue;
                    }

                    neighbor.Parent = currentNode;
                    neighbor.G = tentativeG;
                    neighbor.F = neighbor.G + neighbor.H;
                }
            }

            // if we reach this point, it means that we couldn't find a path
            return new List<Vector2f>();
        }

        private float Heuristic(Node a, Node b)
        {
            return (a.Position - b.Position).Length;
        }

        private IEnumerable<Node> GetNeighbors(List<Node> nodes, Node current)
        {
            var neighbors = new List<Node>();

            var index = nodes.IndexOf(current);
            var x = index % MathF.FloorToInt(Program.Window.Size.X / 32);
            var y = index / MathF.FloorToInt(Program.Window.Size.X / 32);

            TryAddNeighbor(x, y - 1, neighbors);
            TryAddNeighbor(x, y + 1, neighbors);
            TryAddNeighbor(x - 1, y, neighbors);
            TryAddNeighbor(x + 1, y, neighbors);

            return neighbors;
        }

        private void TryAddNeighbor(int x, int y, List<Node> neighbors)
        {
            if (x < 0 || x >= MathF.FloorToInt(Program.Window.Size.X / 32) || y < 0 || y >= MathF.FloorToInt(Program.Window.Size.Y / 32))
            {
                return;
            }

            var index = y * MathF.FloorToInt(Program.Window.Size.X / 32) + x;
            neighbors.Add(new Node(new Vector2f(x * 32, y * 32)));
        }

        private class Node
        {
            public Vector2f Position { get; }
            public float G { get; set; }
            public float H { get; set; }
            public float F { get; set; }
            public Node Parent { get; set; }

            public Node(Vector2f position)
            {
                Position = position;
                G = float.MaxValue;
                H = 0.0f;
                F = float.MaxValue;
                Parent = null;
            }
        }
    }
}
6377 chars
190 lines

Some notes regarding the code:

  • The Enemy class contains the enemy's current position and a list of path nodes that it needs to follow. It also contains a method that updates its position each frame by moving towards the next node in the path.
  • The FindPath method uses the A* pathfinding algorithm to find the shortest path from the enemy's current position to the target position. It uses a 2D grid of nodes to represent the game world, and calculates the cost of moving from one node to another based on their distance. It then selects the lowest cost path from the starting node to the ending node using the following formula: f(x) = g(x) + h(x), where g(x) is the cost from the start node to x, h(x) is the estimated cost from x to the end node, and f(x) is the total estimated cost of the path.
  • GetNeighbors method returns a list of neighboring nodes from the current node in the grid. It also checks if the neighbor nodes are within the bounds of the game world.
  • The Heuristic method calculates the estimated cost from a node to the target node. In this case, it uses the Euclidean distance between the two nodes.
  • The TryAddNeighbor method adds a neighbor node to the list of neighboring nodes, but only if it's within the bounds of the game world.

With this code, you can now make your enemy follow the path towards the target position. You can then render the enemy and the path by creating a sprite and drawing it on the screen using SFML graphics library in C#.

gistlibby LogSnag