/// <summary>
    /// 寻路接口
    /// </summary>
    /// <param name="grid_map">地图格子列表</param>
    /// <param name="startPos">起点</param>
    /// <param name="endPos">终点</param>
    /// <param name="isFindNearstPath">失败时,是否返回最近可移动路径</param>
    /// <returns></returns>
    public eFinderResult search(PathGridMap grid_map, Vector2 startPos, Vector2 endPos, bool isFindNearstPath = false)
    {
        if (grid_map == null)
        {
            return(eFinderResult.FAILED);
        }

        m_array_open.Clear();
        m_array_closed.Clear();
        m_array_search_path.Clear();
        m_array_search_grid.Clear();

        m_start_node = grid_map.getNodeByPostion(startPos.x, startPos.y);
        m_end_node   = grid_map.getNodeByPostion(endPos.x, endPos.y);

        //Log.Debug(string.Format("起点{0},{1};终点{2},{3}", m_start_node.row, m_start_node.col, m_end_node.row, m_end_node.col));
        if (m_start_node == null)
        {
            //Log.Error("AStarPathfinder::findPath - 角色起点在障碍里面");
            return(eFinderResult.FAILED);
        }
        if (m_end_node == null || (!m_end_node.walkable && !isFindNearstPath))
        {
            //Log.Error("AStarPathfinder::findPath - 角色终点在障碍里面");
            return(eFinderResult.FAILED);
        }
        m_start_node.g = 0;
        m_start_node.h = onDiagonal(m_start_node);
        m_start_node.f = m_start_node.g + m_start_node.h;

        //执行寻路
        //float old_time = Time.realtimeSinceStartup;
        eFinderResult result = travel(grid_map, isFindNearstPath);

        //Log.Debug("[AI]AStarPathfinder::search - 寻路总用时:", (Time.realtimeSinceStartup - old_time) + "s");

        return(result);
    }
    /// <summary>
    /// 寻路接口
    /// </summary>
    /// <param name="start_x"></param>
    /// <param name="start_y"></param>
    /// <param name="target_x"></param>
    /// <param name="target_y"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <returns></returns>
    public eFinderResult search(PathGridMap grid_map, float start_x, float start_y, float target_x, float target_y, float width, float height)
    {
        if (grid_map == null)
        {
            return(eFinderResult.FAILED);
        }

        eFace8Type walk_dir = eFace8Type.NONE;

        m_cur_grid            = null;
        m_collide_rect.width  = width;
        m_collide_rect.height = height;

        //上一步的位置
        m_last_pos_x = start_x;
        m_last_pos_y = start_y;

        //步长增长方向
        Vector2 pt_step    = Math2DUtils.normalPoint(start_x, start_y, target_x, target_y, STEP_LENGTH);
        float   x_step_inc = pt_step.x;
        float   y_step_inc = pt_step.y;
        //步长个数
        int stepCount = (int)Mathf.Ceil(Math2DUtils.distance(start_x, start_y, target_x, target_y) / STEP_LENGTH);

        //确定移动方向
        float angle = MathUtils.ToDegree(Math2DUtils.LineRadians(start_x, start_y, target_x, target_y));

        walk_dir = (eFace8Type)Math2DUtils.getFace(angle, 8);

        m_cur_grid = grid_map.getNodeByPostion(start_x, start_y);
        if (m_cur_grid == null)
        {
            return(eFinderResult.FAILED);
        }

        //重新调整方向:判断下一步是否可走
        walk_dir = adjustDir(grid_map, walk_dir, start_x + x_step_inc, start_y + y_step_inc);
        switch (walk_dir)
        {
        case eFace8Type.LEFT:
        case eFace8Type.RIGHT:
            y_step_inc = 0;
            break;

        case eFace8Type.UP:
        case eFace8Type.DOWN:
            x_step_inc = 0;
            break;

        case eFace8Type.NONE:
            return(eFinderResult.FAILED);
        }
        //开始寻路,每次移动一个步长
        eFinderResult result = eFinderResult.FAILED;
        int           i      = 1;

        for (i = 1; i <= stepCount; i++)   //从1开始,当前位置不判断
        {
            if (isWalkableRect(grid_map, walk_dir, start_x + x_step_inc * i, start_y + y_step_inc * i))
            {
                m_last_pos_x = start_x + x_step_inc * i;
                m_last_pos_y = start_y + y_step_inc * i;
                result       = eFinderResult.SUCCEEDED;
            }
            else
            {
                //如果第一步都不能走,设置查询失败
                if (i == 1)
                {
                    result = eFinderResult.FAILED;
                }
                else
                {
                    result = eFinderResult.SUCCEEDED_NEAREST;
                }
                break;
            }
        }
        m_target_pos_x = m_last_pos_x;
        m_target_pos_y = m_last_pos_y;

        return(result);
    }
    /// <summary>
    /// 斜角走的情况下,如果一个方向挡住,自动调整到另外一个方向
    /// </summary>
    /// <param name="dir"></param>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private eFace8Type adjustDir(PathGridMap grid_map, eFace8Type dir, float x, float y)
    {
        PathGrid cur_tile = grid_map.getNodeByPostion(x, y);

        if (cur_tile == null || !cur_tile.walkable)
        {
            return(eFace8Type.NONE);
        }
        m_collide_rect.x = x - m_collide_rect.width * 0.5f;
        m_collide_rect.y = y - m_collide_rect.height * 0.5f;

        int grid_row = cur_tile.row;
        int grid_col = cur_tile.col;

        switch (dir)
        {        //地图{0,0}在左下角
        case eFace8Type.RIGHT_DOWN:
        {
            PathGrid right_tile = grid_map.getNode(grid_row, grid_col + 1);
            PathGrid down_tile  = grid_map.getNode(grid_row - 1, grid_col);
            bool     can_right  = checkCanMove(right_tile, m_collide_rect);
            bool     can_down   = checkCanMove(down_tile, m_collide_rect);
            if (!can_right && !can_down)
            {
                return(eFace8Type.NONE);
            }
            else if (!can_right)
            {
                return(eFace8Type.DOWN);
            }
            else if (!can_down)
            {
                return(eFace8Type.RIGHT);
            }
        }
        break;

        case eFace8Type.RIGHT_UP:
        {
            PathGrid right_tile = grid_map.getNode(grid_row, grid_col + 1);
            PathGrid up_tile    = grid_map.getNode(grid_row + 1, grid_col);
            bool     can_right  = checkCanMove(right_tile, m_collide_rect);
            bool     can_up     = checkCanMove(up_tile, m_collide_rect);
            if (!can_right && !can_up)
            {
                return(eFace8Type.NONE);
            }
            else if (!can_right)
            {
                return(eFace8Type.UP);
            }
            else if (!can_up)
            {
                return(eFace8Type.RIGHT);
            }
        }
        break;

        case eFace8Type.LEFT_DOWN:
        {
            PathGrid left_tile = grid_map.getNode(grid_row, grid_col - 1);
            PathGrid down_tile = grid_map.getNode(grid_row - 1, grid_col);
            bool     can_left  = checkCanMove(left_tile, m_collide_rect);
            bool     can_down  = checkCanMove(down_tile, m_collide_rect);
            if (!can_left && !can_down)
            {
                return(eFace8Type.NONE);
            }
            else if (!can_left)
            {
                return(eFace8Type.DOWN);
            }
            else if (!can_down)
            {
                return(eFace8Type.LEFT);
            }
        }
        break;

        case eFace8Type.LEFT_UP:
        {
            PathGrid left_tile = grid_map.getNode(grid_row, grid_col - 1);
            PathGrid up_tile   = grid_map.getNode(grid_row + 1, grid_col);
            bool     can_left  = checkCanMove(left_tile, m_collide_rect);
            bool     can_up    = checkCanMove(up_tile, m_collide_rect);
            if (!can_left && !can_up)
            {
                return(eFace8Type.NONE);
            }
            else if (!can_left)
            {
                return(eFace8Type.UP);
            }
            else if (!can_up)
            {
                return(eFace8Type.LEFT);
            }
        }
        break;
        }
        return(dir);
    }
    /// <summary>
    /// 是否可行走
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private bool isWalkableRect(PathGridMap grid_map, eFace8Type dir, float x, float y)
    {
        m_collide_rect.x = x - m_collide_rect.width * 0.5f;
        m_collide_rect.y = y - m_collide_rect.height * 0.5f;

        PathGrid grid = grid_map.getNodeByPostion(x, y);

        //在障碍里面
        if (grid == null || !grid.walkable)
        {
            return(false);
        }

        int      grid_row = grid.row;
        int      grid_col = grid.col;
        PathGrid tempTile = null;

        switch (dir)
        {
        case eFace8Type.RIGHT:        //校验相邻格子
            tempTile = grid_map.getNode(grid_row, grid_col + 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.DOWN:
            tempTile = grid_map.getNode(grid_row - 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.LEFT:
            tempTile = grid_map.getNode(grid_row, grid_col - 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.UP:
            tempTile = grid_map.getNode(grid_row + 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.RIGHT_DOWN:
            tempTile = grid_map.getNode(grid_row, grid_col + 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row - 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row - 1, grid_col + 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.RIGHT_UP:
            tempTile = grid_map.getNode(grid_row, grid_col + 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row + 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row + 1, grid_col + 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.LEFT_DOWN:
            tempTile = grid_map.getNode(grid_row, grid_col - 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row - 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row - 1, grid_col - 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);

        case eFace8Type.LEFT_UP:
            tempTile = grid_map.getNode(grid_row, grid_col - 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row + 1, grid_col);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            tempTile = grid_map.getNode(grid_row + 1, grid_col - 1);
            if (!checkCanMove(tempTile, m_collide_rect))
            {
                return(false);
            }
            return(true);
        }

        return(false);
    }
    private eFinderResult travel(PathGridMap grid_map, bool isFindNearstPath)
    {
        PathGrid node         = m_start_node;
        int      search_count = 0;

        while (!node.equal(m_end_node) && search_count < m_max_search_count)
        {
            ++search_count;
            int start_col = Mathf.Max(0, node.col - 1);
            int end_col   = Mathf.Min(grid_map.numCols - 1, node.col + 1);
            int start_row = Mathf.Max(0, node.row - 1);
            int end_row   = Mathf.Min(grid_map.numRows - 1, node.row + 1);
            for (int col = start_col; col <= end_col; col++)
            {
                for (int row = start_row; row <= end_row; row++)
                {
                    PathGrid test = grid_map.getNode(row, col);
                    if (test == null || test.equal(node) ||
                        !test.walkable ||
                        !grid_map.getNode(node.row, test.col).walkable ||   //拐角不能通过
                        !grid_map.getNode(test.row, node.col).walkable
                        )
                    {
                        continue;
                    }
                    float cost = m_straight_cost;
                    if (!((node.col == test.col) || (node.row == test.row)))
                    {
                        cost = m_diag_cost;
                    }

                    /*
                     * 经过前面的这些,留下的就是需要计算的节点。首先计算从开始节点到测试节点的代价(g),
                     * 方法是当前节点的 g 值加上当前节点到测试节点的代价。简化以后就是水平、竖直方向直接加上
                     * _straightCost,对角加上_diagCost.h 通过估价函数计算,然后g和h 求和,得到 f(总代价)
                     */
                    float g = node.g + cost * test.cost;
                    float h = onDiagonal(test);
                    float f = g + h;

                    /*
                     * 下面这个部分有一点小技巧,之前我们并没有谈到。开始的时候,我说过如果一个节点在待考
                     * 察表/已考察表里,因为它已经被考察过了,所以我们不需要再考察。不过这次计算出的结果有可
                     * 能小于你之前计算的结果(比如说,上次计算时是对角,而这次确是上下或左右关系,代价就小一
                     * 些)。所以,就算一个节点在待考察表/已考察表里面,最好还是比较一下当前值和之前值之间的大
                     * 小。具体做法是比较测试节点的总代价与以前计算出来的总代价。如果以前的大,我们就找到了更
                     * 好的节点,我们就需要重新给测试点的 f,g,h 赋值,同时,我们还要把测试点的父节点设为当前
                     * 点。这就要我们向后追溯
                     */
                    if (isOpen(test) || isClosed(test))
                    {
                        if (test.f > f)
                        {
                            test.f      = f;
                            test.g      = g;
                            test.h      = h;
                            test.parent = node;
                        }
                    }
                    else
                    {
                        /*
                         * 如果测试节点不再待考察表/已考察表里面,我们只需要赋值给 f,g,h 和父节点。然后把测
                         * 试点加到待考察表,然后是下一个测试点,找出最佳点
                         */
                        test.f      = f;
                        test.g      = g;
                        test.h      = h;
                        test.parent = node;
                        m_array_open.Add(test);
                    }
                }
            }
            m_array_closed.Add(node);
            if (m_array_open.Count == 0)
            {
                return(eFinderResult.FAILED);
            }
            m_array_open.Sort();
            node = m_array_open[0];
            m_array_open.RemoveAt(0);
        }

        if (search_count >= m_max_search_count)
        {
            return(eFinderResult.FAILED);
        }
        buildPath(node);
        return(eFinderResult.SUCCEEDED);
    }