feat(logging): add advanced logging example with file output

- Created AdvancedLoggingExample class demonstrating composite and file loggers
- Implemented multi-file logging setup with separate error and full logs
- Added game event and performance logging methods
- Included key input handling for log operations (flush, clear)
- Setup proper resource disposal on application quit
This commit is contained in:
Данил Ярош 2025-11-07 14:12:50 +03:00
parent 8647572a37
commit 98d5285b46
107 changed files with 77690 additions and 4442 deletions

View File

@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>event'ов</w>
</words>
</dictionary>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="TypeScriptLibrary" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
Assets/Fonts.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c18aab31685c83e4aade0ec2cc4be64e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Fonts/NEW.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8239d7c546e392049b05507ecf82e3e6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f10bbcf486189d4f95b8de6dc2e2177
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -729,6 +729,17 @@
"action": "Secret",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "5e6a0ace-34fd-439a-84e9-cf814f042efa",
"path": "<Keyboard>/f3",
"interactions": "",
"processors": "",
"groups": ";Keyboard&Mouse",
"action": "Secret",
"isComposite": false,
"isPartOfComposite": false
}
]
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 377d96ec6ef9ae6429ebb389817b028f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -76,9 +76,9 @@ SpriteRenderer:
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_SortingLayerID: 1159882347
m_SortingLayer: -1
m_SortingOrder: -1
m_Sprite: {fileID: 21300000, guid: 6bf6ab5c1534fe44b803733131ceacb9, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@ -186,4 +186,7 @@ MonoBehaviour:
spawnForward: {x: 1, y: 0}
playerTag: Player
activateVfx: {fileID: 0}
activateSfx: {fileID: 0}
activateSfx:
sound: {fileID: 0}
volume: 0
pitch: 0

View File

@ -10,6 +10,7 @@ GameObject:
m_Component:
- component: {fileID: 6708844088101150}
- component: {fileID: -6830167632558745604}
- component: {fileID: 7290507550622501242}
- component: {fileID: 4577859410310799261}
- component: {fileID: -6245296037342915518}
- component: {fileID: 6566017136146807207}
@ -48,6 +49,19 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e679be5faf94f304d8251931f02c036a, type: 3}
m_Name:
m_EditorClassIdentifier: '::'
--- !u!114 &7290507550622501242
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4763496076470024150}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a0811f9af9cb5dc4fbd4d2be780bb43f, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::RR.Common.Managers.SettingsManager
gameSettings: {fileID: 11400000, guid: bdc702de5b61eae45a0dc18413673fd8, type: 2}
--- !u!114 &4577859410310799261
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -155,7 +155,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 200, y: -75}
m_AnchoredPosition: {x: 225, y: -100}
m_SizeDelta: {x: 200, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &22609641
@ -1536,7 +1536,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -75}
m_AnchoredPosition: {x: 0, y: -100}
m_SizeDelta: {x: 200, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &606480483
@ -1734,7 +1734,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -125, y: -74.99994}
m_AnchoredPosition: {x: -150, y: -100}
m_SizeDelta: {x: 200, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &857663739

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
// using UnityEngine;
//
// public class SettingsDemo : MonoBehaviour
// {
// [SerializeField] private SettingsScreen screen;
// void Start()
// => screen.Open();
//
//
// void Update()
// {
// int direction = 0;
// if (Input.GetKeyDown(KeyCode.LeftArrow)) direction -= 1;
// if (Input.GetKeyDown(KeyCode.RightArrow)) direction += 1;
//
// screen.Switch(direction);
//
// if (Input.GetKeyDown(KeyCode.Space)) screen.Accept();
//
// }
// }

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bbc476c5afe6544d9b7fb58df71d41e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8d96790d32e90964ca86a7b61d60c7e8
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -101,16 +101,16 @@ public class ComInputToInputSystemAdapter : MonoBehaviour
if (target == TargetTarget.Button)
{
st = st.WithButton(btn, true);
st = st.WithButton(btn);
}
else
{
st = dir switch
{
DpadDir.Up => st.WithButton(GamepadButton.DpadUp, true),
DpadDir.Down => st.WithButton(GamepadButton.DpadDown, true),
DpadDir.Left => st.WithButton(GamepadButton.DpadLeft, true),
DpadDir.Right => st.WithButton(GamepadButton.DpadRight, true),
DpadDir.Up => st.WithButton(GamepadButton.DpadUp),
DpadDir.Down => st.WithButton(GamepadButton.DpadDown),
DpadDir.Left => st.WithButton(GamepadButton.DpadLeft),
DpadDir.Right => st.WithButton(GamepadButton.DpadRight),
_ => st
};
}
@ -121,7 +121,7 @@ public class ComInputToInputSystemAdapter : MonoBehaviour
if (_deviceCreated && _virtualPad != null) return;
// ищем уже созданный «COM Bridge», чтобы не дублировать
foreach (var dev in InputSystem.devices)
foreach (InputDevice dev in InputSystem.devices)
{
if (dev is not Gamepad { description: { product: "COM Bridge Gamepad" } } gp) continue;
_virtualPad = gp;

View File

@ -34,7 +34,7 @@ public class COMInput : InputSource
}
catch (Exception e)
{
Debug.Log($"Что то в COMinput {e.ToString()}");
GameManager.Logger.Log("COMInput", $"Что то в COMInput {e.ToString()}");
return false;
}
return true;

View File

@ -1,6 +1,10 @@
using System.Text;
using System.Collections;
using System.Text;
using RR.Game2D.Core.Managers;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
/// <summary>
/// Основной класс-обертка для работы с COM-портом и периферийной платой.
@ -13,8 +17,8 @@ public class ComWrapper : MonoBehaviour
[SerializeField] bool enablePrizeChecking = false; // Если хопер с проверкой наличия приза ставим true
public static UnityEvent<int> OnCoinAdded;
public static UnityEvent<int> OnCoinRemoved;
public static UnityEvent<int> OnCreditAdded;
public static UnityEvent<int> OnCreditRemoved;
public static UnityEvent<string> OnInputReceived;
#region Приватные поля
@ -45,9 +49,9 @@ public class ComWrapper : MonoBehaviour
public void Initialize()
{
//Создаем ивенты
OnCoinAdded = new UnityEvent<int>();
OnCoinRemoved = new UnityEvent<int>();
OnInputReceived = new();
OnCreditAdded = new UnityEvent<int>();
OnCreditRemoved = new UnityEvent<int>();
OnInputReceived = new UnityEvent<string>();
//Начальная настройка
if (!saveMoneyBetweenSessions) PlayerPrefs.SetInt("CoinsCount", 0);
@ -55,7 +59,7 @@ public class ComWrapper : MonoBehaviour
initPort(); //вот это важный метод который собственно порт открывает.
//Каждые 5 сек запрашиваем инпуты у платы. Надо потому что при заходе в игру с пинов еще не пришла инфа
if (enablePrizeChecking) InvokeRepeating(nameof(requestInputs), 0, 5);
if (enablePrizeChecking) StartCoroutine(KeepPortAliveProcess());
}
/// <summary>
@ -70,7 +74,7 @@ public class ComWrapper : MonoBehaviour
{
CreditsCount++;
PlayerPrefs.SetInt("CoinsCount", CreditsCount);
OnCoinAdded?.Invoke(CreditsCount);
OnCreditAdded?.Invoke(CreditsCount);
}
/// <summary>
@ -88,7 +92,7 @@ public class ComWrapper : MonoBehaviour
{
int w = ComNativeWrapper.Serial_WriteStringUTF8(h, message,
1); //w-количество записаных символов в порт. можно обработать
//Debug.Log("Wrote: " + w);
//GameManager.Logger.Log("Wrote: " + w);
}
catch
@ -117,13 +121,19 @@ public class ComWrapper : MonoBehaviour
mode switch
{
LightMode.Idle => "106",
LightMode.Orange => "107",
LightMode.Blue => "108",
LightMode.Win => "109",
LightMode.Lose => "110",
LightMode.Prize => "111",
_ => "106"
LightMode.Idle1 => "500",
LightMode.Idle2 => "507",
LightMode.Orange => "501",
LightMode.Blue => "502",
LightMode.Win => "503",
LightMode.Lose => "504",
LightMode.Prize => "505",
LightMode.Tama => "513",
LightMode.Dog => "514",
LightMode.Sam => "515",
LightMode.Mistress => "516",
_ => "500"
};
}
@ -155,7 +165,7 @@ public class ComWrapper : MonoBehaviour
// if (SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл")
// {
// //Счетчики пробега
// PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1);
PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1);
// PlayerPrefs.SetInt(PrizeSystem.key_startsNul, PlayerPrefs.GetInt(PrizeSystem.key_startsNul) + 1);
//
// return true;
@ -170,35 +180,73 @@ public class ComWrapper : MonoBehaviour
PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1);
PlayerPrefs.SetInt(PrizeSystem.key_startsNul, PlayerPrefs.GetInt(PrizeSystem.key_startsNul) + 1);
OnCoinRemoved?.Invoke(CreditsCount);
OnCreditRemoved?.Invoke(CreditsCount);
return true;
}
return false;
}
//===========================end: PUBLIC API===========================//
private void Update() => pollPort();
private void OnDisable() => ClosePort();
private void OnApplicationQuit() => ClosePort();
private IEnumerator KeepPortAliveProcess()
{
OnInputReceived?.AddListener(InputReceivedCather);
GameManager.Logger.Log("Подключаю проверку порта.");
while (true)
{
requestInputs();
yield return new WaitForSeconds(5f);
//нет инпута
if (GetInputToggle() == 0)
{
GameManager.Logger.Log("Плата отвалилась. Запускаю переподключение.");
initPort();
}
//есть инпут
else
{
ToggleInputReceived(0);
}
yield return new WaitForSeconds(5);
}
}
void ToggleInputReceived(int value) => PlayerPrefs.SetInt("inputToggle", value);
int GetInputToggle() => PlayerPrefs.GetInt("inputToggle", 0);
void InputReceivedCather(string input)
{
if (!string.IsNullOrEmpty(input))
{
//пришел инпут
ToggleInputReceived(1);
}
}
#region Приватные методы
//Ну тут по дебаг логам все должно быть понятно
//Если интересно, то h - это хэндл. Что то на плюсовом, типа ID соединения или чтото. Если 0 то нету соед. если меньше то ошибка, больше 0 - норм.
private void initPort()
{
Debug.Log("Старт программы");
GameManager.Logger.Log("ComWrapper", "Старт программы");
ComNativeWrapper.Serial_CloseAll();
var ports = ComNativeWrapper.ListPorts();
Debug.Log("Ports: " + string.Join(", ", ports));
if (ports.Length == 0) { Debug.Log("Нет портов, завершаю"); return; }
GameManager.Logger.Log("ComWrapper","Ports: " + string.Join(", ", ports));
if (ports.Length == 0) { GameManager.Logger.Log("Нет портов, завершаю"); return; }
string port = ports[^1];
Debug.Log("Открываю первый с конца порт: " + port);
GameManager.Logger.Log("ComWrapper","Открываю первый с конца порт: " + port);
//вот эти настройки лучше не ковырять, но если очень хочется можно поменять readTimeoutMs и writeTimeoutMs
@ -208,16 +256,21 @@ public class ComWrapper : MonoBehaviour
h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 5, 10);
Debug.Log($"handle: {h}");
GameManager.Logger.Log("ComWrapper",$"handle: {h}");
if (h <= 0)
{
Debug.Log("Open failed: " + ComNativeWrapper.GetLastError());
GameManager.Logger.Log("ComWrapper","Open failed: " + ComNativeWrapper.GetLastError());
return;
}
else
{
GameManager.Logger.Log("ComWrapper",$"Открыл порт: {port}");
}
}
private static void ClosePort()
{
GameManager.Logger.Log("ComWrapper","Закрываю все порты");
if (h > 0)
{
ComNativeWrapper.Serial_Close(h);
@ -229,17 +282,17 @@ public class ComWrapper : MonoBehaviour
//Тут чтение, не трогаем(опционально)
private static string ReadBufferLine()
{
int read = ComNativeWrapper.Serial_ReadLineUTF8_bytes(h, buf, buf.Length);
if (read > 0)
{
var s = Encoding.UTF8.GetString(buf, 0, read);
//Debug.Log($"ReadLine len={read} data=[{s}]");
//GameManager.Logger.Log($"ReadLine len={read} data=[{s}]");
return s;
}
else if (read < 0)
{
Debug.Log($"ReadLine err={read} :: {ComNativeWrapper.GetLastError()}");
GameManager.Logger.Log("ComWrapper",$"ReadLine err={read} :: {ComNativeWrapper.GetLastError()}");
}
return "";
}
@ -262,10 +315,15 @@ public class ComWrapper : MonoBehaviour
}
public enum LightMode
{
Idle,
Idle1,
Idle2,
Orange,
Blue,
Win,
Lose,
Prize,
Tama,
Dog,
Sam,
Mistress
}

View File

@ -1,4 +1,5 @@
using UnityEngine;
using RR.Game2D.Core.Managers;
using UnityEngine;
/// <summary>
/// Статический фасад для унифицированного доступа к системе ввода.
@ -42,7 +43,7 @@ public class In : MonoBehaviour
var success = source?.Initialize() ?? false;
if (!success) Debug.LogWarning("Не удалось проинициалзировать источник управления.");
if (!success) GameManager.Logger.LogWarning("In.cs","Не удалось проинициалзировать источник управления.");
}
// ================================================================================

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7256e3bbec987684f998648327549db3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -165,6 +165,16 @@ namespace RR.Common.Managers
#region Public API
public void SetMusicVolume(float newVolume)
{
// TODO!!!!!
}
public void SetSfxVolume(float newVolume)
{
// TODO!!!!!
}
/// <summary>
/// Мгновенно запустить музыку (без фейда).
/// </summary>
@ -261,6 +271,8 @@ namespace RR.Common.Managers
PlaySfxAt(chosenEffect.sound, position, chosenEffect.volume, chosenEffect.pitch);
}
#endregion
// ==== Internal helpers ====
private void StopCrossfadeAndFade()
@ -380,6 +392,5 @@ namespace RR.Common.Managers
src.Play();
}
#endregion
}
}

View File

@ -4,6 +4,7 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using RR.Common.Managers;
using RR.Common.Utility;
using RR.Game2D.Behaviour;
using RR.Game2D.Entities;
@ -15,9 +16,19 @@ namespace RR.Game2D.Core.Managers
// Singleton instance
public static GameManager Instance { get; private set; }
// AudioManager instance
// Logger
public static CompositeLogger Logger { get; private set; }
// Game settings
public SettingsManager Settings { get; private set; }
// Audio control for game
public AudioManager Audio {get; private set;}
// Payment control for game
public PaymentManager Payment {get; private set;}
// Globals
[Header("Globals")]
private GameObject globals;
@ -35,6 +46,8 @@ namespace RR.Game2D.Core.Managers
public int StagesByWorld { get; private set; } = 3;
public int Lives { get; set; } = 3;
public int Coins { get; private set; } = 0;
[HideInInspector] public bool ResetLives { get; set; } = true;
private float gameStartTime;
[Header("Игрок")]
@ -54,9 +67,7 @@ namespace RR.Game2D.Core.Managers
public event Action<float> GameOverEvent; // Когда игрок проигрывает
public event Action<float> GameWonEvent; // Когда игрок выигрывает
//---------------------------
// Singleton
//---------------------------
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
@ -66,18 +77,11 @@ namespace RR.Game2D.Core.Managers
Instance = go.AddComponent<GameManager>();
DontDestroyOnLoad(go);
}
// else
// {
// go = Instance.gameObject;
// }
//
// go.AddComponent<ComWrapper>();
// go.AddComponent<In>();
// go.AddComponent<PrizeSystem>();
}
private void Awake()
{
// ---- Singleton ----
if (Instance != null && Instance != this)
{
Destroy(gameObject);
@ -86,13 +90,23 @@ namespace RR.Game2D.Core.Managers
Instance = this;
DontDestroyOnLoad(gameObject);
// -------------------
// Initialize Logger
if (Logger == null)
{
Logger = new CompositeLogger();
FileLogger fileLogger = new(Path.Combine(Directory.GetParent(Application.dataPath)?.ToString() ?? Application.dataPath, "game.log"));
Logger.Initialize(fileLogger);
}
// Initialize Audio control
if (Audio == null)
{
Audio = gameObject.AddComponent<AudioManager>();
Audio.Initialize();
}
if (globals == null)
{
GameObject prefab = Resources.Load<GameObject>(globalsPrefabResourcePath);
@ -103,19 +117,29 @@ namespace RR.Game2D.Core.Managers
}
}
if (Settings == null)
{
Settings = globals.GetComponent<SettingsManager>();
}
// Initialize Payment control
if (Payment == null)
{
Payment = gameObject.AddComponent<PaymentManager>();
Payment.Initialize();
}
gameStartTime = Time.unscaledTime;
Debug.Log($"{gameObject.name}: Start time = {gameStartTime}");
Logger.Log(gameObject.name,$"Game start time = {gameStartTime}");
if (SceneManager.GetActiveScene().name != "MainMenu") _shouldSpawnPlayer = true;
}
private void OnDestroy()
{
Logger = null;
if (Instance == this) Instance = null;
}
//---------------------------
//
//---------------------------
//private void Start()
//{
@ -141,9 +165,11 @@ namespace RR.Game2D.Core.Managers
if (loaded == null)
{
Debug.LogError(
$"GameManager: playerPrefab is null. Resources.Load('{playerPrefabResourcePath}') вернул null. " +
$"Ожидается файл: Assets/Resources/{playerPrefabResourcePath}.prefab (точное совпадение регистра).");
Logger.LogError(
gameObject.name,
$"playerPrefab is null. Resources.Load('{playerPrefabResourcePath}') вернул null." +
$" Ожидается файл: Assets/Resources/{playerPrefabResourcePath}.prefab (точное совпадение регистра)."
);
}
return loaded;
}
@ -217,7 +243,7 @@ namespace RR.Game2D.Core.Managers
else
{
//NewGame();
// Debug.Log("WIN");
// Logger.Log("WIN");
// LoadScene("MainMenu", false);
// Можно заменить на другое поведение

View File

@ -1,4 +1,5 @@
using System;
using RR.Game2D.Core.Managers;
using UnityEngine;
using UnityEngine.InputSystem;
@ -6,6 +7,7 @@ namespace RR.Common.Managers
{
public class GlobalsManager : MonoBehaviour
{
private SettingsManager _settingsManager;
private ComWrapper _comWrapper;
private In _in;
private PrizeSystem _prizeSystem;
@ -13,6 +15,8 @@ namespace RR.Common.Managers
private void Awake()
{
GameManager.Logger.Log(name ,"Initializing global objects...");
_settingsManager = GetComponent<SettingsManager>();
_comWrapper = GetComponent<ComWrapper>();
_in = GetComponent<In>();
_prizeSystem = GetComponent<PrizeSystem>();
@ -21,11 +25,23 @@ namespace RR.Common.Managers
if (_comWrapper == null) gameObject.AddComponent<ComWrapper>();
if (_in == null) gameObject.AddComponent<In>();
if (_prizeSystem == null) gameObject.AddComponent<PrizeSystem>();
if (_settingsManager == null) gameObject.AddComponent<SettingsManager>();
if (_adapter == null) gameObject.AddComponent<ComInputToInputSystemAdapter>();
_settingsManager.Initialize();
GameManager.Logger.Log(name, "SettingsManager successfully initialized.");
_comWrapper.Initialize();
GameManager.Logger.Log(name, "ComWrapper successfully initialized.");
_in.Initialize();
GameManager.Logger.Log(name, "In.cs successfully initialized.");
_prizeSystem.Initialize();
GameManager.Logger.Log(name, "PrizeSystem successfully initialized.");
_adapter.Initialize();
GameManager.Logger.Log(name, "InputAdapter successfully initialized.");
}
}
}

View File

@ -0,0 +1,38 @@
using UnityEngine;
namespace RR.Common.Managers
{
public class PaymentManager : MonoBehaviour
{
public static PaymentManager Instance { get; private set; }
public int CreditsCount;
public void Initialize()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
CreditsCount = 0;
ComWrapper.OnCreditAdded.AddListener(UpdateCredits);
}
private void UpdateCredits(int newCreditsCount)
{
CreditsCount = newCreditsCount;
}
// Если не инициализируется в GameManager, инициализировать тут
// private void Awake()
// {
// Initialize();
// }
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 60e317a2a046c5644ae8e9281284c244

View File

@ -0,0 +1,170 @@
using System;
using System.IO;
using System.Text;
using UnityEngine;
using RR.Game2D.Core.Managers;
using RR.Common.Behaviour.ScriptableObjects;
namespace RR.Common.Managers
{
public class SettingsManager : MonoBehaviour
{
public static SettingsManager Instance { get; private set; }
[SerializeField] private GameSettings gameSettings;
private const string SETTINGS_FILE_NAME = "settings.json";
private string SettingsFilePath => Path.Combine(Application.persistentDataPath, SETTINGS_FILE_NAME);
public Action OnSettingsLoaded;
public Action OnSettingsSaved;
public void Initialize()
{
// ---- Singleton ----
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
// -------------------
// Loading and applying game settings
if (gameSettings == null)
{
GameManager.Logger.Log($"{name}: gameSettings is null. Устанавливаю gameSettings по умолчанию.");
gameSettings = ScriptableObject.CreateInstance<GameSettings>();
}
LoadSettings();
gameSettings.ApplySettings();
}
// Если не инициализируется в GameManager, инициализировать тут
// private void Awake()
// {
// Initialize();
// }
private void OnApplicationQuit()
{
SaveSettings();
}
public void LoadSettings()
{
if (gameSettings == null)
{
Debug.LogError($"{name}: gameSettings is null. Невозможно загрузить настройки.");
}
if (File.Exists(SettingsFilePath))
{
try
{
string json = File.ReadAllText(SettingsFilePath);
Debug.Log($"{name}: Пытаюсь загрузить настройки из: {SettingsFilePath}");
Debug.Log($"{name}: Загруженный JSON: {json}");
GameSettings tempSettings = ScriptableObject.CreateInstance<GameSettings>();
JsonUtility.FromJsonOverwrite(json, tempSettings);
tempSettings.ValidateSettings();
JsonUtility.FromJsonOverwrite(json, gameSettings);
Debug.Log($"{name}: Настройки загружены и проверены успешно.");
Destroy(tempSettings);
}
catch (Exception e)
{
Debug.LogError($"{name}: Ошибка при загрузке настроек: {e.Message}. Применяю настройки по умолчанию.");
gameSettings.SetDefaultValues();
}
}
else
{
Debug.Log($"{name}: Настройки не найдены по пути: {SettingsFilePath}. Применяю настройки по умолчанию.");
gameSettings.SetDefaultValues();
}
gameSettings.ApplySettings();
OnSettingsLoaded?.Invoke();
}
public void SaveSettings()
{
if (gameSettings == null)
{
Debug.LogError($"{name}: gameSettings is null. Невозможно сохранить настройки.");
return;
}
try
{
string json = JsonUtility.ToJson(gameSettings, true);
File.WriteAllText(SettingsFilePath, json, Encoding.UTF8);
Debug.Log($"{name}: Настройки сохранены по пути: {SettingsFilePath}.");
Debug.Log($"{name}: Сохраненный JSON: {json}");
OnSettingsSaved?.Invoke();
}
catch (Exception e)
{
Debug.LogError($"{name}: Ошибка при сохранении настроек: {e.Message}.");
}
}
public void SetFullscreen(bool newFullscreen)
{
gameSettings.isFullscreen = newFullscreen;
Debug.Log($"{name}: Установлен режим: {(newFullscreen ? "полноэкранный" : "оконный")}.");
}
public void SetMusicVolume(float newVolume)
{
gameSettings.musicVolume = newVolume;
Debug.Log($"{name}: Установлена громкость музыки: {newVolume}.");
}
public void SetSfxVolume(float newVolume)
{
gameSettings.sfxVolume = newVolume;
Debug.Log($"{name}: Установлена громкость звуков: {newVolume}.");
}
public void SetIsFreeGame(bool newIsFreeGame)
{
gameSettings.isFreeGame = newIsFreeGame;
Debug.Log($"{name}: Установлена флаг 'free game': {newIsFreeGame}.");
}
public void SetIsGivingPrizes(bool newIsGivingPrizes)
{
gameSettings.isGivingPrizes = newIsGivingPrizes;
Debug.Log($"{name}: Установлена флаг 'give prizes': {newIsGivingPrizes}.");
}
public void SetStartLivesCount(int newStartLivesCount)
{
gameSettings.startLivesCount = newStartLivesCount;
Debug.Log($"{name}: Установлен количество жизней: {newStartLivesCount}.");
}
public void SetLivesIncreasedByCredits(int newLivesIncreasedByCredits)
{
gameSettings.livesIncreasedByCredits = newLivesIncreasedByCredits;
Debug.Log($"{name}: Установлены жизни при пополнении баланса: {newLivesIncreasedByCredits}.");
}
public void SetSetLivesToDefaultAfterFirstLose(bool newSetLivesToDefaultAfterFirstLose)
{
gameSettings.setLivesToDefaultAfterFirstLose = newSetLivesToDefaultAfterFirstLose;
Debug.Log($"{name}: Установлен флаг 'set lives to default after first lose': {newSetLivesToDefaultAfterFirstLose}.");
}
public GameSettings GetCurrentGameSettings() => gameSettings;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a0811f9af9cb5dc4fbd4d2be780bb43f

View File

@ -0,0 +1,70 @@
using RR.Common.UI;
using RR.Game2D.Core.Managers;
using UnityEngine;
namespace RR.Common.Behaviour.ScriptableObjects
{
[CreateAssetMenu(fileName = "GameSettings", menuName = "Game/GameSettings")]
public class GameSettings : ScriptableObject
{
// General settings
public bool isFullscreen;
// ATTENTION: Music between 0f and 1f
public float musicVolume;
public float sfxVolume;
// Payment settings
public bool isFreeGame;
public bool isGivingPrizes;
// Player settings
public int startLivesCount;
public int livesIncreasedByCredits;
public bool setLivesToDefaultAfterFirstLose;
public void SetDefaultValues()
{
isFullscreen = true;
musicVolume = 1f;
sfxVolume = 1f;
isFreeGame = false;
isGivingPrizes = true;
setLivesToDefaultAfterFirstLose = true;
startLivesCount = 3;
livesIncreasedByCredits = 1;
}
public void ValidateSettings()
{
if (musicVolume < 0f || musicVolume > 1f)
musicVolume = Mathf.Clamp01(musicVolume);
if (sfxVolume < 0f || sfxVolume > 1f)
sfxVolume = Mathf.Clamp01(sfxVolume);
if (startLivesCount < 1)
startLivesCount = 3;
if (livesIncreasedByCredits < 0)
livesIncreasedByCredits = 0;
}
public void ApplySettings()
{
if (isFullscreen)
FullscreenToggle.SetFullscreen(isFullscreen);
if (GameManager.Instance != null)
{
if (!setLivesToDefaultAfterFirstLose)
GameManager.Instance.ResetLives = true;
GameManager.Instance.Audio.SetMusicVolume(musicVolume);
GameManager.Instance.Audio.SetSfxVolume(sfxVolume);
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 72dfd8e4297157e468c4edf1a3b4808b

View File

@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 72dfd8e4297157e468c4edf1a3b4808b, type: 3}
m_Name: MainSettings
m_EditorClassIdentifier: Assembly-CSharp::RR.Common.Behaviour.ScriptableObjects.GameSettings
isFullscreen: 1
musicVolume: 1
sfxVolume: 1
isFreeGame: 0
isGivingPrizes: 1
startLivesCount: 3
livesIncreasedByCredits: 1
setLivesToDefaultAfterFirstLose: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bdc702de5b61eae45a0dc18413673fd8
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e703b772daf782145a8a21c757d13738
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,136 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7148623651430003250
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4151967367557128272}
- component: {fileID: 6135362282659755657}
- component: {fileID: 2094297809795678225}
m_Layer: 5
m_Name: OptionPrefab
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4151967367557128272
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7148623651430003250}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 600.0001, y: -25.000015}
m_SizeDelta: {x: 200, y: 50}
m_Pivot: {x: 0, y: 1}
--- !u!222 &6135362282659755657
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7148623651430003250}
m_CullTransparentMesh: 1
--- !u!114 &2094297809795678225
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7148623651430003250}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: New Text
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_sharedMaterial: {fileID: 4262305343556741190, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 26
m_fontSizeBase: 26
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bc723f94fd3335c438f8cf47ea6efc4b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,136 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4694825826381118814
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1856242640539414942}
- component: {fileID: 4462460912188239392}
- component: {fileID: 8886594288047508638}
m_Layer: 5
m_Name: SettingPrefab
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1856242640539414942
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4694825826381118814}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -50.00003}
m_SizeDelta: {x: 550, y: 50}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &4462460912188239392
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4694825826381118814}
m_CullTransparentMesh: 1
--- !u!114 &8886594288047508638
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4694825826381118814}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: New Text
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_sharedMaterial: {fileID: 4262305343556741190, guid: 3f10bbcf486189d4f95b8de6dc2e2177, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 26
m_fontSizeBase: 26
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 959fadb1ff4c40540baceea041c5ab3f
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8822d9ad6616481f9c86e4c709221bd4
timeCreated: 1762348485

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
// [System.Serializable]
// public class GameSettings
// {
// public List<SettingOption> settings;
// public int hash;
// public SettingOption GetSetting(string settingName)
// {
// return settings.Find(s => s.settingName == settingName);
// }
// }

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de80d69f0b559a94c8ec18aeba347d12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
[System.Serializable]
public class SettingOption
{
public string settingName;
public List<string> options;
public int chosenIndex;
public int defaultIndex;
public SettingOption(string settingName, List<string> options, int defaultIndex)
{
this.settingName = settingName;
this.options = options;
this.defaultIndex = defaultIndex;
this.chosenIndex = defaultIndex;
}
public string ChosenOption => options[chosenIndex];
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07644b6c546b12047a530b34680101a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,158 @@
using System.Collections;
using System.Collections.Generic;
using RR.Game2D.Core.Managers;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SettingsCombination : MonoBehaviour
{
GameManager gm;
float resetStartsTimer = 0;
float resetGivenPrizesTimer = 0;
float setNumber = 0;
float telegram = 0;
float timeToReset = 5;
string PRIZE_BASE = "Счетчик призов ";
string STARTS_BASE = "Внесено монет ";
string NULLABLE = "(обнуляемый)";
string UNNULLABLE = "(глобальный)";
public TextMeshProUGUI starts, startsNul, prize, prizeNul;
public Image startsTimerImage, prizeTimerImage, numberImage, telegramImage;
void OnEnable()
{
gm = GameManager.Instance;
RefreshUI();
}
void Update()
{
//сброс монет
bool condition = In.GetButton(ButtonPinMapping.left1) &&
In.GetButton(ButtonPinMapping.right1) &&
In.GetButton(ButtonPinMapping.left2) &&
In.GetButton(ButtonPinMapping.right2);
if (condition)
{
resetStartsTimer += Time.deltaTime;
startsTimerImage.fillAmount = resetStartsTimer / timeToReset;
if (resetStartsTimer >= timeToReset)
{
ResetStarts();
resetStartsTimer = 0;
}
return;
}
else
{
resetStartsTimer -= Time.deltaTime;
resetStartsTimer = Mathf.Clamp(resetStartsTimer, 0, 5);
startsTimerImage.fillAmount = 0;
}
//телега
condition = In.GetButton(ButtonPinMapping.left1) &&
In.GetButton(ButtonPinMapping.right1) &&
/*In.GetButton(ButtonPinMapping.left2) &&*/
In.GetButton(ButtonPinMapping.right2);
if (condition)
{
telegram += Time.deltaTime;
telegramImage.fillAmount = telegram / timeToReset;
if (telegram >= timeToReset)
{
//Тут надо зайти в ввод номера
telegram = 0;
// gm.stateMachine.ChangeState(new EnterTelegramState());
//GetNumber();
}
return;
}
else
{
telegram -= Time.deltaTime;
telegram = Mathf.Clamp(setNumber, 0, 5);
telegramImage.fillAmount = 0;
}
//выданные призы
condition = In.GetButton(ButtonPinMapping.left1) &&
In.GetButton(ButtonPinMapping.left2);
if (condition)
{
resetGivenPrizesTimer += Time.deltaTime;
prizeTimerImage.fillAmount = resetGivenPrizesTimer / timeToReset;
if (resetGivenPrizesTimer >= timeToReset)
{
ResetPrizes();
resetGivenPrizesTimer = 0;
}
return;
}
else
{
resetGivenPrizesTimer -= Time.deltaTime;
resetGivenPrizesTimer = Mathf.Clamp(resetGivenPrizesTimer, 0, 5);
prizeTimerImage.fillAmount = 0;
}
//условие захода в ввод номера
condition = In.GetButton(ButtonPinMapping.right1) &&
In.GetButton(ButtonPinMapping.right2);
if (condition)
{
setNumber += Time.deltaTime;
numberImage.fillAmount = setNumber / timeToReset;
if (setNumber >= timeToReset)
{
//Тут надо зайти в ввод номера
setNumber = 0;
GetNumber();
}
return;
}
else
{
setNumber -= Time.deltaTime;
setNumber = Mathf.Clamp(setNumber, 0, 5);
numberImage.fillAmount = 0;
}
}
void ResetStarts()
{
PlayerPrefs.SetInt(PrizeSystem.key_startsNul, 0);
RefreshUI();
}
void ResetPrizes()
{
PlayerPrefs.SetInt(PrizeSystem.key_prizesgivenNul, 0);
RefreshUI();
}
void GetNumber()
{
// GameManager.instance.stateMachine.ChangeState(new GetNumberState());
}
void RefreshUI()
{
starts.text = STARTS_BASE + UNNULLABLE + ": " + PlayerPrefs.GetInt(PrizeSystem.key_starts);
startsNul.text = STARTS_BASE + NULLABLE + ": " + PlayerPrefs.GetInt(PrizeSystem.key_startsNul);
prize.text = PRIZE_BASE + UNNULLABLE + ": " + PlayerPrefs.GetInt(PrizeSystem.key_prizesgiven);
prizeNul.text = PRIZE_BASE + NULLABLE + ": " + PlayerPrefs.GetInt(PrizeSystem.key_prizesgivenNul);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 61414424e8ee73b4cbc8b75b1c0216d9

View File

@ -0,0 +1,137 @@
// using System.Text;
// using System.Collections.Generic;
// using UnityEngine;
// using UnityEngine.Events;
//
// using RR.Common.Behaviour.ScriptableObjects;
//
// public class SettingsManager : MonoBehaviour
// {
// public static SettingsManager instance;
// public GameSettings gameSettings;
// const string SETTINGS_PREFS_KEY = "SETTINGS";
// int ActualHash => settingsHash(getDefaultSettings());
//
// public UnityEvent OnSettingsChanged;
//
// private void Awake()
// {
// instance ??= this;
// OnSettingsChanged = new UnityEvent();
//
// LoadOrCreateSettings();
// }
// private void LoadOrCreateSettings()
// {
// bool shouldLoadDefault = !PlayerPrefs.HasKey(SETTINGS_PREFS_KEY) || !IsSavedSettingsValid();
//
// if (shouldLoadDefault)
// {
// LoadDefaultSettings();
// }
// else
// {
// LoadSettings();
// }
//
// bool IsSavedSettingsValid()
// {
// LoadSettings();
// return gameSettings.hash == ActualHash;
// }
// }
//
// public void ChangeSetting(string settingName, int newIndex)
// {
// var setting = gameSettings.GetSetting(settingName);
// if (setting != null && newIndex >= 0 && newIndex < setting.options.Count)
// {
// setting.chosenIndex = newIndex;
// SaveSettings();
// }
// }
// public void SaveSettings()
// {
// string json = JsonUtility.ToJson(gameSettings);
//
// PlayerPrefs.SetString(SETTINGS_PREFS_KEY, json);
// PlayerPrefs.Save();
// OnSettingsChanged?.Invoke();
// }
//
// public string GetSettingValue(string settingName)
// => gameSettings.GetSetting(settingName).ChosenOption;
//
//
// int settingsHash(List<SettingOption> settings)
// {
// StringBuilder sb = new StringBuilder();
// foreach (SettingOption settingOption in settings)
// {
// sb.Append(settingOption.settingName);
// foreach (string option in settingOption.options)
// {
// sb.Append(option);
// }
// }
//
// int hash = sb.ToString().GetHashCode();
// return hash;
// }
//
// void LoadDefaultSettings()
// {
// //print("Загрузка стандартных настроек");
//
// gameSettings.settings = getDefaultSettings();
// gameSettings.hash = settingsHash(gameSettings.settings);
//
// SaveSettings();
// }
//
// List<SettingOption> getDefaultSettings()
// {
// List<SettingOption> settings = new List<SettingOption>();
//
// settings.Add(new SettingOption(settingName: "Громкость музыки",
// options: new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
// defaultIndex: 1));
//
// settings.Add(new SettingOption(settingName: "Громкость эффектов",
// options: new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
// defaultIndex: 8));
//
// settings.Add(new SettingOption(settingName: "Выдача призов при игре с ботом",
// options: new List<string>() { "Выдавать", "Не выдавать" },
// defaultIndex: 0));
//
// settings.Add(new SettingOption(settingName: "Озвучка персонажей",
// options: new List<string>() { "Вкл", "Выкл" },
// defaultIndex: 0));
//
// settings.Add(new SettingOption(settingName: "Сложность ботов",
// options: new List<string>() { "Легкая", "Средняя", "Сложная" },
// defaultIndex: 1));
//
// settings.Add(new SettingOption(settingName: "Бесплатная игра",
// options: new List<string>() { "Вкл", "Выкл" },
// defaultIndex: 1));
//
// settings.Add(new SettingOption(settingName: "Показывать контакты администрации",
// options: new List<string>() { "Вкл", "Выкл" },
// defaultIndex: 0));
// return settings;
// }
//
//
// void LoadSettings()
// {
// string json = PlayerPrefs.GetString(SETTINGS_PREFS_KEY);
// gameSettings = JsonUtility.FromJson<GameSettings>(json);
//
// SaveSettings();
// }
//
//
//
// }

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 896b3b244e3f8db4c8a6f4591ca9c68f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -60
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,241 @@
// using System.Collections;
// using System.Collections.Generic;
// using System.Reflection;
// using TMPro;
// using UnityEngine;
//
// public class SettingsScreen : MonoBehaviour
// {
// [Header("Панель с опциями")]
// [SerializeField] RectTransform OptionsRect;
//
// [Header("Панель с вариантами опций")]
// [SerializeField] RectTransform ValuesRect;
//
// [Header("Указатель опций")]
// [SerializeField] RectTransform PointerRect;
//
// [Header("Префаб текста настройки")]
// [SerializeField] GameObject SettingPrefab;
//
// [Header("Префаб текста опции")]
// [SerializeField] GameObject OptionPrefab;
//
//
// List<GameObject> settingsLabels;
// List<GameObject> optionsLabels;
//
// substate currentState;
//
// List<SettingOption> gameSettings => SettingsManager.instance.gameSettings.settings;
// int settingsAmount => SettingsManager.instance.gameSettings.settings.Count;
// List<string> chosenSettingOptions => SettingsManager.instance.gameSettings.settings[currentSettingIndex].options;
// int chosenSettingOptionsAmount => chosenSettingOptions.Count;
//
//
// public int currentSettingIndex = 0;
// int currentOptionIndex = 0;
//
//
// float startx = 50;
// float starty = -50;
//
// //float offsetx = 100;
// float offsetx = 0;
// float offsety = 70;
//
// Coroutine chooseOption;
//
// [SerializeField] float optionsPadding = 20;
//
// public void Open()
// {
// optionsLabels = new();
// settingsLabels = new();
// PointerRect.gameObject.SetActive(true);
// currentState = substate.SettingChoosing;
// currentSettingIndex = 0;
// CreateSettingsLabels();
// }
//
// public void Close()
// {
// ClearOptionsPanel();
// ClearSettingsPanel();
// if (chooseOption != null) StopCoroutine(chooseOption);
// }
//
// public void Switch(int direction)
// {
// switch (currentState)
// {
// case substate.SettingChoosing:
// currentSettingIndex = (currentSettingIndex + settingsAmount + direction) % settingsAmount;
// PointerRect.anchoredPosition = new Vector2(startx + currentSettingIndex * offsetx, starty - currentSettingIndex * offsety);
// break;
// case substate.OptionChoosing:
// currentOptionIndex = (currentOptionIndex + chosenSettingOptionsAmount + direction) % chosenSettingOptionsAmount;//ТУТ НАДО КОЛИЧЕСТВО ОПЦИЙ НАСТРОЙКИ ПОЛУЧИТЬ
// break;
// }
// //SoundManager.instance.playSwitch();
// }
//
// //менять состояние, сохранять данные
// public void Accept()
// {
// if (currentState == substate.SettingChoosing)
// {
// createOptionsLabels(gameSettings[currentSettingIndex]);
// currentState = substate.OptionChoosing;
// PointerRect.gameObject.SetActive(false);
//
// StartCoroutine(DisplayChosenOption());
// }
//
// else if (currentState == substate.OptionChoosing)
// {
// SettingsManager.instance.ChangeSetting(settingName: gameSettings[currentSettingIndex].settingName,
// newIndex: currentOptionIndex);
//
// PointerRect.gameObject.SetActive(true);
// ClearOptionsPanel();
// //Обновляем для учета изменений
// ClearSettingsPanel();
// CreateSettingsLabels();
//
// if (chooseOption != null) StopCoroutine(chooseOption);
//
// currentState = substate.SettingChoosing;
// }
//
// //SoundManager.instance.playChoose();
//
// }
//
//
//
// //Шерстим все существующие настройки
// //Создаем для каждой настройки текстовое поле
// private void CreateSettingsLabels()
// {
// settingsLabels = new List<GameObject>();
// //currentSettingIndex = 0;
// for (int i = 0; i < SettingsManager.instance.gameSettings.settings.Count; i++)
// {
// createLabelFromSettingOption(SettingsManager.instance.gameSettings.settings[i], i);
// //currentSettingIndex++;
// }
// //currentSettingIndex = 0;
// PointerRect.anchoredPosition = new Vector2(startx + currentSettingIndex * offsetx, starty - currentSettingIndex * offsety);
//
// //Процедура которая собственно создает лейблы
// void createLabelFromSettingOption(SettingOption option, int index)
// {
// GameObject label = Instantiate(SettingPrefab, OptionsRect);
// label.name = option.settingName + " Setting";
// settingsLabels.Add(label);
// RectTransform rt = label.GetComponent<RectTransform>();
// rt.anchoredPosition = new Vector2(startx + offsetx * index, starty - offsety * index);
//
// //Звгоняем название настройки
// TextMeshProUGUI text = rt.gameObject.GetComponent<TextMeshProUGUI>();
// text.text = option.settingName;
//
// //Текущая включенная опция
// GameObject label2 = Instantiate(OptionPrefab, rt);
// label2.name = option.settingName + " Value";
// RectTransform option_rt = label2.GetComponent<RectTransform>();
//
// option_rt.anchoredPosition = new Vector2(rt.sizeDelta.x, 0);
//
// text = option_rt.gameObject.GetComponent<TextMeshProUGUI>();
// text.text = option.ChosenOption;
// }
//
//
// }
//
// // Опции настроек
// void createOptionsLabels(SettingOption option)
// {
//
// currentOptionIndex = 0;
//
// float labelWidth = (ValuesRect.sizeDelta.x - optionsPadding * 2) / option.options.Count;
// float labelHeight = ValuesRect.sizeDelta.y - optionsPadding * 2;
// print(ValuesRect.sizeDelta);
//
// foreach (string optionName in option.options)
// {
// GameObject label = Instantiate(OptionPrefab, ValuesRect);
// optionsLabels.Add(label);
//
// RectTransform rt = label.GetComponent<RectTransform>();
// rt.pivot = new Vector2(0, 1f);
// rt.sizeDelta = new Vector2(labelWidth, labelHeight);
// rt.anchoredPosition = new Vector2(optionsPadding + labelWidth * currentOptionIndex, optionsPadding);
//
//
// TextMeshProUGUI text = label.GetComponent<TextMeshProUGUI>();
// text.text = optionName;
// text.alignment = TextAlignmentOptions.Center;
// text.color = Color.gray;
// currentOptionIndex++;
// }
// currentOptionIndex = option.chosenIndex;
// }
// void ClearSettingsPanel()
// {
// foreach (GameObject label in settingsLabels)
// {
// Object.Destroy(label);
// }
// settingsLabels.Clear();
// }
// void ClearOptionsPanel()
// {
// foreach (GameObject label in optionsLabels)
// {
// Object.Destroy(label);
// }
// optionsLabels?.Clear();
// }
//
// IEnumerator DisplayChosenOption()
// {
// Color normalColor = Color.gray;
// Color highlightedColor = Color.white;
//
// int normalSize = 26;
// int chosenSize = 40;
//
// List<TextMeshProUGUI> options = new List<TextMeshProUGUI>();
// foreach (GameObject label in optionsLabels)
// {
// options.Add(label.GetComponent<TextMeshProUGUI>());
// }
//
// while (true)
// {
// for (int i = 0; i < options.Count; i++)
// {
// if (i == currentOptionIndex)
// {
// options[i].color = Vector4.MoveTowards(options[i].color, highlightedColor, Time.deltaTime * 5);
// options[i].fontSize = Mathf.MoveTowards(options[i].fontSize, chosenSize, Time.deltaTime * 140);
// }
// else
// {
// options[i].color = Vector4.MoveTowards(options[i].color, normalColor, Time.deltaTime * 5);
// options[i].fontSize = Mathf.MoveTowards(options[i].fontSize, normalSize, Time.deltaTime * 140);
// }
// }
// yield return null;
// }
//
//
//
// }
//
// enum substate { SettingChoosing, OptionChoosing }
// }

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0343bffc1f51dc4ea7c7d3dc9fc25bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -77,7 +77,7 @@ namespace RR.Common.UI
if (ctx.performed) Toggle();
}
private static void Toggle()
public static void Toggle()
{
if (Application.platform == RuntimePlatform.WebGLPlayer) return;
@ -97,5 +97,21 @@ namespace RR.Common.UI
Screen.SetResolution(w, h, false);
}
}
public static void SetFullscreen(bool fullscreen)
{
if (Application.platform == RuntimePlatform.WebGLPlayer) return;
if (fullscreen)
{
Screen.SetResolution(Display.main.systemWidth, Display.main.systemHeight, true);
Screen.fullScreen = true;
}
else
{
int w = Mathf.RoundToInt(Display.main.systemWidth * 0.5f);
int h = Mathf.RoundToInt(Display.main.systemHeight * 0.5f);
Screen.fullScreen = false;
}
}
}
}

View File

@ -6,6 +6,7 @@ using UnityEngine.UI;
using RR.Game2D.Core.Managers;
using TMPro;
using UnityEngine.EventSystems;
using Random = System.Random;
namespace RR.Common.UI
{
@ -25,20 +26,18 @@ namespace RR.Common.UI
private void OnEnable()
{
if (GameManager.Instance != null)
{
GameManager.Instance.GameWonEvent += OnGameWon;
GameManager.Instance.GameOverEvent += OnGameOver;
}
if (GameManager.Instance == null) return;
GameManager.Instance.GameWonEvent += OnGameWon;
GameManager.Instance.GameOverEvent += OnGameOver;
}
private void OnDisable()
{
if (GameManager.Instance != null)
{
GameManager.Instance.GameWonEvent -= OnGameWon;
GameManager.Instance.GameOverEvent -= OnGameOver;
}
if (GameManager.Instance == null) return;
GameManager.Instance.GameWonEvent -= OnGameWon;
GameManager.Instance.GameOverEvent -= OnGameOver;
}
private void Awake()

View File

@ -17,7 +17,6 @@ public class LoseBannerController : MonoBehaviour
[SerializeField] private Button continueButton;
[SerializeField] private TextMeshProUGUI continueButtonText;
[SerializeField] private float continueButtonCountdown = 10f;
[SerializeField] private Button mainMenuButton;
private Coroutine continueCountdownCoroutine;
@ -52,9 +51,9 @@ public class LoseBannerController : MonoBehaviour
{
// EventSystem.current.firstSelectedGameObject = continueButton.gameObject;
EventSystem.current.SetSelectedGameObject(continueButton.gameObject);
loseText.text = $"Время игры: {GameEndUIController.FormatTime(gameTime)}";
if (continueButtonCountdown <= 0f)
{
ContinueClicked?.Invoke();
@ -67,9 +66,6 @@ public class LoseBannerController : MonoBehaviour
continueButton.gameObject.SetActive(true);
continueButton.interactable = true;
}
//
// if (mainMenuButton)
// mainMenuButton.gameObject.SetActive(false);
// Остановим предыдущую корутину, если есть
if (continueCountdownCoroutine != null)
@ -80,7 +76,7 @@ public class LoseBannerController : MonoBehaviour
continueCountdownCoroutine = StartCoroutine(ContinueCountdownCoroutine(continueButtonCountdown));
}
private IEnumerator ContinueCountdownCoroutine(float duration)
{

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;
using UnityEngine.EventSystems;
using RR.Common.Managers;
using RR.Game2D.Core.Managers;
using RR.Common.Behaviour.ScriptableObjects;
namespace RR.Common.UI
{
public class SettingsUI : MonoBehaviour
{
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 85d4ced96061fd14592d8a7f7fb824de

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e2d8fe2933724164a5d76abd4892856c
timeCreated: 1762512881

View File

@ -0,0 +1,175 @@
using UnityEngine;
using System;
using RR.Common.Utility;
/// <summary>
/// Продвинутый пример использования системы логирования.
/// Демонстрирует различные сценарии использования FileLogger и CompositeLogger.
/// </summary>
public class AdvancedLoggingExample : MonoBehaviour
{
private CompositeLogger compositeLogger;
private FileLogger mainFileLogger;
private FileLogger errorFileLogger;
void Start()
{
SetupAdvancedLogging();
DemonstrateLogging();
}
/// <summary>
/// Настройка продвинутой системы логирования с несколькими выходными файлами
/// </summary>
private void SetupAdvancedLogging()
{
// Создаём композитный логгер
compositeLogger = new CompositeLogger();
compositeLogger.Initialize();
// Главный файл - все логи
mainFileLogger = new FileLogger(
System.IO.Path.Combine(Application.persistentDataPath, "full_log.txt"),
append: true
);
mainFileLogger.filterLogType = LogType.Log; // Все типы сообщений
// Отдельный файл только для ошибок и исключений
errorFileLogger = new FileLogger(
System.IO.Path.Combine(Application.persistentDataPath, "errors_only.txt"),
append: true
);
errorFileLogger.filterLogType = LogType.Warning; // Только Warning, Error, Exception
// Добавляем оба логгера
compositeLogger.AddLoggers(mainFileLogger, errorFileLogger);
Debug.Log($"Логирование настроено. Файлы в: {Application.persistentDataPath}");
}
/// <summary>
/// Демонстрация различных типов логирования
/// </summary>
private void DemonstrateLogging()
{
// Простые сообщения
compositeLogger.Log("=== Демонстрация системы логирования ===");
// Сообщения с тегами
compositeLogger.Log("SYSTEM", "Игра инициализирована");
compositeLogger.LogWarning("AUDIO", "Громкость музыки установлена в 0.5");
// Форматированные сообщения
string playerName = "Player1";
int score = 1000;
compositeLogger.LogFormat(LogType.Log, "Игрок {0} набрал {1} очков", playerName, score);
// Логирование с контекстом
compositeLogger.Log("Объект создан", this.gameObject);
// Имитация ошибки
compositeLogger.LogError("ERROR", "Это тестовая ошибка - будет в обоих файлах!");
// Имитация исключения
try
{
throw new InvalidOperationException("Тестовое исключение для демонстрации");
}
catch (Exception ex)
{
compositeLogger.LogException(ex, this.gameObject);
}
// Информация о путях
Debug.Log($"Полный лог: {Application.persistentDataPath}/full_log.txt");
Debug.Log($"Только ошибки: {Application.persistentDataPath}/errors_only.txt");
}
/// <summary>
/// Пример метода для логирования игровых событий
/// </summary>
public void LogGameEvent(string eventName, params object[] parameters)
{
string paramStr = string.Join(", ", parameters);
compositeLogger.Log("GAME_EVENT", $"{eventName}: {paramStr}");
}
/// <summary>
/// Пример метода для логирования производительности
/// </summary>
public void LogPerformance(string operation, float duration)
{
if (duration > 0.016f) // Больше 16мс (60 FPS)
{
compositeLogger.LogWarning("PERFORMANCE", $"{operation} занял {duration * 1000:F2}мс");
}
else
{
compositeLogger.Log("PERFORMANCE", $"{operation} выполнен за {duration * 1000:F2}мс");
}
}
/// <summary>
/// Пример обёртки для измерения времени выполнения
/// </summary>
public void PerformOperationWithLogging(string operationName, Action operation)
{
float startTime = Time.realtimeSinceStartup;
try
{
operation.Invoke();
float duration = Time.realtimeSinceStartup - startTime;
LogPerformance(operationName, duration);
}
catch (Exception ex)
{
compositeLogger.LogException(ex);
compositeLogger.LogError("OPERATION", $"Операция '{operationName}' завершилась с ошибкой");
}
}
void Update()
{
// Пример: логируем нажатия клавиш
if (Input.GetKeyDown(KeyCode.L))
{
LogGameEvent("KeyPress", "L", Time.time);
}
if (Input.GetKeyDown(KeyCode.F))
{
// Принудительная запись на диск
mainFileLogger.Flush();
errorFileLogger.Flush();
compositeLogger.Log("SYSTEM", "Логи принудительно сохранены на диск");
}
if (Input.GetKeyDown(KeyCode.C))
{
// Очистка логов
mainFileLogger.Clear();
errorFileLogger.Clear();
compositeLogger.Log("SYSTEM", "Файлы логов очищены");
}
}
void OnApplicationQuit()
{
compositeLogger.Log("SYSTEM", "Приложение завершается...");
// Важно: освобождаем ресурсы
mainFileLogger?.Dispose();
errorFileLogger?.Dispose();
Debug.Log("Файловые логгеры закрыты");
}
void OnDestroy()
{
// На всякий случай
mainFileLogger?.Dispose();
errorFileLogger?.Dispose();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 19e26e3438fd4f56aa52414b1ea03657
timeCreated: 1762503889

View File

@ -0,0 +1,354 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
public class CompositeLogger : ILogger, IDisposable
{
public ILogHandler logHandler { get; set; }
public bool logEnabled { get; set; }
public LogType filterLogType { get; set; }
private ILogger _oldLogger;
private readonly List<ILogger> _additiveLoggers = new();
public void Initialize(ILogger additiveLogger = null)
{
_oldLogger = Debug.unityLogger;
logHandler = _oldLogger.logHandler;
logEnabled = true;
filterLogType = _oldLogger.filterLogType;
if (additiveLogger != null) _additiveLoggers.Add(additiveLogger);
}
public void AddLogger(ILogger additiveLogger)
{
if (additiveLogger == null)
throw new ArgumentNullException(nameof(additiveLogger));
_additiveLoggers.Add(additiveLogger);
}
public void AddLoggers(params ILogger[] additiveLoggers)
{
if (additiveLoggers == null)
throw new ArgumentNullException(nameof(additiveLoggers));
foreach (var logger in additiveLoggers)
{
if (logger != null)
_additiveLoggers.Add(logger);
}
}
public void RemoveLogger(ILogger additiveLogger)
{
_additiveLoggers.Remove(additiveLogger);
}
public void RemoveLoggers(params ILogger[] additiveLoggers)
{
_additiveLoggers.RemoveAll(additiveLoggers.Contains);
}
public bool IsLogTypeAllowed(LogType logType)
{
return _oldLogger.IsLogTypeAllowed(logType) || _additiveLoggers.Any(logger => logger.IsLogTypeAllowed(logType));
}
#region Ilogger Implementation
public void LogFormat(LogType logType, string format, params object[] args)
{
_oldLogger.LogFormat(logType, format, args);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogFormat(logType, format, args);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
_oldLogger.LogFormat(logType, context, format, args);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogFormat(logType, context, format, args);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogException(Exception exception)
{
_oldLogger.LogException(exception);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogException(exception);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogException(Exception exception, Object context)
{
_oldLogger.LogException(exception, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogException(exception, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(object message)
{
_oldLogger.Log(message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message)
{
_oldLogger.Log(logType, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message, GameObject gameObject)
{
_oldLogger.Log(logType, message, gameObject);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message, gameObject);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message, Object context)
{
_oldLogger.Log(logType, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, string tag, object message, Object context)
{
_oldLogger.Log(logType, tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, string tag, object message)
{
_oldLogger.Log(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(string tag, object message)
{
_oldLogger.Log(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(string tag, object message, Object context)
{
_oldLogger.Log(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogWarning(string tag, object message)
{
_oldLogger.LogWarning(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogWarning(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogWarning(string tag, object message, Object context)
{
_oldLogger.LogWarning(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogWarning(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogError(string tag, object message)
{
_oldLogger.LogError(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogError(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogError(string tag, object message, Object context)
{
_oldLogger.LogError(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogError(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
lock (_additiveLoggers)
{
foreach (ILogger additiveLogger in _additiveLoggers.ToArray())
{
try
{
if (additiveLogger is IDisposable disposableLogger)
disposableLogger.Dispose();
}
catch (Exception ex)
{
try { _oldLogger?.LogException(ex); } catch { /* ignore */ }
}
}
_additiveLoggers.Clear();
}
}
~CompositeLogger()
{
Dispose();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7eac28e82d25e9f48925ed67c11f58ac

View File

@ -0,0 +1,330 @@
using System;
using System.IO;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object;
namespace RR.Common.Utility
{
/// <summary>
/// Логгер для записи сообщений в файл. Совместим с CompositeLogger.
/// </summary>
public class FileLogger : ILogger, IDisposable
{
public ILogHandler logHandler { get; set; }
public LogType filterLogType { get; set; }
public bool logEnabled { get; set; } = true;
private readonly string _filePath;
private readonly object _lock = new object();
private StreamWriter _writer;
private bool _disposed;
/// <summary>
/// Создает файловый логгер с указанным путём к файлу.
/// </summary>
/// <param name="filePath">Полный путь к файлу лога. Если null, используется Application.persistentDataPath/game.log</param>
/// <param name="append">Если true, добавляет к существующему файлу, иначе перезаписывает</param>
public FileLogger(string filePath = null, bool append = true)
{
_filePath = filePath ?? Path.Combine(Application.persistentDataPath, "game.log");
filterLogType = LogType.Log;
try
{
// Создаем директорию если её нет
string directory = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// Открываем файл для записи
_writer = new StreamWriter(_filePath, append, Encoding.UTF8)
{
AutoFlush = true // Автоматически сбрасывать буфер для надёжности
};
// Записываем заголовок
WriteHeader();
}
catch (Exception ex)
{
Debug.LogError($"[FileLogger] Не удалось открыть файл лога: {_filePath}\n{ex}");
}
}
private void WriteHeader()
{
if (_writer != null)
{
_writer.WriteLine("================================================================================");
_writer.WriteLine($"Новая сессия логирования начата: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
_writer.WriteLine($"Unity версия: {Application.unityVersion}");
_writer.WriteLine($"Платформа: {Application.platform}");
_writer.WriteLine($"Путь к логу: {_filePath}");
_writer.WriteLine("================================================================================");
_writer.WriteLine();
}
}
public bool IsLogTypeAllowed(LogType logType)
{
return logEnabled && logType <= filterLogType;
}
private void WriteToFile(LogType logType, string message)
{
if (!logEnabled || _disposed || _writer == null)
return;
if (!IsLogTypeAllowed(logType))
return;
lock (_lock)
{
try
{
string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
string logTypeStr = GetLogTypeString(logType);
_writer.WriteLine($"[{timestamp}] [{logTypeStr}] {message}");
}
catch (Exception ex)
{
Debug.LogError($"[FileLogger] Ошибка записи в файл: {ex.Message}");
}
}
}
private string GetLogTypeString(LogType logType)
{
return logType switch
{
LogType.Error => "ERROR",
LogType.Assert => "ASSERT",
LogType.Warning => "WARNING",
LogType.Log => "INFO",
LogType.Exception => "EXCEPTION",
_ => "UNKNOWN"
};
}
private string FormatMessage(object message, Object context = null)
{
string msg = message?.ToString() ?? "null";
if (context != null)
{
msg += $" (Context: {context.name})";
}
return msg;
}
private string FormatMessage(string tag, object message, Object context = null)
{
string msg = $"[{tag}] {message?.ToString() ?? "null"}";
if (context != null)
{
msg += $" (Context: {context.name})";
}
return msg;
}
#region ILogger Implementation
public void Log(LogType logType, object message)
{
WriteToFile(logType, FormatMessage(message));
}
public void Log(LogType logType, object message, Object context)
{
WriteToFile(logType, FormatMessage(message, context));
}
public void Log(LogType logType, string tag, object message)
{
WriteToFile(logType, FormatMessage(tag, message));
}
public void Log(LogType logType, string tag, object message, Object context)
{
WriteToFile(logType, FormatMessage(tag, message, context));
}
public void Log(object message)
{
WriteToFile(LogType.Log, FormatMessage(message));
}
public void Log(string tag, object message)
{
WriteToFile(LogType.Log, FormatMessage(tag, message));
}
public void Log(string tag, object message, Object context)
{
WriteToFile(LogType.Log, FormatMessage(tag, message, context));
}
public void LogWarning(string tag, object message)
{
WriteToFile(LogType.Warning, FormatMessage(tag, message));
}
public void LogWarning(string tag, object message, Object context)
{
WriteToFile(LogType.Warning, FormatMessage(tag, message, context));
}
public void LogError(string tag, object message)
{
WriteToFile(LogType.Error, FormatMessage(tag, message));
}
public void LogError(string tag, object message, Object context)
{
WriteToFile(LogType.Error, FormatMessage(tag, message, context));
}
public void LogFormat(LogType logType, string format, params object[] args)
{
try
{
string message = string.Format(format, args);
WriteToFile(logType, message);
}
catch (FormatException ex)
{
WriteToFile(LogType.Error, $"Ошибка форматирования: {ex.Message}");
}
}
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
try
{
string message = string.Format(format, args);
WriteToFile(logType, FormatMessage(message, context));
}
catch (FormatException ex)
{
WriteToFile(LogType.Error, $"Ошибка форматирования: {ex.Message}");
}
}
public void LogException(Exception exception)
{
if (exception == null)
return;
WriteToFile(LogType.Exception, $"{exception.GetType().Name}: {exception.Message}");
WriteToFile(LogType.Exception, $"StackTrace:\n{exception.StackTrace}");
if (exception.InnerException != null)
{
WriteToFile(LogType.Exception,
$"Inner Exception: {exception.InnerException.GetType().Name}: {exception.InnerException.Message}");
}
}
public void LogException(Exception exception, Object context)
{
if (exception == null)
return;
string contextInfo = context != null ? $" (Context: {context.name})" : "";
WriteToFile(LogType.Exception, $"{exception.GetType().Name}: {exception.Message}{contextInfo}");
WriteToFile(LogType.Exception, $"StackTrace:\n{exception.StackTrace}");
if (exception.InnerException != null)
{
WriteToFile(LogType.Exception,
$"Inner Exception: {exception.InnerException.GetType().Name}: {exception.InnerException.Message}");
}
}
#endregion
/// <summary>
/// Принудительно сбрасывает буфер на диск
/// </summary>
public void Flush()
{
lock (_lock)
{
try
{
_writer?.Flush();
}
catch (Exception ex)
{
Debug.LogError($"[FileLogger] Ошибка при сбросе буфера: {ex.Message}");
}
}
}
/// <summary>
/// Очищает файл лога
/// </summary>
public void Clear()
{
lock (_lock)
{
try
{
_writer?.Close();
File.WriteAllText(_filePath, string.Empty);
_writer = new StreamWriter(_filePath, true, Encoding.UTF8) { AutoFlush = true };
WriteHeader();
}
catch (Exception ex)
{
Debug.LogError($"[FileLogger] Ошибка при очистке файла: {ex.Message}");
}
}
}
public void Dispose()
{
if (_disposed)
return;
lock (_lock)
{
try
{
if (_writer != null)
{
_writer.WriteLine();
_writer.WriteLine(
"================================================================================");
_writer.WriteLine($"Сессия логирования завершена: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
_writer.WriteLine(
"================================================================================");
_writer.Flush();
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
catch (Exception ex)
{
Debug.LogError($"[FileLogger] Ошибка при закрытии файла: {ex.Message}");
}
finally
{
_disposed = true;
}
}
}
~FileLogger()
{
Dispose();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 28b8d36a996595144b75e4febc509640

View File

@ -0,0 +1,93 @@
using UnityEngine;
namespace RR.Common.Utility
{
/// <summary>
/// Пример инициализации системы логирования с CompositeLogger и FileLogger.
/// Прикрепите этот скрипт к любому GameObject на сцене.
/// </summary>
public class LoggerInitializer : MonoBehaviour
{
[SerializeField] private bool enableFileLogging = true;
[SerializeField] private string customLogPath = ""; // Оставьте пустым для использования пути по умолчанию
[SerializeField] private bool appendToExistingLog = true;
[SerializeField] private bool clearLogOnStart = false;
private CompositeLogger compositeLogger;
private FileLogger fileLogger;
private void Awake()
{
InitializeLogging();
}
private void InitializeLogging()
{
// Создаем композитный логгер
compositeLogger = new CompositeLogger();
compositeLogger.Initialize();
if (enableFileLogging)
{
// Создаем файловый логгер
string logPath = string.IsNullOrEmpty(customLogPath) ? null : customLogPath;
fileLogger = new FileLogger(logPath, appendToExistingLog);
if (clearLogOnStart)
{
fileLogger.Clear();
}
// Добавляем файловый логгер в композитный
compositeLogger.AddLogger(fileLogger);
Debug.Log(
$"[LoggerInitializer] Файловое логирование включено. Путь: {(logPath ?? Application.persistentDataPath + "/game.log")}");
}
// Опционально: можно заменить стандартный логгер Unity
// Debug.unityLogger = compositeLogger;
// Тестовые сообщения
TestLogging();
}
private void TestLogging()
{
compositeLogger.Log("Система логирования инициализирована");
compositeLogger.LogWarning("Test", "Это тестовое предупреждение");
compositeLogger.LogError("Test", "Это тестовая ошибка");
Debug.Log($"Логи сохраняются в: {Application.persistentDataPath}");
}
private void OnApplicationQuit()
{
// Закрываем файловый логгер при выходе
fileLogger?.Dispose();
Debug.Log("[LoggerInitializer] Файловый логгер закрыт");
}
private void OnDestroy()
{
// На всякий случай закрываем при уничтожении объекта
fileLogger?.Dispose();
}
// Публичные методы для использования из других скриптов
public static void LogToFile(string message)
{
Debug.Log(message);
}
public static void LogWarningToFile(string message)
{
Debug.LogWarning(message);
}
public static void LogErrorToFile(string message)
{
Debug.LogError(message);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8cad0029d1274d6393bd4b7821dd6c6e
timeCreated: 1762503813

View File

@ -0,0 +1,284 @@
# Система логирования для Unity
## Обзор
Система состоит из трёх компонентов:
1. **CompositeLogger** - композитный логгер, пересылающий сообщения на несколько логгеров
2. **FileLogger** - логгер для записи в файл
3. **LoggerInitializer** - компонент для автоматической инициализации
## Быстрый старт
### Вариант 1: Использование через LoggerInitializer (рекомендуется)
1. Создайте пустой GameObject на сцене
2. Прикрепите компонент `LoggerInitializer`
3. Настройте параметры в инспекторе:
- **Enable File Logging** - включить запись в файл
- **Custom Log Path** - путь к файлу (оставьте пустым для автоматического)
- **Append To Existing Log** - добавлять к существующему файлу
- **Clear Log On Start** - очищать файл при запуске
4. Запустите игру - все логи будут автоматически писаться в файл!
### Вариант 2: Ручная настройка в коде
```csharp
using UnityEngine;
public class MyGameManager : MonoBehaviour
{
private CompositeLogger compositeLogger;
private FileLogger fileLogger;
void Awake()
{
// Инициализация композитного логгера
compositeLogger = new CompositeLogger();
compositeLogger.Initialize();
// Создание файлового логгера
fileLogger = new FileLogger(); // Использует путь по умолчанию
// или
// fileLogger = new FileLogger("D:/MyGame/logs/game.log", append: true);
// Добавление файлового логгера в композитный
compositeLogger.AddLogger(fileLogger);
// Теперь все логи через compositeLogger будут идти и в консоль, и в файл
compositeLogger.Log("Игра запущена!");
compositeLogger.LogWarning("Test", "Тестовое предупреждение");
}
void OnApplicationQuit()
{
fileLogger?.Dispose();
}
}
```
## Где находится файл лога?
По умолчанию файл сохраняется в `Application.persistentDataPath/game.log`:
- **Windows**: `C:/Users/<Username>/AppData/LocalLow/<CompanyName>/<ProductName>/game.log`
- **macOS**: `~/Library/Application Support/<CompanyName>/<ProductName>/game.log`
- **Linux**: `~/.config/unity3d/<CompanyName>/<ProductName>/game.log`
- **Android**: `/storage/emulated/0/Android/data/<PackageName>/files/game.log`
Узнать точный путь можно через:
```csharp
Debug.Log(Application.persistentDataPath);
```
## Примеры использования
### Базовое логирование
```csharp
// Через композитный логгер
compositeLogger.Log("Простое сообщение");
compositeLogger.LogWarning("Tag", "Предупреждение");
compositeLogger.LogError("Tag", "Ошибка");
// Через стандартный Debug (если вы заменили Debug.unityLogger)
Debug.Log("Это тоже пойдёт в файл");
```
### Логирование с контекстом
```csharp
compositeLogger.Log(LogType.Log, "Игрок спавнится", playerGameObject);
compositeLogger.LogWarning("AI", "Путь не найден", enemyGameObject);
```
### Логирование исключений
```csharp
try
{
// Опасный код
}
catch (Exception ex)
{
compositeLogger.LogException(ex);
}
```
### Форматированное логирование
```csharp
compositeLogger.LogFormat(LogType.Log, "Игрок {0} имеет {1} очков здоровья", playerName, health);
```
### Добавление нескольких логгеров
```csharp
var fileLogger1 = new FileLogger("logs/debug.log");
var fileLogger2 = new FileLogger("logs/errors.log");
fileLogger2.filterLogType = LogType.Error; // Только ошибки
compositeLogger.AddLoggers(fileLogger1, fileLogger2);
```
## Настройка фильтрации
### На уровне FileLogger
```csharp
fileLogger.filterLogType = LogType.Warning; // Логировать только Warning, Error, Exception
fileLogger.logEnabled = false; // Временно отключить
```
### Очистка файла лога
```csharp
fileLogger.Clear(); // Очистить файл и записать новый заголовок
```
### Принудительная запись на диск
```csharp
fileLogger.Flush(); // Сбросить буфер на диск
```
## Формат записи в файле
```
================================================================================
Новая сессия логирования начата: 2025-11-07 14:30:15
Unity версия: 2022.3.5f1
Платформа: WindowsEditor
Путь к логу: C:/Users/User/AppData/LocalLow/DefaultCompany/MyGame/game.log
================================================================================
[14:30:15.123] [INFO] Игра запущена!
[14:30:16.456] [WARNING] [AI] Путь не найден
[14:30:17.789] [ERROR] [Network] Соединение потеряно
[14:30:18.012] [EXCEPTION] NullReferenceException: Object reference not set to an instance of an object
StackTrace:
at MyScript.MyMethod () [0x00000] in <filename>:0
```
## Важные замечания
### Потокобезопасность
FileLogger потокобезопасен - использует `lock` для синхронизации записи.
### Производительность
- `AutoFlush = true` гарантирует сохранность данных при крашах, но снижает производительность
- Если нужна максимальная производительность, установите `AutoFlush = false` и вызывайте `Flush()` вручную
### Освобождение ресурсов
**ВАЖНО:** Всегда вызывайте `Dispose()` при завершении работы:
```csharp
void OnApplicationQuit()
{
fileLogger?.Dispose();
}
```
### Ротация логов
Для больших проектов можно реализовать ротацию:
```csharp
string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string logPath = $"logs/game_{timestamp}.log";
var fileLogger = new FileLogger(logPath, append: false);
```
## Расширенные сценарии
### Отдельные логи для разных систем
```csharp
// Общий лог
var mainLogger = new FileLogger("logs/game.log");
// Лог только для сетевой подсистемы
var networkLogger = new FileLogger("logs/network.log");
networkLogger.filterLogType = LogType.Log;
// Лог только для ошибок
var errorLogger = new FileLogger("logs/errors.log");
errorLogger.filterLogType = LogType.Error;
compositeLogger.AddLoggers(mainLogger, networkLogger, errorLogger);
```
### Условное логирование
```csharp
#if UNITY_EDITOR
// В редакторе не пишем в файл, только в консоль
#else
compositeLogger.AddLogger(new FileLogger());
#endif
```
### Логирование с метками времени
```csharp
public class TimestampLogger
{
private CompositeLogger logger;
public void LogWithTimestamp(string message)
{
logger.Log($"[{Time.time:F2}s] {message}");
}
}
```
## Troubleshooting
### Файл не создаётся
- Проверьте права доступа к папке
- Проверьте путь через `Debug.Log(Application.persistentDataPath)`
- Убедитесь, что вызвали `Initialize()` на CompositeLogger
### Логи не пишутся в файл
- Убедитесь, что `logEnabled = true`
- Проверьте `filterLogType` - возможно фильтр слишком строгий
- Проверьте, что логгер добавлен через `AddLogger()`
### Файл заблокирован
- Убедитесь, что вызвали `Dispose()` перед закрытием приложения
- Закройте все программы, которые могут держать файл открытым
### Производительность низкая
- Установите `AutoFlush = false` в конструкторе FileLogger
- Вызывайте `Flush()` периодически (например, раз в секунду)
- Используйте более строгий `filterLogType`
## API Reference
### FileLogger
#### Конструктор
```csharp
FileLogger(string filePath = null, bool append = true)
```
#### Свойства
- `bool logEnabled` - включено ли логирование
- `LogType filterLogType` - минимальный уровень логирования
- `ILogHandler logHandler` - обработчик логов (для совместимости)
#### Методы
- `void Flush()` - принудительно сбросить буфер на диск
- `void Clear()` - очистить файл лога
- `void Dispose()` - закрыть файл и освободить ресурсы
- `bool IsLogTypeAllowed(LogType)` - проверка фильтра
### CompositeLogger
#### Методы управления
- `void Initialize(ILogger additiveLogger = null)` - инициализация
- `void AddLogger(ILogger)` - добавить логгер
- `void AddLoggers(params ILogger[])` - добавить несколько логгеров
- `void RemoveLogger(ILogger)` - удалить логгер
- `void RemoveLoggers(params ILogger[])` - удалить несколько логгеров
## Лицензия
Свободное использование в ваших проектах.

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cb2132721bf244f8820de54ff90cb599
timeCreated: 1762503862

8
Assets/Sprites.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4514587d1b9c1ad419ab31158a3910b5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a3d18453b59539458b79a410b71caa8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57d3ae40e9eeeab40a18e9f2592e9ef1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 8d6432ff047c62946a41e3235f28ba57
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 625
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 1024
resizeAlgorithm: 0
textureFormat: 29
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: caa69a9f2e4f2204fa32aec6915c6cba
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: e2e62195d0338f2489f389664ec84938
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: aa46adafa643d084abe86ac0f3697446
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Some files were not shown because too many files have changed in this diff Show More