TheOneWake
TheOneWake

Reputation: 21

(Unity3D) Getting a grid-system to detect GameObject that is bigger than one tile

I have run into this issue where I have a GameObject, for example size 1x3 - and the gameobject is only recognized for being on a tile because it's placed there and given coordinates in the inspector (for example 5 x, 0 y, 6 z). The problem being that the code only detects that there is a GameObject on one tile, the middle tile of the grid code.

I need to make sure the other tiles the GameObject is covering is aware that there is a GameObject there, so I can set them all to for example that they're not walkable on - or that you can use them as cover by being next to them. But so far the code only registers 1 tile of the GameObject - therein lies the problem.

Thing is, I don't know where to start. I have some ideas, maybe do a loop to check tiles next to them if there is a gameobject but then the code won't know if something is on that tile and the loop would also be quite heavy on performance - unless I do that loop on Awake or Start or something similar.

Can anyone point me in the right direction? Is there some built-in thing in Unity that can detect if something is above the specific tile I created?

It's an own gridsystem for the record to support 3D, so I'm not using the built-in tilemaps system for 2D games that Unity offers these days.

Unity Version 2017.3

Down below is Gridbase.cs - this handles the world and spawns all nodes (tiles)

public class GridBase : MonoBehaviour
{
    //Grid Scale
    public int sizeX = 32;
    //amount of floors/levels, Y is up
    public int sizeY = 3;
    public int sizeZ = 32;
    public float scaleXZ = 1;
    // character scale
    public float scaleY = 2.3f;

    public Node[,,] grid;
    public List<YLevels> yLevels = new List<YLevels>();

    public bool debugNode = true;
    public Material debugMaterial;
    GameObject debugNodeObj;

    void Start()
    {
        InitPhase();
    }

    public void InitPhase()
    {
        if (debugNode)
            debugNodeObj = WorldNode();

        //debug check all values for the grid.
        Check();

        //Spawn Grid Function
        CreateGrid();

        GameManager.singleton.Init();


    }

    void Check()
    {
        if(sizeX == 0)
        {
            Debug.Log("Size x is 0, assigning min");
            sizeX = 16;
        }
        if(sizeY == 0)
        {
            Debug.Log("Size y is 0, assigning min");
            sizeY = 1;
        }

        if (sizeZ == 0)
        {
            Debug.Log("Size z is 0, assigning min");
            sizeX = 1;
        }

        if (scaleXZ == 0)
        {
            Debug.Log("ScaleXZ is 0, assigning min");
            scaleXZ = 1;
        }

        if (scaleY == 0)
        {
            Debug.Log("ScaleY is 0, assigning min");
            scaleY = 2;
        }
    }


    void CreateGrid()
    {
        grid = new Node[sizeX, sizeY, sizeZ];

        for (int y = 0; y < sizeY; y++)
        {
            YLevels ylvl = new YLevels();
            ylvl.nodeParent = new GameObject();
            ylvl.nodeParent.name = "Level" + y.ToString();
            ylvl.y = y;
            yLevels.Add(ylvl);

            //Creating Collision for all nodes)
            CreateCollision(y);

            for (int x = 0; x < sizeX; x++)
            {
                for (int z = 0; z < sizeZ; z++)
                {
                    Node n = new Node();
                    n.x = x;
                    n.y = y;
                    n.z = z;

                    n.isWalkable = true;

                    if(debugNode)
                    {

                        Vector3 targetPosition = WorldCoordinatesFromNode(x, y, z);
                        GameObject go = Instantiate(debugNodeObj,
                            targetPosition,
                        Quaternion.identity ) as GameObject;

                        go.transform.parent = ylvl.nodeParent.transform;

                    }



                    grid[x, y, z] = n;


                }

            }
        }
    }

    void CreateCollision(int y)
    {
        YLevels lvl = yLevels[y];
        GameObject go = new GameObject();
        BoxCollider box = go.AddComponent<BoxCollider>();
        // Creates a box collider that has the whole size of the grid + a little bit more
        box.size = new Vector3(sizeX * scaleXZ + (scaleXZ * 2),
            0.2f,
            sizeZ * scaleXZ + (scaleXZ * 2));

        //Spawn box collider in center
        box.transform.position = new Vector3((sizeX * scaleXZ) * .5f - (scaleXZ * .5f),
            y * scaleY,
            (sizeZ * scaleXZ) * 0.5f - (scaleXZ * .5f));

        lvl.CollisionObj = go;
        lvl.CollisionObj.name = "lvl " + y + " collision";

    }

    public Node GetNode(int x, int y, int z)
    {
        x = Mathf.Clamp(x, 0, sizeX - 1);
        y = Mathf.Clamp(y, 0, sizeY - 1);
        z = Mathf.Clamp(z, 0, sizeZ - 1);
        return grid[x, y, z];
    }

    //get World Cordinates from any Node
    public Vector3 WorldCoordinatesFromNode(int x, int y, int z)
    {
        Vector3 r = Vector3.zero;
        r.x = x * scaleXZ;
        r.y = y * scaleY;
        r.z = z * scaleXZ;
        return r;
    }

    GameObject WorldNode()
    {
        GameObject go = new GameObject();
        GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
        Destroy(quad.GetComponent<Collider>());
        quad.transform.parent = go.transform;
        quad.transform.localPosition = Vector3.zero;
        quad.transform.localEulerAngles = new Vector3(90, 0, 0);
        quad.transform.localScale = Vector3.one * 0.95f;
        quad.GetComponentInChildren<MeshRenderer>().material = debugMaterial;
        return go;


    }

    public static GridBase singleton;
    private void Awake()
    {
        singleton = this;
    }
}


[System.Serializable]
public class YLevels
{
    public int y;
    public GameObject nodeParent;
    public GameObject CollisionObj;
}

And this is the Node.cs file

public class Node
{
    //Node's position in the grid
    public int x;
    public int y;
    public int z;

    //Node's costs for pathfinding purposes
    public float hCost;
    public float gCost;

    public float fCost
    {
        get //the fCost is the gCost+hCost so we can get it directly this way
        {
            return gCost + hCost;
        }
    }

    public Node parentNode;
    public bool isWalkable = true;

    //Reference to the world object so we can have the world position of the node among other things
    public GameObject worldObject;

    //Types of nodes we can have, we will use this later on a case by case examples
    public NodeType nodeType;
    public enum NodeType
    {
        ground,
        air
    }
}

this is the GridBase code pastebin.com/gazu9jPR that spawns the nodes (tiles) and this is the node script that triggers whenever gridbase references to Node class - pastebin.com/jk25n6ee

Upvotes: 2

Views: 3174

Answers (2)

Behnam Rasooli
Behnam Rasooli

Reputation: 722

All the tiles need to have colliders. Also the object you place on the grid needs to have a collider(tick as trigger) which is big enough to overlap with tiles' colliders. Then you can use that to check how many tiles are covered by that object. The way you can do this is by using Collider.bounds which is of type Bounds. Bounds object has a method called Contains which you can pass in the tile's center point and see if it's covered by the object.

Upvotes: 1

captainCrazy
captainCrazy

Reputation: 132

You can use trigger colliders at the place the gameobjects will be created on. You'll need a seperate collider for each grid cell. Make the gameobject with the collider the child of the grid cell (I don't exactly know your hierarchy, so I'll just keep guessing). Then, when the collider detects the gameobject, let the corresponding cell know that it is occupied. Hope this helps.

Upvotes: 2

Related Questions