修复已知功能
1. 添加章节开始 UI 介绍 2. 清理不使用的资源 3. 补充 AIChat 入口 UI 4. 接上所有的正常对话流程
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20cdfc6b68f9f074a88435387f8f30a3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,51 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class InputComponent : MonoBehaviour
|
||||
{
|
||||
private PlayerInputActions _inputActions;
|
||||
private bool _isListening = false;
|
||||
|
||||
private Vector3 _direction = Vector3.zero;
|
||||
public Vector3 Direction => _direction;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_inputActions = new();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_isListening && _inputActions != null)
|
||||
{
|
||||
ReadInput();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_inputActions = null;
|
||||
_isListening = false;
|
||||
}
|
||||
|
||||
private void ReadInput()
|
||||
{
|
||||
Vector2 rawDir = _inputActions.Game.Move.ReadValue<Vector2>();
|
||||
_direction = new Vector3(rawDir.x, 0, rawDir.y);
|
||||
}
|
||||
|
||||
public void SetListening(bool isListening)
|
||||
{
|
||||
_isListening = isListening;
|
||||
if (_isListening)
|
||||
{
|
||||
_inputActions.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputActions.Disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class MovementComponent : MonoBehaviour
|
||||
{
|
||||
private bool _isMoving = false;
|
||||
private Vector3 _direction = Vector3.forward;
|
||||
private float _speed = 0;
|
||||
private Transform _cachedTransform;
|
||||
|
||||
public void OnInit(float speed, Transform transform)
|
||||
{
|
||||
_speed = speed;
|
||||
_cachedTransform = transform;
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_isMoving && _cachedTransform != null)
|
||||
{
|
||||
Move(elapseSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_speed = 0;
|
||||
_cachedTransform = null;
|
||||
_isMoving = false;
|
||||
}
|
||||
|
||||
private void Move(float deltaTime = 0)
|
||||
{
|
||||
_cachedTransform.Translate(_direction * _speed * deltaTime);
|
||||
}
|
||||
|
||||
public void SetMove(bool isMoving) => _isMoving = isMoving;
|
||||
public void SetDirection(Vector3 direction) => _direction = direction;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DataTable;
|
||||
using Definition;
|
||||
@@ -14,22 +15,45 @@ namespace CustomComponent
|
||||
[DisallowMultipleComponent]
|
||||
public class DialogComponent : GameFrameworkComponent
|
||||
{
|
||||
[Serializable]
|
||||
private sealed class SpeakerPortraitBinding
|
||||
{
|
||||
[SerializeField] private string _speakerId = string.Empty;
|
||||
[SerializeField] private List<Sprite> _portraits = new List<Sprite>();
|
||||
|
||||
public string SpeakerId => _speakerId;
|
||||
public List<Sprite> Portraits => _portraits;
|
||||
}
|
||||
|
||||
#region Property
|
||||
|
||||
private const int DialogChapterDivisor = 1000;
|
||||
private const int LineChapterDivisor = 100000000;
|
||||
private const int LineDialogDivisor = 100000;
|
||||
|
||||
[Header("角色立绘配置(拖拽 Sprite)")]
|
||||
[SerializeField] private List<SpeakerPortraitBinding> _speakerPortraitBindings = new List<SpeakerPortraitBinding>();
|
||||
|
||||
private readonly Dictionary<int, DRDialog> _dialogMap = new();
|
||||
|
||||
private readonly Dictionary<int, List<DRDialogLine>> _dialogLinesMap = new();
|
||||
|
||||
private readonly Dictionary<int, int> _dialogFirstLineIdMap = new();
|
||||
private static readonly HashSet<string> NonPortraitSpeakerIds = new(System.StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Other",
|
||||
"Subtitle",
|
||||
"Time"
|
||||
};
|
||||
|
||||
private IDataTable<DRDialog> _dtDialog;
|
||||
private IDataTable<DRDialogLine> _dtDialogLine;
|
||||
private DialogFormController _formController;
|
||||
private DialogFormContext _formContext;
|
||||
private int _pendingStartDialogId;
|
||||
private int _activeChapterTitleRequestId;
|
||||
private int _nextChapterTitleRequestId = 1;
|
||||
private bool _chapterTitleShownAfterInit;
|
||||
|
||||
private int _currentChapterId;
|
||||
private int _currentLineIndex = -1;
|
||||
@@ -46,6 +70,31 @@ namespace CustomComponent
|
||||
|
||||
public int CurrentLineId => _formContext != null ? _formContext.CurrentLineId : 0;
|
||||
|
||||
public int GetSpeakerPortraitCount(string speakerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(speakerId))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SpeakerPortraitBinding binding = FindSpeakerPortraitBinding(speakerId);
|
||||
if (binding == null || binding.Portraits == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validCount = 0;
|
||||
for (int i = 0; i < binding.Portraits.Count; i++)
|
||||
{
|
||||
if (binding.Portraits[i] != null)
|
||||
{
|
||||
validCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return validCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -54,6 +103,7 @@ namespace CustomComponent
|
||||
GameEntry.Event.Subscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
|
||||
GameEntry.Event.Subscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
|
||||
GameEntry.Event.Subscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
|
||||
GameEntry.Event.Subscribe(ChapterTitleCompletedEventArgs.EventId, OnChapterTitleCompleted);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@@ -61,6 +111,7 @@ namespace CustomComponent
|
||||
GameEntry.Event.Unsubscribe(DialogNextLineRequestEventArgs.EventId, OnDialogNextLineRequest);
|
||||
GameEntry.Event.Unsubscribe(DialogSkipRequestEventArgs.EventId, OnDialogSkipRequest);
|
||||
GameEntry.Event.Unsubscribe(DialogStopRequestEventArgs.EventId, OnDialogStopRequest);
|
||||
GameEntry.Event.Unsubscribe(ChapterTitleCompletedEventArgs.EventId, OnChapterTitleCompleted);
|
||||
}
|
||||
|
||||
public bool Init(int chapterId)
|
||||
@@ -159,10 +210,14 @@ namespace CustomComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
PreloadChapterPortraitSprites();
|
||||
EnsureFormController();
|
||||
_formContext = null;
|
||||
_currentChapterId = chapterId;
|
||||
_currentLineIndex = -1;
|
||||
_pendingStartDialogId = 0;
|
||||
_activeChapterTitleRequestId = 0;
|
||||
_chapterTitleShownAfterInit = false;
|
||||
_isInitialized = true;
|
||||
_isPlaying = false;
|
||||
return true;
|
||||
@@ -196,6 +251,24 @@ namespace CustomComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_activeChapterTitleRequestId > 0)
|
||||
{
|
||||
_pendingStartDialogId = dialogId;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_chapterTitleShownAfterInit)
|
||||
{
|
||||
_chapterTitleShownAfterInit = true;
|
||||
_pendingStartDialogId = dialogId;
|
||||
if (TryOpenChapterTitle(dialogRow))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_pendingStartDialogId = 0;
|
||||
}
|
||||
|
||||
if (_isPlaying)
|
||||
{
|
||||
StopDialog();
|
||||
@@ -267,6 +340,21 @@ namespace CustomComponent
|
||||
|
||||
public void StopDialog()
|
||||
{
|
||||
_pendingStartDialogId = 0;
|
||||
|
||||
if (_activeChapterTitleRequestId > 0)
|
||||
{
|
||||
_activeChapterTitleRequestId = 0;
|
||||
if (GameEntry.UI != null)
|
||||
{
|
||||
UGuiForm chapterTitleForm = GameEntry.UI.GetUIForm(UIFormId.ChapterTitleForm);
|
||||
if (chapterTitleForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(chapterTitleForm.UIForm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isPlaying)
|
||||
{
|
||||
return;
|
||||
@@ -286,6 +374,9 @@ namespace CustomComponent
|
||||
_formContext = null;
|
||||
_currentChapterId = 0;
|
||||
_currentLineIndex = -1;
|
||||
_pendingStartDialogId = 0;
|
||||
_activeChapterTitleRequestId = 0;
|
||||
_chapterTitleShownAfterInit = false;
|
||||
_isInitialized = false;
|
||||
_isPlaying = false;
|
||||
}
|
||||
@@ -368,6 +459,138 @@ namespace CustomComponent
|
||||
_formContext.IsLastLine = lineIndex >= totalLines - 1;
|
||||
}
|
||||
|
||||
private void PreloadChapterPortraitSprites()
|
||||
{
|
||||
if (GameEntry.SpriteCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HashSet<string> speakerIds = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);
|
||||
foreach (KeyValuePair<int, List<DRDialogLine>> pair in _dialogLinesMap)
|
||||
{
|
||||
List<DRDialogLine> lines = pair.Value;
|
||||
if (lines == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
DRDialogLine line = lines[i];
|
||||
if (line == null || !IsHumanSpeakerId(line.SpeakerId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
speakerIds.Add(line.SpeakerId);
|
||||
}
|
||||
}
|
||||
|
||||
if (speakerIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string speakerId in speakerIds)
|
||||
{
|
||||
RegisterSpeakerPortraitsToCache(speakerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterSpeakerPortraitsToCache(string speakerId)
|
||||
{
|
||||
SpeakerPortraitBinding binding = FindSpeakerPortraitBinding(speakerId);
|
||||
if (binding == null || binding.Portraits == null || binding.Portraits.Count == 0)
|
||||
{
|
||||
Log.Warning("Portrait preload skipped. Missing speaker portrait binding. speakerId='{0}'.", speakerId);
|
||||
return;
|
||||
}
|
||||
|
||||
int portraitIndex = 1;
|
||||
for (int i = 0; i < binding.Portraits.Count; i++)
|
||||
{
|
||||
Sprite portrait = binding.Portraits[i];
|
||||
if (portrait == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string cacheKey = $"Characters/{speakerId}-{portraitIndex}.png";
|
||||
GameEntry.SpriteCache.RegisterSprite(cacheKey, portrait, true);
|
||||
portraitIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private SpeakerPortraitBinding FindSpeakerPortraitBinding(string speakerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(speakerId) || _speakerPortraitBindings == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _speakerPortraitBindings.Count; i++)
|
||||
{
|
||||
SpeakerPortraitBinding binding = _speakerPortraitBindings[i];
|
||||
if (binding == null || string.IsNullOrWhiteSpace(binding.SpeakerId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(binding.SpeakerId.Trim(), speakerId.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsHumanSpeakerId(string speakerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(speakerId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !NonPortraitSpeakerIds.Contains(speakerId);
|
||||
}
|
||||
|
||||
private bool TryOpenChapterTitle(DRDialog dialogRow)
|
||||
{
|
||||
if (GameEntry.UI == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int requestId = _nextChapterTitleRequestId++;
|
||||
string subtitle = dialogRow == null || string.IsNullOrWhiteSpace(dialogRow.Title)
|
||||
? string.Empty
|
||||
: dialogRow.Title.Trim();
|
||||
ChapterTitleFormContext context = new ChapterTitleFormContext
|
||||
{
|
||||
ChapterId = _currentChapterId,
|
||||
RequestId = requestId,
|
||||
Title = BuildChapterTitle(_currentChapterId),
|
||||
Subtitle = subtitle
|
||||
};
|
||||
|
||||
int? formSerialId = GameEntry.UI.OpenUIForm(UIFormId.ChapterTitleForm, context);
|
||||
if (!formSerialId.HasValue)
|
||||
{
|
||||
Log.Warning("Open chapter title form failed. chapterId='{0}'.", _currentChapterId.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
_activeChapterTitleRequestId = requestId;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string BuildChapterTitle(int chapterId)
|
||||
{
|
||||
return chapterId > 0 ? $"第{chapterId}章" : string.Empty;
|
||||
}
|
||||
|
||||
private static int ParseChapterIdFromDialogId(int dialogId)
|
||||
{
|
||||
return dialogId / DialogChapterDivisor;
|
||||
@@ -415,6 +638,29 @@ namespace CustomComponent
|
||||
StopDialog();
|
||||
}
|
||||
|
||||
private void OnChapterTitleCompleted(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is ChapterTitleCompletedEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.RequestId <= 0 || args.RequestId != _activeChapterTitleRequestId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeChapterTitleRequestId = 0;
|
||||
int pendingDialogId = _pendingStartDialogId;
|
||||
_pendingStartDialogId = 0;
|
||||
if (pendingDialogId <= 0 || !_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartDialog(pendingDialogId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,59 +14,192 @@ namespace CustomComponent
|
||||
[SerializeField] private Vector2 _defaultPivot = new(0.5f, 0.5f);
|
||||
|
||||
private Dictionary<string, Sprite> _spriteCache;
|
||||
private Dictionary<string, List<Action<Sprite>>> _pendingCallbacks;
|
||||
private HashSet<string> _loadingSpriteAssets;
|
||||
private ResourceComponent _resource;
|
||||
|
||||
void Start()
|
||||
{
|
||||
_spriteCache = new Dictionary<string, Sprite>();
|
||||
_resource = GameEntry.Resource;
|
||||
EnsureRuntimeCaches();
|
||||
}
|
||||
|
||||
private void EnsureRuntimeCaches()
|
||||
{
|
||||
_spriteCache ??= new Dictionary<string, Sprite>();
|
||||
_pendingCallbacks ??= new Dictionary<string, List<Action<Sprite>>>();
|
||||
_loadingSpriteAssets ??= new HashSet<string>();
|
||||
_resource ??= GameEntry.Resource;
|
||||
}
|
||||
|
||||
public bool TryGetCachedSprite(string assetName, out Sprite sprite)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (string.IsNullOrWhiteSpace(assetName))
|
||||
{
|
||||
sprite = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return _spriteCache.TryGetValue(assetName, out sprite);
|
||||
}
|
||||
|
||||
public void GetSprite(string assetName, Action<Sprite> callback)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (string.IsNullOrWhiteSpace(assetName))
|
||||
{
|
||||
callback?.Invoke(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_spriteCache.TryGetValue(assetName, out var sprite))
|
||||
{
|
||||
callback?.Invoke(sprite);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (callback != null)
|
||||
{
|
||||
_resource.LoadAsset
|
||||
(
|
||||
AssetUtility.GetUISpriteAsset(assetName),
|
||||
Constant.AssetPriority.UIFormAsset,
|
||||
new LoadAssetCallbacks(
|
||||
(resourcePath, asset, duration, userData) =>
|
||||
AddPendingCallback(assetName, callback);
|
||||
}
|
||||
|
||||
if (_loadingSpriteAssets.Contains(assetName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_loadingSpriteAssets.Add(assetName);
|
||||
StartLoadSprite(assetName);
|
||||
}
|
||||
|
||||
public void PreloadSprite(string assetName)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (string.IsNullOrWhiteSpace(assetName) || _spriteCache.ContainsKey(assetName) ||
|
||||
_loadingSpriteAssets.Contains(assetName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_loadingSpriteAssets.Add(assetName);
|
||||
StartLoadSprite(assetName);
|
||||
}
|
||||
|
||||
public void PreloadSprites(IEnumerable<string> assetNames)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (assetNames == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string assetName in assetNames)
|
||||
{
|
||||
PreloadSprite(assetName);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterSprite(string assetName, Sprite sprite, bool overwrite = true)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (string.IsNullOrWhiteSpace(assetName) || sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!overwrite && _spriteCache.ContainsKey(assetName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_spriteCache[assetName] = sprite;
|
||||
InvokeAndClearPendingCallbacks(assetName, sprite);
|
||||
_loadingSpriteAssets.Remove(assetName);
|
||||
}
|
||||
|
||||
private void StartLoadSprite(string assetName)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (_resource == null)
|
||||
{
|
||||
InvokeAndClearPendingCallbacks(assetName, null);
|
||||
_loadingSpriteAssets.Remove(assetName);
|
||||
return;
|
||||
}
|
||||
|
||||
_resource.LoadAsset
|
||||
(
|
||||
AssetUtility.GetUISpriteAsset(assetName),
|
||||
Constant.AssetPriority.UIFormAsset,
|
||||
new LoadAssetCallbacks(
|
||||
(resourcePath, asset, duration, userData) =>
|
||||
{
|
||||
Texture2D texture = asset as Texture2D;
|
||||
Sprite loadedSprite = null;
|
||||
if (texture != null)
|
||||
{
|
||||
Log.Debug(resourcePath);
|
||||
Texture2D texture = asset as Texture2D;
|
||||
if (texture != null)
|
||||
{
|
||||
Sprite newSprite = Sprite.Create(
|
||||
texture,
|
||||
new Rect(0, 0, texture.width, texture.height),
|
||||
_defaultPivot,
|
||||
_pixelsPerUnit);
|
||||
_spriteCache.Add(assetName, newSprite);
|
||||
callback?.Invoke(newSprite);
|
||||
}
|
||||
},
|
||||
(resourcePath, status, errorMessage, userData) =>
|
||||
{
|
||||
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
|
||||
assetName,
|
||||
resourcePath,
|
||||
errorMessage);
|
||||
loadedSprite = Sprite.Create(
|
||||
texture,
|
||||
new Rect(0, 0, texture.width, texture.height),
|
||||
_defaultPivot,
|
||||
_pixelsPerUnit);
|
||||
_spriteCache[assetName] = loadedSprite;
|
||||
}
|
||||
)
|
||||
);
|
||||
else
|
||||
{
|
||||
Log.Warning("Load sprite succeeded but asset is not Texture2D. AssetName='{0}', Path='{1}'.",
|
||||
assetName, resourcePath);
|
||||
}
|
||||
|
||||
InvokeAndClearPendingCallbacks(assetName, loadedSprite);
|
||||
_loadingSpriteAssets.Remove(assetName);
|
||||
},
|
||||
(resourcePath, status, errorMessage, userData) =>
|
||||
{
|
||||
Log.Error("Can not load icon '{0}' from '{1}' with error message '{2}'.",
|
||||
assetName,
|
||||
resourcePath,
|
||||
errorMessage);
|
||||
InvokeAndClearPendingCallbacks(assetName, null);
|
||||
_loadingSpriteAssets.Remove(assetName);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void AddPendingCallback(string assetName, Action<Sprite> callback)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (!_pendingCallbacks.TryGetValue(assetName, out List<Action<Sprite>> callbackList))
|
||||
{
|
||||
callbackList = new List<Action<Sprite>>();
|
||||
_pendingCallbacks.Add(assetName, callbackList);
|
||||
}
|
||||
|
||||
callbackList.Add(callback);
|
||||
}
|
||||
|
||||
private void InvokeAndClearPendingCallbacks(string assetName, Sprite sprite)
|
||||
{
|
||||
EnsureRuntimeCaches();
|
||||
if (!_pendingCallbacks.TryGetValue(assetName, out List<Action<Sprite>> callbackList))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingCallbacks.Remove(assetName);
|
||||
for (int i = 0; i < callbackList.Count; i++)
|
||||
{
|
||||
callbackList[i]?.Invoke(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_spriteCache.Clear();
|
||||
_spriteCache?.Clear();
|
||||
_pendingCallbacks?.Clear();
|
||||
_loadingSpriteAssets?.Clear();
|
||||
_resource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,7 @@ namespace Definition.Enum
|
||||
None,
|
||||
Shake,
|
||||
Blink,
|
||||
EntityCue,
|
||||
EntityCueClose,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Definition.Enum
|
||||
{
|
||||
public enum ExpressionType
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
Shock,
|
||||
Angry,
|
||||
}
|
||||
}
|
||||
@@ -59,5 +59,9 @@ namespace UI
|
||||
MainOverlayForm = 107,
|
||||
|
||||
AIChatForm = 108,
|
||||
|
||||
ChapterTitleForm = 109,
|
||||
|
||||
AIChatEntryForm = 110,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7c4b051e63f0c2418a55e8817bf4ba9
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026151
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58efe0589d7e90f498c8bcbbf94c6be4
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026151
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,62 +0,0 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity.EntityData
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class EntityDataBase
|
||||
{
|
||||
[SerializeField]
|
||||
private int m_Id = 0;
|
||||
|
||||
[SerializeField]
|
||||
private int m_TypeId = 0;
|
||||
|
||||
[SerializeField]
|
||||
private Vector3 m_Position = Vector3.zero;
|
||||
|
||||
[SerializeField]
|
||||
private Quaternion m_Rotation = Quaternion.identity;
|
||||
|
||||
public EntityDataBase(int entityId, int typeId)
|
||||
{
|
||||
m_Id = entityId;
|
||||
m_TypeId = typeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实体编号。
|
||||
/// </summary>
|
||||
public int Id => m_Id;
|
||||
|
||||
/// <summary>
|
||||
/// 实体类型编号。
|
||||
/// </summary>
|
||||
public int TypeId => m_TypeId;
|
||||
|
||||
/// <summary>
|
||||
/// 实体位置。
|
||||
/// </summary>
|
||||
public Vector3 Position
|
||||
{
|
||||
get => m_Position;
|
||||
set => m_Position = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实体朝向。
|
||||
/// </summary>
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get => m_Rotation;
|
||||
set => m_Rotation = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.DataTable;
|
||||
using System;
|
||||
using CustomUtility;
|
||||
using DataTable;
|
||||
using Entity;
|
||||
using Entity.EntityData;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public static class EntityExtension
|
||||
{
|
||||
private static int s_SerialId = 0;
|
||||
|
||||
public static EntityBase GetGameEntity(this EntityComponent entityComponent, int entityId)
|
||||
{
|
||||
UnityGameFramework.Runtime.Entity entity = entityComponent.GetEntity(entityId);
|
||||
if (entity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (EntityBase)entity.Logic;
|
||||
}
|
||||
|
||||
public static void HideEntity(this EntityComponent entityComponent, EntityBase entity)
|
||||
{
|
||||
entityComponent.HideEntity(entity.Entity);
|
||||
}
|
||||
|
||||
public static void AttachEntity(this EntityComponent entityComponent, EntityBase entityBase, int ownerId,
|
||||
string parentTransformPath = null, object userData = null)
|
||||
{
|
||||
entityComponent.AttachEntity(entityBase.Entity, ownerId, parentTransformPath, userData);
|
||||
}
|
||||
|
||||
private static void ShowEntity(this EntityComponent entityComponent, Type logicType, string entityGroup,
|
||||
int priority, EntityDataBase data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Log.Warning("Data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
IDataTable<DREntity> dtEntity = GameEntry.DataTable.GetDataTable<DREntity>();
|
||||
DREntity drEntity = dtEntity.GetDataRow(data.TypeId);
|
||||
if (drEntity == null)
|
||||
{
|
||||
Log.Warning("Can not load entity id '{0}' from data table.", data.TypeId.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
entityComponent.ShowEntity(data.Id, logicType, AssetUtility.GetEntityAsset(drEntity.AssetName), entityGroup,
|
||||
priority, data);
|
||||
}
|
||||
|
||||
public static int GenerateSerialId(this EntityComponent entityComponent)
|
||||
{
|
||||
return --s_SerialId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 626632079980ad34595c3b43a062a664
|
||||
timeCreated: 1528026153
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74fbdf043ed3bc141a0a0b09756bc7d2
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026153
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,122 +0,0 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using Entity.EntityData;
|
||||
using GameFramework;
|
||||
using StarForce;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public abstract class EntityBase : EntityLogic
|
||||
{
|
||||
[SerializeField] private EntityDataBase _entityData = null;
|
||||
|
||||
public int Id => Entity.Id;
|
||||
|
||||
public Animation CachedAnimation
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnInit(object userData)
|
||||
#else
|
||||
protected internal override void OnInit(object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnInit(userData);
|
||||
CachedAnimation = GetComponent<Animation>();
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnRecycle()
|
||||
#else
|
||||
protected internal override void OnRecycle()
|
||||
#endif
|
||||
{
|
||||
base.OnRecycle();
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnShow(object userData)
|
||||
#else
|
||||
protected internal override void OnShow(object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
_entityData = userData as EntityDataBase;
|
||||
if (_entityData == null)
|
||||
{
|
||||
Log.Error("Entity data is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
Name = Utility.Text.Format("[Entity {0}]", Id);
|
||||
CachedTransform.localPosition = _entityData.Position;
|
||||
CachedTransform.localRotation = _entityData.Rotation;
|
||||
CachedTransform.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
#else
|
||||
protected internal override void OnHide(bool isShutdown, object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
|
||||
#else
|
||||
protected internal override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnAttached(childEntity, parentTransform, userData);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnDetached(EntityLogic childEntity, object userData)
|
||||
#else
|
||||
protected internal override void OnDetached(EntityLogic childEntity, object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnDetached(childEntity, userData);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
|
||||
#else
|
||||
protected internal override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnAttachTo(parentEntity, parentTransform, userData);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
|
||||
#else
|
||||
protected internal override void OnDetachFrom(EntityLogic parentEntity, object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnDetachFrom(parentEntity, userData);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
#else
|
||||
protected internal override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
#endif
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 878fbbe82b4bde944812bcb7c964cff8
|
||||
timeCreated: 1528026154
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
|
||||
namespace Event
|
||||
{
|
||||
public class ChapterTitleCompletedEventArgs : GameEventArgs
|
||||
{
|
||||
public static readonly int EventId = typeof(ChapterTitleCompletedEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public int ChapterId { get; private set; }
|
||||
public int RequestId { get; private set; }
|
||||
|
||||
public ChapterTitleCompletedEventArgs()
|
||||
{
|
||||
ChapterId = 0;
|
||||
RequestId = 0;
|
||||
}
|
||||
|
||||
public static ChapterTitleCompletedEventArgs Create(int chapterId, int requestId)
|
||||
{
|
||||
ChapterTitleCompletedEventArgs args = ReferencePool.Acquire<ChapterTitleCompletedEventArgs>();
|
||||
args.ChapterId = chapterId;
|
||||
args.RequestId = requestId;
|
||||
return args;
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
ChapterId = 0;
|
||||
RequestId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9baf591df06b9bb44ba5313457ba84b3
|
||||
guid: 2de1f87831fa4a81ad93a2f5277cfaa2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -23,7 +23,6 @@ namespace Procedure
|
||||
{
|
||||
public static readonly string[] DataTableNames = new string[]
|
||||
{
|
||||
"Entity",
|
||||
"BGM",
|
||||
"Scene",
|
||||
"UIForm",
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Procedure
|
||||
protected override void OnEnter(ProcedureOwner procedureOwner)
|
||||
{
|
||||
base.OnEnter(procedureOwner);
|
||||
AIChatEntryRuntime.EnsureOpen();
|
||||
|
||||
GameEntry.Dialog.Init(1);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using GameFramework.Fsm;
|
||||
using GameFramework.Procedure;
|
||||
using UnityEngine;
|
||||
using UI;
|
||||
|
||||
namespace Procedure
|
||||
{
|
||||
@@ -13,6 +14,7 @@ namespace Procedure
|
||||
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
|
||||
{
|
||||
base.OnEnter(procedureOwner);
|
||||
AIChatEntryRuntime.EnsureOpen();
|
||||
|
||||
GameEntry.Dialog.Init(1);
|
||||
GameEntry.Dialog.StartDialog(1001);
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace Procedure
|
||||
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
|
||||
{
|
||||
base.OnEnter(procedureOwner);
|
||||
AIChatEntryRuntime.EnsureOpen();
|
||||
|
||||
var e = GameEntry.Event;
|
||||
e.Subscribe(MenuStartEventArgs.EventId, MenuStart);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ff69ece58f4d3f4e8cf9a40f813e745
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026159
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,30 +0,0 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public class HideByBoundary : MonoBehaviour
|
||||
{
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
GameObject go = other.gameObject;
|
||||
EntityBase entity = go.GetComponent<EntityBase>();
|
||||
if (entity == null)
|
||||
{
|
||||
Log.Warning("Unknown GameObject '{0}', you must use entity only.", go.name);
|
||||
Destroy(go);
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Entity.HideEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 649621152579e224a955822793b63aca
|
||||
timeCreated: 1528026159
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,7 +1,6 @@
|
||||
using CustomUtility;
|
||||
using DataTable;
|
||||
using Definition;
|
||||
using Entity;
|
||||
using GameFramework.DataTable;
|
||||
using GameFramework.Sound;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace UI
|
||||
{
|
||||
public static class AIChatEntryRuntime
|
||||
{
|
||||
private static AIChatEntryFormController s_entryController;
|
||||
private static AIChatEntryFormContext s_entryContext;
|
||||
|
||||
public static void EnsureOpen()
|
||||
{
|
||||
if (GameEntry.UI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_entryController == null)
|
||||
{
|
||||
s_entryController = new AIChatEntryFormController();
|
||||
}
|
||||
|
||||
if (s_entryContext == null)
|
||||
{
|
||||
s_entryContext = new AIChatEntryFormContext();
|
||||
}
|
||||
|
||||
s_entryController.OpenUI(s_entryContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f754cc5f339dcfa4aaec3aa32260c4b8
|
||||
guid: 8f5f8c4efc7e47a8b0354c7f89fef0b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace UI
|
||||
{
|
||||
public sealed class AIChatEntryFormContext : UIContext
|
||||
{
|
||||
public AIChatEntryFormController Controller;
|
||||
public bool IsChatOpened;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 174e046d822a14d49843dc415067f13c
|
||||
guid: 0b5fd3ce4a3e4e7a8ed2ab9f9472e0a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace UI
|
||||
{
|
||||
public class ChapterTitleFormContext : UIContext
|
||||
{
|
||||
public int ChapterId = 0;
|
||||
public int RequestId = 0;
|
||||
public string Title = string.Empty;
|
||||
public string Subtitle = string.Empty;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afb91fe3ed8105e4a96b7bd5917ecc2b
|
||||
timeCreated: 1528026152
|
||||
licenseType: Pro
|
||||
guid: d2f2414b0ec34d2cb7a64c46c18cb48c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
@@ -0,0 +1,188 @@
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public sealed class AIChatEntryFormController : IFormController<AIChatEntryFormContext>
|
||||
{
|
||||
private AIChatEntryFormContext _entryContext;
|
||||
private AIChatEntryForm _entryForm;
|
||||
private int? _entryFormSerialId;
|
||||
private bool _pendingEntryRefresh;
|
||||
|
||||
private AIChatFormController _chatFormController;
|
||||
private AIChatFormContext _chatContext;
|
||||
|
||||
public AIChatEntryFormController()
|
||||
{
|
||||
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
|
||||
GameEntry.Event.Subscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
|
||||
}
|
||||
|
||||
~AIChatEntryFormController()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OnOpenUIFormSuccess);
|
||||
GameEntry.Event.Unsubscribe(CloseUIFormCompleteEventArgs.EventId, OnCloseUIFormComplete);
|
||||
}
|
||||
|
||||
public int? OpenUI(AIChatEntryFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("AIChatEntryFormController open failed. context is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
context.Controller = this;
|
||||
_entryContext = context;
|
||||
SyncEntryContextState();
|
||||
|
||||
if (_entryForm != null)
|
||||
{
|
||||
_entryForm.RefreshUI(_entryContext);
|
||||
return _entryFormSerialId;
|
||||
}
|
||||
|
||||
UGuiForm openedForm = GameEntry.UI.GetUIForm(UIFormId.AIChatEntryForm);
|
||||
if (openedForm is AIChatEntryForm existingForm)
|
||||
{
|
||||
_entryForm = existingForm;
|
||||
_entryFormSerialId = existingForm.UIForm.SerialId;
|
||||
_entryForm.RefreshUI(_entryContext);
|
||||
return _entryFormSerialId;
|
||||
}
|
||||
|
||||
_pendingEntryRefresh = true;
|
||||
_entryFormSerialId = GameEntry.UI.OpenUIForm(UIFormId.AIChatEntryForm, _entryContext);
|
||||
return _entryFormSerialId;
|
||||
}
|
||||
|
||||
public void CloseUI()
|
||||
{
|
||||
_pendingEntryRefresh = false;
|
||||
|
||||
if (_entryFormSerialId.HasValue)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(_entryFormSerialId.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryForm != null)
|
||||
{
|
||||
_entryForm.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleAIChatForm()
|
||||
{
|
||||
UGuiForm openedChatForm = GameEntry.UI.GetUIForm(UIFormId.AIChatForm);
|
||||
if (openedChatForm != null)
|
||||
{
|
||||
if (_chatFormController != null)
|
||||
{
|
||||
_chatFormController.CloseUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(openedChatForm.UIForm);
|
||||
}
|
||||
|
||||
RefreshEntryState();
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureChatContext();
|
||||
_chatContext.Controller = _chatFormController;
|
||||
_chatFormController.OpenUI(_chatContext);
|
||||
RefreshEntryState();
|
||||
}
|
||||
|
||||
private void EnsureChatContext()
|
||||
{
|
||||
if (_chatFormController == null)
|
||||
{
|
||||
_chatFormController = new AIChatFormController();
|
||||
}
|
||||
|
||||
if (_chatContext == null)
|
||||
{
|
||||
_chatContext = new AIChatFormContext
|
||||
{
|
||||
ClearHistoryOnOpen = false,
|
||||
LanguageMode = 0,
|
||||
Messages = new List<AIChatMessageContext>()
|
||||
};
|
||||
}
|
||||
|
||||
_chatContext.Controller = _chatFormController;
|
||||
_chatContext.Messages ??= new List<AIChatMessageContext>();
|
||||
}
|
||||
|
||||
private void SyncEntryContextState()
|
||||
{
|
||||
if (_entryContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_entryContext.IsChatOpened = GameEntry.UI.GetUIForm(UIFormId.AIChatForm) != null;
|
||||
}
|
||||
|
||||
private void RefreshEntryState()
|
||||
{
|
||||
SyncEntryContextState();
|
||||
|
||||
if (_entryForm == null || _entryContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_entryForm.SetChatOpened(_entryContext.IsChatOpened);
|
||||
}
|
||||
|
||||
private void OnOpenUIFormSuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is OpenUIFormSuccessEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entryFormSerialId.HasValue &&
|
||||
args.UIForm != null &&
|
||||
args.UIForm.SerialId == _entryFormSerialId.Value &&
|
||||
args.UserData == _entryContext)
|
||||
{
|
||||
_entryForm = args.UIForm.Logic as AIChatEntryForm;
|
||||
if (_entryForm == null)
|
||||
{
|
||||
Log.Warning("AIChatEntryFormController open success but form logic is invalid.");
|
||||
}
|
||||
else if (_pendingEntryRefresh)
|
||||
{
|
||||
_entryForm.RefreshUI(_entryContext);
|
||||
_pendingEntryRefresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshEntryState();
|
||||
}
|
||||
|
||||
private void OnCloseUIFormComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is CloseUIFormCompleteEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.SerialId == _entryFormSerialId)
|
||||
{
|
||||
_entryForm = null;
|
||||
_entryFormSerialId = null;
|
||||
_pendingEntryRefresh = false;
|
||||
}
|
||||
|
||||
RefreshEntryState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f9ad5dc60834e1cb1aa5d0f04d2756c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -17,6 +17,7 @@ namespace UI
|
||||
private static TMP_FontAsset _mainTMPFont = null;
|
||||
private Canvas _cachedCanvas = null;
|
||||
private CanvasGroup _canvasGroup = null;
|
||||
private Coroutine _canvasFadeCoroutine = null;
|
||||
private List<Canvas> _cachedCanvasContainer = new List<Canvas>();
|
||||
|
||||
public int OriginalDepth { get; private set; }
|
||||
@@ -30,7 +31,7 @@ namespace UI
|
||||
|
||||
public void Close(bool ignoreFade)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StopCanvasFadeCoroutine();
|
||||
|
||||
if (ignoreFade)
|
||||
{
|
||||
@@ -38,7 +39,7 @@ namespace UI
|
||||
}
|
||||
else
|
||||
{
|
||||
StartCoroutine(CloseCo(FadeTime));
|
||||
_canvasFadeCoroutine = StartCoroutine(CloseCo(FadeTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +137,7 @@ namespace UI
|
||||
base.OnOpen(userData);
|
||||
|
||||
_canvasGroup.alpha = 0f;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(_canvasGroup.FadeToAlpha(1f, FadeTime));
|
||||
PlayCanvasFade(1f, FadeTime);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
@@ -146,6 +146,7 @@ namespace UI
|
||||
protected internal override void OnClose(bool isShutdown, object userData)
|
||||
#endif
|
||||
{
|
||||
StopCanvasFadeCoroutine();
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
@@ -167,8 +168,7 @@ namespace UI
|
||||
base.OnResume();
|
||||
|
||||
_canvasGroup.alpha = 0f;
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(_canvasGroup.FadeToAlpha(1f, FadeTime));
|
||||
PlayCanvasFade(1f, FadeTime);
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
@@ -229,7 +229,31 @@ namespace UI
|
||||
private IEnumerator CloseCo(float duration)
|
||||
{
|
||||
yield return _canvasGroup.FadeToAlpha(0f, duration);
|
||||
_canvasFadeCoroutine = null;
|
||||
GameEntry.UI.CloseUIForm(this);
|
||||
}
|
||||
|
||||
private void PlayCanvasFade(float alpha, float duration)
|
||||
{
|
||||
StopCanvasFadeCoroutine();
|
||||
_canvasFadeCoroutine = StartCoroutine(FadeCanvasCo(alpha, duration));
|
||||
}
|
||||
|
||||
private IEnumerator FadeCanvasCo(float alpha, float duration)
|
||||
{
|
||||
yield return _canvasGroup.FadeToAlpha(alpha, duration);
|
||||
_canvasFadeCoroutine = null;
|
||||
}
|
||||
|
||||
private void StopCanvasFadeCoroutine()
|
||||
{
|
||||
if (_canvasFadeCoroutine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopCoroutine(_canvasFadeCoroutine);
|
||||
_canvasFadeCoroutine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public sealed class AIChatEntryForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private Button _entryButton;
|
||||
[SerializeField] private TMP_Text _entryLabel;
|
||||
[SerializeField] private string _openLabel = "问 AI";
|
||||
[SerializeField] private string _closeLabel = "关闭 AI";
|
||||
|
||||
private AIChatEntryFormController _controller;
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
BindEntryButton();
|
||||
|
||||
if (!(userData is AIChatEntryFormContext context))
|
||||
{
|
||||
Log.Error("AIChatEntryFormContext is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
UnbindEntryButton();
|
||||
_controller = null;
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
public void RefreshUI(AIChatEntryFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_controller = context.Controller;
|
||||
SetChatOpened(context.IsChatOpened);
|
||||
}
|
||||
|
||||
public void SetChatOpened(bool isChatOpened)
|
||||
{
|
||||
if (_entryLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_entryLabel.text = isChatOpened ? _closeLabel : _openLabel;
|
||||
}
|
||||
|
||||
public void OnEntryButtonClick()
|
||||
{
|
||||
_controller?.ToggleAIChatForm();
|
||||
}
|
||||
|
||||
private void BindEntryButton()
|
||||
{
|
||||
if (_entryButton == null)
|
||||
{
|
||||
Log.Warning("AIChatEntryForm entry button is not assigned.");
|
||||
return;
|
||||
}
|
||||
|
||||
_entryButton.onClick.RemoveListener(OnEntryButtonClick);
|
||||
_entryButton.onClick.AddListener(OnEntryButtonClick);
|
||||
}
|
||||
|
||||
private void UnbindEntryButton()
|
||||
{
|
||||
if (_entryButton == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_entryButton.onClick.RemoveListener(OnEntryButtonClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17d9f89cf8b5472e8f167d90f9d2621c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -57,6 +57,7 @@ namespace UI
|
||||
}
|
||||
|
||||
int requestId = context.TransitionRequestId;
|
||||
bool shouldFadeTransition = _currentSprite != null;
|
||||
|
||||
if (context.Sprite != null)
|
||||
{
|
||||
@@ -87,7 +88,7 @@ namespace UI
|
||||
}
|
||||
|
||||
int version = ++_refreshVersion;
|
||||
FadeToBlack(() =>
|
||||
TweenCallback loadAndApplySprite = () =>
|
||||
{
|
||||
if (version != _refreshVersion)
|
||||
{
|
||||
@@ -115,9 +116,25 @@ namespace UI
|
||||
Log.Warning("BgForm refresh failed. Can not load sprite '{0}'.", targetAssetName);
|
||||
}
|
||||
|
||||
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
|
||||
if (shouldFadeTransition)
|
||||
{
|
||||
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
|
||||
}
|
||||
else
|
||||
{
|
||||
PublishTransitionCompleted(requestId);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (shouldFadeTransition)
|
||||
{
|
||||
FadeToBlack(loadAndApplySprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadAndApplySprite.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshWithSprite(Sprite sprite, string backgroundAssetName, int requestId)
|
||||
@@ -137,8 +154,9 @@ namespace UI
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldFadeTransition = _currentSprite != null;
|
||||
int version = ++_refreshVersion;
|
||||
FadeToBlack(() =>
|
||||
TweenCallback applySprite = () =>
|
||||
{
|
||||
if (version != _refreshVersion)
|
||||
{
|
||||
@@ -153,8 +171,25 @@ namespace UI
|
||||
{
|
||||
_bgImage.sprite = sprite;
|
||||
}
|
||||
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
|
||||
});
|
||||
|
||||
if (shouldFadeTransition)
|
||||
{
|
||||
FadeFromBlack(() => { PublishTransitionCompleted(requestId); });
|
||||
}
|
||||
else
|
||||
{
|
||||
PublishTransitionCompleted(requestId);
|
||||
}
|
||||
};
|
||||
|
||||
if (shouldFadeTransition)
|
||||
{
|
||||
FadeToBlack(applySprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
applySprite.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private void FadeToBlack(TweenCallback onComplete)
|
||||
|
||||
@@ -4,6 +4,7 @@ using DG.Tweening;
|
||||
using Definition.Enum;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
@@ -19,9 +20,9 @@ namespace UI
|
||||
|
||||
[SerializeField] private TMP_Text _contentText;
|
||||
|
||||
[SerializeField] private Image _leftSprite;
|
||||
[FormerlySerializedAs("_leftSprite")] [SerializeField] private Image _leftImage;
|
||||
|
||||
[SerializeField] private Image _rightSprite;
|
||||
[FormerlySerializedAs("_rightSprite")] [SerializeField] private Image _rightImage;
|
||||
|
||||
[SerializeField] private int _leftSpritePosition = 450;
|
||||
|
||||
@@ -35,8 +36,6 @@ namespace UI
|
||||
|
||||
[SerializeField] private GameObject _speakerNameArea;
|
||||
|
||||
private readonly int _singleSpeakerCenterPosition = Screen.width / 2;
|
||||
|
||||
private string _leftSpeakerToken = string.Empty;
|
||||
private string _rightSpeakerToken = string.Empty;
|
||||
private Sequence _layoutSequence;
|
||||
@@ -95,6 +94,7 @@ namespace UI
|
||||
}
|
||||
|
||||
string speakerName = _context.SpeakerName;
|
||||
bool isHumanSpeaker = IsHumanSpeaker(_context.SpeakerId);
|
||||
|
||||
if (_speakerArea != null)
|
||||
{
|
||||
@@ -103,7 +103,7 @@ namespace UI
|
||||
|
||||
if (_speakerNameArea != null)
|
||||
{
|
||||
_speakerNameArea.SetActive(!string.IsNullOrEmpty(speakerName) && IsHumanSpeaker(_context.SpeakerId));
|
||||
_speakerNameArea.SetActive(!string.IsNullOrEmpty(speakerName) && isHumanSpeaker);
|
||||
}
|
||||
|
||||
if (_speakerNameText != null)
|
||||
@@ -112,6 +112,7 @@ namespace UI
|
||||
}
|
||||
|
||||
PlayTypewriter(_contentText, _context.Text, _currentPlayingSpeed);
|
||||
MainOverlayForm.DispatchCue(_context.Emphasis);
|
||||
|
||||
if (string.IsNullOrEmpty(speakerName))
|
||||
{
|
||||
@@ -120,6 +121,13 @@ namespace UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isHumanSpeaker)
|
||||
{
|
||||
ClearSpeakerState();
|
||||
ApplySpeakerLayout(false, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isRightSpeaker = context.Direction > 0;
|
||||
if (isRightSpeaker)
|
||||
{
|
||||
@@ -133,21 +141,35 @@ namespace UI
|
||||
bool hasLeftSpeaker = !string.IsNullOrEmpty(_leftSpeakerToken);
|
||||
bool hasRightSpeaker = !string.IsNullOrEmpty(_rightSpeakerToken);
|
||||
|
||||
UpdateSpeakerPortrait(isRightSpeaker ? _rightSprite : _leftSprite, _context.SpeakerId,
|
||||
UpdateSpeakerPortrait(isRightSpeaker ? _rightImage : _leftImage, _context.SpeakerId,
|
||||
_context.CurrentLineId, isRightSpeaker);
|
||||
ApplySpeakerLayout(hasLeftSpeaker, hasRightSpeaker, false);
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
ResetSpeakerVisualState();
|
||||
base.OnOpen(userData);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
ClearSpeakerState();
|
||||
KillLayoutTween();
|
||||
ApplySpeakerLayout(false, false, true);
|
||||
ResetSpeakerVisualState();
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void ClearSpeakerState()
|
||||
{
|
||||
if (_leftImage != null)
|
||||
{
|
||||
_leftImage.sprite = null;
|
||||
}
|
||||
|
||||
if (_rightImage != null)
|
||||
{
|
||||
_rightImage.sprite = null;
|
||||
}
|
||||
|
||||
_leftSpeakerToken = string.Empty;
|
||||
_rightSpeakerToken = string.Empty;
|
||||
_leftPortraitRequestVersion++;
|
||||
@@ -161,17 +183,27 @@ namespace UI
|
||||
_layoutSequence.Kill();
|
||||
_layoutSequence = null;
|
||||
}
|
||||
|
||||
if (_leftImage != null && _leftImage.rectTransform != null)
|
||||
{
|
||||
_leftImage.rectTransform.DOKill();
|
||||
}
|
||||
|
||||
if (_rightImage != null && _rightImage.rectTransform != null)
|
||||
{
|
||||
_rightImage.rectTransform.DOKill();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySpeakerLayout(bool hasLeftSpeaker, bool hasRightSpeaker, bool instant)
|
||||
{
|
||||
if (_leftSprite == null || _rightSprite == null)
|
||||
if (_leftImage == null || _rightImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool leftCurrentlyVisible = _leftSprite.gameObject.activeSelf;
|
||||
bool rightCurrentlyVisible = _rightSprite.gameObject.activeSelf;
|
||||
bool leftCurrentlyVisible = _leftImage.gameObject.activeSelf;
|
||||
bool rightCurrentlyVisible = _rightImage.gameObject.activeSelf;
|
||||
|
||||
bool leftTargetVisible = hasLeftSpeaker;
|
||||
bool rightTargetVisible = hasRightSpeaker;
|
||||
@@ -191,22 +223,22 @@ namespace UI
|
||||
|
||||
if (instant || _moveDuration <= 0f)
|
||||
{
|
||||
SetSpritePosition(_leftSprite.rectTransform, leftTargetX);
|
||||
SetSpritePosition(_rightSprite.rectTransform, rightTargetX);
|
||||
SetSpriteVisible(_leftSprite, leftTargetVisible);
|
||||
SetSpriteVisible(_rightSprite, rightTargetVisible);
|
||||
SetSpritePosition(_leftImage.rectTransform, leftTargetX);
|
||||
SetSpritePosition(_rightImage.rectTransform, rightTargetX);
|
||||
SetSpriteVisible(_leftImage, leftTargetVisible);
|
||||
SetSpriteVisible(_rightImage, rightTargetVisible);
|
||||
return;
|
||||
}
|
||||
|
||||
_layoutSequence = DOTween.Sequence();
|
||||
|
||||
Tween leftTween = CreateMoveTween(_leftSprite.rectTransform, leftTargetX);
|
||||
Tween leftTween = CreateMoveTween(_leftImage.rectTransform, leftTargetX);
|
||||
if (leftTween != null)
|
||||
{
|
||||
_layoutSequence.Join(leftTween);
|
||||
}
|
||||
|
||||
Tween rightTween = CreateMoveTween(_rightSprite.rectTransform, rightTargetX);
|
||||
Tween rightTween = CreateMoveTween(_rightImage.rectTransform, rightTargetX);
|
||||
if (rightTween != null)
|
||||
{
|
||||
_layoutSequence.Join(rightTween);
|
||||
@@ -216,17 +248,17 @@ namespace UI
|
||||
{
|
||||
_layoutSequence.OnComplete(() =>
|
||||
{
|
||||
SetSpriteVisible(_leftSprite, leftTargetVisible);
|
||||
SetSpriteVisible(_rightSprite, rightTargetVisible);
|
||||
SetSpriteVisible(_leftImage, leftTargetVisible);
|
||||
SetSpriteVisible(_rightImage, rightTargetVisible);
|
||||
_layoutSequence = null;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSpritePosition(_leftSprite.rectTransform, leftTargetX);
|
||||
SetSpritePosition(_rightSprite.rectTransform, rightTargetX);
|
||||
SetSpriteVisible(_leftSprite, leftTargetVisible);
|
||||
SetSpriteVisible(_rightSprite, rightTargetVisible);
|
||||
SetSpritePosition(_leftImage.rectTransform, leftTargetX);
|
||||
SetSpritePosition(_rightImage.rectTransform, rightTargetX);
|
||||
SetSpriteVisible(_leftImage, leftTargetVisible);
|
||||
SetSpriteVisible(_rightImage, rightTargetVisible);
|
||||
_layoutSequence.Kill();
|
||||
_layoutSequence = null;
|
||||
}
|
||||
@@ -243,25 +275,25 @@ namespace UI
|
||||
if (leftTargetVisible && !leftCurrentlyVisible)
|
||||
{
|
||||
float leftStartX = GetAppearStartX(true, rightCurrentlyVisible, hasLeftSpeaker, hasRightSpeaker);
|
||||
SetSpritePosition(_leftSprite.rectTransform, leftStartX);
|
||||
SetSpriteVisible(_leftSprite, true);
|
||||
SetSpritePosition(_leftImage.rectTransform, leftStartX);
|
||||
SetSpriteVisible(_leftImage, true);
|
||||
}
|
||||
|
||||
if (rightTargetVisible && !rightCurrentlyVisible)
|
||||
{
|
||||
float rightStartX = GetAppearStartX(false, leftCurrentlyVisible, hasLeftSpeaker, hasRightSpeaker);
|
||||
SetSpritePosition(_rightSprite.rectTransform, rightStartX);
|
||||
SetSpriteVisible(_rightSprite, true);
|
||||
SetSpritePosition(_rightImage.rectTransform, rightStartX);
|
||||
SetSpriteVisible(_rightImage, true);
|
||||
}
|
||||
|
||||
if (leftCurrentlyVisible && !leftTargetVisible)
|
||||
{
|
||||
SetSpriteVisible(_leftSprite, true);
|
||||
SetSpriteVisible(_leftImage, true);
|
||||
}
|
||||
|
||||
if (rightCurrentlyVisible && !rightTargetVisible)
|
||||
{
|
||||
SetSpriteVisible(_rightSprite, true);
|
||||
SetSpriteVisible(_rightImage, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +305,7 @@ namespace UI
|
||||
// single -> multi: hidden side starts from center, then both move to side positions.
|
||||
if (otherCurrentlyVisible)
|
||||
{
|
||||
return _singleSpeakerCenterPosition;
|
||||
return GetSingleSpeakerCenterPosition(isLeft);
|
||||
}
|
||||
|
||||
return isLeft ? _leftSpritePosition : _rightSpritePosition;
|
||||
@@ -293,7 +325,7 @@ namespace UI
|
||||
if (hasLeftSpeaker || hasRightSpeaker)
|
||||
{
|
||||
// multi -> single: both move to center first, then inactive side hides.
|
||||
return _singleSpeakerCenterPosition;
|
||||
return GetSingleSpeakerCenterPosition(isLeft);
|
||||
}
|
||||
|
||||
return isLeft ? _leftSpritePosition : _rightSpritePosition;
|
||||
@@ -350,7 +382,8 @@ namespace UI
|
||||
return;
|
||||
}
|
||||
|
||||
string portraitAssetName = $"Characters/{speakerId}-{ResolvePortraitIndex(speakerId, lineId)}.png";
|
||||
int portraitCount = ResolveAvailablePortraitCount(speakerId);
|
||||
string portraitAssetName = $"Characters/{speakerId}-{ResolvePortraitIndex(speakerId, lineId, portraitCount)}.png";
|
||||
if (GameEntry.SpriteCache == null)
|
||||
{
|
||||
return;
|
||||
@@ -360,32 +393,33 @@ namespace UI
|
||||
{
|
||||
if (isRightSpeaker)
|
||||
{
|
||||
if (requestVersion != _rightPortraitRequestVersion || _rightSprite == null)
|
||||
if (requestVersion != _rightPortraitRequestVersion || _rightImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
_rightSprite.sprite = sprite;
|
||||
_rightImage.sprite = sprite;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestVersion != _leftPortraitRequestVersion || _leftSprite == null)
|
||||
if (requestVersion != _leftPortraitRequestVersion || _leftImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
_leftSprite.sprite = sprite;
|
||||
_leftImage.sprite = sprite;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static int ResolvePortraitIndex(string speakerId, int lineId)
|
||||
private static int ResolvePortraitIndex(string speakerId, int lineId, int portraitCount)
|
||||
{
|
||||
int safePortraitCount = portraitCount > 0 ? portraitCount : 3;
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
@@ -393,16 +427,26 @@ namespace UI
|
||||
hash = hash * 31 + (string.IsNullOrEmpty(speakerId)
|
||||
? 0
|
||||
: System.StringComparer.OrdinalIgnoreCase.GetHashCode(speakerId));
|
||||
int mod = hash % 3;
|
||||
int mod = hash % safePortraitCount;
|
||||
if (mod < 0)
|
||||
{
|
||||
mod += 3;
|
||||
mod += safePortraitCount;
|
||||
}
|
||||
|
||||
return mod + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int ResolveAvailablePortraitCount(string speakerId)
|
||||
{
|
||||
if (GameEntry.Dialog == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GameEntry.Dialog.GetSpeakerPortraitCount(speakerId);
|
||||
}
|
||||
|
||||
private static bool IsHumanSpeaker(string speakerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(speakerId))
|
||||
@@ -412,5 +456,37 @@ namespace UI
|
||||
|
||||
return !NonPortraitSpeakerIds.Contains(speakerId);
|
||||
}
|
||||
|
||||
private void ResetSpeakerVisualState()
|
||||
{
|
||||
KillLayoutTween();
|
||||
ClearSpeakerState();
|
||||
ApplySpeakerLayout(false, false, true);
|
||||
}
|
||||
|
||||
private float GetSingleSpeakerCenterPosition(bool isLeft)
|
||||
{
|
||||
RectTransform rectTransform = isLeft
|
||||
? _leftImage != null ? _leftImage.rectTransform : null
|
||||
: _rightImage != null ? _rightImage.rectTransform : null;
|
||||
return GetCenterAnchoredX(rectTransform);
|
||||
}
|
||||
|
||||
private static float GetCenterAnchoredX(RectTransform rectTransform)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (!(rectTransform.parent is RectTransform parent))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float anchorX = rectTransform.anchorMin.x;
|
||||
return (0.5f - anchorX) * parent.rect.width;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
using System.Collections;
|
||||
using Event;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class ChapterTitleForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private CanvasGroup _contentGroup;
|
||||
[SerializeField] private Image _dimImage;
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
[SerializeField] private TMP_Text _subtitleText;
|
||||
[SerializeField] private float _fadeInDuration = 0.3f;
|
||||
[SerializeField] private float _holdDuration = 1.2f;
|
||||
[SerializeField] private float _fadeOutDuration = 0.3f;
|
||||
|
||||
private Coroutine _playCoroutine;
|
||||
private int _requestId;
|
||||
private int _chapterId;
|
||||
private bool _hasPublishedCompletion;
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
EnsureView();
|
||||
ApplyContext(userData as ChapterTitleFormContext);
|
||||
StartPlay();
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
StopPlay();
|
||||
SetContentAlpha(0f);
|
||||
if (!isShutdown)
|
||||
{
|
||||
PublishCompletionIfNeeded();
|
||||
}
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void EnsureView()
|
||||
{
|
||||
RectTransform rootRect = transform as RectTransform;
|
||||
if (rootRect == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_contentGroup == null)
|
||||
{
|
||||
_contentGroup = CreateContentGroup(rootRect);
|
||||
}
|
||||
|
||||
RectTransform contentRect = _contentGroup != null && _contentGroup.transform is RectTransform contentTransform
|
||||
? contentTransform
|
||||
: rootRect;
|
||||
|
||||
if (_dimImage == null)
|
||||
{
|
||||
_dimImage = CreateImage("Dim", contentRect, new Color(0f, 0f, 0f, 0.72f), true);
|
||||
}
|
||||
|
||||
if (_titleText == null)
|
||||
{
|
||||
_titleText = CreateText("Title", contentRect, 108f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
RectTransform titleRect = _titleText.rectTransform;
|
||||
titleRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
titleRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
titleRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
titleRect.sizeDelta = new Vector2(2200f, 220f);
|
||||
titleRect.anchoredPosition = new Vector2(0f, 40f);
|
||||
}
|
||||
|
||||
if (_subtitleText == null)
|
||||
{
|
||||
_subtitleText = CreateText("Subtitle", contentRect, 42f, FontStyles.Normal, TextAlignmentOptions.Center);
|
||||
RectTransform subtitleRect = _subtitleText.rectTransform;
|
||||
subtitleRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
subtitleRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
subtitleRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
subtitleRect.sizeDelta = new Vector2(2200f, 120f);
|
||||
subtitleRect.anchoredPosition = new Vector2(0f, -60f);
|
||||
}
|
||||
|
||||
if (_dimImage != null)
|
||||
{
|
||||
_dimImage.transform.SetAsFirstSibling();
|
||||
}
|
||||
|
||||
if (_subtitleText != null)
|
||||
{
|
||||
_subtitleText.transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
if (_titleText != null)
|
||||
{
|
||||
_titleText.transform.SetAsLastSibling();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyContext(ChapterTitleFormContext context)
|
||||
{
|
||||
_hasPublishedCompletion = false;
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
_requestId = 0;
|
||||
_chapterId = 0;
|
||||
SetTitleText(string.Empty, string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
_requestId = context.RequestId;
|
||||
_chapterId = context.ChapterId;
|
||||
|
||||
string title = string.IsNullOrWhiteSpace(context.Title)
|
||||
? BuildDefaultChapterTitle(_chapterId)
|
||||
: context.Title.Trim();
|
||||
string subtitle = string.IsNullOrWhiteSpace(context.Subtitle) ? string.Empty : context.Subtitle.Trim();
|
||||
SetTitleText(title, subtitle);
|
||||
}
|
||||
|
||||
private void SetTitleText(string title, string subtitle)
|
||||
{
|
||||
if (_titleText != null)
|
||||
{
|
||||
_titleText.text = title;
|
||||
}
|
||||
|
||||
if (_subtitleText != null)
|
||||
{
|
||||
_subtitleText.text = subtitle;
|
||||
_subtitleText.gameObject.SetActive(!string.IsNullOrEmpty(subtitle));
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPlay()
|
||||
{
|
||||
StopPlay();
|
||||
_playCoroutine = StartCoroutine(PlayRoutine());
|
||||
}
|
||||
|
||||
private void StopPlay()
|
||||
{
|
||||
if (_playCoroutine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopCoroutine(_playCoroutine);
|
||||
_playCoroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator PlayRoutine()
|
||||
{
|
||||
SetContentAlpha(0f);
|
||||
yield return FadeAlpha(0f, 1f, _fadeInDuration);
|
||||
yield return WaitUnscaled(_holdDuration);
|
||||
yield return FadeAlpha(1f, 0f, _fadeOutDuration);
|
||||
_playCoroutine = null;
|
||||
|
||||
PublishCompletionIfNeeded();
|
||||
if (GameEntry.UI != null && UIForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(UIForm);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator FadeAlpha(float from, float to, float duration)
|
||||
{
|
||||
float safeDuration = Mathf.Max(0f, duration);
|
||||
if (safeDuration <= 0f)
|
||||
{
|
||||
SetContentAlpha(to);
|
||||
yield break;
|
||||
}
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < safeDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / safeDuration);
|
||||
SetContentAlpha(Mathf.Lerp(from, to, t));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetContentAlpha(to);
|
||||
}
|
||||
|
||||
private static IEnumerator WaitUnscaled(float duration)
|
||||
{
|
||||
float safeDuration = Mathf.Max(0f, duration);
|
||||
if (safeDuration <= 0f)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < safeDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetContentAlpha(float alpha)
|
||||
{
|
||||
if (_contentGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_contentGroup.alpha = Mathf.Clamp01(alpha);
|
||||
}
|
||||
|
||||
private void PublishCompletionIfNeeded()
|
||||
{
|
||||
if (_hasPublishedCompletion || _requestId <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hasPublishedCompletion = true;
|
||||
GameEntry.Event.Fire(this, ChapterTitleCompletedEventArgs.Create(_chapterId, _requestId));
|
||||
}
|
||||
|
||||
private static string BuildDefaultChapterTitle(int chapterId)
|
||||
{
|
||||
return chapterId > 0 ? $"第{chapterId}章" : string.Empty;
|
||||
}
|
||||
|
||||
private static CanvasGroup CreateContentGroup(RectTransform parent)
|
||||
{
|
||||
GameObject go = new GameObject("ContentRoot", typeof(RectTransform), typeof(CanvasGroup));
|
||||
RectTransform rect = go.GetComponent<RectTransform>();
|
||||
rect.SetParent(parent, false);
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
return go.GetComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
private static Image CreateImage(string name, RectTransform parent, Color color, bool raycastTarget)
|
||||
{
|
||||
GameObject go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
RectTransform rect = go.GetComponent<RectTransform>();
|
||||
rect.SetParent(parent, false);
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
|
||||
Image image = go.GetComponent<Image>();
|
||||
image.color = color;
|
||||
image.raycastTarget = raycastTarget;
|
||||
return image;
|
||||
}
|
||||
|
||||
private static TMP_Text CreateText(string name, RectTransform parent, float fontSize, FontStyles style,
|
||||
TextAlignmentOptions alignment)
|
||||
{
|
||||
GameObject go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
||||
RectTransform rect = go.GetComponent<RectTransform>();
|
||||
rect.SetParent(parent, false);
|
||||
|
||||
TextMeshProUGUI text = go.GetComponent<TextMeshProUGUI>();
|
||||
if (TMP_Settings.defaultFontAsset != null)
|
||||
{
|
||||
text.font = TMP_Settings.defaultFontAsset;
|
||||
}
|
||||
|
||||
text.text = string.Empty;
|
||||
text.fontSize = fontSize;
|
||||
text.fontStyle = style;
|
||||
text.alignment = alignment;
|
||||
text.color = new Color(0.96f, 0.94f, 0.88f, 1f);
|
||||
text.raycastTarget = false;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bb48f2d6a2e4df6a8360f0f84c1f58a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,268 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Definition.Enum;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class MainOverlayForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private Image _cueImage;
|
||||
[SerializeField] private List<Sprite> _cueBindings = new List<Sprite>();
|
||||
[SerializeField] private float _showDuration = 0.2f;
|
||||
[SerializeField] private float _hideDuration = 0.2f;
|
||||
|
||||
private static int _dispatchCount;
|
||||
private Coroutine _transitionCoroutine;
|
||||
private bool _isClosingByCue;
|
||||
private Vector3 _baseScale = Vector3.one;
|
||||
private bool _baseScaleInitialized;
|
||||
|
||||
public static void DispatchCue(EmphasisType emphasis)
|
||||
{
|
||||
if (GameEntry.UI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UGuiForm existingForm = GameEntry.UI.GetUIForm(UIFormId.MainOverlayForm);
|
||||
if (emphasis == EmphasisType.EntityCueClose)
|
||||
{
|
||||
if (existingForm is MainOverlayForm overlay)
|
||||
{
|
||||
overlay.PlayHideAndClose();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (emphasis != EmphasisType.EntityCue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingForm is MainOverlayForm overlayForm)
|
||||
{
|
||||
overlayForm.ShowNextCue();
|
||||
return;
|
||||
}
|
||||
|
||||
int? formSerialId = GameEntry.UI.OpenUIForm(UIFormId.MainOverlayForm);
|
||||
if (!formSerialId.HasValue)
|
||||
{
|
||||
Log.Warning("MainOverlayForm open failed. Check UIForm.txt config and prefab asset.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
EnsureBaseScaleInitialized();
|
||||
ShowNextCue();
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
StopTransition();
|
||||
HideCueImage(clearSprite: true);
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void ShowNextCue()
|
||||
{
|
||||
if (_cueImage == null)
|
||||
{
|
||||
Log.Warning("MainOverlayForm cue image is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cueBindings == null || _cueBindings.Count == 0)
|
||||
{
|
||||
HideCueImage(clearSprite: false);
|
||||
Log.Warning("MainOverlayForm cue bindings are empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = _dispatchCount % _cueBindings.Count;
|
||||
_dispatchCount++;
|
||||
|
||||
Sprite sprite = _cueBindings[index];
|
||||
if (sprite == null)
|
||||
{
|
||||
HideCueImage(clearSprite: false);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureBaseScaleInitialized();
|
||||
StopTransition();
|
||||
_transitionCoroutine = StartCoroutine(PlayShowRoutine(sprite));
|
||||
}
|
||||
|
||||
private void PlayHideAndClose()
|
||||
{
|
||||
if (_isClosingByCue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cueImage == null)
|
||||
{
|
||||
if (GameEntry.UI != null && UIForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(UIForm);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
StopTransition();
|
||||
_transitionCoroutine = StartCoroutine(PlayHideAndCloseRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator PlayShowRoutine(Sprite sprite)
|
||||
{
|
||||
_cueImage.sprite = sprite;
|
||||
_cueImage.SetNativeSize();
|
||||
_cueImage.gameObject.SetActive(true);
|
||||
|
||||
SetCueVisual(0f, 0f);
|
||||
yield return PlayLerp(0f, 1f, 0f, 0.6f, _showDuration);
|
||||
_transitionCoroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator PlayHideAndCloseRoutine()
|
||||
{
|
||||
_isClosingByCue = true;
|
||||
float fromAlpha = _cueImage.color.a;
|
||||
float fromScale = GetCurrentScale01();
|
||||
yield return PlayLerp(fromAlpha, 0f, fromScale, 0f, _hideDuration);
|
||||
HideCueImage(clearSprite: true);
|
||||
_isClosingByCue = false;
|
||||
_transitionCoroutine = null;
|
||||
|
||||
if (GameEntry.UI != null && UIForm != null)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(UIForm);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator PlayLerp(float fromAlpha, float toAlpha, float fromScale, float toScale, float duration)
|
||||
{
|
||||
if (_cueImage == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
float safeDuration = Mathf.Max(0f, duration);
|
||||
if (safeDuration <= 0f)
|
||||
{
|
||||
SetCueVisual(toAlpha, toScale);
|
||||
yield break;
|
||||
}
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < safeDuration)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / safeDuration);
|
||||
float alpha = Mathf.Lerp(fromAlpha, toAlpha, t);
|
||||
float scale = Mathf.Lerp(fromScale, toScale, t);
|
||||
SetCueVisual(alpha, scale);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetCueVisual(toAlpha, toScale);
|
||||
}
|
||||
|
||||
private void SetCueVisual(float alpha, float scale01)
|
||||
{
|
||||
if (_cueImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = _cueImage.color;
|
||||
color.a = Mathf.Clamp01(alpha);
|
||||
_cueImage.color = color;
|
||||
|
||||
RectTransform rectTransform = _cueImage.rectTransform;
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float safeScale = Mathf.Clamp01(scale01);
|
||||
rectTransform.localScale = _baseScale * safeScale;
|
||||
}
|
||||
|
||||
private float GetCurrentScale01()
|
||||
{
|
||||
if (_cueImage == null)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
EnsureBaseScaleInitialized();
|
||||
RectTransform rectTransform = _cueImage.rectTransform;
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
float ratioX = Mathf.Abs(_baseScale.x) > 0.0001f ? rectTransform.localScale.x / _baseScale.x : 1f;
|
||||
float ratioY = Mathf.Abs(_baseScale.y) > 0.0001f ? rectTransform.localScale.y / _baseScale.y : 1f;
|
||||
return Mathf.Clamp01((ratioX + ratioY) * 0.5f);
|
||||
}
|
||||
|
||||
private void EnsureBaseScaleInitialized()
|
||||
{
|
||||
if (_baseScaleInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cueImage == null || _cueImage.rectTransform == null)
|
||||
{
|
||||
_baseScale = Vector3.one;
|
||||
_baseScaleInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_baseScale = _cueImage.rectTransform.localScale;
|
||||
if (_baseScale == Vector3.zero)
|
||||
{
|
||||
_baseScale = Vector3.one;
|
||||
}
|
||||
|
||||
_baseScaleInitialized = true;
|
||||
}
|
||||
|
||||
private void StopTransition()
|
||||
{
|
||||
if (_transitionCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_transitionCoroutine);
|
||||
_transitionCoroutine = null;
|
||||
}
|
||||
|
||||
_isClosingByCue = false;
|
||||
}
|
||||
|
||||
private void HideCueImage(bool clearSprite)
|
||||
{
|
||||
if (_cueImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_cueImage.gameObject.SetActive(false);
|
||||
SetCueVisual(0f, 0f);
|
||||
if (clearSprite)
|
||||
{
|
||||
_cueImage.sprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4e1c3f77c2743b7a1d7d2f44f5eb9aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user