Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current approach requires multiple materials for different sized images #16

Open
chrisdeeming opened this issue Mar 11, 2021 · 1 comment

Comments

@chrisdeeming
Copy link

For the project I am working on, there are many UI elements which will have different dimensions and different radius values. There are buttons of various sizes, there are UI panels with various sizes which are sometimes nested within each other.

I didn't initially realise that each of these UI elements would need different materials, so all of my rounded corner elements were all using the same material.

Of course I'd be setting a large panel with a width/height of 1280 x 768 to have a radius value of 50 then I'd be setting a very small button to have a radius of 10 and those values would be affecting the large panel.

Because there will be many different elements with many different dimensions, I don't really want to be making a material each time.

I also found the setup somewhat strenuous.

  1. Create material
  2. Add Image game object
  3. Add Rounded corners component
  4. Assign material to both image and rounded corners component

Is there a an alternative approach that is worth exploring?

I'm really quite new to this but I made some adjustments which seem to work for me, but I can't quite find a consensus as to whether it may have wider issues.

using UnityEngine;
using UnityEngine.UI;

[ExecuteInEditMode, RequireComponent(typeof(Image))]
public class RoundedImage : MonoBehaviour
{
	private static readonly int Props = Shader.PropertyToID("_WidthHeightRadius");

	public float radius;

	private Image _image;
	private bool _isSetup;

	void OnRectTransformDimensionsChange()
	{
		Refresh();
	}
	
	private void OnValidate()
	{
		Refresh();
	}
		
	private void SetupMaterial()
	{
		if (_isSetup)
		{
			return;
		}

		_image = GetComponent<Image>();
		if (_image.material != null)
		{
			_image.material = Instantiate(_image.material);
		}

		_isSetup = true;	
	}

	private void Refresh()
	{
		SetupMaterial();
			
		var rect = ((RectTransform) transform).rect;
		_image.material.SetVector(Props, new Vector4(rect.width, rect.height, radius, 0));
	}
}

After creating a single material which can be re-used across any image of any size, the steps to set it up are as follows:

  1. Add Image game object
  2. Add Rounded corners component
  3. Assign material to image only

The changes to the script are as follows:

  • We use RequireComponent to ensure we always have an Image component.
  • There is no longer a public property to provide a reference to the material.
  • Because we know the image already has a reference to the material we get that component and get a reference to the material from that.
  • If we made changes to the material on the image directly we would still experience the same issue I've had with the different values on different objects fighting with each other.
  • So we instead replace the reference to the material on the image with a clone that we instantiate.

I imagine there is some overhead to instantiating a new instance of the material but I think these would represent what would have to be multiple separate materials for my use case anyway so it seems ok to me.

This is how it looks. You can see the outer white panel has a much smaller radius than the inner red panel. This is using the same material on different objects.

image

If I flip back to the original approach you can see that both the outer and inner element have now ended up with the same radius:

image

I haven't yet looked at converting the independent rounded corners script but I suspect it would work in a similar way.

Does this seem beneficial or might there be considerable issues with this approach?

@BirukTes
Copy link

BirukTes commented Mar 19, 2021

Really, we want 5px for one object and another 20px, can they not be independent?

Thank you @kirevdokimov This solutions works for me👍.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants