Warm tip: This article is reproduced from stackoverflow.com, please click
c# image pixel unity3d user-interface

UI.Image.pixelsPerUnitMultiplier doesn't update via code

发布于 2020-04-05 23:39:13

I'm making a Neural Network modeling program, and I've already made a tiled Image for my background grid.

I'm trying to change Image Pixels per Unit Multiplier via code, which doesnt work (Editing it via editor in runtime works just fine)

Here's my code for Zoom In/Out:

float desiredSize = this.GetComponent<Camera>().orthographicSize;
desiredSize -= Input.mouseScrollDelta.y * 3;

//Clamp between min and max
desiredSize = Mathf.Clamp(desiredSize, minSize, maxSize);

//Smoothly interpolates to desired size
this.GetComponent<Camera>().orthographicSize = Mathf.Lerp(this.GetComponent<Camera>().orthographicSize, desiredSize, 5f * Time.deltaTime);

//Change pixels per unit multiplier to half of camera's size (with only 1 decimal char)
gridImage.pixelsPerUnitMultiplier = Mathf.Round( (this.GetComponent<Camera>().orthographicSize / 2) * 10f) / 10f;

It changes the value of pixels per unit just as intended, but it doesnt seems to have any effect on the Image itself.

Questioner
Thiago
Viewed
70
derHugo 2019-08-29 11:12

Interesting that this property is nowhere documented. I didn't even find it in the source codes of the ImageEditor nor the GraphcisEditor yet so now I'm really interested in what is actually responsible for displaying this field ^^

What I found however is that after doing changes to the size settings the editors call EditorUtility.SetDirty(graphic);. Ofcourse this is not available in a build but it made me look for - I think - the correct solution:

After changing the property you have to call SetVerticesDirty

gridImage.SetVerticesDirty();

or simply SetAllDirty

gridImage.SetAllDirty();

enter image description here


I'm not sure about efficiency though .. you probably should call this only if the value actually changed.

Also avoid all those repeated GetComponent calls! Do it once in Awake and then re-use the reference.

Finally currently your Lerp is useless! Since you get the current size and calculate the desired size on that one there will be no smooth scaling at all. Rather use a global field in the class and update that one:

// would even be better if you can already reference this in the Inspector
[SerializeField] private Camera _camera;

public Image gridImage;

public float minSize;
public float maxSize;

private float desiredSize;

private void Awake()
{
    if (!_camera) _camera = GetComponent<Camera>();

    // initialize the desired size with the current one
    desiredSize = Mathf.Clamp(_camera.orthographicSize, minSize, maxSize);

    // do it once on game start
    UpdateImage(desiredSize);
}

// Update is called once per frame
private void Update()
{
    var currentSize = _camera.orthographicSize;
    desiredSize -= Input.mouseScrollDelta.y * 3;

    //Clamp between min and max
    desiredSize = Mathf.Clamp(desiredSize, minSize, maxSize);

    // is the scaling already done?
    // -> Skip unnecessary changes/updates
    if (Mathf.Approximately(desiredSize, currentSize)) return;

    //Smoothly interpolates to desired size
    _camera.orthographicSize = Mathf.Lerp(currentSize, desiredSize, 5f * Time.deltaTime);

    UpdateImage(_camera.orthographicSize);
}

private void UpdateImage(float size)
{
    //Change pixels per unit multiplier to half of camera's size (with only 1 decimal char)
    gridImage.pixelsPerUnitMultiplier = Mathf.Round(size / 2 * 10f) / 10f;

    gridImage.SetVerticesDirty();
}

Fixed Version:

enter image description here

This slight jitter at the end of the "animation" comes from your rounding when assigning the pixelsPerUnitMultiplier you might want to consider removing this round and simply apply

gridImage.pixelsPerUnitMultiplier = size / 2f