fixes #8
This commit is contained in:
parent
4ea0f58bc7
commit
4456431f25
22 changed files with 334 additions and 127 deletions
|
@ -15,11 +15,11 @@ MonoBehaviour:
|
||||||
addTransition: 1
|
addTransition: 1
|
||||||
propertyName: opacity
|
propertyName: opacity
|
||||||
duration:
|
duration:
|
||||||
duration: 0.3
|
duration: 1
|
||||||
unit: 2
|
unit: 2
|
||||||
easing: 3
|
easing: 3
|
||||||
delay:
|
delay:
|
||||||
duration: 0
|
duration: 1
|
||||||
unit: 2
|
unit: 2
|
||||||
from: 0
|
from: 0
|
||||||
to: 1
|
to: 1
|
||||||
|
|
25
Assets/SOAP/UI/Fade Out.asset
Normal file
25
Assets/SOAP/UI/Fade Out.asset
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 94cc50e7b5f20a817aef3384d0889279, type: 3}
|
||||||
|
m_Name: Fade Out
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
addTransition: 1
|
||||||
|
propertyName: opacity
|
||||||
|
duration:
|
||||||
|
duration: 1
|
||||||
|
unit: 2
|
||||||
|
easing: 3
|
||||||
|
delay:
|
||||||
|
duration: 1
|
||||||
|
unit: 2
|
||||||
|
from: 1
|
||||||
|
to: 0
|
8
Assets/SOAP/UI/Fade Out.asset.meta
Normal file
8
Assets/SOAP/UI/Fade Out.asset.meta
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fd3397ffea6cbccc5992ef2e70a2ed44
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -14,7 +14,9 @@ MonoBehaviour:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
visualTreeAsset: {fileID: 9197481963319205126, guid: bc70bdaaf37609283aac14435c5441a2, type: 3}
|
visualTreeAsset: {fileID: 9197481963319205126, guid: bc70bdaaf37609283aac14435c5441a2, type: 3}
|
||||||
effects:
|
effects:
|
||||||
- timing: 0
|
- uiEvent: 0
|
||||||
effect: {fileID: 11400000, guid: 0902dc21238be2bb3ae758e1d5218922, type: 2}
|
effect: {fileID: 11400000, guid: 0902dc21238be2bb3ae758e1d5218922, type: 2}
|
||||||
|
- uiEvent: 1
|
||||||
|
effect: {fileID: 11400000, guid: fd3397ffea6cbccc5992ef2e70a2ed44, type: 2}
|
||||||
titleId: title
|
titleId: title
|
||||||
contentId: content
|
contentId: content
|
||||||
|
|
|
@ -922,6 +922,9 @@ MonoBehaviour:
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
oneTimeUse: 0
|
oneTimeUse: 0
|
||||||
|
onTarget:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
onInteracted:
|
onInteracted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
@ -1211,7 +1214,6 @@ MonoBehaviour:
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
camera: {fileID: 330585546}
|
camera: {fileID: 330585546}
|
||||||
registerChildren: 1
|
|
||||||
--- !u!114 &1281046470
|
--- !u!114 &1281046470
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
@ -1227,6 +1229,9 @@ MonoBehaviour:
|
||||||
orchestrator: {fileID: 11400000, guid: 994ab04db2c2004abb8272830cb16b26, type: 2}
|
orchestrator: {fileID: 11400000, guid: 994ab04db2c2004abb8272830cb16b26, type: 2}
|
||||||
root: {fileID: 1281046468}
|
root: {fileID: 1281046468}
|
||||||
panelSettings: {fileID: 11400000, guid: 2bc58aab5867867e5b0feeae2df42fd0, type: 2}
|
panelSettings: {fileID: 11400000, guid: 2bc58aab5867867e5b0feeae2df42fd0, type: 2}
|
||||||
|
despawnTimeout:
|
||||||
|
duration: 2
|
||||||
|
unit: 2
|
||||||
prefab: {fileID: 0}
|
prefab: {fileID: 0}
|
||||||
--- !u!1 &1363717994
|
--- !u!1 &1363717994
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -2323,6 +2328,34 @@ PrefabInstance:
|
||||||
propertyPath: oneTimeUse
|
propertyPath: oneTimeUse
|
||||||
value: 1
|
value: 1
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.size
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Mode
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Target
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 1740748263}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_CallState
|
||||||
|
value: 2
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_MethodName
|
||||||
|
value: Toggle
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName
|
||||||
|
value: KitsuneCafe.UI.TestThing, Assembly-CSharp
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
|
propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName
|
||||||
|
value: UnityEngine.Object, UnityEngine
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 2368094401510200604, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
- target: {fileID: 2368094401510200604, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
value: -2
|
value: -2
|
||||||
|
|
19
Assets/Scripts/Extension/VisualElement.cs
Normal file
19
Assets/Scripts/Extension/VisualElement.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Threading;
|
||||||
|
using R3;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace KitsuneCafe.Extension
|
||||||
|
{
|
||||||
|
public static class VisualElementExtensions
|
||||||
|
{
|
||||||
|
public static Observable<T> ObserveEvent<T>(this VisualElement target, CancellationToken token = default) where T : EventBase<T>, new()
|
||||||
|
{
|
||||||
|
return Observable.FromEvent<EventCallback<T>, T>(
|
||||||
|
h => e => h(e),
|
||||||
|
e => target.RegisterCallback(e),
|
||||||
|
e => target.UnregisterCallback(e),
|
||||||
|
token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
Assets/Scripts/Extension/VisualElement.cs.meta
Normal file
2
Assets/Scripts/Extension/VisualElement.cs.meta
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aaf4cd39f81ac6dd8b5949c0f4a2f7ef
|
|
@ -80,6 +80,8 @@ namespace KitsuneCafe.Interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInteractable { get; }
|
bool IsInteractable { get; }
|
||||||
|
|
||||||
|
virtual void Target(IInteractor interactor) { }
|
||||||
IResult<Unit, InteractionError> Interact(IInteractor interactor);
|
IResult<Unit, InteractionError> Interact(IInteractor interactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,9 @@ namespace KitsuneCafe.Interaction
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private bool oneTimeUse = false;
|
private bool oneTimeUse = false;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private UnityEvent<IInteractor> onTarget = default;
|
||||||
|
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private UnityEvent<IInteractor> onInteracted = default;
|
private UnityEvent<IInteractor> onInteracted = default;
|
||||||
|
|
||||||
|
@ -16,6 +19,12 @@ namespace KitsuneCafe.Interaction
|
||||||
|
|
||||||
public bool IsInteractable => isInteractable;
|
public bool IsInteractable => isInteractable;
|
||||||
|
|
||||||
|
|
||||||
|
void IInteractable.Target(IInteractor interactor)
|
||||||
|
{
|
||||||
|
onTarget.Invoke(interactor);
|
||||||
|
}
|
||||||
|
|
||||||
public IResult<Unit, InteractionError> Interact(IInteractor interactor)
|
public IResult<Unit, InteractionError> Interact(IInteractor interactor)
|
||||||
{
|
{
|
||||||
onInteracted.Invoke(interactor);
|
onInteracted.Invoke(interactor);
|
||||||
|
|
|
@ -169,6 +169,8 @@ namespace KitsuneCafe.Player
|
||||||
{
|
{
|
||||||
if (interactable == null) { return; }
|
if (interactable == null) { return; }
|
||||||
|
|
||||||
|
interactable.Target(this);
|
||||||
|
|
||||||
if (!interactable.TryGetComponent(out Outline outline))
|
if (!interactable.TryGetComponent(out Outline outline))
|
||||||
{
|
{
|
||||||
outline = interactable.gameObject.AddComponent<Outline>();
|
outline = interactable.gameObject.AddComponent<Outline>();
|
||||||
|
|
|
@ -8,9 +8,6 @@ namespace KitsuneCafe.Rendering
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private new Transform camera;
|
private new Transform camera;
|
||||||
|
|
||||||
[SerializeField]
|
|
||||||
private bool registerChildren = false;
|
|
||||||
|
|
||||||
private readonly List<Transform> transforms = new();
|
private readonly List<Transform> transforms = new();
|
||||||
|
|
||||||
private void Reset()
|
private void Reset()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
namespace KitsuneCafe.System
|
namespace KitsuneCafe.System
|
||||||
{
|
{
|
||||||
|
@ -26,8 +27,10 @@ namespace KitsuneCafe.System
|
||||||
public Duration(long ticks) : this(ticks, TimeUnit.Ticks) { }
|
public Duration(long ticks) : this(ticks, TimeUnit.Ticks) { }
|
||||||
|
|
||||||
public static implicit operator TimeSpan(Duration duration) => duration.AsTimeSpan();
|
public static implicit operator TimeSpan(Duration duration) => duration.AsTimeSpan();
|
||||||
|
public static implicit operator TimeValue(Duration duration) => duration.AsTimeValue();
|
||||||
|
|
||||||
public readonly TimeSpan AsTimeSpan() => new(Value);
|
public readonly TimeSpan AsTimeSpan() => new(Value);
|
||||||
|
public readonly TimeValue AsTimeValue() => new(Into(TimeUnit.Seconds));
|
||||||
|
|
||||||
public static Duration From(long value, TimeUnit unit)
|
public static Duration From(long value, TimeUnit unit)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,10 +15,7 @@ namespace KitsuneCafe.System
|
||||||
|
|
||||||
|
|
||||||
public static implicit operator Duration(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
public static implicit operator Duration(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||||
public static implicit operator TimeValue(SerializableDuration d)
|
public static implicit operator TimeSpan(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||||
{
|
public static implicit operator TimeValue(SerializableDuration d) => Duration.From(d.duration, d.unit);
|
||||||
Duration duration = d;
|
|
||||||
return new TimeValue(duration.Into(TimeUnit.Seconds));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace KitsuneCafe.UI
|
||||||
return new StyleList<T>(new List<T> { value });
|
return new StyleList<T>(new List<T> { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<R3.Unit> Execute(VisualElement target, CancellationToken token)
|
public Observable<Unit> Execute(VisualElement target, CancellationToken token)
|
||||||
{
|
{
|
||||||
target.style.transitionProperty = ToStyleList(PropertyName);
|
target.style.transitionProperty = ToStyleList(PropertyName);
|
||||||
target.style.transitionDuration = ToStyleList(Duration);
|
target.style.transitionDuration = ToStyleList(Duration);
|
||||||
|
|
|
@ -4,6 +4,8 @@ using KitsuneCafe.System.Attributes;
|
||||||
using R3;
|
using R3;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
|
using KitsuneCafe.Extension;
|
||||||
|
using Unit = R3.Unit;
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
namespace KitsuneCafe.UI
|
||||||
{
|
{
|
||||||
|
@ -55,25 +57,35 @@ namespace KitsuneCafe.UI
|
||||||
To = to;
|
To = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoTransition(VisualElement target)
|
private Observable<Unit> ObserveGeometryChange(VisualElement target, CancellationToken token)
|
||||||
{
|
{
|
||||||
target.style.opacity = From;
|
return target.ObserveEvent<GeometryChangedEvent>(token)
|
||||||
target.style.opacity = To;
|
.OnErrorResumeAsFailure()
|
||||||
|
.AsUnitObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Observable<Unit> Defer(VisualElement target, CancellationToken token)
|
||||||
|
{
|
||||||
|
return Observable.Merge(
|
||||||
|
ObserveGeometryChange(target, token),
|
||||||
|
Observable.EveryUpdate(UnityFrameProvider.PostLateUpdate, token)
|
||||||
|
)
|
||||||
|
.Where(_ => target.resolvedStyle.width > 0 && target.resolvedStyle.height > 0)
|
||||||
|
.Take(1);
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<R3.Unit> Execute(VisualElement target, CancellationToken token)
|
public Observable<Unit> Execute(VisualElement target, CancellationToken token)
|
||||||
{
|
{
|
||||||
target.style.opacity = From;
|
target.style.opacity = From;
|
||||||
|
|
||||||
var to = To;
|
var to = To;
|
||||||
return Observable.NextFrame(cancellationToken: token)
|
return Defer(target, token)
|
||||||
.Do(_ =>
|
.Do(_ => target.style.opacity = to)
|
||||||
{
|
.Select(_ => target.ObserveEvent<TransitionEndEvent>())
|
||||||
target.style.opacity = to;
|
.Switch()
|
||||||
})
|
.Where(evt => evt.stylePropertyNames.Contains("opacity"))
|
||||||
.AsUnitObservable();
|
.Take(1)
|
||||||
|
.TakeUntil(token)
|
||||||
|
.AsUnitObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
143
Assets/Scripts/UI/Orchestration/UiElementInstance.cs
Normal file
143
Assets/Scripts/UI/Orchestration/UiElementInstance.cs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using KitsuneCafe.System;
|
||||||
|
using R3;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Unit = R3.Unit;
|
||||||
|
|
||||||
|
namespace KitsuneCafe.UI
|
||||||
|
{
|
||||||
|
public class UiElementInstance : MonoBehaviour, IDisposable
|
||||||
|
{
|
||||||
|
public UIDocument Document;
|
||||||
|
public PooledObject PooledObject;
|
||||||
|
|
||||||
|
private IUiElement uiElement;
|
||||||
|
private VisualElement visualElement;
|
||||||
|
|
||||||
|
private DisposableBag activeEffects;
|
||||||
|
private IDisposable instance;
|
||||||
|
|
||||||
|
private CancellationTokenSource cts;
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initial configuration of the UIDocument. Called by UISceneManager on spawn/reuse.
|
||||||
|
/// </summary>
|
||||||
|
public void Configure(IUiElement element, PanelSettings settings)
|
||||||
|
{
|
||||||
|
uiElement = element;
|
||||||
|
|
||||||
|
cts = new();
|
||||||
|
|
||||||
|
Document.visualTreeAsset = element.VisualTreeAsset;
|
||||||
|
Document.panelSettings = settings;
|
||||||
|
|
||||||
|
instance = ConfigureWhenReady(element)
|
||||||
|
.Subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal configuration of the VisualElement. Called when rootVisualElement is ready.
|
||||||
|
/// </summary>
|
||||||
|
private void Configure(IUiElement uiElement, VisualElement visualElement)
|
||||||
|
{
|
||||||
|
this.visualElement = visualElement;
|
||||||
|
|
||||||
|
uiElement.Configure(visualElement);
|
||||||
|
|
||||||
|
Raise(UiEvent.OnCreate, cts.Token)
|
||||||
|
.Subscribe()
|
||||||
|
.AddTo(ref activeEffects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the UIDocument's rootVisualElement to be ready.
|
||||||
|
/// </summary>
|
||||||
|
private Observable<Unit> ConfigureWhenReady(IUiElement element)
|
||||||
|
{
|
||||||
|
return Observable.EveryValueChanged(Document, d => d.rootVisualElement)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Take(1)
|
||||||
|
.TakeUntil(cts.Token)
|
||||||
|
.Do(root => Configure(element, root))
|
||||||
|
.AsUnitObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises effects for a given UI event, returning an Observable that completes when all effects are done.
|
||||||
|
/// </summary>
|
||||||
|
private Observable<Unit> Raise(UiEvent evt, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var effects = uiElement.GetEffectsFor(evt).ToArray();
|
||||||
|
var obs = new List<Observable<Unit>>(effects.Length);
|
||||||
|
|
||||||
|
foreach (var effect in effects)
|
||||||
|
{
|
||||||
|
if (effect != null)
|
||||||
|
{
|
||||||
|
obs.Add(effect
|
||||||
|
.Instantiate()
|
||||||
|
.Execute(visualElement, token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Observable.Merge(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forcefully disposes of all active effects and instance lifecycle subscriptions.
|
||||||
|
/// This is for internal cleanup. It does NOT release the object to the pool.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
cts?.Cancel();
|
||||||
|
cts?.Dispose();
|
||||||
|
cts = null;
|
||||||
|
|
||||||
|
// activeEffects.Dispose();
|
||||||
|
activeEffects.Clear();
|
||||||
|
|
||||||
|
instance?.Dispose();
|
||||||
|
|
||||||
|
uiElement = default;
|
||||||
|
visualElement = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initiates the despawn process: runs OnDestroy effects, waits for them to finish,
|
||||||
|
/// then disposes everything and releases the object back to the pool.
|
||||||
|
/// </summary>
|
||||||
|
public Observable<Unit> Despawn(CancellationTokenSource cts = default)
|
||||||
|
{
|
||||||
|
cts ??= new CancellationTokenSource();
|
||||||
|
|
||||||
|
return Raise(UiEvent.OnDestroy, cts.Token)
|
||||||
|
.Do(onCompleted: _ =>
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
PooledObject.Release();
|
||||||
|
},
|
||||||
|
onErrorResume: _ => cts.Cancel())
|
||||||
|
.TakeUntil(cts.Token)
|
||||||
|
.AsUnitObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately disposes of effects and releases the object to the pool,
|
||||||
|
/// without awaiting OnDestroy effects.
|
||||||
|
/// </summary>
|
||||||
|
public void DespawnNow()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
PooledObject.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
using UnityEngine.Pool;
|
using UnityEngine.Pool;
|
||||||
using KitsuneCafe.System;
|
using KitsuneCafe.System;
|
||||||
using KitsuneCafe.Extension;
|
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
namespace KitsuneCafe.UI
|
||||||
{
|
{
|
||||||
|
@ -15,6 +14,7 @@ namespace KitsuneCafe.UI
|
||||||
|
|
||||||
[SerializeField] private Transform root;
|
[SerializeField] private Transform root;
|
||||||
[SerializeField] private PanelSettings panelSettings;
|
[SerializeField] private PanelSettings panelSettings;
|
||||||
|
[SerializeField] private SerializableDuration despawnTimeout;
|
||||||
[SerializeField] private GameObject prefab;
|
[SerializeField] private GameObject prefab;
|
||||||
|
|
||||||
private readonly Dictionary<ElementId, GameObject> activeElements = new();
|
private readonly Dictionary<ElementId, GameObject> activeElements = new();
|
||||||
|
@ -74,17 +74,24 @@ namespace KitsuneCafe.UI
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
obj = new GameObject("Pooled Object", typeof(UIDocument));
|
obj = new GameObject(
|
||||||
|
"Pooled Object",
|
||||||
|
typeof(UIDocument),
|
||||||
|
typeof(PooledObject),
|
||||||
|
typeof(UiElementInstance)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.AddComponent<UiElementInstance>();
|
|
||||||
|
|
||||||
var doc = obj.GetComponent<UIDocument>();
|
var doc = obj.GetComponent<UIDocument>();
|
||||||
doc.panelSettings = panelSettings;
|
doc.panelSettings = panelSettings;
|
||||||
|
|
||||||
var poolObj = obj.AddComponent<PooledObject>();
|
var poolObj = obj.GetComponent<PooledObject>();
|
||||||
poolObj.objectPool = pool;
|
poolObj.objectPool = pool;
|
||||||
|
|
||||||
|
var instance = obj.GetComponent<UiElementInstance>();
|
||||||
|
instance.Document = doc;
|
||||||
|
instance.PooledObject = poolObj;
|
||||||
|
|
||||||
obj.transform.SetParent(root);
|
obj.transform.SetParent(root);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -110,47 +117,41 @@ namespace KitsuneCafe.UI
|
||||||
return GetOrCreatePool(element).Get();
|
return GetOrCreatePool(element).Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<T> ObserveEvent<T>(VisualElement target) where T : EventBase<T>, new()
|
|
||||||
{
|
|
||||||
return Observable.FromEvent<EventCallback<T>, T>(
|
|
||||||
h => e => h(e),
|
|
||||||
e => target.RegisterCallback(e),
|
|
||||||
e => target.UnregisterCallback(e)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SpawnElement(ElementId id, IUiElement element, Vector3 worldPosition)
|
private void SpawnElement(ElementId id, IUiElement element, Vector3 worldPosition)
|
||||||
{
|
{
|
||||||
var obj = GetUiDocument(element);
|
var obj = GetUiDocument(element);
|
||||||
|
activeElements[id] = obj;
|
||||||
|
|
||||||
var trans = obj.transform;
|
var trans = obj.transform;
|
||||||
trans.SetParent(root);
|
trans.SetParent(root);
|
||||||
trans.SetPositionAndRotation(worldPosition, Quaternion.identity);
|
trans.SetPositionAndRotation(worldPosition, Quaternion.identity);
|
||||||
trans.localScale = Vector3.one;
|
trans.localScale = Vector3.one;
|
||||||
|
|
||||||
if (obj.TryGetComponent(out UIDocument doc) && obj.TryGetComponent(out UiElementInstance instance))
|
if (obj.TryGetComponent(out UiElementInstance instance))
|
||||||
{
|
{
|
||||||
doc.visualTreeAsset = element.VisualTreeAsset;
|
instance.Configure(element, panelSettings);
|
||||||
doc.panelSettings = panelSettings;
|
|
||||||
Observable.EveryValueChanged(doc, d => d.rootVisualElement)
|
|
||||||
.WhereNotNull()
|
|
||||||
.Select(_ => doc)
|
|
||||||
.Subscribe(doc =>
|
|
||||||
{
|
|
||||||
instance.Configure(element, doc.rootVisualElement);
|
|
||||||
})
|
|
||||||
.AddTo(doc.gameObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activeElements[id] = obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: track which pool each active element belongs to?
|
||||||
private void DespawnElement(ElementId id)
|
private void DespawnElement(ElementId id)
|
||||||
{
|
{
|
||||||
if (activeElements.TryGetValue(id, out GameObject obj))
|
if (activeElements.TryGetValue(id, out GameObject obj))
|
||||||
{
|
{
|
||||||
obj.GetComponent<PooledObject>().Release();
|
if (obj.TryGetComponent<UiElementInstance>(out var instance))
|
||||||
activeElements.Remove(id);
|
{
|
||||||
|
instance.Despawn()
|
||||||
|
.Race(Observable.Timer(despawnTimeout).Do(_ => instance.DespawnNow()))
|
||||||
|
.Do(onCompleted: _ => activeElements.Remove(id))
|
||||||
|
.Subscribe()
|
||||||
|
.AddTo(obj);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (obj.TryGetComponent(out PooledObject poolObj))
|
||||||
|
{
|
||||||
|
poolObj.Release();
|
||||||
|
activeElements.Remove(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,32 @@ namespace KitsuneCafe.UI
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private ModalElement modal;
|
private ModalElement modal;
|
||||||
|
|
||||||
private void Start()
|
private ElementId id;
|
||||||
|
|
||||||
|
public void Open()
|
||||||
{
|
{
|
||||||
orchestrator.SpawnElement(modal.Create("test", "fuck !!!! :3"), transform.position + Vector3.up);
|
id = orchestrator.SpawnElement(modal.Create("<3", "fuck !!!! :3"), transform.position + (Vector3.up * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
if (id != default)
|
||||||
|
{
|
||||||
|
orchestrator.DespawnElement(id);
|
||||||
|
id = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Toggle()
|
||||||
|
{
|
||||||
|
if (id != default)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Open();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
using System;
|
|
||||||
using R3;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UIElements;
|
|
||||||
|
|
||||||
namespace KitsuneCafe.UI
|
|
||||||
{
|
|
||||||
public class UiElementInstance : MonoBehaviour, IDisposable
|
|
||||||
{
|
|
||||||
private IUiElement uiElement;
|
|
||||||
private VisualElement visualElement;
|
|
||||||
|
|
||||||
private readonly CompositeDisposable activeEffects = new();
|
|
||||||
|
|
||||||
private bool configured = false;
|
|
||||||
|
|
||||||
public void Configure(IUiElement uiElement, VisualElement visualElement)
|
|
||||||
{
|
|
||||||
configured = true;
|
|
||||||
this.uiElement = uiElement;
|
|
||||||
this.visualElement = visualElement;
|
|
||||||
|
|
||||||
activeEffects.Clear();
|
|
||||||
|
|
||||||
uiElement.Configure(visualElement);
|
|
||||||
|
|
||||||
Raise(UiEvent.OnCreate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Raise(UiEvent evt)
|
|
||||||
{
|
|
||||||
foreach (var effect in uiElement.GetEffectsFor(evt))
|
|
||||||
{
|
|
||||||
// using explicit null comparison because of the way
|
|
||||||
// GameObjects handle being null
|
|
||||||
if (effect != null)
|
|
||||||
{
|
|
||||||
effect.Instantiate()
|
|
||||||
.Execute(visualElement, destroyCancellationToken)
|
|
||||||
.Subscribe()
|
|
||||||
.AddTo(activeEffects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!configured) { return; }
|
|
||||||
|
|
||||||
Raise(UiEvent.OnDestroy);
|
|
||||||
|
|
||||||
activeEffects.Dispose();
|
|
||||||
activeEffects.Clear();
|
|
||||||
|
|
||||||
uiElement = default;
|
|
||||||
visualElement = default;
|
|
||||||
|
|
||||||
configured = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue