예제 #1
0
        // Computes a (hopefully) efficient connection from one Platform start to Platform end
        private List <Waypoint> ComputeConnection(NavGraphNode start, NavGraphNode end)
        {
            List <Waypoint> res = new List <Waypoint>();

            Collidable startC = start.Platform;
            Collidable endC   = end.Platform;

            // Position of the transition waypoints between the two nodes
            Vector2 fst, snd;

            foreach (Tuple <NavGraphNode, Vector2, Vector2> t in start.ReachableNodes)
            {
                if (t.Item1 == end)
                {
                    fst = t.Item2;                 // waypoint on start
                    snd = t.Item3;                 // waypoint on end

                    // if different platforms (but roughly on same height): ensure, that the waypoint clearly
                    // lies on the target platform
                    if (end.Platform != start.Platform && Math.Abs(fst.Y - snd.Y) <= AiConfig.DISTANCE_JUMP_VERT &&
                        Math.Abs(fst.X - snd.X) > AiConfig.DISTANCE_JUMP_MIN_HOR)
                    {
                        if (fst.X < snd.X)
                        {
                            snd.X += AiConfig.DISTANCE_OVERSHOOT_HOR;
                        }
                        else if (fst.X > snd.X)
                        {
                            snd.X -= AiConfig.DISTANCE_OVERSHOOT_HOR;
                        }
                    }

                    // If target-waypoint is higher up (and only reachable by climbing): ensure that the
                    // AI climbs high enough and doesn't get stuck at the top
                    if (fst.Y > snd.Y && Math.Abs(fst.Y - snd.Y) > AiConfig.DISTANCE_JUMP_VERT)
                    {
                        fst.Y -= AiConfig.DISTANCE_OVERSHOOT_UP;
                        snd.Y -= 0.8f * AiConfig.DISTANCE_OVERSHOOT_UP;
                    }
                    else if (fst.Y < snd.Y && Math.Abs(fst.Y - snd.Y) > AiConfig.DISTANCE_JUMP_VERT)
                    {
                        fst.Y -= AiConfig.DISTANCE_OVERSHOOT_UP;
                    }

                    // Add in the correct order
                    res.Add(new Waypoint(new Vector3(fst.X, fst.Y, 0), startC));
                    res.Add(new Waypoint(new Vector3(snd.X, snd.Y, 0), endC));

                    return(res);
                }
            }


            return(res);
        }
예제 #2
0
        // Compute waypoints from start to target via the paths defined with the pathMap<next, previous>
        private List <Waypoint> BacktrackPath(Dictionary <NavGraphNode, NavGraphNode> pathMap, NavGraphNode start, NavGraphNode target)
        {
            List <Waypoint> res = new List <Waypoint>();

            NavGraphNode current = target;

            while (current != start)
            {
                NavGraphNode next = pathMap[current];
                // Compute path from previous (next) to the current one (<- reverse order)
                res.InsertRange(0, ComputeConnection(next, current));
                current = next;
            }

            return(res);
        }
예제 #3
0
        public NavGraph(Scene scene)
        {
            this.scene     = scene;
            NavGraphNodes  = new NavGraphNode[scene.WalkableCollidables.Count];
            CurrClimbGroup = new List <ClimbGroup>();
            //Init a NavGraphNode for each walkable platform in the scene
            for (var i = 0; i < NavGraphNodes.Length; i++)
            {
                NavGraphNodes[i] = new NavGraphNode(scene.WalkableCollidables[i]);
            }

            InitClimbGroups();

            InitClimbReachableNodes();

            InitCloseReachableNodes();

            scene.NavigationGraph = this;
        }
예제 #4
0
        private List <Waypoint> ComputePath(Collidable currP, Collidable tgtP)
        {
            List <Waypoint> res = new List <Waypoint>();
            // Map to remember last step of fastest connection to <key>
            Dictionary <NavGraphNode, NavGraphNode> pathMap = new Dictionary <NavGraphNode, NavGraphNode>();

            NavGraphNode start  = scene.NavigationGraph.findNode(currP);
            NavGraphNode target = null;

            // Do breadth-first-search on NavGraph (start by adding all neighboring nodes of the start node to "to visit")
            // <which new node to visit, last platform before this new one>
            List <Tuple <NavGraphNode, NavGraphNode> > toVisit = new List <Tuple <NavGraphNode, NavGraphNode> >();

            foreach (Tuple <NavGraphNode, Vector2, Vector2> t in start.ReachableNodes)
            {
                if (!pathMap.ContainsKey(t.Item1))
                {
                    pathMap.Add(t.Item1, start);
                    toVisit.Add(new Tuple <NavGraphNode, NavGraphNode>(t.Item1, start));
                }
            }

            while (toVisit.Count > 0)
            {
                Tuple <NavGraphNode, NavGraphNode> t = toVisit.First();
                NavGraphNode next = t.Item1;
                toVisit.RemoveAt(0);

                // Check if the currently visited node is the target platform
                if (next.Platform == tgtP)
                {
                    // Found our target platform
                    if (!pathMap.ContainsKey(next))
                    {
                        pathMap.Add(next, t.Item2);
                    }
                    target = t.Item1;
                    break;
                }

                // Not yet reached our target, check connections to other platforms (which not have been visited yet)
                foreach (Tuple <NavGraphNode, Vector2, Vector2> newConn in next.ReachableNodes)
                {
                    if (!pathMap.ContainsKey(newConn.Item1))
                    {
                        // Not yet visited
                        if (!pathMap.ContainsKey(newConn.Item1))
                        {
                            pathMap.Add(newConn.Item1, next);
                            toVisit.Add(new Tuple <NavGraphNode, NavGraphNode>(newConn.Item1, next));
                        }
                    }
                }
            }

            // Check if target platform has been found
            if (target != null)
            {
                // Compute final path
                res = BacktrackPath(pathMap, start, target);
            }

            return(res);
        }
예제 #5
0
        private void InitCloseReachableNodes()
        {
            for (int i = 0; i < NavGraphNodes.Length; i++)
            {
                NavGraphNode n = NavGraphNodes[i];
                for (int j = i; j < NavGraphNodes.Length; j++)
                {
                    NavGraphNode o = NavGraphNodes[j];
                    // Check if vertically close enough
                    if (Math.Abs(n.Platform.MinHeight - o.Platform.MinHeight) <= AiConfig.DISTANCE_JUMP_VERT)
                    {
                        // Check horizontal displacement
                        Vector2 nMm      = n.Platform.WalkableMinMax;
                        Vector2 oMm      = o.Platform.WalkableMinMax;
                        float   leftDis  = nMm.X - oMm.Y;
                        float   rightDis = oMm.X - nMm.Y;
                        if (leftDis <= AiConfig.DISTANCE_JUMP_MAX_HOR && leftDis >= -AiConfig.DISTANCE_VALID_OVERLAP)
                        {
                            // Horizontally reachable (both ways) on the left side of n
                            Vector2 wpN = new Vector2(nMm.X + AiConfig.DISTANCE_OFFS_DROP_HOR, n.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_HOR);
                            Vector2 wpO = new Vector2(oMm.Y - AiConfig.DISTANCE_OFFS_DROP_HOR, o.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_HOR);

                            Vector2 diff = wpN - wpO;
                            if (!IsWithinPlatform(wpN - 0.3f * diff) && !IsWithinPlatform(wpO + 0.3f * diff) &&
                                !IsWithinPlatform(wpN) && !IsWithinPlatform(wpO))
                            {
                                n.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(o, wpN, wpO));
                                o.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(n, wpO, wpN));
                            }
                        }
                        else if (rightDis <= AiConfig.DISTANCE_JUMP_MAX_HOR && rightDis >= -AiConfig.DISTANCE_VALID_OVERLAP)
                        {
                            // Horizontally reachable (both ways) on the right side of n
                            Vector2 wpN = new Vector2(nMm.Y - AiConfig.DISTANCE_OFFS_DROP_HOR, n.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_HOR);
                            Vector2 wpO = new Vector2(oMm.X + AiConfig.DISTANCE_OFFS_DROP_HOR, o.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_HOR);

                            Vector2 diff = wpN - wpO;
                            if (!IsWithinPlatform(wpN - 0.3f * diff) && !IsWithinPlatform(wpO + 0.3f * diff) &&
                                !IsWithinPlatform(wpN) && !IsWithinPlatform(wpO))
                            {
                                n.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(o, wpN, wpO));
                                o.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(n, wpO, wpN));
                            }
                        }
                    }
                    else
                    {
                        NavGraphNode above, below;
                        if (n.Platform.MinHeight < o.Platform.MinHeight)
                        {
                            // N is higher then O
                            above = n;
                            below = o;
                        }
                        else
                        {
                            // O is higher then N
                            above = o;
                            below = n;
                        }

                        // Check horizontal overlap of the platforms
                        Vector2 abMm  = above.Platform.WalkableMinMax;
                        Vector2 belMm = above.Platform.WalkableMinMax;
                        if (abMm.X >= belMm.X)
                        {
                            // Can drop from platform above to below (left side)
                            Vector2 wpAbove = new Vector2(abMm.X - AiConfig.DISTANCE_OFFS_DROP_COLL, above.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_COLL_VERT);
                            Vector2 wpBelow = new Vector2(abMm.X - AiConfig.DISTANCE_OFFS_DROP_COLL, below.Platform.MinHeight - 5);
                            if (!IntersectsWithCollidable(wpAbove, wpBelow - wpAbove))
                            {
                                wpAbove += new Vector2(-AiConfig.DISTANCE_OFFS_DROP_HOR, AiConfig.DISTANCE_OFFS_DROP_COLL_VERT - AiConfig.DISTANCE_OFFS_DROP_HOR);
                                wpBelow += new Vector2(-AiConfig.DISTANCE_OFFS_DROP_HOR, 0);
                                above.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(below, wpAbove, wpBelow));
                            }
                        }
                        else if (abMm.Y <= belMm.Y)
                        {
                            // Can drop from platform above to below (right side)
                            Vector2 wpAbove = new Vector2(abMm.X + AiConfig.DISTANCE_OFFS_DROP_COLL, above.Platform.MinHeight - AiConfig.DISTANCE_OFFS_DROP_COLL_VERT);
                            Vector2 wpBelow = new Vector2(abMm.X + AiConfig.DISTANCE_OFFS_DROP_COLL, below.Platform.MinHeight - 5);
                            if (!IntersectsWithCollidable(wpAbove, wpBelow - wpAbove))
                            {
                                wpAbove += new Vector2(AiConfig.DISTANCE_OFFS_DROP_HOR, AiConfig.DISTANCE_OFFS_DROP_COLL_VERT - AiConfig.DISTANCE_OFFS_DROP_HOR);
                                wpBelow += new Vector2(AiConfig.DISTANCE_OFFS_DROP_HOR, 0);
                                above.ReachableNodes.Add(new Tuple <NavGraphNode, Vector2, Vector2>(below, wpAbove, wpBelow));
                            }
                        }
                    }
                }
            }
        }