Tactics RPG Anchored UI

The user interface (UI) is one of those areas you always end up spending a lot of time implementing, and every game needs one in some form or fashion. I have built up a variety of reusable libraries in the past, but with Unity’s new UI tools I find myself starting over again. If you’re like me, the anchor and pivot system provided by a RectTransform may have been a bit confusing. I like working with it pretty well in the inspector, because I can modify the anchors and pivot to any corner for easy placement. In code, it wasn’t quite as easy, so this lesson is dedicated to the creation of a few reusable components which will, hopefully, make all our lives easier for awhile.

Layout Anchor

The first component I want to create will provide an easy way to move a RectTransform in relationship to its parent RectTransform, as easily as I am able to via the inspector. The process needs to be as easy as setting text alignment – in fact, I reuse a TextAnchor enum for this purpose. I want to be able to snap it into place, or animate it, and maintain full control over timing and easing curves, etc.

Create a new subfolder in Scripts/Common/ called UI and add a new script there called LayoutAnchor. Because this script will operate on a RectTransform, we can use a tag to make it a required component:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(RectTransform))]
public class LayoutAnchor : MonoBehaviour 
{
	
}

Everything our new script will do is based on its own RectTransform and its parent RectTransform, so we will want to add fields for both, and then assign them in the Awake method. In the event that this object is not part of a view hierarchy, I throw an error message as a warning.

RectTransform myRT;
RectTransform parentRT;

void Awake ()
{
	myRT = transform as RectTransform;
	parentRT = transform.parent as RectTransform;
	if (parentRT == null)
		Debug.LogError( "This component requires a RectTransform parent to work.", gameObject );
}

When positioning our RectTransform, we will need to know the general offsets to use based on the location of the anchor we want and the size of the RectTransform’s rect. Let’s make a method which allows us to get this information from either of the RectTransforms we might want to pass to it:

Vector2 GetPosition (RectTransform rt, TextAnchor anchor)
{
	Vector2 retValue = Vector2.zero;

	switch (anchor)
	{
	case TextAnchor.LowerCenter: 
	case TextAnchor.MiddleCenter: 
	case TextAnchor.UpperCenter:
		retValue.x += rt.rect.width * 0.5f;
		break;
	case TextAnchor.LowerRight: 
	case TextAnchor.MiddleRight: 
	case TextAnchor.UpperRight:
		retValue.x += rt.rect.width;
		break;
	}

	switch (anchor)
	{
	case TextAnchor.MiddleLeft: 
	case TextAnchor.MiddleCenter: 
	case TextAnchor.MiddleRight:
		retValue.y += rt.rect.height * 0.5f;
		break;
	case TextAnchor.UpperLeft: 
	case TextAnchor.UpperCenter: 
	case TextAnchor.UpperRight:
		retValue.y += rt.rect.height;
		break;
	}

	return retValue;
}

I am doing something here that some of the beginners may not understand – I am intentionally not using a break statement between each of the case statements in my switch statement. This allows cases to fall through each other until a break is reached. In other words, any of the Vertical Center anchor settings will modify the return values ‘x’ value by half, and any of the Vertical Right anchor settings will modify the return value by the full width of the RectTransforms rect.

The next method is a bit confusing, I apologize in advance, but I’m not sure I understand the pivot and anchor system 100% myself – I just kept fiddling with it until I got something which worked. It’s purpose is to find the value you would use to make a RectTransform appear in the correct place based on the anchor points you specify. I wanted this to work regardless of the RectTransform’s own pivot and anchor settings, which is why this method is more complex than it could be. For example, if you could assume that both the parent and current RectTrasnform had values of zero for all anchor and pivot settings then the calcuations would have been very easy to determine. However such an assumption doesnt allow for things like anchors which stretch a UI element based on the parent canvas, screen aspect ratio, etc. If any of my brilliant readers out there knows a way to simplify this any further, please let me know:

public Vector2 AnchorPosition (TextAnchor myAnchor, TextAnchor parentAnchor, Vector2 offset)
{
	Vector2 myOffset = GetPosition(myRT, myAnchor);
	Vector2 parentOffset = GetPosition(parentRT, parentAnchor);
	Vector2 anchorCenter = new Vector2( Mathf.Lerp(myRT.anchorMin.x, myRT.anchorMax.x, myRT.pivot.x), Mathf.Lerp(myRT.anchorMin.y, myRT.anchorMax.y, myRT.pivot.y) );
	Vector2 myAnchorOffset = new Vector2(parentRT.rect.width * anchorCenter.x, parentRT.rect.height * anchorCenter.y);
	Vector2 myPivotOffset = new Vector2(myRT.rect.width * myRT.pivot.x, myRT.rect.height * myRT.pivot.y);
	Vector2 pos = parentOffset - myAnchorOffset - myOffset + myPivotOffset + offset;
	pos.x = Mathf.RoundToInt(pos.x);
	pos.y = Mathf.RoundToInt(pos.y);
	return pos;
}

Now that we can determine where to place our RectTransform, lets add a convenient method to actually do it. The value might be useful by itself in some cases, but most of the time I imagine we will just want it to take care of itself:

public void SnapToAnchorPosition (TextAnchor myAnchor, TextAnchor parentAnchor, Vector2 offset)
{
	myRT.anchoredPosition = AnchorPosition(myAnchor, parentAnchor, offset);
}

Let’s add one last option which will allow us to animate moving the RectTransform into position. Note that this bit of code wont compile until you add some more animation extensions which we will add next. Because the method returns a Tweener you can modify all aspects of the animation such as how long it should take or what kind of animation curve to use. You could even register for animation completion events etc.

public Tweener MoveToAnchorPosition (TextAnchor myAnchor, TextAnchor parentAnchor, Vector2 offset)
{
	return myRT.AnchorTo(AnchorPosition(myAnchor, parentAnchor, offset));
}

Anchor Position Tweener

In the Scripts/Common/Animation/ folder add a new script named RectTransformAnchorPositionTweener. This is a very simple script which maintains a reference to its own RectTransform and sets the interpolated Vector (from the update loop) as the anchoredPosition value.

using UnityEngine;
using System.Collections;

public class RectTransformAnchorPositionTweener : Vector3Tweener 
{
	RectTransform rt;

	protected override void Awake ()
	{
		base.Awake ();
		rt = transform as RectTransform;
	}

	protected override void OnUpdate (object sender, System.EventArgs e)
	{
		base.OnUpdate (sender, e);
		rt.anchoredPosition = currentValue;
	}
}

Animation Extensions

When using my animation libraries, I prefer having animation extensions that allow whatever it is being animtated, to animate itself. It’s not necessary, but I find it makes my code more readable:

using UnityEngine;
using System;
using System.Collections;

public static class RectTransformAnimationExtensions 
{
	public static Tweener AnchorTo (this RectTransform t, Vector3 position)
	{
		return AnchorTo (t, position, Tweener.DefaultDuration);
	}
	
	public static Tweener AnchorTo (this RectTransform t, Vector3 position, float duration)
	{
		return AnchorTo (t, position, duration, Tweener.DefaultEquation);
	}
	
	public static Tweener AnchorTo (this RectTransform t, Vector3 position, float duration, Func<float, float, float, float> equation)
	{
		RectTransformAnchorPositionTweener tweener = t.gameObject.AddComponent<RectTransformAnchorPositionTweener> ();
		tweener.startValue = t.anchoredPosition;
		tweener.endValue = position;
		tweener.easingControl.duration = duration;
		tweener.easingControl.equation = equation;
		tweener.easingControl.Play ();
		return tweener;
	}
}

Test Drive

Let’s take our new components out for a test drive! Create a new scene (from the menu bar choose File->New Scene. Add a Panel (from the menu bar choose GameObject->UI->Panel). Select the Panel and add the Layout Anchor component.

Create a temporary script (place it wherever you like – perhaps a Temp folder) named AnchorTests. This script will loop through all of the combinations of anchors and snap or move (animated) the panel accordingly and allow you a chance to watch and make sure that everything works as expected. Attach the AnchorTests script to the same Panel which had the LayoutAnchor.

using UnityEngine;
using System.Collections;

public class AnchorTests : MonoBehaviour 
{
	[SerializeField] bool animated;
	[SerializeField] float delay = 0.5f;

	IEnumerator Start ()
	{
		LayoutAnchor anchor = GetComponent<LayoutAnchor>();
		while (true)
		{
			for (int i = 0; i < 9; ++i)
			{
				for (int j = 0; j < 9; ++j)
				{
					TextAnchor a1 = (TextAnchor)i;
					TextAnchor a2 = (TextAnchor)j;
					Debug.Log(string.Format("A1:{0}   A2:{1}", a1, a2));
					if (animated)
					{
						Tweener t = anchor.MoveToAnchorPosition( a1, a2, Vector2.zero );
						while (t != null)
							yield return null;
					}
					else
					{
						anchor.SnapToAnchorPosition(a1, a2, Vector2.zero);
					}
					yield return new WaitForSeconds(delay);
				}
			}
		}
	}
}

Reset the Panel’s RectTransform values, a good 100×100 panel is perfect for watching the snapped positions (but note that you can experiment with different values for the Anchors and Pivot and it should still work as expected). I also chose to give our Panel a solid red color in its Image component so it was easier to see.

Play the scene. I like to have a Scene view and Game view up at the same time, so I can also watch the positions which move the panel outside of the camera area. Keep the scene around because we will add another test in a bit.

Panel

Let’s add another very reusable component inside the Scripts/Common/UI folder called Panel. It wont hurt my feelings if you prefer a different name to avoid confusion with a Unity Panel (which is really just a GameObject with a RectTransform – no Panel component here). I think Panel is a fitting name though and it isn’t used (via a class name) so I think it’s fair game.

The purpose of this script will be to define target positions and then work with our LayoutAnchor to snap or move to them when necessary. For example, you may want to define which anchors and offsets to use when the panel is supposed to be On-Screen and different anchors and offsets when it is Off-Screen. If you were doing some sort of Navigation View Stack you might have a few different Off-Screen positions (one for when it is not part of the stack, and one for when it is). To maintain flexibility I didn’t force any position naming system, and allowed the user to define how many there will be, and what names to use. The script will also be able to tell you what position it is currently in, and whether or not a transition is active.

Because the Panel component requires the LayoutAnchor component to work, let’s add the RequireComponent tag:

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

[RequireComponent(typeof(LayoutAnchor))]
public class Panel : MonoBehaviour 
{
	
}

The target positions this panel can hold will be implemented as a Serializable class. I am defining this class within the Panel class because that is the only context in which I intend for it to be used. Also, I can imagine a name like Positions being generic enough that I might want to use it again elsewhere. Note that because the class is serializable you will be able to see it appear in the inspector.

[Serializable]
public class Position
{
	public string name;
	public TextAnchor myAnchor;
	public TextAnchor parentAnchor;
	public Vector2 offset;
	
	public Position (string name)
	{
		this.name = name;
	}
	
	public Position (string name, TextAnchor myAnchor, TextAnchor parentAnchor) : this(name)
	{
		this.myAnchor = myAnchor;
		this.parentAnchor = parentAnchor;
	}
	
	public Position (string name, TextAnchor myAnchor, TextAnchor parentAnchor, Vector2 offset) : this(name, myAnchor, parentAnchor)
	{
		this.offset = offset;
	}
}

I want a way to preconfigure a list of target positions via the inspector. However, I dont want the list to be used anywhere else in code – it is only for the initial implementation. So let’s add this property using the SerializeField tag. Ideally, I want to access Positions via a dictionary where a string (name) points to an instance of Position. This too will be private, but will be used internally in my script. If Unity knew how to show Dictionaries in the inspector I wouldn’t need the list, but alas, that wish has not yet been granted (outside of paying for plugins). Add the following fields (to our Panel class now – not inside the Position class), and then implement them in the Awake method.

[SerializeField] List<Position> positionList;
Dictionary<string, Position> positionMap;
LayoutAnchor anchor;

void Awake ()
{
	anchor = GetComponent<LayoutAnchor>();
	positionMap = new Dictionary<string, Position>(positionList.Count);
	for (int i = positionList.Count - 1; i >= 0; --i)
		AddPosition(positionList[i]);
}

Now let’s add a few properties. I can imagine wanting to know the current position, whether or not we are in a transition, and if so, to be able to access the Tweener. I also want a way to get access to a Position instance using a string name. I will do this using an indexer since I kept the Dicitionary private.

public Position CurrentPosition { get; private set; }
public Tweener Transition { get; private set; }
public bool InTransition { get { return Transition != null; }}

public Position this[string name]
{
	get
	{
		if (positionMap.ContainsKey(name))
			return positionMap[name];
		return null;
	}
}

It’s possible that a user may wish to add or remove Positions dynamically so let’s add a few methods to handle this:

public void AddPosition (Position p)
{
	positionMap[p.name] = p;
}

public void RemovePosition (Position p)
{
	if (positionMap.ContainsKey(p.name))
		positionMap.Remove(p.name);
}

The real purpose of this script though, is to actually move the Panel to one of its specified positions. I will support setting a position based both on a string name and a reference to a position instance.

public Tweener SetPosition (string positionName, bool animated)
{
	return SetPosition(this[positionName], animated);
}

public Tweener SetPosition (Position p, bool animated)
{
	CurrentPosition = p;
	if (CurrentPosition == null)
		return null;

	if (InTransition)
		Transition.easingControl.Stop();

	if (animated)
	{
		Transition = anchor.MoveToAnchorPosition(p.myAnchor, p.parentAnchor, p.offset);
		return Transition;
	}
	else
	{
		anchor.SnapToAnchorPosition(p.myAnchor, p.parentAnchor, p.offset);
		return null;
	}
}

If no Position has been set by the time we reach the Start method, I’ll go ahead and assign the first Position in our list as the default position. I’ll also cause it to Snap into the correct position.

void Start ()
{
	if (CurrentPosition == null && positionList.Count > 0)
		SetPosition(positionList[0], false);
}

Test Drive 2

Now let’s modify our test scene to make use of the Panel component. Remove the AnchorTests script from our Panel object and then add the Panel script. Using the inspector we will pre-configure the first two positions. In most use-cases I would imagine that all UI will have its positions pre-configured and saved as part of a prefab.

Create and attach another test script named PanelTests to our Panel object.

using UnityEngine;
using System.Collections;

public class PanelTests : MonoBehaviour 
{
	Panel panel;
	const string Show = "Show";
	const string Hide = "Hide";
	const string Center = "Center";

	void Start ()
	{
		panel = GetComponent<Panel>();
		Panel.Position centerPos = new Panel.Position(Center, TextAnchor.MiddleCenter, TextAnchor.MiddleCenter);
		panel.AddPosition(centerPos);
	}

	void OnGUI ()
	{
		if (GUI.Button(new Rect(10, 10, 100, 30), Show))
			panel.SetPosition(Show, true);
		if (GUI.Button(new Rect(10, 50, 100, 30), Hide))
			panel.SetPosition(Hide, true);
		if (GUI.Button(new Rect(10, 90, 100, 30), Center))
		{
			Tweener t = panel.SetPosition(Center, true);
			t.easingControl.equation = EasingEquations.EaseInOutBack;
		}
	}
}

This script shows how to add a third position in code. It also makes a few simple legacy GUI buttons so we can toggle between the positions of the panel and watch it move based on our input. When moving to our dynamically added position, it also shows how to intercept the Tweener and modify it. Play the scene and try moving the panel around!

Quick Note – the use of OnGUI here is only for the sake of a simpler and quicker demo. The equivalent setup using the new UI would have required more steps including extra scene object setup. The use of OnGUI is by no means required as part of the implementation of the components we created. Furthermore, I don’t recommend the use of OnGUI in a live project, and in my tactics project I won’t be using OnGUI anywhere.

Summary

In this lesson we created a few very reusable components to help make the positioning and animation of UI elements much easier. We created individual tests which showed off the component’s functionality, and provided some ideas about how you could use them in code later on. If you watched the Series Intro video, then you will know I will need to show a variety of UI such as stat panels for the attacker and target as well as a menu which allows you to choose an action or skill. I’ve also created a UI which shows dialog to the user for conversations before and after a battle. All of these UI pieces will be able to make great use of our new components, allowing us to save quite a bit of effort in code.

44 thoughts on “Tactics RPG Anchored UI

    1. I still have a bit more content in there to write on. 🙂

      Ultimately, I would like the series to implement a full battle, including a variety of different skills, statuses, equipment, mission objectives, simple A.I. etc. I had stats and skills in the original sample, but I keep going back and forth on the implementation. It’s one thing to get something working for yourself, and a whole different thing to stick it out in the open for everyone to critique. So, there may be a pause in the series after a few more posts, but I will probably keep working on this one for a long time to come.

      1. I do appreciate that all u’ve shared and u r going to share. Not only I’ve learned a lot about Unity & Game Programming but also the attitude of figure things out. BTW, there r many Unity developers from China love this series. But it’s a pity that I’ve not got any useful feedback about things u wanna discuss. Except one that still confusing about the goodness of combine Monobehaviour and State Mechine.

        1. Thanks for the kind words, and I am really glad to hear that people are enjoying this series in China. If you are getting questions you can’t answer feel free to ask here and I will try to help. The combination of Monobehaviour and State Machine seemed to be a big issue for people here as well. I went back and forth on the decision myself – I finally chose Monobehaviour because I thought it would be easier for the less experienced users. I got a lot of feedback on that post, and provided some of my own reasons on the design decision on a reddit entry here http://www.reddit.com/r/Unity3D/comments/3833y3/tactics_rpg_tutorial_state_machine/

  1. I have to ask. But why are you mixing the legacy system with the new UI system’s anchors?

    RectTransforms only work on canvases and the OnGUI functionality doesn’t use that at all.

    Hopefully I’ve just missed something fundamental in what you are trying to achieve.

    AFAIK, you should not mix legacy GUI with the new UI system as they are at crossed purposes.

    1. Great question Simon. The OnGUI example was merely a short demo to test functionality. I used OnGUI because it is very simple and easy to implement – it can be done entirely through a few lines of code, whereas the same demo created with the new UI would have required more setup (game objects with scripts etc). The Tactics project itself doesn’t use OnGUI anywhere.

  2. Would you please be so kind to explain what are these Vector2 stand for?
    myOffset , parentOffset, anchorCenter ,myAnchorOffset, myPivotOffset (in the AnchorPosition method) And why is the final position to be parentOffset- myOffset-myAnchorOffset+myPivotOffset+offset. I couldn’t figure it out by myself. Thank you!

    1. As I mentioned in the paragraph just before that method, even I was a bit confused while trying to work through it. I really just kept trying different things until I got it working. To start down the path toward understanding it, I think I began by imagining a scenario where I could assume a common origin. For example, imagine you have two different sized rectangles where the lower left corner of each is at 0,0. We will imagine the larger of the two rectangles is the parent, and the smaller of the two is the child.

      Next I picked a random idea – like I want to move the small rectangle so that its origin (the lower left) is at the upper right of the large rectangle. I can see that I need to offset the small rectangle by the full width and height of the large rectangle. This value would be the “parentOffset” Vector2. If no other values were referenced and you were only moving the small rectangle’s origin to a position on the large rectangle it would be all you would need.

      Next, I wanted to be able to put any corner of the small rectangle in any corner of the large rectangle. This is the “myOffset” Vector2. Matching the lower left anchor already worked (because lower left GetPosition is 0,0). What if instead of the lower left (origin) of the small rectangle being moved to the upper right of the large rectangle, I decide I want the upper right corner of each to align. In order to achieve this, I see that I can’t move the small rectangle as far – I actually need to subtract the small rectangles full width and height in order to get the upper right corners aligned. This is why I subtract that vector from the parentOffset vector.

      Next, I have to deal with the unfortunate fact that I couldn’t assume a 0,0 (lower-left) origin for the panels (either the parent or the child), and this is where it started to get confusing. The origin could be in the middle, upper right, or anywhere in between! You can start with the parent – this is what the “anchorCenter” is for. I used a linear interpolation from the min to max anchor point using the child’s pivot as the amount to cross. This gave me a new sort of origin but in a “normalized” space (as in a percentage from 0-1).

      To get a real unit-space origin I next needed to multiply the anchorCenter vector against the size of the parent panel’s rect to determine where the origin really was, and this gave me the “myAnchorOffset” vector.

      Finally, because the pivot of the child is also not necessarily at 0,0 then I also need to determine how much of its own size was offset. So I multiplied its own pivot against its own size.

      Now take all of these offsets and add them together until it works right with any configuration and that’s what I was doing in the “pos” vector. Hopefully that clears it up a little.

  3. I get this error:

    ArgumentNullException: Argument cannot be null.
    Parameter name: key

    Do you know what typically causes this? If so, I can figure out what went wrong.

    1. A stack trace would be helpful in determining the cause of your problem, but I am guessing that you have added the “Panel” component to a GameObject and not configured it in the inspector. Make sure that you have added entries to the Position List – there is a sample image you can copy from in this post.

      1. I double checked and it’s configured the exact same way yours is. This is Unity 5.2.3, but I doubt that makes a difference.

        System.Collections.Generic.Dictionary`2[System.String,Panel+Position].set_Item(System.String key, .Position value) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:155)

        Panel.AddPosition (.Position p) (at Assets/Scripts/Common/UI/Panel.cs:84)

        PanelTests.Start() (at Assets/Scripts/Tests/PanelTests.cs:18)

        So from what I see, it looks like there’s no key set when it tries to add the new position in code via Start() ? In theory what should happen is, it gets added and it shows up in the inspector as a third position in the PositionList when it starts, right?

        1. Alright, that extra bit helped. It says the problem is on line 84 of the “Panel” script in the Method “AddPosition”. The problem is probably this line:
          positionMap[p.name] = p;
          I suppose you have somehow tried adding a position with a name that is null. Dictionaries don’t support null keys. You can always add some debug code just before it, check for null and if it is, use something like this:
          Debug.Log("Null key name", gameObject);
          Then if you click on the output message in the console window, it will highlight the GameObject in the hierarchy panel which is causing the problem. Hopefully that can help you track down the issue.

          What the code in Start is supposed to be doing is to make sure that the Panel is assigned a Position – it will cause the object it is on to snap into its starting place and will aid in having transitions etc. You have the ability to manually assign a Position such as through an Awake method or by programmatically adding the component and then configuring it yourself. Start runs later, so if nothing has specified the position, then it will just default to assigning itself the first position in its own list. It doesn’t create a new position, it simply uses what you had provided.

      2. public Position (string name, TextAnchor myAnchor, TextAnchor parentAnchor) : this(name)

        Turns out I forgot the “: this(name)” part when I typed in my copy of this function. Everything works now 🙂

  4. I dont know if i missing something but unity has anchors points where you can set the pivot to vector2.zero and that does mean we can use the anchored position x and y like always we do in anyplace where is the benefit of do all this?

    1. I would say that the greatest benefit will be seen when animating your UI. You will probably see the value of the library if you tried doing the same things “without” using this or similar code.

      1. but,thats why we have the anchors points min and max we can create a parent empty rect and all children translations will be relative to that rect, i still dont understand where is big flaw using what of no doing this??.

          1. Keep in mind that this post is nearly three years old now and that the Unity libraries evolve over time. Some of this might benefit from being updated. If you see value in it, then use it. If you don’t, then that’s fine too.

            Regardless, I still feel that the code here has value and makes it easier to position things, and is especially more easy when it comes to animation – particularly dynamic animation such as would be needed when making UI that can adapt to a variety of aspect ratios for a wide range of potential screens.

  5. Yeah i know its a old post, i can see the power of dynamic animation and there is where i am interested, but i dont understand some things, i will try doing this myself to see if i can get some advantage from this things. Anyway thanks for this project its good learning resource to do a complete game.

  6. Just curious about the implementation of the indexer. It’s mentioned that it’s used due to the positionMap dictionary being private, but when I tested the SetPosition() method with “positionMap[positionName]” instead of the “this[positionName]”, the PanelTests component still worked as intended, which makes sense, as the SetPanel() method exists in the Panel class, regardless of where it is called from, and therefore has a reference to positionMap regardless. This is my first exposure to indexers, so I’m not surprised if I’m missing something in regards to its requirement in future use, but as is, I’m confused as to why the indexer is required at all, at least in regards to the context in which it’s currently being used.

    Otherwise, great tutorials, they’ve helped me immensely, and thanks in advance for any reply.

    1. Good question. You are correct that it is not strictly “necessary” to use the indexer in the SetPanel method, except that by using the indexer I am also reusing a bit of code that makes accessing the dictionary “safe”. If I try to grab a value from a dictionary with a key that does not exist, it will cause an exception to be thrown. Using the indexer, I can just have null returned instead. Of course the indexer is also able to be used by objects outside of its class. Does that help?

      1. Ahh, I didn’t consider the null reference. I figured the latter might be the case, though, but wasn’t sure. I guess one more advantage is also that it looks a little cleaner. Yeah, that clears everything up. Thanks.

  7. Oh, I figure I should probably mention this, since it might make the tutorial a little more fluid. The use of the term “anchor” in reference to both the TextAnchor Alignments and the Rect Anchors created a fair bit of confusion for me. Once I was able to parse the concepts apart, the tutorial went far more smoothly. Specifying between the Text Anchors and Rect Anchors in certain points (around the AnchorPosition method example, most specifically, but in other places as well) might alleviate some of the confusion around the concept.

    Now that I’m done the tutorial, I’m finding that the system is really great, by the way. Thanks a ton.

  8. What i don’t understood is in reality how you’re
    not actually a lot more well-appreciated than you might be now.

    You’re so intelligent. You recognize thus considerably in terms of this matter, made me individually imagine it from numerous various angles.

    Its like men and women are not interested unless it is something to do with Lady
    gaga! Your own stuffs nice. All the time care for it up!

    1. Haha, that’s very kind! I’ve gotten a lot of pats on the back from my friends, including yourself, and that’s good enough for me 🙂

  9. I think everything wrote was actually very reasonable.
    But, what about this? suppose you added a little information?
    I mean, I don’t wish to tell you how to run your blog, however what if you added
    something to maybe grab people’s attention? I mean Tactics RPG Anchored UI | The Liquid Fire is a little boring.
    You might look at Yahoo’s front page and note how they create article headlines to grab viewers interested.
    You might try adding a video or a pic or two to get readers interested about everything’ve got to
    say. In my opinion, it would bring your posts a little livelier.

  10. Appreciating the persistence you put into your blog and in depth information you provide.
    It’s great to come across a blog every once in a while that isn’t the
    same unwanted rehashed material. Fantastic read! I’ve bookmarked your site and I’m including your RSS feeds to my Google account.

    1. This stuff is great! Congratulations for this amazing post.

      I have one question about the animation system, though. When you start a tween, it creates components in the object, and when it’s over, those component’s are destroyed. But if you stop an animation mid play and start a new one (such as in the “Test Drive 2” section, where you can use buttons to stop an animation in the middle), the components stay forever. I know this probably won’t cause game breaking bug but might consume a lot of memory over the run of a match? Also it’s not so tidy. Is there an easy way to fix that?

      1. Great question. The design I have in place allows you to stop the animation without auto-removing it, because there is reason to believe that if you stop it you may wish to start it again later. If that is not the case, then whatever controller code is driving the animations in the first place can do a get component and destroy the components as needed. Alternatively, if you NEVER find yourself wanting to reuse a stopped animation, then you could add code similar to what happens for completion to the stop method. Does that help?

  11. Not sur if you’re still getting notifications on this, but I’m hoping so…

    I’m getting an error from the RectTransformAnchorPositionTweener script that’s keeping me from moving forward with anything else…I’m getting “no suitable method found to override” errors thrown on both the Awake and OnUpdate methods, and can’t seem to find a solution to it. When I tried deactivating within the script with /*, it borked up so many other things that I just don’t know what to do next.

    Any suggestions on that?

    1. Double check the class declaration for RectTransformAnchorPositionTweener and make sure it inherits from Vector3Tweener. Like so:
      public class RectTransformAnchorPositionTweener : Vector3Tweener

      1. Here is the code as-is:

        using UnityEngine;
        using System.Collections;

        public class RectTransformAnchorPositionTweener : Vector3Tweener
        {
        RectTransform rt;

        protected override void Awake()
        {
        base.Awake();
        rt = transform as RectTransform;
        }

        protected override void OnUpdate()
        {
        base.OnUpdate();
        rt.anchoredPosition = currentTweenValue;
        }
        }

          1. Right now, it’s the only one throwing errors. I’m going through all of the other scripts now to see if I made a mistake somewhere else along the way that’s cascading here.

  12. Hi, I don’t know if you still monitor this for messages. I am following along and find it a wealth of good advice and information. Really kind of you to spend your time making this for people, so thanks very much!

    In terms of UI, I’ve not got a lot of experience in kind of hard-coding it like this.
    In regards to the AnchorTests section:
    Whenever the Position of the panel is set to the right hand side of the screen, the panel moves off the screen, as I imagine the achor of the panel is set to the left of the panel at this point instead of the right, to keep it inside the screen. Is this just how the demo is set up? Or have I missed something?

    I’ve looked at the code to try and find where I could edit it to remedy this, but I’m not too clued up on UI code. Just wanting to make sure I haven’t missed something.
    Thanks again!

    1. Hey there, I do still monitor these and am always happy to know it’s still helpful to people. Keep in mind that the tests are showing all the combinations. You might consider viewing it from the scene view instead of the camera view so you can see positions that are off screen. The combination of anchors are printed to the console so if you see the position you are looking for, pause the game and look at which combo it was.

      I’m pretty certain this is all working correctly, so if it doesn’t look right on yours you may consider downloading from my repository and comparing the code.

      1. I feel like a prize fool. I must have been tired that day. I was thinking it was cycling through all of the A1 text anchors in one run-through. But, I realised it cycles the A1 anchor every ninth time. Just checked it today and can see I’ve had a mental fart.

        Apologies for taking up your time and thanks very much for your help!

Leave a Reply to thejon2014 Cancel reply

Your email address will not be published. Required fields are marked *