• MMORPG programming in Silverlight Tutorial (8)A* Algorithm


        From this chapter, I will introduce map engine, it refer to 2 aspects, as follows:

        1) Implementation the map. Including map’s splitting, composing and rendering style.

        2) Implementation the objects of the map. Including obstruction, mask and transportation.

        Now let’s begin with path finding.

        At present, the most classic path finding algorithm is A*, there are many algorithms originate from it, for example, the Manhattan method. In this chapter, i don’t plan to introduce A* algorithm’s implementation. If you are interested, you can visit this article to study more:

    http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c12527/

        This guy implemented this algorithm in C#, so my friend Xuan Qin, referred to his implementation and wrote higher performance algorithm base on it. He named it QXGame_Silverlight3.dll. You can download it here: QXGame_Silverlight3.zip

        The picture below shows the high performance of this algorithm implementation. The brown grid stands for obstruction, the start point is the left top corner, the end point is the red grid in the center of the canvas. The blue grid stands for the shortest path. The algorithms cost 0.0030s to find the path, it is perfect.

        A* algorithm is difficult to understand, but it doesn’t matter whether you master it completely, we only care about how to use it in our game engine.

       1st, add reference to this dll, and declare its namespace in the code behind:

    using QXGame_Silverlight3.AStar;
     

       2nd, init global variables:

    //a matrix used for finding path
    private byte[,] matrix = new byte[1024, 1024];
    
    //the size of the unit grid
    private int gridSize = 20;
    
    //the start and end point of the moving animation
    private Point start, end;
    

     

       The variable gridSize is very important. As we know, the game map is so large that we can’t define each grid is an obstruction or not, so we must scale down the coordinate, the gridSize is the ratio.

        For example, the window size is 800*600, supposed a point(80, 100) in the window, according to the ratio: gridSize =20, the point is converted to (80/20, 100/20) = (4, 5). It is convenient for us to implement every effect in different scenarios. I will explain this advantage in the following chapters.

        Attention, the matrix‘s Uppound must be the pow of 2; otherwise, A* algorithm will throw an exception. In our demo, it is 1024. It is enough. It is adjusted dynamically, I will talk about it in the following chapters.

        3rd, init matrix, as follows, we set its elements, default value is 1, and 0 stands for obstruction:

    void InitMatrix()
    {
        for (int y = 0; y < matrix.GetUpperBound(1); y++)
        {
            for (int x = 0; x < matrix.GetUpperBound(0); x++)
            {
                //default to 1, to stand for not obstraction
                matrix[x, y] = 1;
            }
        }
    
        //init obstruction
        for (int i = 0; i < 18; i++)
        {
            //set to 0, to stand for obstraction
            matrix[i, 12] = 0;
    
            rect = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Red),
                Width = gridSize,
                Height = gridSize
            };
    
            Carrier.Children.Add(rect);
    
            Canvas.SetLeft(rect, i * gridSize);
            Canvas.SetTop(rect, 12 * gridSize);
        }
    
        //init obstruction
        for (int i = 12; i < 17; i++)
        {
            matrix[17, i] = 0;
    
            rect = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Red),
                Width = gridSize,
                Height = gridSize
            };
    
            Carrier.Children.Add(rect);
    
            Canvas.SetLeft(rect, 17 * gridSize);
            Canvas.SetTop(rect, i * gridSize);
        }
    
        //init obstruction
        for (int i = 3; i < 18; i++)
        {
            matrix[i, 16] = 0;
    
            rect = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Red),
                Width = gridSize,
                Height = gridSize
            };
    
            Carrier.Children.Add(rect);
    
            Canvas.SetLeft(rect, i * gridSize);
            Canvas.SetTop(rect, 16 * gridSize);
        }
    }
    
     

        In the code snippet above, I init 3 obstruction regions, and add 18+5+15 = 38 rectangles in the canvas. They are all obstructions, filled in Red. The effect is as follows:

    image

        The value of gridSize is not fixed. If the value is small, there will be so many small rectangle generated in the canvas; if the value is large, maybe it cannot describe the obstruction accurately. So you must adjust it in different scenarios.

        4th, Find path between start and end point.

        The start point is fixed, I init its value as (1, 1).

        In the method Carrier_MouseLeftButtonDown, we reset end point in the first 4 sentences.

    private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        //set end point's coordinate
        Point p = e.GetPosition(Carrier);
        int x = (int)p.X / gridSize;
        int y = (int)p.Y / gridSize;
        end = new Point(x, y);
    
        IPathFinder pathFinder = new PathFinderFast(matrix);
        pathFinder.Formula = HeuristicFormula.Manhattan;
        pathFinder.SearchLimit = 2000;   //the step number is less than 2000
        //pathFinder.Diagonals = false;
    
        var path = pathFinder.FindPath(start, end);
        if (path == null)
        {
            MessageBox.Show("Path is not existing");
            return;
        }
    
        for (int i = path.Count - 1; i >= 0; i--)
        {
            rect = new Rectangle()
            {
                Fill = new SolidColorBrush(Colors.Green),
                Width = gridSize,
                Height = gridSize
            };
    
            Carrier.Children.Add(rect);
    
            Canvas.SetLeft(rect, path[i].X * gridSize);
            Canvas.SetTop(rect, path[i].Y * gridSize);
        }
    }
    

        Next, we use path finder algorithm, take matrix as the first parameter, A* algorithm will return an path array if there is a path between start and end point. I mark each element of the path array in green and locate all of them to their original point(path[i].X * gridSize, path[i].Y * gridSize).

        Now, press Ctrl+F5, and click on the canvas, we can see the effect as follows, A* algorithm find a green path for us, although it is not well optimized.

    image

        There is still a problem left for us. As you can see in the picture as follow, why not go straight from A to B? It is shorter than the path: A->C->B. I will resolve it in the following chapters.

    image

    Summary: This chapter introduce A* algorithm and how to use it in our games.

        Next chapter, I will implement how to add keyFrame dynamically, so you can change the animation even the object is still moving. Please focus on it.

        Chinese friend, you can also visit this Chinese blog if you feel difficult to read English, http://www.cnblogs.com/alamiye010/archive/2009/06/17/1505346.html, part of my article is base on it.

        Demo download: http://silverlightrpg.codeplex.com/releases/view/40978

  • 相关阅读:
    松软科技web课堂:JavaScript 比较和逻辑运算符
    松软科技web课堂:JavaScript 布尔(逻辑)
    松软科技web课堂:随机Math.random()
    松软科技web课堂:JavaScript Math 对象
    松原网站建设,巨惠分享0元11月启动
    松原本地专业网站建设,公众号,微网站,小程序定制开发
    JavaScript 日期格式
    松软科技前端课堂:JavaScript 日期
    松软科技web课堂:JavaScript 数组迭代方法
    安装npm install app-inspector -g 提示错误
  • 原文地址:https://www.cnblogs.com/Jax/p/1674605.html
Copyright © 2020-2023  润新知