Skip to content

Commit

Permalink
Improve MVVM performance (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: Nikolai Selivanov <[email protected]>
  • Loading branch information
kekchpek and Nikolai Selivanov authored Sep 3, 2024
1 parent e4a51e0 commit 0300ce9
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 58 deletions.
Binary file not shown.
33 changes: 18 additions & 15 deletions MvvmUnityProj/CCG/Assets/Tests/EditorTests/Tests/CardsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ public void ClickDownCard_CardSelected()
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
cards[0].OnMouseClickDown();
var firstCard = cards.First();
firstCard.OnMouseClickDown();

// Assert
Assert.IsTrue(cards[0].IsSelected.Value, "Card is not selected after clicking on it.");
Assert.IsTrue(firstCard.IsSelected.Value, "Card is not selected after clicking on it.");
}

[Test]
Expand All @@ -71,11 +72,12 @@ public void ClickDownCard_ClickUp_CardDeselected()
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
cards[0].OnMouseClickDown();
cards[0].OnMouseClickUp();
var firstCard = cards.First();
firstCard.OnMouseClickDown();
firstCard.OnMouseClickUp();

// Assert
Assert.IsFalse(cards[0].IsSelected.Value, "Card is still selected after click release on it.");
Assert.IsFalse(firstCard.IsSelected.Value, "Card is still selected after click release on it.");
}

[Test]
Expand All @@ -99,14 +101,15 @@ void CardDestroyed(IViewModel vm)
{
isDestroyed = true;
}
var firstCard = cards.First();

cards[0].Destroyed += CardDestroyed;

cards[0].OnMouseClickDown();
cards[0].OnCardEnterBoard();
cards[0].OnMouseClickUp();
firstCard.Destroyed += CardDestroyed;

cards[0].Destroyed -= CardDestroyed;
firstCard.OnMouseClickDown();
firstCard.OnCardEnterBoard();
firstCard.OnMouseClickUp();

firstCard.Destroyed -= CardDestroyed;

// Assert
Assert.IsTrue(isDestroyed, "Card is not destroyed after playing on the board.");
Expand Down Expand Up @@ -141,10 +144,10 @@ public void DragCard_OverBoard_CardPlayed()
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();

cards[0].OnMouseClickDown();
cards[0].OnCardEnterBoard();
cards[0].OnMouseClickUp();
var firstCard = cards.First();
firstCard.OnMouseClickDown();
firstCard.OnCardEnterBoard();
firstCard.OnMouseClickUp();

// Assert
card.Received().Play();
Expand Down
17 changes: 11 additions & 6 deletions MvvmUnityProj/CCG/Assets/Tests/EditorTests/Tests/GameTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void GameStarted_CardsCreated()
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
Assert.IsTrue(cards.Length > 0, "Cards were not created after game started.");
Assert.IsTrue(cards.Any(), "Cards were not created after game started.");
}

[Test]
Expand All @@ -65,7 +65,8 @@ public void AddCard_CardAdded()
// Store current cards to assert.
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
.GetSubviews<ICardViewModel>()
.ToArray();

// Add one card.
var handController = testApp
Expand All @@ -76,7 +77,8 @@ public void AddCard_CardAdded()
// Assert
var newCards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
.GetSubviews<ICardViewModel>()
.ToArray();
Assert.AreEqual(cards.Length + 1, newCards.Length, "Card is not added after click add card button.");
}

Expand Down Expand Up @@ -105,7 +107,8 @@ public void AddCards_OverMax_CardsCountIsMax()
// Assert
var newCards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
.GetSubviews<ICardViewModel>()
.ToArray();
Assert.AreEqual(cardsModel.MaxCardsCount, newCards.Length, "Card is not added after click add card button.");
}

Expand All @@ -131,7 +134,8 @@ public void SwitchCardsPattern_CardPositionIsArch()
var handModel = testApp.GetViewModelDependency<IHandModel>();
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
.GetSubviews<ICardViewModel>()
.ToArray();
for (int i = 0; i < handModel.CardsCount.Value; i++)
{
var data = CardMath.GetRegularTransform(
Expand Down Expand Up @@ -173,7 +177,8 @@ public void SwitchCardsPattern_AndSwitchBack_CardPositionIsArch()
var handModel = testApp.GetViewModelDependency<IHandModel>();
var cards = testApp
.GetViewModel<IMainScreenViewModel>(ViewLayerIds.MainUI)
.GetSubviews<ICardViewModel>();
.GetSubviews<ICardViewModel>()
.ToArray();
for (int i = 0; i < handModel.CardsCount.Value; i++)
{
var data = CardMath.GetArchTransform(
Expand Down
Binary file not shown.
5 changes: 4 additions & 1 deletion src/UnityMVVM/IViewBehaviour.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
namespace UnityMVVM
{
internal interface IViewBehaviour
/// <summary>
/// Base interface for views inherited from <see cref="UnityEngine.MonoBehaviour"/>
/// </summary>
public interface IViewBehaviour
{
}
}
3 changes: 1 addition & 2 deletions src/UnityMVVM/Pool/ViewPool.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;

namespace UnityMVVM.Pool
{
Expand Down Expand Up @@ -31,7 +30,7 @@ protected virtual void OnViewReturnToPool(T poolableView)
/// <inheritdoc />
public bool TryPop(out IPoolableView? poolableView)
{
if (_poolCollection.Any())
if (_poolCollection.Count > 0)
{
var view = _poolCollection.Pop();
OnViewTakenFromPool((T)view);
Expand Down
4 changes: 2 additions & 2 deletions src/UnityMVVM/ViewBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ void IViewInitializer.SetViewModel(IViewModel viewModel)
/// <inheritdoc />
public void SetParent(Transform parent)
{
Transform t = transform;
var t = transform;
t.SetParent(parent);
t.localPosition = Vector3.zero;
t.localRotation = Quaternion.identity;
t.localScale = _initialScale;
}

/// <inheritdoc />
public void SetPartOfPoolableView(bool isPartOfPooledView)
void IViewInitializer.SetPartOfPoolableView(bool isPartOfPooledView)
{
_isPartOfPooledView = isPartOfPooledView;
}
Expand Down
3 changes: 2 additions & 1 deletion src/UnityMVVM/ViewManager/IViewManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AsyncReactAwait.Bindable;
using AsyncReactAwait.Promises;
Expand Down Expand Up @@ -34,7 +35,7 @@ public interface IViewManager
/// Returns all view layer ids.
/// </summary>
/// <returns>All layer ids.</returns>
string[] GetLayerIds();
IReadOnlyList<string> GetLayerIds();

/// <summary>
/// Creates viewModel and corresponding view.
Expand Down
40 changes: 25 additions & 15 deletions src/UnityMVVM/ViewManager/ViewManagerImpl.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using AsyncReactAwait.Promises;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AsyncReactAwait.Bindable;
using UnityEngine;
using UnityEngine.Scripting;
using UnityMVVM.DI;
using UnityMVVM.ViewManager.ViewLayer;
using UnityMVVM.ViewModelCore;
// ReSharper disable All

namespace UnityMVVM.ViewManager
{
Expand All @@ -18,7 +18,8 @@ internal class ViewManagerImpl : IViewManager

private const string UnknownViewName = "<UNKNOWN>";

private readonly IViewLayer[] _layers;
private readonly IReadOnlyList<IViewLayer> _layers;
private readonly IReadOnlyList<string> _layerIds;
private readonly IViewsModelsContainerAdapter _viewsContainer;

private readonly IDictionary<IViewModel, string> _createdViewsNames = new Dictionary<IViewModel, string>();
Expand All @@ -43,7 +44,14 @@ public ViewManagerImpl(
IEnumerable<IViewLayer> layers,
IViewsModelsContainerAdapter viewsContainerAdapter)
{
_layers = layers.ToArray();
_layers = System.Linq.Enumerable.ToArray(layers);
var layerIds = new string[_layers.Count];
for (var i = 0; i < _layers.Count; i++)
{
layerIds[i] = _layers[i].Id;
}
_layerIds = layerIds;

_viewsContainer = viewsContainerAdapter;
foreach (var viewLayer in _layers)
{
Expand Down Expand Up @@ -72,11 +80,7 @@ private void UpdateHighestViewLayer()

public async IPromise OpenExact(string viewLayerId, string viewName, IPayload? payload = null)
{
var layer = _layers.FirstOrDefault(x => x.Id == viewLayerId);
if (layer == null)
{
throw new Exception($"Can not find layer with id = {viewLayerId}");
}
var layer = GetLayer(viewLayerId);

await layer.Clear();
await CreateViewOnLayer(viewName, layer, payload);
Expand All @@ -85,13 +89,13 @@ public async IPromise OpenExact(string viewLayerId, string viewName, IPayload? p
/// <inheritdoc cref="IViewManager.CloseExact(string)"/>
public async IPromise CloseExact(string viewLayerId)
{
await _layers.First(l => l.Id == viewLayerId).Clear();
await GetLayer(viewLayerId).Clear();
}

/// <inheritdoc cref="IViewManager.Close(string)"/>
public async IPromise Close(string viewLayerId)
{
for (var i = _layers.Length - 1;;i--)
for (var i = _layers.Count - 1;;i--)
{
await _layers[i].Clear();
if (_layers[i].Id == viewLayerId)
Expand Down Expand Up @@ -121,18 +125,24 @@ public async IPromise Close(string viewLayerId)
/// <inheritdoc />
public IViewLayer GetLayer(string viewLayerId)
{
return _layers.First(x => x.Id == viewLayerId);
foreach (var l in _layers)
{
if (l.Id == viewLayerId)
return l;
}

throw new InvalidOperationException($"Layer with id={viewLayerId} is not found.");
}

/// <inheritdoc />
public IViewModel? GetView(string viewLayerId)
{
return _layers.First(x => x.Id == viewLayerId).CurrentView.Value;
return GetLayer(viewLayerId).CurrentView.Value;
}

public string[] GetLayerIds()
public IReadOnlyList<string> GetLayerIds()
{
return _layers.Select(x => x.Id).ToArray();
return _layerIds;
}

public async Task<IViewModel> Create(IViewModel parent, string viewName, Transform container, IPayload? payload = null)
Expand All @@ -151,7 +161,7 @@ public async Task<IViewModel> Create(IViewModel parent, string viewName, Transfo
_openingLayer = viewLayerId;
try
{
for (int i = _layers.Length - 1; i >= 0; i--)
for (int i = _layers.Count - 1; i >= 0; i--)
{
// close opened view
var openedViewModel = _layers[i].CurrentView.Value;
Expand Down
3 changes: 2 additions & 1 deletion src/UnityMVVM/ViewModelCore/IViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AsyncReactAwait.Promises;
using System;
using System.Collections.Generic;
using UnityMVVM.ViewManager.ViewLayer;

namespace UnityMVVM.ViewModelCore
Expand Down Expand Up @@ -45,7 +46,7 @@ public interface IViewModel
/// </summary>
/// <typeparam name="T">The type of a subviews.</typeparam>
/// <returns>Returns array of subviews of specified type or an empty array.</returns>
T[] GetSubviews<T>() where T : IViewModel;
IEnumerable<T> GetSubviews<T>() where T : IViewModel;

/// <summary>
/// Fire <see cref="CloseStarted"/> event and internal view model close handling.
Expand Down
15 changes: 11 additions & 4 deletions src/UnityMVVM/ViewModelCore/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,23 @@ protected async IPromise OpenView(string viewLayerId, string viewName, IPayload?
/// <inheritdoc />
public T? GetSubview<T>() where T : IViewModel
{
return (T?)_subviews.FirstOrDefault(x => x is T);
foreach (var subview in _subviews)
{
if (subview is T outcome)
{
return outcome;
}
}

return default;
}

/// <inheritdoc />
public T[] GetSubviews<T>() where T : IViewModel
public IEnumerable<T> GetSubviews<T>() where T : IViewModel
{
return _subviews
.Where(x => x is T)
.Cast<T>()
.ToArray();
.Cast<T>();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal interface IViewModelsFactory
/// <param name="parent">Parent view model to set to the created view model.</param>
/// <param name="parentTransform">The transform to instantiate the view to.</param>
/// <param name="payload">View model payload.</param>
/// <returns>Returns created view model to conrol the view.</returns>
/// <returns>Returns created view model to control the view.</returns>
Task<IViewModel> Create(IViewLayer viewLayer,
IViewModel? parent,
Transform parentTransform,
Expand Down
Loading

0 comments on commit 0300ce9

Please sign in to comment.