Friday, September 4, 2015

Easy Gridlayout in Unity 5

Hello again!  This week's tutorial on Unity's Auto Layouts is probably one of the more useful tutorials so far.  Auto Layouts are like little hidden gems in Unity's expansive engine!



Unity's UI Rect Transform layout system allows for placing of elements in virtually any arrangement.  However, sometimes a bit more structure is needed.  Unity's easy-to-use Auto Layout system provides ways to place elements in organized, nested layout groups. It also allows elements to be automatically re-sized according to the content.


In this tutorial we will explore the Auto Layout system's Grid Layout, but first let's get to know the Auto Layout system a bit better.

The Auto Layout system is built on top of the basic Rect Transform layout system. It can be used on some or all elements of a group.  The Auto Layout system has two basic components: Layout Elements - those that are nested in groups, and Layout Controllers - those that control the sizes and positions of layout elements.  For a more thorough explanation of Layout Elements and Layout Controllers visit the documentation linked in the description below.


In the Prehistoric Scroller project, I am using the Grid Layout to manage the Inventory window.  Watch as each item is added to the inventory.  As each item is added, the Grid Layout controller re-sizes and re-positions the elements of the group.



Let's look at how this is set up.  On the main camera, a second canvas is added to control the inventory.  The parent game object InventoryItems has the Gridlayout Group attached, and is considered a Layout Controller.  The Grid Layout controller has several properties to set, depending on how you want your grid to behave.

First is Padding.  This is the space on the left, right, top, or bottom **inside** the edges of the layout group. It is the same principle as Padding in the Box Model for web development. For more information on the Box Model, visit the documentation linked in the description below.

Cell Size is the size set for each element in the gird. Just like Excel, the X is the column width, and the Y is the row height.

Think of Spacing as the margin between each column, X, and row, Y.



The Start Corner is the corner where the first element is placed.  It works with the Start Axis and Constraint to control how the grid will grow.

If Upper Left, Horizontal is set, as in the Prehistoric Scroller, the next element will be placed to the right of the first cell along the horizontal axis until there are four elements in the row.  The fifth element will start a new row.



Child Alignment aligns elements if they don't fill out the available cell space.  This is similar to Paragraph alignment options.  It is important to remember the children Layout Elements have no control over their sizes.

In the Prehistoric Scroller a script enables the inventory items as the player collects them.  Let's add a Sixths item.



 Each of the inventory items are actually buttons I know I have a bone item to put into the game so  lets make this the inventory bone button. I'll speed things up as I configure the button just like normal.  Great! Now let's put the prefab bone item into the scene and see what happens. The bone inventory button displays perfectly in the InventoryItems grid!! Awesome!!

But what if you have an item that is smaller than the cell size you set on the Grid Layout controller?



Let's look at an example.  This cracked egg sprite is much smaller than the bone sprite. When I add it as a Layout Element its size is being controlled by the GridLayout controller and its scaled to be larger and doesn't really look nice.



To avoid this,I'm going to remove the button and add a blank UI Image instead. I'll add the Cracked Egg Button as a child of the Image. I'll speed things up as I format the button. Finished! Now next to the other inventory items the cracked egg looks great!

That's it!! Auto layouts, like the Grid Layout, make it easy to quickly set up organized UI in Unity. See you next time!

2 comments:

  1. Hi, I know the question I'm about to ask isn't irrelevant to this tutorial but I was wondering if you could help me! I'm creating a topdown 2d click to move game on unity (as most of you may already know due to recent post) and having trouble with the animation. I created two states, idle and walk, in the animator then created a new blend tree in both states. I also created 5 parameters; walking, SpeedX, SpeedY, LastMoveX and LastMoveY. I had to alter my code so that the animation could work with my click to move code, but i'm having problems. My player isn't going back to idle state once it's reached it's position, I think it has to do with the private boolean touched, but if i were to take it out it would be glitchy and makes my player face both ways once it reaches it's position. Here's an image of what my animator looks like when my player reaches it's destination ( http://i.imgur.com/y7jtqKb.png) - sorry for low resolution (and here's and image of what my player would look like if I were to take out my touched boolean https://imgur.com/sDhM3ox - but much more faster). As you can see my player is stuck in the walking state instead of returning back to idle state once it's reached it's destination. Thank you.

    public class Playermovement : MonoBehaviour {

    private Animator anim;
    public float speed = 15f;
    private Vector3 target;
    private bool touched;


    void Start () {
    target = transform.position;
    anim = GetComponent ();
    }


    void Update () {
    if (Input.GetMouseButtonDown (0)) {
    Vector3 mousePosition = Input.mousePosition;
    mousePosition.z = 10; // distance from the camera
    target = Camera.main.ScreenToWorldPoint(mousePosition);
    target.z = transform.position.z;
    }


    if (Input.touchCount > 0) {
    target.x = Input.touches [0].deltaPosition.x;
    target.y = Input.touches [0].deltaPosition.y;
    }

    transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);

    if (Input.touchCount > 0)
    {
    touched = true;
    }else {
    touched = false;
    }


    var movementDirection = (target - transform.position).normalized;

    if (movementDirection.x != 0 || movementDirection.y != 0) {
    anim.SetBool ("walking", false);
    anim.SetFloat("SpeedX", movementDirection.x);
    anim.SetFloat("SpeedY", movementDirection.y);
    anim.SetBool ("walking", true);
    }



    Vector3 movement = new Vector3 (
    speed * movementDirection.x,
    speed * movementDirection.y,
    0);

    movement *= Time.deltaTime;

    transform.Translate (movement);
    }

    void FixedUpdate () {
    Debug.Log (touched);
    if (touched) {

    float LastInputX = transform.position.x - target.x;
    float LastInputY = transform.position.y - target.y;
    Debug.Log(LastInputX + ", " + LastInputY);


    if (LastInputX != 0 || LastInputY != 0) {
    anim.SetBool ("walking", true);
    if (LastInputX < 0) {
    anim.SetFloat ("LastMoveX", 1f);
    } else if (LastInputX > 0) {
    anim.SetFloat ("LastMoveX", -1f);
    } else {
    anim.SetFloat ("LastMoveX", 0f);
    }

    if (LastInputY > 0) {
    anim.SetFloat ("LastMoveY", 1f);
    } else if (LastInputY < 0) {
    anim.SetFloat ("LastMoveY", -1f);
    } else {
    anim.SetFloat ("LastMoveY", 0f);
    }

    } else {
    anim.SetBool ("walking", false);
    touched = false;
    Debug.Log("Walking is false");
    }
    }
    }
    }

    ReplyDelete