388 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Setting", menuName = "Settings/Setting")]
[System.Serializable]
public class Setting : ScriptableObject
{
#region FIELDS
//general
[Header("General")]
public SettingType Type;
public string Id;
public string Alias;
[TextArea] public string Description;
//type: Bool
public bool BoolValue;
//type: Int
public int IntValue;
public int IntMin;
public int IntMax;
public int IntStep;
//type: Float
public float FloatValue;
public float FloatMin;
public float FloatMax;
public float FloatStep;
//type: String
public string StringValue;
public List<string> AllowedValues;
#endregion
#region CONSTRUCTORS
/// <summary>
/// Базовый конструктор
/// </summary>
private Setting(string id,
string alias,
SettingType type)
{
this.Id = id;
this.Alias = alias;
this.Type = type;
}
/// <summary>
/// Bool конструктор
/// </summary>
public Setting(string id, string alias, SettingType type, bool value) : this(id, alias, type)
{
//Валидация типа
CheckType(type, SettingType.Bool, id);
BoolValue = value;
}
/// <summary>
/// Int конструктор
/// </summary>
public Setting(string id, string alias, SettingType type, int value, int min, int max, int step) : this(id, alias, type)
{
CheckType(type, SettingType.Int, id);
ValidateInt(value: ref value,
min: ref min,
max: ref max,
step: ref step,
settingId: id);
this.IntValue = value;
this.IntMin = min;
this.IntMax = max;
this.IntStep = step;
}
/// <summary>
/// Float конструктор
/// </summary>
public Setting(string id, string alias, SettingType type, float value, float min, float max, float step) : this(id, alias, type)
{
CheckType(type, SettingType.Float, id);
ValidateFloat(value: ref value,
min: ref min,
max: ref max,
step: ref step,
settingId: id);
this.FloatValue = value;
this.FloatMin = min;
this.FloatMax = max;
this.FloatStep = step;
}
/// <summary>
/// String конструктор
/// </summary>
public Setting(string id, string alias, SettingType type, string value, List<string> allowedValues) : this(id, alias, type)
{
CheckType(type, SettingType.String, id);
ValidateString(ref value, allowedValues, id);
this.AllowedValues = allowedValues;
this.StringValue = value;
}
#endregion
#region HELPERS
private void CheckType(SettingType type, SettingType expectedType, string id)
{
if (type != expectedType) throw new System.Exception($"Настройка {id}. Несоответствие типов.");
}
private void ValidateInt(ref int value, ref int min, ref int max, ref int step, string settingId)
{
bool hasWarning = false;
string WarningText = string.Empty;
if (min > max)
{
hasWarning = true;
WarningText += $"Максимум ({max}) ниже минимума ({min}). Минимум и максимум поменяны местами.\n";
(min, max) = (max, min);
}
if (value < min)
{
hasWarning = true;
WarningText += $"Значение {value} ниже минимума {min}. Установлено значение {min}.\n";
value = min;
}
if (value > max)
{
hasWarning = true;
WarningText += $"Значение {value} выше максимума {max}. Установлено значение {max}.\n";
value = max;
}
int delta = max - min;
if (step > delta)
{
hasWarning = true;
WarningText += $"Шаг ({step}) установлен выше чем разница максимума и минимума ({delta}). Шаг установлен в значение {delta}.\n";
step = delta;
}
if (step < 0)
{
hasWarning = true;
WarningText += $"Шаг ({step}) не может быть отрицательным. Значение инвертировано ({-step}).\n";
step = -step;
}
if (hasWarning) Debug.LogWarning($"Настройка {settingId}:\n" + WarningText);
}
private void ValidateFloat(ref float value, ref float min, ref float max, ref float step, string settingId)
{
bool hasWarning = false;
string WarningText = string.Empty;
if (min > max)
{
hasWarning = true;
WarningText += $"Максимум ({max}) ниже минимума ({min}). Минимум и максимум поменяны местами.\n";
(min, max) = (max, min);
}
if (value < min)
{
hasWarning = true;
WarningText += $"Значение {value} ниже минимума {min}. Установлено значение {min}.\n";
value = min;
}
if (value > max)
{
hasWarning = true;
WarningText += $"Значение {value} выше максимума {max}. Установлено значение {max}.\n";
value = max;
}
float delta = max - min;
if (step > delta)
{
hasWarning = true;
WarningText += $"Шаг ({step}) установлен выше чем разница максимума и минимума ({delta}). Шаг установлен в значение {delta}.\n";
step = delta;
}
if (step < 0)
{
hasWarning = true;
WarningText += $"Шаг ({step}) не может быть отрицательным. Значение инвертировано ({-step}).\n";
step = -step;
}
if (hasWarning) Debug.LogWarning($"Настройка {settingId}:\n" + WarningText);
}
private void ValidateString(ref string value, List<string> allowedValues, string settingId)
{
if (allowedValues == null)
{
Debug.LogWarning($"Настройка {settingId}:\nСписок значений null");
}
else if (allowedValues.Count == 0)
{
Debug.LogWarning($"Настройка {settingId}:\nСписок значений пуст");
}
else if (!allowedValues.Contains(value))
{
string newValue = allowedValues[allowedValues.Count / 2];
Debug.LogWarning($"Настройка {settingId}:\nСписок значений не содержит установленного значения{value}. Установлено среднее значение {newValue}.");
value = newValue;
}
}
#endregion
#region CONTROLS
//Возвращает изменилось ли значение
public bool ChangeValue(int direction)
=> Type switch
{
SettingType.Bool => ChangeBool(direction),
SettingType.Int => ChangeInt(direction),
SettingType.Float => ChangeFloat(direction),
SettingType.String => ChangeString(direction),
_ => false
};
private bool ChangeBool(int direction)
{
if (direction == 0) return false;
else
{
bool newValue = direction > 0;
if (newValue == BoolValue)
return false;
BoolValue = newValue;
return true;
}
}
private bool ChangeInt(int direction)
{
if (direction == 0) return false;
int sign = Mathf.Clamp(direction, -1, 1);
int newValue = IntValue + IntStep * sign;
newValue = Mathf.Clamp(newValue, IntMin, IntMax);
if (IntValue == newValue)
return false;
IntValue = newValue;
return true;
}
private bool ChangeFloat(int direction)
{
if (direction == 0) return false;
float sign = Mathf.Sign(direction);
float newValue = FloatValue + FloatStep * sign;
newValue = Mathf.Clamp(newValue, FloatMin, FloatMax);
// Сравнение float с учетом погрешности
if (Mathf.Abs(FloatValue - newValue) < float.Epsilon)
return false;
FloatValue = newValue;
return true;
}
private bool ChangeString(int direction)
{
if (direction == 0 || AllowedValues == null || AllowedValues.Count == 0)
return false;
int sign = direction > 0 ? 1 : -1;
int currentIndex = AllowedValues.IndexOf(StringValue);
// Если значение не в списке
if (currentIndex < 0)
{
return false;
}
int newIndex = currentIndex + sign;
int maxIndex = AllowedValues.Count - 1;
newIndex = Mathf.Clamp(newIndex, 0, maxIndex);
if (currentIndex == newIndex)
return false;
StringValue = AllowedValues[newIndex];
return true;
}
#endregion
#region SERIALIZATION
//Преобразовать класс в DTO
public SettingData ToSerializableData()
{
return new SettingData
{
Id = this.Id,
Alias = this.Alias,
Type = this.Type,
BoolValue = this.BoolValue,
IntValue = this.IntValue,
FloatValue = this.FloatValue,
StringValue = this.StringValue,
IntMin = this.IntMin,
IntMax = this.IntMax,
IntStep = this.IntStep,
FloatMin = this.FloatMin,
FloatMax = this.FloatMax,
FloatStep = this.FloatStep,
AllowedValues = this.AllowedValues
};
}
//Восстановить класс из DTO
public static Setting FromSerializableData(SettingData data)
{
return data.Type switch
{
SettingType.Bool => new Setting(id: data.Id,
alias: data.Alias,
type: data.Type,
value: data.BoolValue),
SettingType.Int => new Setting(id: data.Id,
alias: data.Alias,
type: data.Type,
value: data.IntValue,
min: data.IntMin,
max: data.IntMax,
step: data.IntStep),
SettingType.Float => new Setting(id: data.Id,
alias: data.Alias,
type: data.Type,
value: data.FloatValue,
min: data.FloatMin,
max: data.FloatMax,
step: data.FloatStep),
SettingType.String => new Setting(id: data.Id,
alias: data.Alias,
type: data.Type,
value: data.StringValue,
allowedValues: data.AllowedValues),
_ => null
};
}
public string GetStringForView()
{
return Type switch
{
SettingType.Bool => BoolValue.ToString(),
SettingType.Int => IntValue.ToString(),
SettingType.Float => FloatValue.ToString(),
SettingType.String => StringValue,
_ => null
};
}
#endregion
public enum SettingType
{
Int,
Float,
String,
Bool
}
}