Я знаю, что есть принятый ответ, который работает, но я чувствую, что использование переменных ScriptableObject, как описано в связанном видео, было неверно истолковано.
Я думаю, вам лучше сделать переменную FloatVariable независимой от вычислений.
Предположим, что вы рассчитываете на здоровье игрока, и ваше значение заполнения будет рассчитано как currentHealth/maxHealth
.
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private float maxHealth;
[SerializeField] private float currentHealth;
void Update()
{
this.floatReference.value = currentHealth/maxHealth;
}
}
public class ImageFillSetter: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Image imageReference;
void Update()
{
this.imageReference.fill = this.floatReference.value;
}
}
Или, скажем, здоровье игрока хранится в двойном виде:
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private double maxHealth;
[SerializeField] private double currentHealth;
void Update()
{
this.floatReference.value = (float)(currentHealth/maxHealth);
}
}
Теперь предположим, что вы добавили поле ввода, в которое можно ввести значение заполнения в виде процентной строки (например, «76»):
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Input input;
void Update()
{
if(Input.GetKeyDown(KeyCode.Enter))
{
this.floatReference.value = float.Parse(input.text)/100f;
}
}
}
ImageFillSetter будет «наблюдать» переменную Float, не зная, как рассчитывался этот float.
Таким образом, вам когда-либо потребуется только один ImageFillSetter, который можно использовать для любого изображения и любого источника данных, при этом имеется 1 или более способов изменения заливки, которые не требуют внесения каких-либо изменений в ImageFillSetter.
Например, предположим, что вы хотите использовать тот же подход, чтобы указать прогресс загрузки асинхронного уровня:
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
private AsyncOperation loadOperation;
void LoadLevelAsync(string levelName)
{
this.loadOperation = SceneManager.LoadLevelAsync(levelName, LoadSceneMode.Additive);
}
void Update()
{
this.floatReference.value = this.loadOperation?.progress ?? 0;
}
}
Это будет работать без каких-либо других изменений, если ваш ImageFillSetter ссылается на тот же FloatVariable.
Думайте о FloatVariable (или любом другом примитиве, например DoubleVariable) как о значении, хранящемся в базе данных. Любой может прочитать значение, и любой может сохранить новое значение. Было бы странно хранить все возможные вычисления для значения в базе данных вместо того, чтобы делать вычисления и просто хранить ответ.
Это не меняет того факта, что вам нужны реализации Scriptable для каждого примитива:
- FloatVariable
- DoubleVariable
- StringVariable
- BoolVariable
- и т.д.
но вам понадобится только один из них, как показано в первом разделе ответа derHugo.