feat: обновлены параметры спрайта и позиционирования для улучшения UI

This commit is contained in:
Cviat 2026-02-05 13:48:25 +03:00
parent 7e26d6f618
commit aa358f9794
25 changed files with 1 additions and 1456 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -1,127 +0,0 @@
fileFormatVersion: 2
guid: dabfc47e871e05d49b87ab1d70550b0f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
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: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
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: 0
spriteTessellationDetail: -1
textureType: 0
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: 0
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: Server
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:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Scenes/test.unity (Stored with Git LFS)

Binary file not shown.

View File

@ -1,94 +0,0 @@
using System.Collections;
using UnityEngine;
using TMPro;
public class Coin : MonoBehaviour
{
public CoinFlipUI flipUI;
public TextMeshProUGUI coinText;
[Header("Flying Coins")]
public CoinFlipUI smallCoinPrefab; // ïðåôàá ìîíåòêè (CoinFlipUI)
public int spawnCount = 10; // ñêîëüêî ñïàâíèòü
public float spawnRadius = 30f; // ðàçáðîñ ñòàðòà
public float fallDistance = 100f; // ðàññòîÿíèå âíèç
public float duration = 0.8f; // äëèòåëüíîñòü àíèìàöèè
public void FreezeCoin()
{
if (flipUI != null)
flipUI.FreezeCoin();
}
public void UnfreezeCoin()
{
if (flipUI != null)
flipUI.UnfreezeCoin();
}
// Îáíîâëÿåì òåêñò
public void ChangeCoinText(string text)
{
if (coinText != null)
coinText.text = text;
}
// -------------------- Àíèìàöèÿ ïîïîëíåíèÿ --------------------
public void PlayAddCoinsAnimation()
{
if (smallCoinPrefab == null || flipUI == null) return;
RectTransform parentRt = flipUI.GetComponent<RectTransform>();
for (int i = 0; i < spawnCount; i++)
{
CoinFlipUI coin = Instantiate(smallCoinPrefab, parentRt.parent); // ñïàâíèì â òîì æå êîíòåéíåðå, ÷òî è flipUI
RectTransform rt = coin.GetComponent<RectTransform>();
// ñòàðòîâàÿ ïîçèöèÿ = ïîçèöèÿ áîëüøîé ìîíåòû
rt.position = parentRt.position;
// óìåíüøàåì ìîíåòêó â 3 ðàçà
rt.localScale = smallCoinPrefab.transform.localScale * 0.33f;
// äîáàâëÿåì ñëó÷àéíîå ñìåùåíèå âîêðóã áîëüøîé ìîíåòû
Vector2 randomOffset = Random.insideUnitCircle * spawnRadius;
rt.anchoredPosition += randomOffset;
// çàïóñêàåì àíèìàöèþ
StartCoroutine(FlyAndShrinkRoutine(rt));
}
}
private IEnumerator FlyAndShrinkRoutine(RectTransform rt)
{
Vector2 startPos = rt.anchoredPosition;
Vector2 endPos = startPos - new Vector2(0, fallDistance);
Vector3 startScale = rt.localScale;
Vector3 endScale = Vector3.zero;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
rt.anchoredPosition = Vector2.Lerp(startPos, endPos, t);
rt.localScale = Vector3.Lerp(startScale, endScale, t);
// äåëàåì ïðîçðà÷íîñòü
var img = rt.GetComponent<UnityEngine.UI.Image>();
if (img != null)
{
var c = img.color;
c.a = Mathf.Lerp(1f, 0f, t);
img.color = c;
}
yield return null;
}
Destroy(rt.gameObject);
}
}

View File

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

View File

@ -1,69 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
public class CoinFlipUI : MonoBehaviour
{
[Header("UI")]
public Image coinImage; // ńŕě UI-îáúĺęň ń ęŕđňčíęîé
public Sprite headsSprite; // îđ¸ë
public Sprite tailsSprite; // đĺřęŕ
[Header("Spin Settings")]
public float spinSpeed = 360f; // ăđŕäóńîâ â ńĺęóíäó
public bool autoSpin = true; // ęđóňčňń˙ ďîńňî˙ííî
private bool showingHeads = true;
private Color originalColor;
private bool frozen = false; // ôëŕă Ťçŕěîđîçęčť
private void Awake()
{
if (coinImage != null)
originalColor = coinImage.color; // ńîőđŕí˙ĺě čńőîäíűé öâĺň
}
/// <summary>Äĺëŕĺě ěîíĺňó ńĺđîé č îńňŕíŕâëčâŕĺě âđŕůĺíčĺ</summary>
public void FreezeCoin()
{
if (coinImage != null)
{
coinImage.color = Color.grey;
coinImage.sprite = headsSprite; // âńĺăäŕ ďîęŕçűâŕĺě ďĺđâűé ńďđŕéň
// ńáđŕńűâŕĺě âđŕůĺíčĺ, ÷ňîáű ěîíĺňŕ ńěîňđĺëŕ "âďĺđ¸ä"
coinImage.rectTransform.localEulerAngles = Vector3.zero;
}
frozen = true;
}
/// <summary>Âîçâđŕůŕĺě ěîíĺňó â íîđěŕëüíîĺ ńîńňî˙íčĺ č âęëţ÷ŕĺě âđŕůĺíčĺ</summary>
public void UnfreezeCoin()
{
if (coinImage != null)
coinImage.color = originalColor;
frozen = false;
}
void Update()
{
if (frozen || !autoSpin) return;
// ęđóňčě ěîíĺňęó
coinImage.rectTransform.Rotate(Vector3.up * spinSpeed * Time.deltaTime);
float yAngle = coinImage.rectTransform.localEulerAngles.y;
// ěĺí˙ĺě ńďđŕéň íŕ îáîđîňíîé ńňîđîíĺ
if (yAngle > 90f && yAngle < 270f && showingHeads)
{
coinImage.sprite = tailsSprite;
showingHeads = false;
}
else if ((yAngle <= 90f || yAngle >= 270f) && !showingHeads)
{
coinImage.sprite = headsSprite;
showingHeads = true;
}
}
}

View File

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

View File

@ -1,73 +0,0 @@
using UnityEngine;
/// <summary>
/// Îòâå÷àåò òîëüêî çà îòîáðàæåíèå è îáíîâëåíèå UI ìîíåò
/// </summary>
public class CoinUI : MonoBehaviour
{
[SerializeField] private int playerId;
[SerializeField] private Coin coin;
private int lastCoins;
private bool eventsSubscribed;
private void OnEnable()
{
ComWrapper.OnInitialize += Subscribe;
if (!eventsSubscribed && (ComWrapper.onCoinAdded != null || ComWrapper.onCoinRemoved != null))
Subscribe();
}
private void OnDisable()
{
if (eventsSubscribed)
{
if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.RemoveListener(HandleCoinsChanged);
if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.RemoveListener(HandleCoinsChanged);
eventsSubscribed = false;
}
ComWrapper.OnInitialize -= Subscribe;
}
private void Subscribe()
{
if (eventsSubscribed) return;
if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.AddListener(HandleCoinsChanged);
if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.AddListener(HandleCoinsChanged);
lastCoins = ComWrapper.GetPlayerCredits(playerId);
eventsSubscribed = true;
}
private void Start()
{
int current = ComWrapper.GetPlayerCredits(playerId);
lastCoins = current;
if (coin != null)
{
coin.ChangeCoinText($"{current}");
if (current == 0) coin.FreezeCoin();
else coin.UnfreezeCoin();
}
}
private void HandleCoinsChanged(int playerID, int newAmount)
{
if (coin == null || playerID != playerId) return;
coin.ChangeCoinText($"{newAmount}");
if (newAmount == 0) coin.FreezeCoin();
else coin.UnfreezeCoin();
if (newAmount > lastCoins)
coin.PlayAddCoinsAnimation();
lastCoins = newAmount;
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0b362969ae104ca7bda6aadc36f41dbd
timeCreated: 1770037304

View File

@ -1,297 +0,0 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class EnemyCardController : MonoBehaviour
{
[Header("UI Elements")]
public Image avatarImage;
public Animator avatarAnimator;
public TMP_Text nicknameText;
public TMP_Text dialogueText;
public RectTransform dialoguePanel;
[Header("Settings")]
public float typingSpeed = 0.05f;
public float panelExpandWidth = 300f;
public float panelExpandSpeed = 5f;
[Header("Animation names")]
public string idleAnimationName = "Idle_bot_1";
public string transitionIn = "forvard_bot";
public string transitionOut = "back_bot";
private Coroutine panelAnimCoroutine;
private Coroutine textTypingCoroutine;
private Vector2 dialogueOriginalPos;
private WaitForSeconds cachedTypingWait;
private System.Collections.Generic.HashSet<string> availableStates = new System.Collections.Generic.HashSet<string>();
private void Awake()
{
if (dialoguePanel != null)
dialogueOriginalPos = dialoguePanel.anchoredPosition;
cachedTypingWait = new WaitForSeconds(typingSpeed);
ValidateReferences();
}
private void OnValidate()
{
ValidateReferences();
}
private void ValidateReferences()
{
if (avatarImage == null) Debug.LogWarning($"[{name}] avatarImage íå íàçíà÷åí!", this);
if (nicknameText == null) Debug.LogWarning($"[{name}] nicknameText íå íàçíà÷åí!", this);
if (dialogueText == null) Debug.LogWarning($"[{name}] dialogueText íå íàçíà÷åí!", this);
if (dialoguePanel == null) Debug.LogWarning($"[{name}] dialoguePanel íå íàçíà÷åí!", this);
}
private void OnDisable()
{
StopAllAnimations();
ResetUIState();
}
public void SetEnemy(EnemyData enemy)
{
if (enemy == null)
{
Debug.LogWarning($"[{name}] SetEnemy: enemy is null!");
return;
}
if (nicknameText != null)
nicknameText.text = enemy.nickname;
SetupAnimatorOrSprite(enemy);
ResetUIState();
// Óñòàíàâëèâàåì ïîðÿäîê ïàíåëè (òîëüêî êîãäà îáúåêò àêòèâåí)
if (dialoguePanel != null && gameObject.activeInHierarchy)
dialoguePanel.SetSiblingIndex(0);
}
private void SetupAnimatorOrSprite(EnemyData enemy)
{
availableStates.Clear();
if (enemy.animatorOverride != null && avatarAnimator != null)
{
avatarAnimator.runtimeAnimatorController = enemy.animatorOverride;
avatarAnimator.enabled = true;
// Êýøèðóåì äîñòóïíûå êëèïû îäèí ðàç
var overrides = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<AnimationClip, AnimationClip>>();
enemy.animatorOverride.GetOverrides(overrides);
foreach (var pair in overrides)
{
if (pair.Value != null) // Åñòü ïåðåîïðåäåë¸ííûé êëèï
availableStates.Add(pair.Key.name);
}
SetEmotion(idleAnimationName);
}
else
{
if (avatarAnimator != null)
avatarAnimator.enabled = false;
if (enemy.characterSprite != null && avatarImage != null)
avatarImage.sprite = enemy.characterSprite;
}
}
private void ResetUIState()
{
if (dialogueText != null)
dialogueText.text = "";
if (dialoguePanel != null)
{
dialoguePanel.anchoredPosition = dialogueOriginalPos;
}
}
private void StopAllAnimations()
{
if (panelAnimCoroutine != null)
{
StopCoroutine(panelAnimCoroutine);
panelAnimCoroutine = null;
}
if (textTypingCoroutine != null)
{
StopCoroutine(textTypingCoroutine);
textTypingCoroutine = null;
}
}
/// <summary>
/// Îòêðûâàåò êàðòî÷êó ñ àíèìàöèåé, âûçûâàåò callback êîãäà îòêðûëàñü
/// </summary>
public void ShowCard(string emotionState, System.Action onShown = null)
{
if (panelAnimCoroutine != null)
StopCoroutine(panelAnimCoroutine);
panelAnimCoroutine = StartCoroutine(ShowCardCoroutine(emotionState, onShown));
}
/// <summary>
/// Çàêðûâàåò êàðòî÷êó ñ àíèìàöèåé, âûçûâàåò callback êîãäà çàêðûëàñü
/// </summary>
public void HideCard(System.Action onHidden = null)
{
if (panelAnimCoroutine != null)
StopCoroutine(panelAnimCoroutine);
panelAnimCoroutine = StartCoroutine(HideCardCoroutine(onHidden));
}
/// <summary>
/// Ïå÷àòàåò òåêñò ïîñèìâîëüíî, âûçûâàåò callback ïî îêîí÷àíèè
/// </summary>
public void TypeText(string text, System.Action onComplete = null, bool skipSizePreparation = false)
{
if (textTypingCoroutine != null)
StopCoroutine(textTypingCoroutine);
textTypingCoroutine = StartCoroutine(TypeTextCoroutine(text, onComplete, skipSizePreparation));
}
/// <summary>
/// Ìãíîâåííî óñòàíàâëèâàåò òåêñò áåç àíèìàöèè ïå÷àòè
/// </summary>
public void SetTextImmediate(string text)
{
if (textTypingCoroutine != null)
{
StopCoroutine(textTypingCoroutine);
textTypingCoroutine = null;
}
if (dialogueText != null)
dialogueText.text = text;
}
/// <summary>
/// Ïîäãîòàâëèâàåò ðàçìåð òåêñòà çàðàíåå (÷òîáû èçáåæàòü "áëèêà" ïðè îòêðûòèè ïàíåëè)
/// </summary>
public IEnumerator PrepareTextSizeAsync(string text)
{
yield return PrepareTextSize(text);
}
private IEnumerator ShowCardCoroutine(string emotionState, System.Action onShown)
{
if (dialoguePanel == null) yield break;
SetEmotion(transitionIn);
Vector2 hiddenPos = dialogueOriginalPos;
Vector2 shownPos = hiddenPos + new Vector2(panelExpandWidth, 0);
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * panelExpandSpeed;
dialoguePanel.anchoredPosition = Vector2.Lerp(hiddenPos, shownPos, t);
yield return null;
}
dialoguePanel.anchoredPosition = shownPos;
SetEmotion(emotionState);
onShown?.Invoke();
}
private IEnumerator HideCardCoroutine(System.Action onHidden)
{
if (dialoguePanel == null) yield break;
SetEmotion(transitionOut);
if (dialogueText != null)
dialogueText.text = "";
Vector2 hiddenPos = dialogueOriginalPos;
Vector2 shownPos = hiddenPos + new Vector2(panelExpandWidth, 0);
float t = 0f;
while (t < 1f)
{
t += Time.deltaTime * panelExpandSpeed;
dialoguePanel.anchoredPosition = Vector2.Lerp(shownPos, hiddenPos, t);
yield return null;
}
dialoguePanel.anchoredPosition = hiddenPos;
SetEmotion(idleAnimationName);
onHidden?.Invoke();
}
private IEnumerator TypeTextCoroutine(string text, System.Action onComplete, bool skipSizePreparation)
{
if (dialogueText == null) yield break;
// Ïîäãîòîâêà ðàçìåðà øðèôòà (ïðîïóñêàåì åñëè óæå ñäåëàíî)
if (!skipSizePreparation)
{
yield return PrepareTextSize(text);
}
// Ïå÷àòü ïî ñèìâîëàì ÷åðåç maxVisibleCharacters (îïòèìèçàöèÿ)
dialogueText.text = text;
dialogueText.ForceMeshUpdate();
int totalChars = dialogueText.textInfo.characterCount;
dialogueText.maxVisibleCharacters = 0;
for (int i = 0; i <= totalChars; i++)
{
dialogueText.maxVisibleCharacters = i;
yield return cachedTypingWait;
}
onComplete?.Invoke();
}
private IEnumerator PrepareTextSize(string text)
{
if (dialogueText == null) yield break;
dialogueText.enableWordWrapping = true;
dialogueText.overflowMode = TextOverflowModes.Overflow;
dialogueText.text = text;
dialogueText.enableAutoSizing = true;
dialogueText.fontSizeMin = 5;
yield return null; // Æä¸ì êàäð äëÿ ðàñ÷¸òà
float finalFontSize = dialogueText.fontSize;
dialogueText.enableAutoSizing = false;
dialogueText.fontSize = finalFontSize;
dialogueText.text = "";
}
public void SetEmotion(string stateName)
{
if (avatarAnimator == null || !avatarAnimator.enabled || string.IsNullOrEmpty(stateName))
return;
if (availableStates.Contains(stateName))
avatarAnimator.Play(stateName);
}
}

View File

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

View File

@ -1,112 +0,0 @@
using UnityEngine;
using System;
[CreateAssetMenu(fileName = "NewEnemyData", menuName = "Enemies/EnemyData")]
public class EnemyData : ScriptableObject
{
public string nickname;
[Header("Àíèìàöèè ïåðñîíàæà")]
public AnimatorOverrideController animatorOverride; // äëÿ ïîêàäðîâîé àíèìàöèè
public Sprite characterSprite; // òåêóùèé/íåéòðàëüíûé ñïðàéò (fallback)
[Header("Îçâó÷êà ïîáåäû èãðîêà")]
public AudioClip[] playerVictory;
[Header("Îçâó÷êà ïîáåäû áîòà")]
public AudioClip[] playerDefeat;
[Header("Ôðàçû äëÿ äèàëîãà")]
public DialogueLine[] dialogues;
[Header("Ñîáûòèéíûå äèàëîãè")]
public DialogueLine[] onPlayerHit; // Èãðîê ïîïàë
public DialogueLine[] onPlayerMiss; // Èãðîê ïðîìàçàë
public DialogueLine[] onBotHit; // Áîò ïîïàë
public DialogueLine[] onBotMiss; // Áîò ïðîìàçàë
public DialogueLine[] onPlayerShipDestroyed; // Êîðàáëü èãðîêà óíè÷òîæåí
public DialogueLine[] onBotShipDestroyed; // Êîðàáëü áîòà óíè÷òîæåí
// Âñïîìîãàòåëüíûé ìåòîä äëÿ ïîëó÷åíèÿ ñëó÷àéíîé ôðàçû èç êàòåãîðèè
public DialogueLine? GetRandomDialogue(DialogueEventType eventType)
{
DialogueLine[] sourceArray = null;
switch (eventType)
{
case DialogueEventType.PlayerHit: sourceArray = onPlayerHit; break;
case DialogueEventType.PlayerMiss: sourceArray = onPlayerMiss; break;
case DialogueEventType.BotHit: sourceArray = onBotHit; break;
case DialogueEventType.BotMiss: sourceArray = onBotMiss; break;
case DialogueEventType.PlayerShipDestroyed: sourceArray = onPlayerShipDestroyed; break;
case DialogueEventType.BotShipDestroyed: sourceArray = onBotShipDestroyed; break;
}
if (sourceArray != null && sourceArray.Length > 0)
{
return sourceArray[UnityEngine.Random.Range(0, sourceArray.Length)];
}
return null;
}
}
public enum DialogueEventType
{
PlayerHit,
PlayerMiss,
BotHit,
BotMiss,
PlayerShipDestroyed,
BotShipDestroyed
}
// Enum äëÿ èì¸í ñîñòîÿíèé àíèìàòîðà
public enum AnimationName
{
None,
TalkBot1,
TalkBot2,
TalkBot3,
TalkBot4,
AngerBot1,
AngerBot2,
LaughterBot1,
BackBot,
ForwardBot
}
[System.Serializable]
public struct DialogueLine
{
[TextArea(2, 5)]
public string text;
// Òåïåðü èñïîëüçóåì enum âìåñòî ñòðîêè — â èíñïåêòîðå áóäåò âûïàäàþùèé ñïèñîê
public AnimationName animationName; // íàïðèìåð AnimationName.TalkBot1
public AudioClip voiceClip;
}
// Âñïîìîãàòåëüíûé êëàññ, êîòîðûé äà¸ò ñòðîêîâîå èìÿ ñîñòîÿíèÿ àíèìàòîðà äëÿ êàæäîãî enum-÷ëåíà
public static class AnimationNameExtensions
{
public static string ToStateName(this AnimationName name)
{
switch (name)
{
case AnimationName.TalkBot1: return "talk_bot_1";
case AnimationName.TalkBot2: return "talk_bot_2";
case AnimationName.TalkBot3: return "talk_bot_3";
case AnimationName.TalkBot4: return "talk_bot_4";
case AnimationName.AngerBot1: return "anger_bot_1";
case AnimationName.AngerBot2: return "anger_bot_2";
case AnimationName.LaughterBot1: return "laughter_bot_1";
case AnimationName.BackBot: return "back_bot";
case AnimationName.ForwardBot: return "forward_bot";
default: return string.Empty;
}
}
}

View File

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

View File

@ -1,271 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyManager : MonoBehaviour
{
[Header("Ñïèñîê âðàãîâ")]
public EnemyData[] enemies;
[Header("UI êîíòðîëëåð êàðòî÷êè")]
public EnemyCardController cardController;
[Header("Settings")]
public float dialogueHoldTime = 1f;
private int currentIndex = 0;
private int playerId = 1;
private Coroutine currentDialogueCoroutine;
private bool isDialoguePlaying = false;
// Îòñëåæèâàíèå èñïîëüçîâàííûõ ðåïëèê äëÿ êàæäîãî òèïà ñîáûòèÿ
private Dictionary<DialogueEventType, List<int>> usedDialogueIndices = new Dictionary<DialogueEventType, List<int>>();
private void Start()
{
var player = gameObject.GetComponentInParent<Player>();
if (player != null)
playerId = player.PlayerId;
else
Debug.LogWarning($"[{name}] Player íå íàéäåí â ðîäèòåëÿõ!");
}
public void SetEnemy(int index)
{
if (enemies == null || index < 0 || index >= enemies.Length)
{
Debug.LogWarning($"[{name}] SetEnemy: invalid index {index}");
return;
}
if (!cardController.gameObject.activeSelf)
cardController.gameObject.SetActive(true);
currentIndex = index;
cardController.SetEnemy(enemies[index]);
isDialoguePlaying = false;
// Ñáðàñûâàåì èñòîðèþ èñïîëüçîâàííûõ ðåïëèê ïðè ñìåíå âðàãà
usedDialogueIndices.Clear();
}
public void SetEnemyPlayer()
{
// Ïðîôèëè èãðîêîâ - ýòî ïîñëåäíèå 2 ýëåìåíòà â ìàññèâå enemies
// playerId = 1 -> enemies[Length-2], playerId = 2 -> enemies[Length-1]
if (enemies == null || enemies.Length < 2)
{
Debug.LogWarning($"[{name}] SetEnemyPlayer: íåäîñòàòî÷íî âðàãîâ â ìàññèâå äëÿ ïðîôèëåé èãðîêîâ!");
cardController.gameObject.SetActive(false);
return;
}
int playerProfileIndex = enemies.Length - (3 - playerId); // playerId 1 -> -2, playerId 2 -> -1
if (playerProfileIndex < 0 || playerProfileIndex >= enemies.Length)
{
Debug.LogWarning($"[{name}] SetEnemyPlayer: íåêîððåêòíûé èíäåêñ ïðîôèëÿ èãðîêà {playerProfileIndex}");
cardController.gameObject.SetActive(false);
return;
}
currentIndex = playerProfileIndex;
// Ñêðûâàåì êàðòî÷êó (èãðîê íå ïîêàçûâàåò UI)
cardController.gameObject.SetActive(false);
isDialoguePlaying = false;
// Ñáðàñûâàåì èñòîðèþ èñïîëüçîâàííûõ ðåïëèê
usedDialogueIndices.Clear();
}
/// <summary>
/// Ïîêàçûâàåò äèàëîã ñ êàðòî÷êîé: îòêðûâàåò ? ãîâîðèò ? çàêðûâàåò
/// Åñëè êàðòî÷êà íåàêòèâíà - ïðîñòî èãðàåò çâóê
/// </summary>
public void ShowDialogueWithCard(DialogueLine line)
{
// Åñëè êàðòî÷êà íåàêòèâíà - ïðîñòî èãðàåì çâóê
if (cardController == null || !cardController.gameObject.activeInHierarchy)
{
PlayVoiceOnly(line.voiceClip);
return;
}
if (currentDialogueCoroutine != null)
StopCoroutine(currentDialogueCoroutine);
currentDialogueCoroutine = StartCoroutine(DialogueWithCardSequence(line));
}
/// <summary>
/// Ïðîèãðûâàåò òîëüêî çâóê áåç êàðòî÷êè
/// </summary>
public void PlayVoiceOnly(AudioClip clip)
{
if (clip != null && SoundManager.Instance != null)
{
SoundManager.Instance.PlayVoice(playerId, clip);
}
}
/// <summary>
/// Ïîêàçûâàåò òåêñò íà óæå îòêðûòîé êàðòî÷êå (áåç àíèìàöèè îòêðûòèÿ/çàêðûòèÿ)
/// </summary>
public void ShowTextOnly(string text, string emotion = null)
{
if (!string.IsNullOrEmpty(emotion))
cardController.SetEmotion(emotion);
cardController.TypeText(text);
}
private IEnumerator DialogueWithCardSequence(DialogueLine line)
{
isDialoguePlaying = true;
// 1. Ïîäãîòàâëèâàåì ðàçìåð òåêñòà ÄÎ îòêðûòèÿ ïàíåëè (÷òîáû íå áûëî "áëèêà")
yield return cardController.PrepareTextSizeAsync(line.text);
// 2. Îòêðûâàåì êàðòî÷êó ñ ýìîöèåé ïåðåõîäà
bool cardOpened = false;
cardController.ShowCard(line.animationName.ToStateName(), () => cardOpened = true);
// Æä¸ì, ïîêà êàðòî÷êà îòêðîåòñÿ
yield return new WaitUntil(() => cardOpened);
// 3. Èãðàåì çâóê
if (line.voiceClip != null && SoundManager.Instance != null)
{
SoundManager.Instance.PlayVoice(playerId, line.voiceClip);
}
// 4. Ïå÷àòàåì òåêñò (ðàçìåð óæå ïîäãîòîâëåí)
bool textComplete = false;
cardController.TypeText(line.text, () => textComplete = true, skipSizePreparation: true);
// Æä¸ì, ïîêà òåêñò íàïå÷àòàåòñÿ
yield return new WaitUntil(() => textComplete);
// 4. Äåðæèì òåêñò íåìíîãî
yield return new WaitForSeconds(dialogueHoldTime);
// 5. Çàêðûâàåì êàðòî÷êó
bool cardClosed = false;
cardController.HideCard(() => cardClosed = true);
yield return new WaitUntil(() => cardClosed);
isDialoguePlaying = false;
}
/// <summary>
/// Âûçûâàåò äèàëîã ðåàêöèè íà ñîáûòèå (ñ êàðòî÷êîé)
/// Âûáèðàåò ðåïëèêó êîòîðàÿ åù¸ íå èñïîëüçîâàëàñü, ïîêà âñå íå êîí÷àòñÿ
/// </summary>
public void TriggerEventReaction(DialogueEventType eventType)
{
if (enemies == null || currentIndex < 0 || currentIndex >= enemies.Length)
return;
if(isDialoguePlaying) return;
var enemy = enemies[currentIndex];
DialogueLine? line = GetNonRepeatingDialogue(enemy, eventType);
if (line.HasValue)
{
ShowDialogueWithCard(line.Value);
}
}
/// <summary>
/// Ïîëó÷àåò ñëó÷àéíóþ ðåïëèêó êîòîðàÿ åù¸ íå èñïîëüçîâàëàñü äëÿ äàííîãî òèïà ñîáûòèÿ
/// </summary>
private DialogueLine? GetNonRepeatingDialogue(EnemyData enemy, DialogueEventType eventType)
{
if (enemy == null)
return null;
// Ïîëó÷àåì íóæíûé ìàññèâ ðåïëèê ïî òèïó ñîáûòèÿ
DialogueLine[] sourceArray = null;
switch (eventType)
{
case DialogueEventType.PlayerHit: sourceArray = enemy.onPlayerHit; break;
case DialogueEventType.PlayerMiss: sourceArray = enemy.onPlayerMiss; break;
case DialogueEventType.BotHit: sourceArray = enemy.onBotHit; break;
case DialogueEventType.BotMiss: sourceArray = enemy.onBotMiss; break;
case DialogueEventType.PlayerShipDestroyed: sourceArray = enemy.onPlayerShipDestroyed; break;
case DialogueEventType.BotShipDestroyed: sourceArray = enemy.onBotShipDestroyed; break;
}
if (sourceArray == null || sourceArray.Length == 0)
return null;
// Èíèöèàëèçèðóåì ñïèñîê èñïîëüçîâàííûõ ðåïëèê äëÿ ýòîãî òèïà ñîáûòèÿ åñëè íóæíî
if (!usedDialogueIndices.ContainsKey(eventType))
{
usedDialogueIndices[eventType] = new List<int>();
}
var usedList = usedDialogueIndices[eventType];
// Ñîçäà¸ì ñïèñîê íåèñïîëüçîâàííûõ èíäåêñîâ
var unusedIndices = new List<int>();
for (int i = 0; i < sourceArray.Length; i++)
{
if (!usedList.Contains(i))
unusedIndices.Add(i);
}
// Åñëè âñå ðåïëèêè èñïîëüçîâàíû - ñáðàñûâàåì è íà÷èíàåì çàíîâî
if (unusedIndices.Count == 0)
{
usedList.Clear();
for (int i = 0; i < sourceArray.Length; i++)
{
unusedIndices.Add(i);
}
}
// Âûáèðàåì ñëó÷àéíûé íåèñïîëüçîâàííûé èíäåêñ
int randomIndex = unusedIndices[Random.Range(0, unusedIndices.Count)];
usedList.Add(randomIndex);
return sourceArray[randomIndex];
}
/// <summary>
/// Ðåàêöèÿ íà êîíåö èãðû (òîëüêî çâóê, áåç êàðòî÷êè)
/// </summary>
public void TriggerEndGameReaction(bool playerWon)
{
if (enemies == null || enemies.Length == 0) return;
if (currentIndex < 0 || currentIndex >= enemies.Length) return;
isDialoguePlaying = true;
var enemy = enemies[currentIndex];
var clips = playerWon ? enemy.playerVictory : enemy.playerDefeat;
if (clips == null || clips.Length == 0) return;
// Îñòàíàâëèâàåì ãîëîñîâîé êàíàë òåêóùåãî èãðîêà, çàòåì ïðîèãðûâàåì ôèíàëüíóþ ôðàçó
if (SoundManager.Instance != null)
{
SoundManager.Instance.StopVoice(playerId);
}
Debug.Log(clips[0].name);
PlayVoiceOnly(clips[Random.Range(0, clips.Length)]);
}
public EnemyData GetCurrentEnemy()
{
if (enemies == null || currentIndex < 0 || currentIndex >= enemies.Length)
return null;
return enemies[currentIndex];
}
}

View File

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

View File

@ -1,13 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
public class GameOver : MonoBehaviour
{
[SerializeField] private Sprite[] sprites;
public Image image; // Ýòî Image íà ñöåíå, êóäà âñòàâèòñÿ ñïðàéò
public void GameOverSwitch(bool victory)
{
image.sprite = victory ? sprites[0] : sprites[1];
}
}

View File

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

View File

@ -1,102 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
using TMPro;
/// <summary>
/// Îòâå÷àåò çà îòîáðàæåíèå ýêðàíà GameOver è îáíîâëåíèå êíîïîê
/// </summary>
public class GameOverUI : MonoBehaviour
{
[SerializeField] private int playerId;
[SerializeField] private GameManager gameManager;
[Header("UI Elements")]
[SerializeField] private Button replayButton;
[SerializeField] private Button continueButton;
[SerializeField] private TextMeshProUGUI resultText;
[SerializeField] private GameObject uiParticle;
[SerializeField] private GameOver gameOverStatusImage;
public void Show(bool isWin, bool isBotGame)
{
if (uiParticle != null)
uiParticle.SetActive(isWin);
if (resultText != null)
{
resultText.text = isWin ? "Õîðîø!" : "Óâû...";
resultText.color = isWin ? new Color(1f, 0.84f, 0f) : Color.white;
if (gameOverStatusImage != null)
gameOverStatusImage.GameOverSwitch(isWin);
}
int credits = ComWrapper.GetPlayerCredits(playerId);
if (continueButton != null) continueButton.gameObject.SetActive(false);
if (replayButton != null) replayButton.gameObject.SetActive(false);
if (isBotGame)
{
ShowBotGameButtons(isWin, credits);
}
else
{
ShowPvPGameButtons(credits);
}
}
private void ShowBotGameButtons(bool isWin, int credits)
{
if (isWin)
{
if (continueButton != null)
{
continueButton.gameObject.SetActive(true);
continueButton.interactable = true;
}
}
else
{
if (replayButton != null)
{
replayButton.gameObject.SetActive(true);
replayButton.interactable = credits >= 1;
var txt = replayButton.GetComponentInChildren<TMP_Text>();
if (txt != null) txt.text = "Ïåðåèãðàòü";
}
}
}
private void ShowPvPGameButtons(int credits)
{
if (replayButton != null)
{
int oponentId = playerId == 1 ? 2 : 1;
int oponentCredits = ComWrapper.GetPlayerCredits(oponentId);
replayButton.gameObject.SetActive(true);
replayButton.interactable = credits >= 2 || (credits >= 1 && oponentCredits >= 1);
var txt = replayButton.GetComponentInChildren<TMP_Text>();
if (txt != null) txt.text = "Åù¸ ðàç!";
}
}
public void UpdateButtons()
{
int credits = ComWrapper.GetPlayerCredits(playerId);
int oponentId = playerId == 1 ? 2 : 1;
int oponentCredits = ComWrapper.GetPlayerCredits(oponentId);
if (continueButton != null && continueButton.gameObject.activeSelf)
continueButton.interactable = credits >= 1;
if (replayButton != null && replayButton.gameObject.activeSelf)
{
if (gameManager != null && gameManager.gameMode == GameMode.PlayerVsBot)
replayButton.interactable = credits >= 1;
else
replayButton.interactable = credits >= 2 || (credits >= 1 && oponentCredits >= 1);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: da85480a628a444182a5c189091dc623
timeCreated: 1770037344

View File

@ -1,74 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Îòâå÷àåò çà îáíîâëåíèå interactable ñîñòîÿíèÿ êíîïîê ãëàâíîãî ìåíþ ïî áàëàíñó
/// </summary>
public class MainMenuButtonsUpdater : MonoBehaviour
{
[SerializeField] private int playerId;
[SerializeField] private Button playVsBotButton;
[SerializeField] private Button playVsPlayerButton;
private bool eventsSubscribed;
private void OnEnable()
{
ComWrapper.OnInitialize += Subscribe;
if (!eventsSubscribed && (ComWrapper.onCoinAdded != null || ComWrapper.onCoinRemoved != null))
Subscribe();
}
private void OnDisable()
{
if (eventsSubscribed)
{
if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.RemoveListener(OnCoinsChanged);
if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.RemoveListener(OnCoinsChanged);
eventsSubscribed = false;
}
ComWrapper.OnInitialize -= Subscribe;
}
private void Subscribe()
{
if (eventsSubscribed) return;
if (ComWrapper.onCoinAdded != null) ComWrapper.onCoinAdded.AddListener(OnCoinsChanged);
if (ComWrapper.onCoinRemoved != null) ComWrapper.onCoinRemoved.AddListener(OnCoinsChanged);
eventsSubscribed = true;
UpdateButtons();
}
private void Start()
{
UpdateButtons();
}
private void OnCoinsChanged(int playerID, int newAmount)
{
UpdateButtons();
}
public void UpdateButtons()
{
int myCoins = ComWrapper.GetPlayerCredits(playerId);
int enemyId = playerId == 1 ? 2 : 1;
int enemyCoins = ComWrapper.GetPlayerCredits(enemyId);
bool freePlay = SettingsManager.instance != null &&
SettingsManager.instance.GetSettingValue("Áåñïëàòíàÿ èãðà") == "Âêë";
if (playVsBotButton != null)
playVsBotButton.interactable = myCoins >= 1 || freePlay;
bool bothHaveEnough = myCoins >= 2 || (myCoins >= 1 && enemyCoins >= 1) || freePlay;
if (playVsPlayerButton != null)
playVsPlayerButton.interactable = bothHaveEnough;
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 868ec3b4c73d4bddaffbbec6ed83002a
timeCreated: 1770037344

View File

@ -1,51 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
public class PlacementChoiceOverlay : MonoBehaviour
{
[SerializeField] private GameManager gameManager;
[SerializeField] private UIManager uiManager;
[SerializeField] private Button autoButton;
[SerializeField] private Button manualButton;
[Header("Optional focus after auto")]
[SerializeField] private Button focusAfterAuto; // êíîïêà "â áîé" íà ýêðàíå ðàññòàíîâêè
private void Awake()
{
if (autoButton != null) autoButton.onClick.AddListener(OnAuto);
if (manualButton != null) manualButton.onClick.AddListener(OnManual);
}
private void OnAuto()
{
if (gameManager == null || uiManager == null) return;
uiManager.HideAllOverlays();
gameManager.SetState(GameState.ShipPlacement);
gameManager.SetInputMode(GameManager.InputMode.UI);
// Àâòî-ðàññòàíîâêà
gameManager.ArrangeGame();
// Íàâåñòè êóðñîð íà êíîïêó "â áîé"
if (focusAfterAuto != null)
{
var highlight = focusAfterAuto.GetComponent<UIButtonHighlight>();
if (highlight != null)
highlight.SetHighlighted(true);
}
}
private void OnManual()
{
if (gameManager == null || uiManager == null) return;
uiManager.HideAllOverlays();
gameManager.SetState(GameState.ShipPlacement);
gameManager.SetInputMode(GameManager.InputMode.ShipPlacement);
gameManager.InitTargetCursor(true);
// Íè÷åãî íå ðàññòàâëÿåì — èãðîê äåëàåò ñàì
}
}

View File

@ -1,85 +0,0 @@
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Êîìïîíåíò äëÿ ïëàâíîãî èçìåíåíèÿ çíà÷åíèÿ Slider.
/// Âûçûâàé ìåòîä AnimateTo() íàïðÿìóþ äëÿ àíèìàöèè.
/// </summary>
[RequireComponent(typeof(Slider))]
public class SmoothSlider : MonoBehaviour
{
[Header("Íàñòðîéêè ïëàâíîñòè")]
[Tooltip("Äëèòåëüíîñòü àíèìàöèè â ñåêóíäàõ")]
[SerializeField] private float duration = 0.3f;
[Tooltip("Êðèâàÿ àíèìàöèè (ïî óìîë÷àíèþ - ïëàâíàÿ)")]
[SerializeField] private AnimationCurve curve = AnimationCurve.EaseInOut(0, 0, 1, 1);
private Slider slider;
private Coroutine animationCoroutine;
private float targetValue;
private System.Action<float> pendingCallback;
private int animationId; // Óíèêàëüíûé ID äëÿ îòñëåæèâàíèÿ îòìåíåííûõ àíèìàöèé
private void Awake()
{
slider = GetComponent<Slider>();
targetValue = slider.value;
animationId = 0;
}
/// <summary>
/// Ïëàâíî àíèìèðóåò ñëàéäåð ê íîâîìó çíà÷åíèþ.
/// Â êîíöå àíèìàöèè âûçîâåò callback onComplete.
/// </summary>
public void AnimateTo(float endValue, System.Action<float> onComplete = null)
{
if (animationCoroutine != null)
{
StopCoroutine(animationCoroutine);
// ÂÀÆÍÎ: Ñîõðàíÿåì ÑÒÀÐÎÅ öåëåâîå çíà÷åíèå è callback ÏÅÐÅÄ îáíîâëåíèåì
float previousTarget = targetValue;
System.Action<float> previousCallback = pendingCallback;
// Óñòàíàâëèâàåì ôèíàëüíîå çíà÷åíèå ïðåäûäóùåé àíèìàöèè
slider.value = previousTarget;
// Âûçûâàåì callback ñ ïðàâèëüíûì ôèíàëüíûì çíà÷åíèåì
previousCallback?.Invoke(previousTarget);
}
// Òåïåðü îáíîâëÿåì íà íîâûå çíà÷åíèÿ
targetValue = endValue;
pendingCallback = onComplete;
animationId++; // Íîâûé ID äëÿ íîâîé àíèìàöèè
animationCoroutine = StartCoroutine(AnimateToValue(endValue, onComplete, animationId));
}
private IEnumerator AnimateToValue(float endValue, System.Action<float> onComplete, int myAnimationId)
{
float startValue = slider.value;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / duration);
slider.value = Mathf.Lerp(startValue, endValue, curve.Evaluate(t));
yield return null;
}
// Ôèíàëüíîå çíà÷åíèå - òîëüêî åñëè àíèìàöèÿ íå áûëà îòìåíåíà
if (myAnimationId == animationId)
{
slider.value = endValue;
targetValue = endValue;
onComplete?.Invoke(endValue);
animationCoroutine = null;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5e80540a01c44b2e9f69fbf8a703cee6
timeCreated: 1765969628

View File

@ -1,9 +0,0 @@
using UnityEngine;
public enum UIPanelLayer { Screen, Overlay }
public class UIPanelId : MonoBehaviour
{
public string id; // íàïðèìåð "PlacementHint"
public UIPanelLayer layer; // Screen èëè Overlay
}