Unity-UI-Rounded-Corners
Unity-UI-Rounded-Corners copied to clipboard
Current approach requires multiple materials for different sized images
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.
- Create material
- Add Image game object
- Add Rounded corners component
- 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:
- Add Image game object
- Add Rounded corners component
- 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.
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:
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?
Really, we want 5px for one object and another 20px, can they not be independent?
Thank you @kirevdokimov This solutions works for me👍.