コード例 #1
0
ファイル: RRTKDTreeCombat.cs プロジェクト: muntac/unitytool
        public List<Node> Compute(int startX, int startY, int endX, int endY, int attemps, float stepSize, float playerMaxHp, float playerSpeed, float playerDps, Cell[][][] matrix, bool smooth = false)
        {
            // Initialization
            tree = new KDTree (3);
            deathPaths = new List<List<Node>> ();
            nodeMatrix = matrix;

            //Start and ending node
            Node start = GetNode (0, startX, startY);
            start.visited = true;
            start.parent = null;
            start.playerhp = playerMaxHp;
            foreach (Enemy e in enemies) {
                start.enemyhp.Add (e, e.maxHealth);
            }

            // Prepare start and end node
            Node end = GetNode (0, endX, endY);
            tree.insert (start.GetArray (), start);

            // Prepare the variables
            Node nodeVisiting = null;
            Node nodeTheClosestTo = null;

            float tan = playerSpeed / 1;
            angle = 90f - Mathf.Atan (tan) * Mathf.Rad2Deg;

            /*Distribution algorithm
             * List<Distribution.Pair> pairs = new List<Distribution.Pair> ();

            for (int x = 0; x < matrix[0].Length; x++)
                for (int y = 0; y < matrix[0].Length; y++)
                    if (((Cell)matrix [0] [x] [y]).waypoint)
                        pairs.Add (new Distribution.Pair (x, y));

            pairs.Add (new Distribution.Pair (end.x, end.y));

            Distribution rd = new Distribution(matrix[0].Length, pairs.ToArray());*/

            DDA dda = new DDA (tileSizeX, tileSizeZ, nodeMatrix [0].Length, nodeMatrix [0] [0].Length);
            //RRT algo
            for (int i = 0; i <= attemps; i++) {

                //Get random point
                int rt = Random.Range (1, nodeMatrix.Length);
                int rx = Random.Range (0, nodeMatrix [rt].Length);
                int ry = Random.Range (0, nodeMatrix [rt] [rx].Length);
                //Distribution.Pair p = rd.NextRandom();
                //int rx = p.x, ry = p.y;
                nodeVisiting = GetNode (rt, rx, ry);
                if (nodeVisiting.visited || nodeVisiting.cell.blocked) {
                    i--;
                    continue;
                }

                nodeTheClosestTo = (Node)tree.nearest (new double[] {rx, rt, ry});

                // Skip downwards movement
                if (nodeTheClosestTo.t > nodeVisiting.t)
                    continue;

                // Skip if player is dead
                if (nodeTheClosestTo.playerhp <= 0)
                    continue;

                // Only add if we are going in ANGLE degrees or higher
                Vector3 p1 = nodeVisiting.GetVector3 ();
                Vector3 p2 = nodeTheClosestTo.GetVector3 ();
                Vector3 pd = p1 - p2;
                if (Vector3.Angle (pd, new Vector3 (pd.x, 0f, pd.z)) < angle) {
                    continue;
                }

                // And we have line of sight
                if (nodeVisiting.cell.blocked) {
                    continue;
                }

                // Check for all alive enemies
                List<Cell[][][]> seenList = new List<Cell[][][]> ();
                foreach (Enemy e in enemies) {
                    if (nodeTheClosestTo.enemyhp [e] > 0)
                        seenList.Add (e.seenCells);
                }

                Node hit = dda.Los3D (nodeMatrix, nodeTheClosestTo, nodeVisiting, seenList.ToArray ());

                if (hit != null) {
                    if (hit.cell.blocked || (!simulateCombat && hit.cell.seen && !hit.cell.safe)) // Collision with obstacle, ignore. If we don't simulate combat, ignore collision with enemy
                        continue;
                    else {
                        // Which enemy has seen me?
                        Enemy toFight = null;
                        foreach (Enemy e in enemies) {
                            if (e.seenCells [hit.t] [hit.x] [hit.y] != null && nodeTheClosestTo.enemyhp [e] > 0)
                                toFight = e;
                        }

                        // Solve the time
                        float timef = nodeTheClosestTo.enemyhp [toFight] / (playerDps * stepSize);
                        int timeT = Mathf.CeilToInt (timef);

                        // Search for more enemies
                        List<object> more = new List<object> ();
                        foreach (Enemy e2 in enemies) {
                            if (toFight != e2)
                                for (int t = hit.t; t < hit.t + timeT; t++)
                                    if (e2.seenCells [t] [hit.x] [hit.y] != null && nodeTheClosestTo.enemyhp [e2] > 0) {
                                        Tuple<Enemy, int> whenSeen = new Tuple<Enemy, int> (e2, t);
                                        more.Add (whenSeen);
                                        break; // Skip this enemy
                                    }
                        }

                        // Did another enemy saw the player while he was fighting?
                        if (more.Count > 0) {

                            // Who dies when
                            List<object> dyingAt = new List<object> ();

                            // First, save when the first fight starts
                            Node firstFight = NewNode (hit.t, hit.x, hit.y);
                            firstFight.parent = nodeTheClosestTo;

                            // Then when the first guy dies
                            Tuple<Enemy, int> death = new Tuple<Enemy, int> (toFight, firstFight.t + timeT);
                            dyingAt.Add (death);

                            // And proccess the other stuff
                            copy (nodeTheClosestTo, firstFight);
                            firstFight.fighting.Add (toFight);

                            // Navigation node
                            Node lastNode = firstFight;

                            // Solve for all enemies joining the fight
                            foreach (object o in more) {
                                Tuple<Enemy, int> joined = (Tuple<Enemy, int>)o;

                                // Calculate dying time
                                timef = timef + lastNode.enemyhp [joined.First] / (playerDps * stepSize);
                                timeT = Mathf.CeilToInt (timef);
                                death = new Tuple<Enemy, int> (joined.First, timeT + hit.t);
                                dyingAt.Add (death);

                                // Create the node structure
                                Node startingFight = NewNode (joined.Second, hit.x, hit.y);

                                // Add to fighting list
                                copy (lastNode, startingFight);
                                startingFight.fighting.Add (joined.First);

                                // Correct parenting
                                startingFight.parent = lastNode;
                                lastNode = startingFight;
                            }

                            // Solve for all deaths
                            foreach (object o in dyingAt) {

                                Tuple<Enemy, int> dead = (Tuple<Enemy, int>)o;
                                Node travel = lastNode;
                                bool didDie = false;
                                while (!didDie && travel.parent != null) {

                                    // Does this guy dies between two nodes?
                                    if (dead.Second > travel.parent.t && dead.Second < travel.t) {

                                        // Add the node
                                        Node adding = NewNode (dead.Second + hit.t, hit.x, hit.y);
                                        adding.fighting = new List<Enemy> ();
                                        adding.fighting.AddRange (travel.parent.fighting);

                                        // And remove the dead people
                                        adding.fighting.Remove (dead.First);
                                        adding.died = dead.First;

                                        Node remove = lastNode;

                                        // Including from nodes deeper in the tree
                                        while (remove != travel.parent) {
                                            remove.fighting.Remove (dead.First);
                                            remove = remove.parent;
                                        }

                                        // Reparent the nodes
                                        adding.parent = travel.parent;
                                        travel.parent = adding;
                                        didDie = true;
                                    }

                                    travel = travel.parent;
                                }
                                if (!didDie) {
                                    // The guy didn't die between, that means he's farthest away than lastNode
                                    Node adding = NewNode (dead.Second, hit.x, hit.y);
                                    copy (lastNode, adding);
                                    adding.fighting.Remove (dead.First);
                                    adding.enemyhp [dead.First] = 0;
                                    adding.died = dead.First;
                                    adding.parent = lastNode;

                                    // This is the new lastNode
                                    lastNode = adding;
                                }
                            }

                            // Grab the first node with fighting
                            Node first = lastNode;
                            while (first.parent != nodeTheClosestTo)
                                first = first.parent;

                            while (first != lastNode) {

                                Node navigate = lastNode;
                                // And grab the node just after the first
                                while (navigate.parent != first)
                                    navigate = navigate.parent;

                                // Copy the damage already applied
                                navigate.playerhp = first.playerhp;

                                // And deal more damage
                                foreach (Enemy dmgDealer in first.fighting)
                                    navigate.playerhp -= (navigate.t - first.t) * dmgDealer.dps * stepSize;

                                // Goto next node
                                first = navigate;

                            }
                            // Make the tree structure
                            nodeVisiting = lastNode;
                        } else {
                            // Only one enemy has seen me
                            Node toAdd = NewNode (hit.t, hit.x, hit.y);
                            nodeVisiting = NewNode (hit.t + timeT, hit.x, hit.y);

                            nodeVisiting.parent = toAdd;
                            toAdd.parent = nodeTheClosestTo;

                            copy (nodeTheClosestTo, toAdd);
                            toAdd.fighting.Add (toFight);

                            copy (nodeTheClosestTo, nodeVisiting);
                            nodeVisiting.playerhp = toAdd.playerhp - timef * toFight.dps * stepSize;
                            nodeVisiting.enemyhp [toFight] = 0;
                            nodeVisiting.died = toFight;
                        }
                    }
                } else {
                    // Nobody has seen me
                    nodeVisiting.parent = nodeTheClosestTo;
                    copy (nodeTheClosestTo, nodeVisiting);
                }

                try {
                    tree.insert (nodeVisiting.GetArray (), nodeVisiting);
                } catch (KeyDuplicateException) {
                }

                nodeVisiting.visited = true;

                // Add the path to the death paths list
                if (nodeVisiting.playerhp <= 0) {
                    Node playerDeath = nodeVisiting;
                    while (playerDeath.parent.playerhp <= 0)
                        playerDeath = playerDeath.parent;

                    deathPaths.Add (ReturnPath (playerDeath, smooth));
                }

                // Attemp to connect to the end node
                if (nodeVisiting.playerhp > 0) {
                    // Compute minimum time to reach the end node
                    p1 = nodeVisiting.GetVector3 ();
                    p2 = end.GetVector3 ();
                    p2.y = p1.y;
                    float dist = Vector3.Distance (p1, p2);

                    float t = dist * Mathf.Tan (angle);
                    pd = p2;
                    pd.y += t;

                    if (pd.y <= nodeMatrix.GetLength (0)) {
                        Node endNode = GetNode ((int)pd.y, (int)pd.x, (int)pd.z);
                        // Try connecting

                        seenList = new List<Cell[][][]> ();
                        foreach (Enemy e in enemies) {
                            if (nodeTheClosestTo.enemyhp [e] > 0)
                                seenList.Add (e.seenCells);
                        }

                        hit = dda.Los3D (nodeMatrix, nodeVisiting, endNode, seenList.ToArray ());

                        // To simplify things, only connect if player isn't seen or collides with an obstacle
                        if (hit == null) {
                            endNode.parent = nodeVisiting;
                            copy (endNode.parent, endNode);
                            List<Node> done = ReturnPath (endNode, smooth);
                            //UpdateNodeMatrix (done);
                            return done;
                        }
                    }

                    if (nodeVisiting.playerhp < playerMaxHp)
                        // Health pack solving
                        foreach (HealthPack pack in packs) {
                            if (!nodeVisiting.picked.Contains(pack)) {
                                // Compute minimum time to reach the pack
                                p1 = nodeVisiting.GetVector3 ();
                                p2 = new Vector3(pack.posX, p1.y, pack.posZ);
                                dist = Vector3.Distance (p1, p2);

                                t = dist * Mathf.Tan (angle);
                                pd = p2;
                                pd.y += t;

                                if (pd.y <= nodeMatrix.GetLength (0)) {
                                    // TODO If the node is already on the Tree, things may break!
                                    // but we need to add it to the tree and retrieve from it to make it a possible path!
                                    Node packNode = GetNode ((int)pd.y, (int)pd.x, (int)pd.z);

                                    // Try connecting
                                    seenList = new List<Cell[][][]> ();
                                    foreach (Enemy e in enemies) {
                                        if (nodeVisiting.enemyhp [e] > 0)
                                            seenList.Add (e.seenCells);
                                    }

                                    hit = dda.Los3D (nodeMatrix, nodeVisiting, packNode, seenList.ToArray ());

                                    // To simplify things, only connect if player isn't seen or collides with an obstacle
                                    if (hit == null) {
                                        packNode.parent = nodeVisiting;
                                        copy (packNode.parent, packNode);
                                        packNode.picked.Add(pack);
                                        packNode.playerhp = playerMaxHp;
                                        try {
                                            tree.insert(packNode.GetArray(), packNode);
                                        } catch (KeyDuplicateException) {
                                        }
                                    }
                                }
                            }
                        }
                }

                //Might be adding the neighboor as a the goal
                if (nodeVisiting.x == end.x & nodeVisiting.y == end.y) {
                    //Debug.Log ("Done2");
                    List<Node> done = ReturnPath (nodeVisiting, smooth);
                    //UpdateNodeMatrix (done);
                    return done;

                }
            }

            return new List<Node> ();
        }
コード例 #2
0
        public static void ComputePathsLoSValues(List <Path> paths, Enemy[] enemies, Vector3 min, float tileSizeX, float tileSizeZ, Cell[][][] fullMap, float[][] dangerCells, float maxDanger)
        {
            // Compute the y = ax + b equation for each FoV (i.e. enemy=)
            float[][] formula = new float[enemies.Length][];
            for (int i = 0; i < formula.Length; i++)
            {
                formula [i]     = new float[2];
                formula [i] [0] = (180 - enemies [i].fovAngle) * -2;
                formula [i] [1] = enemies [i].fovAngle - formula [i] [0];
            }

            DDA dda = new DDA(tileSizeX, tileSizeZ, fullMap [0].Length, fullMap [0] [0].Length);

            foreach (Path currentPath in paths)
            {
                if (currentPath.length3d == 0)
                {
                    currentPath.length3d = ComputeLength3D(currentPath.points);
                }

                Node cur = currentPath.points [currentPath.points.Count - 1];
                Node par = cur.parent;
                while (cur.parent != null)
                {
                    Vector3 p1 = cur.GetVector3();
                    Vector3 p2 = par.GetVector3();
                    Vector3 pd = p1 - p2;

                    float pt = (cur.t - par.t);

                    // Navigate through time to find the right cells to start from
                    for (int t = 0; t < pt; t++)
                    {
                        float delta = ((float)t) / pt;

                        Vector3 pos = p2 + pd * delta;

                        for (int enemyIndex = 0; enemyIndex < enemies.Length; enemyIndex++)
                        {
                            Vector3 enemy = enemies [enemyIndex].positions [par.t + t];

                            Vector2 pos2d   = new Vector2(pos.x, pos.z);
                            Vector2 enemy2d = new Vector2((enemy.x - min.x) / tileSizeX, (enemy.z - min.z) / tileSizeZ);

                            bool seen = dda.HasLOS(fullMap[par.t + t], pos2d, enemy2d);

                            // If the cell is in LoS
                            if (seen && enemies [enemyIndex].cells [par.t + t].Length > 0)
                            {
                                // Grab the cell closest to my position
                                Vector2 shortest = enemies [enemyIndex].cells [par.t + t] [0];
                                float   dist     = Vector2.Distance(shortest, pos2d);
                                float   ddist;
                                for (int k = 1; k < enemies[enemyIndex].cells[par.t + t].Length; k++)
                                {
                                    ddist = Vector2.Distance(enemies [enemyIndex].cells [par.t + t] [k], pos2d);
                                    if (ddist < dist)
                                    {
                                        dist     = ddist;
                                        shortest = enemies [enemyIndex].cells [par.t + t] [k];
                                    }
                                }

                                // Calculate the angle between them
                                float angle = Vector2.Angle(enemies [enemyIndex].positions [par.t + t].normalized, (pos2d - enemy2d).normalized);
                                if (angle <= enemies [enemyIndex].fovAngle)
                                {
                                    angle = 1;
                                }
                                else
                                {
                                    angle = (angle - formula [enemyIndex] [1]) / formula [enemyIndex] [0];
                                }

                                float f = angle / (pos2d - shortest).sqrMagnitude;
                                float g = angle / Mathf.Pow((pos2d - shortest).magnitude, 3);
                                if (f == Mathf.Infinity)
                                {
                                    f = angle / (pos2d - enemy2d).sqrMagnitude;
                                }
                                if (g == Mathf.Infinity)
                                {
                                    g = angle / Mathf.Pow((pos2d - enemy2d).magnitude, 3);
                                }

                                // Store in 'per-time' metric
                                pathMap [currentPath] [(int)Heuristic.Los] [par.t + t]      = f;
                                pathMap [currentPath] [(int)Heuristic.Los3] [par.t + t]     = g;
                                pathMap [currentPath] [(int)Heuristic.Los3Norm] [par.t + t] = g * (dangerCells [(int)pos2d.x] [(int)pos2d.y] / maxDanger);

                                // Increment the total metric
                                currentPath.los      += pathMap [currentPath] [(int)Heuristic.Los] [par.t + t];
                                currentPath.los3     += pathMap [currentPath] [(int)Heuristic.Los3] [par.t + t];
                                currentPath.los3Norm += pathMap [currentPath] [(int)Heuristic.Los3Norm] [par.t + t];
                            }
                        }
                    }

                    cur = par;
                    par = par.parent;
                }
                currentPath.los      /= currentPath.length3d;
                currentPath.los3     /= currentPath.length3d;
                currentPath.los3Norm /= currentPath.length3d;
            }
        }