Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by dragonking300 · Dec 30, 2017 at 05:29 AM · c#aipathfindingmath

I'm trying to convert a 2d a* pathfinding algorithm to 3d but I've run into numerous problems[go to bold area to see main problem]

After a lot of debugging here are my problems that I've found thus far.

note: my script is based on https://www.youtube.com/watch?v=mZfyt03LDH4&t=748s

  1. The neighbour nodes seem to be around the empty pathfinding object with my grid and pathfinding script instead of the object's i've assigned to be the player/enemy node

  2. pathfinding's starting point is way off from the seeker and target ![alt text][1]

3.The math for finding the distance between the player's position and the enemy position is most likely wrong(Or could stem from problem 1)

here are my 3 scripts for path finding(Look below scripts for areas I think are problems)

PathfindingScript

    using UnityEngine;
     using System.Collections;
     using System.Collections.Generic;
     
     public class Pathfinding : MonoBehaviour
     {
         public Transform seeker, target;
     
         Grid grid;
         
         void Awake()
         {
             grid = GetComponent<Grid>();
         }
     
         void Update()
         {
             FindPath(seeker.position, target.position);
         }
     
         void FindPath(Vector3 StartPos, Vector3 TargetPos)
         {
             Node StartNode = grid.NodeFromWorldPoint(StartPos);
             Node TargetNode = grid.NodeFromWorldPoint(TargetPos);
     
             List<Node> openSet = new List<Node>();
             HashSet<Node> closedSet = new HashSet<Node>();
             openSet.Add(StartNode);
     
             while(openSet.Count > 0)
             {
                 Node currentNode = openSet[0];
                 for(int i = 1; i < openSet.Count; i++)
                 {
                     if(openSet[i].fCost < currentNode.fCost || openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)
                     {
                         currentNode = openSet[i];
                     }
                 }
                 openSet.Remove(currentNode);
                 closedSet.Add(currentNode);
     
                 if(currentNode == TargetNode)
                 {
                     //Debug.Log("this is getting called");
                     RetracePath(StartNode, TargetNode);
                     return;
                 }
                 foreach(Node neighbour in grid.GetNeighbours(currentNode))
                 {
                     if(!neighbour.walkable || closedSet.Contains(neighbour))
                     {
                         continue;
                     }
                     int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour);
                     if(newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour))
                     {
                         neighbour.gCost = newMovementCostToNeighbour;
                         neighbour.hCost = GetDistance(neighbour, TargetNode);
                         neighbour.parent = currentNode;
     
                         if (!openSet.Contains(neighbour))
                         {
                             openSet.Add(neighbour);
                         }
                     }
                 }
             }
         }
         void RetracePath(Node StartNode, Node EndNode)
         {
             List<Node> path = new List<Node>();
             Node currentNode = EndNode;
     
             while(currentNode != StartNode)
             {
                 path.Add(currentNode);
                 currentNode = currentNode.parent;
             }
             path.Reverse();
     
             grid.path = path;
         }
     
     int GetDistance(Node nodeA, Node nodeB)
         {
             int distX = Mathf.Abs(nodeA.GridX - nodeB.GridX);
             int distY = Mathf.Abs(nodeA.GridY - nodeB.GridY);
             int distZ = Mathf.Abs(nodeA.GridZ - nodeB.GridZ);
     
             if(distX > distY && distX > distZ)
             {
                // Debug.Log("1");
                 return 14 * distY + 10 * (distX - distY);
             }
             else if(distY > distX && distY > distZ)
             {
                // Debug.Log("2");
                 return 14 * distX + 10 * (distY - distX);
             }
             else if(distZ > distX && distZ < distY)
             {
                // Debug.Log("3");
                 return 17 * distZ + 10 * (distZ - distX);
                
             }
            // Debug.Log(distX);
             //Debug.Log(distY);
             //Debug.Log(distZ);
             return 17 * distZ + 10 * (distZ - distY);
         }
     }

Grid Script(creats the nodes that pathfinding uses)

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class Grid : MonoBehaviour
 {
     public GameObject enemy;
     public LayerMask unwalkableMask;
     public Vector3 gridWorldSize;
     public float nodeRadius;
     Node[,,] grid;
     public List<Node> temp;
     float nodeDiameter;
     int gridSizeX, gridSizeY, gridSizeZ;
 
     void Start()
     {
         temp = new List<Node>();
         nodeDiameter = nodeRadius * 2;
         gridSizeX = Mathf.RoundToInt(gridWorldSize.x / nodeDiameter);
         gridSizeY = Mathf.RoundToInt(gridWorldSize.y / nodeDiameter);
         gridSizeZ = Mathf.RoundToInt(gridWorldSize.z / nodeDiameter);
         CreateGrid();
     }
 
     void Update()
     {
         CreateGrid();
     }
 
     void CreateGrid()
     {
         grid = new Node[gridSizeX, gridSizeY, gridSizeZ];
         Vector3 worldBottomLeft = transform.position - Vector3.right * gridWorldSize.x / 2 - Vector3.forward * gridWorldSize.y / 2 - Vector3.up * gridWorldSize.z / 2;
 
         for (int x = 0; x < gridSizeX; x++)
         {
             for (int y = 0; y < gridSizeY; y++)
             {
                 for (int z = 0; z < gridSizeZ; z++)
                 {
                     Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.forward * (y * nodeDiameter + nodeRadius) + Vector3.up * (z * nodeDiameter + nodeRadius);
                     bool walkable = !(Physics.CheckSphere(worldPoint, nodeRadius - .1f, unwalkableMask));
                     grid[x, y, z] = new Node(walkable, worldPoint, x, y, z);
                 }
 
             }
         }
     }
     public List<Node> GetNeighbours(Node node)
     {
         List<Node> neighbours = new List<Node>();
         for (int x = -1; x <= 1; x++)
         {
             for (int y = -1; y <= 1; y++)
             {
                 for (int z = -1; z <= 1; z++)
                 {
                     if (x == 0 && y == 0 && z == 0)
                     {
                         continue;
                     }
                     int checkX = node.GridX + x;
                     int checkY = node.GridY + y;
                     int checkZ = node.GridZ + z;
 
                     if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY && checkZ >= 0 && checkZ < gridSizeZ)
                     {
                         neighbours.Add(grid[checkX, checkY, checkZ]);
 
                     }
 
                 }
             }
         }
 
         return neighbours;
     }
     public Node NodeFromWorldPoint(Vector3 worldPosition)
     {
         float percentX = (worldPosition.x + gridWorldSize.x / 2) / gridWorldSize.x;
         float percentY = (worldPosition.z + gridWorldSize.z / 2) / gridWorldSize.z;
         float percentZ = (worldPosition.y + gridWorldSize.y / 2) / gridWorldSize.y;
         percentX = Mathf.Clamp01(percentX);
         percentY = Mathf.Clamp01(percentY);
         percentZ = Mathf.Clamp01(percentZ);
 
         //Debug.Log(percentX + " " + percentY + " " + percentZ);
 
         int x = Mathf.RoundToInt((gridSizeX - 1) * percentX);
         int y = Mathf.RoundToInt((gridSizeY - 1) * percentY);
         int z = Mathf.RoundToInt((gridSizeZ - 1) * percentZ);
 
         //  Debug.Log(x + " " + y + " " + z);
 
         return grid[x, y, z];
 
     }
 
     public List<Node> path;
     public Node currentNode;
     void OnDrawGizmos()
     {
         Gizmos.DrawWireCube(transform.position, new Vector3(gridWorldSize.x, gridWorldSize.z, gridWorldSize.y));
         //Node Startnode = NodeFromWorldPoint(enemy.transform.position);
        // Debug.Log(Startnode.worldPosition);
        // temp = GetNeighbours(Startnode);
 
         if (grid != null)
         {
             foreach (Node n in grid)
             {
                 //Gizmos.color = (n.walkable) ? Color.white : Color.red;
                 if (path != null)
                 {
                     if (path.Contains(n))
                     {
                         Gizmos.color = Color.black;
                         Gizmos.DrawCube(n.worldPosition, Vector3.one * (nodeDiameter - .1f));
                     }
 
                 }
                 /* foreach(Node neighbour in temp)
                  {
                      Gizmos.color = Color.black;
                      Gizmos.DrawCube(neighbour.worldPosition, Vector3.one * (nodeDiameter - .1f));
                  }*/
                 //}
             }
         }
     }
 }

Node(Basis for nodes)

 using UnityEngine;
 using System.Collections;
 
 public class Node
 {
 
     public bool walkable;
     public Vector3 worldPosition;
     public int GridX;
     public int GridY;
     public int GridZ;
 
     public int gCost;
     public int hCost;
     public Node parent;
     public Node(bool _walkable, Vector3 _worldPos, int _GridX, int _GridY, int _GridZ)
     {
         walkable = _walkable;
         worldPosition = _worldPos;
         GridX = _GridX;
         GridY = _GridY;
         GridZ = _GridZ;
     }
 
 public int fCost
     {
         get
         {
             return gCost + hCost;
         }
     }
 }

Areas that I think are problems

for problem 1. , I keep getting worldposition to be the same as the empty gameobject with the pathfinding and grid script's position and not the player/enemy thats supposed to be pathfinding

my modified function(THIS IS THE PROBLEM AREA)


  public Node NodeFromWorldPoint(Vector3 worldPosition)
         {
             float percentX = (worldPosition.x + gridWorldSize.x / 2) / gridWorldSize.x;
             float percentY = (worldPosition.z + gridWorldSize.z / 2) / gridWorldSize.z;
             float percentZ = (worldPosition.y + gridWorldSize.y / 2) / gridWorldSize.y;
             percentX = Mathf.Clamp01(percentX);
             percentY = Mathf.Clamp01(percentY);
             percentZ = Mathf.Clamp01(percentZ);
     
             //Debug.Log(percentX + " " + percentY + " " + percentZ);
     
             int x = Mathf.RoundToInt((gridSizeX - 1) * percentX);
             int y = Mathf.RoundToInt((gridSizeY - 1) * percentY);
             int z = Mathf.RoundToInt((gridSizeZ - 1) * percentZ);
     
             //  Debug.Log(x + " " + y + " " + z);
     
             return grid[x, y, z];
     
         }

the unmodified function

 public Node NodeFromWorldPoint(Vector3 worldPosition) {
         float percentX = (worldPosition.x + gridWorldSize.x/2) / gridWorldSize.x;
         float percentY = (worldPosition.z + gridWorldSize.y/2) / gridWorldSize.y;
         percentX = Mathf.Clamp01(percentX);
         percentY = Mathf.Clamp01(percentY);
 
         int x = Mathf.RoundToInt((gridSizeX-1) * percentX);
         int y = Mathf.RoundToInt((gridSizeY-1) * percentY);
         return grid[x,y];
     }



pathfindingexample.png (190.3 kB)
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image sparkzbarca · Dec 30, 2017 at 07:15 AM 1
Share

This is a little off topic but I think the first thing you have to ask yourself is why are you attempting to write pathfinding algorithms?

If this is a purely I just think it's a fun learning experience fine. But if your actually making this to actually use it, your kind of getting caught in the weeds here and just need to use a built in unity pathfinding or a free version on the asset store.

Pathfinding is a lot of rather complicated math and frankly, it's like writing a shader or writing code to draw objects etc. You can totally write code to draw objects, but Unity already draws objects for you, shaders for most types of materials already exist etc.

Your almost always better off not re-inventing the wheel, especially when that wheel is like a custom F1 vehicle wheel.

I just have seen several people on here who kind of get stuck in the weeds and don't seem to realize they probably got in this to make video games not math equations, if you like the math equations great, i'm not saying it's not a great legitimate thing to do, but if your passion is making a game, don't waste 100 hours writing code you could download online to do tedious functions. It's already been solved for you.

It's important you understand how pathfinding works, when to use it, how to kind of use it correctly, but debugging your own homebrewed path finding code is seldom a great use of time unless your intent is i'm passionate about just wanting to learn this for myself, you have a VERY unique game that requires super custom pathfinding for some reason, maybe you for example have newtonian physics or something and want your A.I.'s use of pathfinding to reflect that etc, but in general you should get to the business of making a game and not a path finder.

avatar image dragonking300 sparkzbarca · Dec 30, 2017 at 08:58 AM 0
Share

ah, I'm doing it the hard way because I want to learn these skills for my self so I can do even more complicated things in the future. I also have no money to spend on assets so i can't rely on those either. Also its fun! but, I'm a little stuck on the math and have been for awhile and I just need some help with that.

3 Replies

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Bunny83 · Jan 03, 2018 at 12:51 PM

Yes, your distance algorithm doesn't make much sense. Actually for the heuristic the estimated remaining distance doesn't need to be exact. The only important property is that the heuristic is monoton and the distance must not be overestimated. So the estimate should never be larger than the actual cost. That's why most A* algorithms simply use the euclidean distance to the target which is always the shortest distance and will never be larger than the actual solution / cost.


Though if you prefer using a heuristic that is closer to the actual cost you have to approach it different for 3d. In 2d you only have two ways you can move: laterally or diagonally. You first try to move diagonally (since it's more efficient) and move the rest laterally. In 3d you have 3 different ways you can move: laterally, diagonally in a plane, diagonally in a cube. You first want to move along a cube diagonal until the smallest distance is eleminated. Then the remaining distance is like the 2d version where you first move diagonally in the remaining plane and finally laterally. Though the required cases gets quite long if you implement it with an ordinary if else chain. It's easier to follow if you break it down into those 3 steps: cubic diagonal, plane diagonal, lateral movement:

 int GetDistance(Node nodeA, Node nodeB)
 {
     int dX = Mathf.Abs(nodeA.GridX - nodeB.GridX);
     int dY = Mathf.Abs(nodeA.GridY - nodeB.GridY);
     int dZ = Mathf.Abs(nodeA.GridZ - nodeB.GridZ);
     int cost = 0;
     // cube diagonal
     if (dX < dY && dX < dZ)
     {
         cost += dX * 17;
         dY -= dX;
         dX = dZ - dX;
     }
     else if (dY < dZ)
     {
         cost += dY * 17;
         dX -= dY;
         dY = dZ - dY;
     }
     else
     {
         cost += dZ * 17;
         dX -= dZ;
         dY -= dZ;
     }
     
     // remaining distance 2d in dX and dY
     if (dX < dY)
     {
         cost += dX * 14;
         dX = dY - dX;
     }
     else
     {
         cost += dY * 14;
         dX -= dY;
     }
     
     // remaining lateral distance in dX
     return cost + dX * 10;
 }

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image dragonking300 · Jan 03, 2018 at 09:25 PM 0
Share

Ahh, it looks like it works. Now the only problem I seem to have now is that the path stems from a point that isn't anywhere near where I designated the seeker and target to bealt text

The red object is the seeker and the white object is the target

example.png (111.3 kB)
avatar image
0

Answer by JusSumGuy · Dec 31, 2017 at 04:47 AM

I think the problem is that your 3d coordinates don't match up with the 2d coordinates. It's similar to when your using a ray cast to select units in a 3d space from your monitor which is 2d. There's a math formula to deal with this problem.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image dragonking300 · Dec 31, 2017 at 05:16 AM 0
Share

care to share where I can find this math formula? Also where don't they match because i have alot of cordinates

avatar image
0

Answer by dragonking300 · Jan 07, 2018 at 03:57 AM

OK, I learned the pathfinding was way off because it added the position of the empty gameobject I attached my pathfinding script to... However, bunny helped fix my math so I'm accepting his awnser

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

444 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

NavMeshAgent Finding Alternative Route 1 Answer

How do I implement A* pathfinding to my 2d game, without tiles? 4 Answers

AI Pathfinding script with Speed Adjustment 1 Answer

Editing NavMesh Paths? 0 Answers

Is there a way for me to find the walkable edges of my traversable game map? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges