Добавить правила игры и титры с управляемой прокруткой

Сверстать страницы правил и подключить открытие из главного меню. Добавить управление обоими джойстиками, выход по бездействию через 60 секунд и автоматический расчёт высоты титров.
This commit is contained in:
Сергей Бондарь 2026-09-18 14:42:41 +03:00
parent ec0920350d
commit db3a0fd0bb
8 changed files with 7386 additions and 20 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -11408,6 +11408,7 @@ RectTransform:
- {fileID: 6665465969600127458}
- {fileID: 558601564}
- {fileID: 1637412870}
- {fileID: 2176217594842950452}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@ -23456,6 +23457,192 @@ PrefabInstance:
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 5c7519dd1985416449513fd70e70071b, type: 3}
--- !u!1001 &2176217594842950451
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 1122363135}
m_Modifications:
- target: {fileID: 547542351198818363, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1208514197097485402, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 1208514197097485402, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 2261118259935661958, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 2261118259935661958, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 2447164397015372978, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 2614948261999961729, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_Name
value: Rules Modal
objectReference: {fileID: 0}
- target: {fileID: 2614948261999961729, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4818940474544323459, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 4818940474544323459, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 5374056385884960016, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 5374056385884960016, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 7082345753563547521, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 7082345753563547521, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 7393475559755245248, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 7393475559755245248, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8336436696493915681, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 8336436696493915681, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 8338652482255218203, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 8338652482255218203, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
- target: {fileID: 8890559238728539214, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_fontColor32.rgba
value: 4278190080
objectReference: {fileID: 0}
- target: {fileID: 8890559238728539214, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
propertyPath: m_TextStyleHashCode
value: -1183493901
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
--- !u!224 &2176217594842950452 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 8317052904930279136, guid: 7b6fa78f390fffb408e54c9a557b3a31, type: 3}
m_PrefabInstance: {fileID: 2176217594842950451}
m_PrefabAsset: {fileID: 0}
--- !u!1 &2253736680806208210
GameObject:
m_ObjectHideFlags: 0

View File

@ -75,8 +75,8 @@ public class Menu : MonoBehaviour, IControlable
private void Disable()
{
ControlsEnabled = false;
menuPanelManager.CloseModal();
ControlsEnabled = false;
_promoTimer.Reset();
_settingsTimer.Reset();
}

View File

@ -12,6 +12,10 @@ public class RulesPanel : InfoPanel
[SerializeField] private List<RectTransform> images;
[SerializeField] private float ruleImageDuration, ruleImageOpenY, ruleImageCloseY, ruleImageDelay;
private RulesController _modal;
public override bool BlocksNavigation => _modal != null && _modal.IsOpen;
public override float closeDelay => ((images.Count * ruleImageDelay)
+ infoDuration
+ buttonDuration
@ -74,4 +78,18 @@ public class RulesPanel : InfoPanel
public override void Initialize()
{
}
}
public override void Interact()
{
if (_modal == null)
_modal = FindFirstObjectByType<RulesController>(FindObjectsInactive.Include);
if (_modal != null)
_modal.Open();
}
public override void CloseModal()
{
_modal?.Close();
}
}

View File

@ -1,44 +1,161 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
public class RulesController : MonoBehaviour
public class RulesController : MonoBehaviour, IControlable
{
[FormerlySerializedAs("pages")]
[SerializeField] private List<GameObject> _pages;
[SerializeField] private RectTransform _creditsContent;
[SerializeField] private RectTransform _creditsViewport;
[SerializeField] private TMPro.TMP_Text _sectionLabel;
[SerializeField] private float _timeout = 60f;
[SerializeField] private float _autoScrollSpeed = 35f;
[SerializeField] private float _manualScrollSpeed = 300f;
[SerializeField] private float _autoResumeDelay = 1f;
private Menu _menu;
private int _currentIndex;
private float _lastActivityAt;
private float _lastInputAt;
private float _scrollOffset;
private int currentIndex;
[SerializeField] List<GameObject> pages;
public bool IsOpen { get; private set; }
public bool ControlsEnabled { get; set; }
private void Update()
{
if (!IsOpen)
return;
if (In.AnyButton())
_lastActivityAt = Time.unscaledTime;
if (Time.unscaledTime - _lastActivityAt >= _timeout)
Close();
}
private void OnDisable()
{
if (IsOpen)
Close();
}
public void Open()
{
currentIndex = 0;
OpenPage(currentIndex);
if (IsOpen || _pages == null || _pages.Count == 0)
return;
_menu = FindFirstObjectByType<Menu>(FindObjectsInactive.Include);
if (_menu == null || !_menu.ControlsEnabled)
return;
_menu.ControlsEnabled = false;
_lastActivityAt = Time.unscaledTime;
_lastInputAt = _lastActivityAt;
_currentIndex = 0;
IsOpen = true;
ControlsEnabled = true;
gameObject.SetActive(true);
transform.SetAsLastSibling();
OpenPage(_currentIndex);
ControlsPerformer.Instance.SetControlable(this);
}
public void Close() => CloseAllPages();
public void Close()
{
if (!IsOpen)
return;
IsOpen = false;
ControlsEnabled = false;
CloseAllPages();
gameObject.SetActive(false);
if (_menu != null)
{
_menu.ControlsEnabled = true;
if (ControlsPerformer.Instance != null)
ControlsPerformer.Instance.SetControlable(_menu);
}
}
public void UpdateControls()
{
if (In.GetAction(JoystickSide.Both, InteractMode.Discrete))
{
Close();
return;
}
SwitchPage(In.GetHorizontalAxis(JoystickSide.Both, InteractMode.Discrete));
if (_currentIndex != _pages.Count - 1 || _creditsContent == null)
return;
int direction = In.GetVerticalAxis(JoystickSide.Both, InteractMode.Continious);
if (In.AnyButton())
_lastInputAt = Time.unscaledTime;
float speed = direction != 0 ? -direction * _manualScrollSpeed
: Time.unscaledTime - _lastInputAt >= _autoResumeDelay ? _autoScrollSpeed : 0f;
float limit = Mathf.Max(0f, _creditsContent.rect.height - _creditsViewport.rect.height);
if (direction == 0 && speed > 0f && _scrollOffset >= limit)
_scrollOffset = 0f;
_scrollOffset = Mathf.Clamp(_scrollOffset + speed * Time.unscaledDeltaTime, 0f, limit);
_creditsContent.anchoredPosition = Vector2.up * _scrollOffset;
}
public void SwitchPage(int direction)
{
bool invalidOperation = pages.Count == 0 || direction == 0;
if (invalidOperation) return;
if (!IsOpen || direction == 0)
return;
direction = Mathf.Clamp(direction, -1, 1);
currentIndex = (currentIndex + direction + pages.Count) % pages.Count;
OpenPage(currentIndex);
_lastActivityAt = Time.unscaledTime;
_currentIndex = (_currentIndex + (direction > 0 ? 1 : -1) + _pages.Count) % _pages.Count;
OpenPage(_currentIndex);
}
private void OpenPage(int index)
{
CloseAllPages();
pages[index].SetActive(true);
_pages[index].SetActive(true);
if (_sectionLabel != null)
_sectionLabel.text = index == _pages.Count - 1 ? "КОНЕЦ ИГРЫ" : "ПРАВИЛА";
_scrollOffset = 0f;
_lastInputAt = Time.unscaledTime;
if (_creditsContent != null)
_creditsContent.anchoredPosition = Vector2.zero;
if (index == _pages.Count - 1)
RefreshCreditsLayout();
}
private void RefreshCreditsLayout()
{
if (_creditsContent == null || _creditsViewport == null)
return;
Canvas.ForceUpdateCanvases();
float bottom = 0f;
Vector3[] corners = new Vector3[4];
foreach (TMPro.TMP_Text text in _creditsContent.GetComponentsInChildren<TMPro.TMP_Text>())
{
RectTransform rect = text.rectTransform;
float height = text.GetPreferredValues(text.text, rect.rect.width, Mathf.Infinity).y;
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
text.ForceMeshUpdate();
rect.GetWorldCorners(corners);
for (int i = 0; i < corners.Length; i++)
bottom = Mathf.Max(bottom, _creditsContent.rect.yMax
- _creditsContent.InverseTransformPoint(corners[i]).y);
}
// Нижний отступ даёт последним строкам пройти через всю видимую область до повтора.
_creditsContent.SetSizeWithCurrentAnchors(
RectTransform.Axis.Vertical, bottom + _creditsViewport.rect.height);
}
private void CloseAllPages()
{
foreach (GameObject page in pages)
{
foreach (GameObject page in _pages)
page.SetActive(false);
}
}
}

BIN
Assets/Sprites/arrow_button.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,143 @@
fileFormatVersion: 2
guid: e3c025e9e0f9e0549aea310601ff00f4
TextureImporter:
internalIDToNameTable:
- first:
213: 555834993836193372
second: arrow_button_0
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: 2
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: 4
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: 4
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
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: arrow_button_0
rect:
serializedVersion: 2
x: 88
y: 88
width: 336
height: 336
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: c5676516bf8b6b700800000000000000
internalID: 555834993836193372
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
arrow_button_0: 555834993836193372
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant: