diff --git a/Assets/GameVariables.asset b/Assets/GameVariables.asset index 9d43359..8fab45d 100644 --- a/Assets/GameVariables.asset +++ b/Assets/GameVariables.asset @@ -19,13 +19,13 @@ MonoBehaviour: gemsPrefabs: - {fileID: 3808538059049426536, guid: 724e93e48c6cc0b4ab3d44e5ea34f2ec, type: 3} - {fileID: 4490600519223577409, guid: 784323496d719684cb6201b200b95864, type: 3} - - {fileID: 745607475630438949, guid: 93bd623174244c047af9ce43cc254c32, type: 3} - {fileID: 6213027626580313688, guid: 473855e3d0d3c8143836b678c9a1b8b5, type: 3} - {fileID: 1845825807271331471, guid: 91ba2370328500d4db689dad894b1602, type: 3} + - {fileID: 745607475630438949, guid: 93bd623174244c047af9ce43cc254c32, type: 3} destroyEffectPrefabs: [] bonusAmount: 0.5 bombChance: 2 - dropHeight: 0 + dropHeight: 1 gemSpeed: 7 scoreSpeed: 5 width: 7 diff --git a/Assets/Scripts/Presenter.meta b/Assets/Scripts/Presenter.meta new file mode 100644 index 0000000..a673f35 --- /dev/null +++ b/Assets/Scripts/Presenter.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 126a4d7fcb1147ed869399287b5b9d18 +timeCreated: 1765696722 \ No newline at end of file diff --git a/Assets/Scripts/Presenter/GemPresenter.cs b/Assets/Scripts/Presenter/GemPresenter.cs new file mode 100644 index 0000000..3a192dd --- /dev/null +++ b/Assets/Scripts/Presenter/GemPresenter.cs @@ -0,0 +1,30 @@ +using Services; +using UnityEngine; +using Utils; +using VContainer.Unity; +using Views; + +namespace Presenter { + public class GemPresenter { + private Gem gem; + private GemView gemView; + + public Gem Gem => this.gem; + public GemView GemView => this.gemView; + + public GemPresenter(Gem gem, GemView gemView) { + this.gem = gem; + this.gemView = gemView; + } + + public void Tick() { + if (this.gemView == null) { + return; + } + + if (!this.gem.Position.Compare(this.gemView.transform.localPosition)) { + this.gemView.UpdatePosition(this.gem.Position); + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Presenter/GemPresenter.cs.meta b/Assets/Scripts/Presenter/GemPresenter.cs.meta new file mode 100644 index 0000000..a3f4f0a --- /dev/null +++ b/Assets/Scripts/Presenter/GemPresenter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a89c766a122b41faa5df785594db1b6e +timeCreated: 1765696732 \ No newline at end of file diff --git a/Assets/Scripts/SC_Gem.cs b/Assets/Scripts/SC_Gem.cs index 333f65c..8bf17f2 100644 --- a/Assets/Scripts/SC_Gem.cs +++ b/Assets/Scripts/SC_Gem.cs @@ -53,7 +53,6 @@ // this.scGameLogic = _ScGameLogic; // } // -// //Not every gem needs this // private void OnMouseDown() // { // if (this.scGameLogic.CurrentState == GameState.Move) @@ -63,7 +62,6 @@ // } // } // -// //Not every gem needs this // private void CalculateAngle() // { // this.swipeAngle = Mathf.Atan2(this.finalTouchPosition.y - this.firstTouchPosition.y, this.finalTouchPosition.x - this.firstTouchPosition.x); @@ -73,7 +71,6 @@ // MovePieces(); // } // -// //Not every gem needs this, maybe // private void MovePieces() // { // this.previousPos = this.posIndex; @@ -110,7 +107,6 @@ // StartCoroutine(CheckMoveCo()); // } // -// //Why are we checking matches on the Gem itself // public IEnumerator CheckMoveCo() // { // this.scGameLogic.SetState(GameState.Wait); diff --git a/Assets/Scripts/Scopes/LevelLifetimeScope.cs b/Assets/Scripts/Scopes/LevelLifetimeScope.cs index 67eea6b..7432a41 100644 --- a/Assets/Scripts/Scopes/LevelLifetimeScope.cs +++ b/Assets/Scripts/Scopes/LevelLifetimeScope.cs @@ -1,5 +1,6 @@ using Models; using Models.Interfaces; +using Presenter; using ScriptableObjects; using Services; using Services.Interfaces; @@ -28,10 +29,10 @@ namespace Scopes builder.Register(Lifetime.Scoped); builder.Register>(c => - new ObjectPoolService(this.gameVariables.gemsPrefabs, this.gemsHolder, this.gameVariables.width * this.gameVariables.height), + new ObjectPoolService(this.gameVariables.gemsPrefabs, this.gemsHolder), Lifetime.Scoped); - - builder.Register(Lifetime.Scoped); + + builder.Register(Lifetime.Scoped).AsImplementedInterfaces(); builder.RegisterEntryPoint(); } diff --git a/Assets/Scripts/ScriptableObjects/GameVariables.cs b/Assets/Scripts/ScriptableObjects/GameVariables.cs index fabc141..10d3073 100644 --- a/Assets/Scripts/ScriptableObjects/GameVariables.cs +++ b/Assets/Scripts/ScriptableObjects/GameVariables.cs @@ -14,7 +14,7 @@ namespace ScriptableObjects { public float bonusAmount = 0.5f; public float bombChance = 2f; public int dropHeight = 1; - public float gemSpeed = 7; + public float gemSpeed = 0.1f; public float scoreSpeed = 5; public int width; public int height; diff --git a/Assets/Scripts/Services/GameBoardService.cs b/Assets/Scripts/Services/GameBoardService.cs index b653781..bdfc4a1 100644 --- a/Assets/Scripts/Services/GameBoardService.cs +++ b/Assets/Scripts/Services/GameBoardService.cs @@ -3,22 +3,26 @@ using System.Linq; using Cysharp.Threading.Tasks; using Enums; using Models.Interfaces; +using Presenter; using ScriptableObjects; using Services.Interfaces; using UnityEngine; using Utils; +using VContainer.Unity; using Views; using Object = UnityEngine.Object; using Random = UnityEngine.Random; namespace Services { - public class GameBoardService : IGameBoardService { - private IGameBoard gameBoard; - private GameVariables gameVariables; - private IMatchService matchService; - private IScoreService scoreService; - private IObjectPool objectPool; - private Transform gemsHolder; + public class GameBoardService : IGameBoardService, ITickable { + private readonly IGameBoard gameBoard; + private readonly GameVariables gameVariables; + private readonly IMatchService matchService; + private readonly IScoreService scoreService; + private readonly IObjectPool objectPool; + private readonly Transform gemsHolder; + + private readonly List gemPresenters = new List(); public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IObjectPool objectPool, Transform gemsHolder) { this.gameBoard = gameBoard; @@ -29,8 +33,17 @@ namespace Services { this.gemsHolder = gemsHolder; } + public void Tick() { + int i = 0; + foreach (GemPresenter gemPresenter in gemPresenters) { + gemPresenter.Tick(); + i++; + } + } + + //Instantiates background tiles and calls SpawnGems + //Uses MatchService.MatchesAt to avoid matching Gems public void Setup() { - Debug.Log("Setting up the board"); for (int x = 0; x < this.gameBoard.Width; x++) for (int y = 0; y < this.gameBoard.Height; y++) { @@ -52,24 +65,31 @@ namespace Services { } } + //Uses the ObjectPool to spawn a gem at the given position public void SpawnGem(Vector2Int position, GemView gemPrefab, GemType gemType) { - Debug.Log("Spawning gem at " + position + " with type " + gemType + ""); if (Random.Range(0, 100f) < this.gameVariables.bombChance) gemPrefab = this.gameVariables.bombPrefab; GemView gemView = this.objectPool.Get(gemType, position, this.gameVariables.dropHeight); gemView.name = "Gem - " + position.x + ", " + position.y; - SetGem(new Vector2Int(position.x,position.y), new Gem(gemType, position)); + Gem gem = new Gem(gemType, position); + gemView.Bind(gem); + + this.gemPresenters.Add(new GemPresenter(gem, gemView)); + SetGem(new Vector2Int(position.x,position.y), gem); } + //Sets the gem on the GameBoard public void SetGem(Vector2Int position, Gem gem) { this.gameBoard.SetGemAt(new Vector2Int(position.x, position.y), gem); } + //Gets the gem from the GameBoard public Gem GetGem(Vector2Int position) { return this.gameBoard.GetGemAt(position); } + //If there are matches, destroys them and moves the gems down public void DestroyMatches() { for (int i = 0; i < this.matchService.CurrentMatches.Count; i++) if (this.matchService.CurrentMatches[i] != null) @@ -83,8 +103,6 @@ namespace Services { public async UniTask MoveGemsDown() { await UniTask.Delay(2); - // why the delay though? - // yield return new WaitForSeconds(.2f); int nullCounter = 0; for (int x = 0; x < this.gameBoard.Width; x++) @@ -141,6 +159,7 @@ namespace Services { CheckMisplacedGems(); } + //Checks if there are gems that are not in the board public void CheckMisplacedGems() { List gemsViews = GemsViews(); @@ -157,8 +176,12 @@ namespace Services { } } - foreach (GemView g in gemsViews) - Object.Destroy(g.gameObject); + foreach (GemView g in gemsViews) { + RemovePresenterFor(g); + + g.Unbind(); + this.objectPool.Release(g); + } } public void DestroyMatchedGems(Vector2Int position) { @@ -167,14 +190,32 @@ namespace Services { if (currentGem != null) { GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem); + if (gemView is null) { + return; + } + + //ToDo: Destroy effect if(this.gameVariables.destroyEffectPrefabs.Length > 0) Object.Instantiate(this.gameVariables.destroyEffectPrefabs[(int)currentGem.Type], new Vector2(position.x, position.y), Quaternion.identity); - Object.Destroy(gemView!.gameObject); + RemovePresenterFor(gemView); + gemView.Unbind(); + this.objectPool.Release(gemView); SetGem(position, null); } } + private void RemovePresenterFor(GemView gemView) { + if (gemView is null) { + return; + } + + List presentersToRemove = this.gemPresenters.Where(p => p.GemView == gemView).ToList(); + foreach (GemPresenter presenter in presentersToRemove) { + this.gemPresenters.Remove(presenter); + } + } + private List GemsViews() { return this.gemsHolder.GetComponentsInChildren().ToList(); } diff --git a/Assets/Scripts/Services/LevelEntryPoint.cs b/Assets/Scripts/Services/LevelEntryPoint.cs index fd8d55f..fb19533 100644 --- a/Assets/Scripts/Services/LevelEntryPoint.cs +++ b/Assets/Scripts/Services/LevelEntryPoint.cs @@ -18,9 +18,7 @@ namespace Services public void Start() { - Debug.Log("Level Entry Point"); this.gameBoardService.Setup(); - } } } \ No newline at end of file diff --git a/Assets/Scripts/Services/ObjectPoolService.cs b/Assets/Scripts/Services/ObjectPoolService.cs index 1b4d001..481ec79 100644 --- a/Assets/Scripts/Services/ObjectPoolService.cs +++ b/Assets/Scripts/Services/ObjectPoolService.cs @@ -1,40 +1,37 @@ -using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using Enums; using Services.Interfaces; using UnityEngine; -using Utils; using Views; using Object = UnityEngine.Object; -using Random = UnityEngine.Random; namespace Services { public class ObjectPoolService:IObjectPool { private readonly GemView[] prefabs; private readonly Transform parent; - private readonly int size; private readonly Stack pool = new Stack(); - public ObjectPoolService(GemView[] prefabs, Transform parent, int size = 5) { + public ObjectPoolService(GemView[] prefabs, Transform parent) { this.prefabs = prefabs; this.parent = parent; - this.size = size; } public GemView Get(GemType type, Vector2Int position, float dropHeight) { int typeAsInt = (int) type; GemView gemView; + float randomOffset = Random.Range(1f, 2.5f); + Vector2 vector2Position = new Vector2(position.x, position.y + dropHeight * randomOffset); if (this.pool.Count > 0) { gemView = this.pool.Pop(); - gemView.transform.localPosition = new Vector2(position.x, position.y + dropHeight); + + + gemView.transform.localPosition = vector2Position; return gemView; } - gemView = Object.Instantiate(this.prefabs[typeAsInt], new Vector2(position.x, position.y + dropHeight), Quaternion.identity, this.parent); + gemView = Object.Instantiate(this.prefabs[typeAsInt], vector2Position, Quaternion.identity, this.parent); return gemView; } diff --git a/Assets/Scripts/Utils/Vector2IntUtils.cs b/Assets/Scripts/Utils/Vector2IntUtils.cs new file mode 100644 index 0000000..cfa9c8e --- /dev/null +++ b/Assets/Scripts/Utils/Vector2IntUtils.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace Utils { + public static class Vector2IntUtils { + public static bool Compare(this Vector2Int a, Vector2 b) { + Vector2 aVector2 = new Vector2(a.x, a.y); + return Vector2.Distance(aVector2, b) < 0.01f; + } + + public static Vector2 ToVector2(this Vector2Int v) { + return new Vector2(v.x, v.y); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Utils/Vector2IntUtils.cs.meta b/Assets/Scripts/Utils/Vector2IntUtils.cs.meta new file mode 100644 index 0000000..1183fbe --- /dev/null +++ b/Assets/Scripts/Utils/Vector2IntUtils.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2a5066db9b0b4aadba9aa22854bb4cf9 +timeCreated: 1765697266 \ No newline at end of file diff --git a/Assets/Scripts/Views/GemView.cs b/Assets/Scripts/Views/GemView.cs index e48d738..b479354 100644 --- a/Assets/Scripts/Views/GemView.cs +++ b/Assets/Scripts/Views/GemView.cs @@ -1,15 +1,43 @@ +using Cysharp.Threading.Tasks; using Enums; using Services; using UnityEngine; +using Utils; namespace Views { public class GemView : MonoBehaviour { private Gem gem; public Gem Gem => this.gem; + + private bool isFalling; + + public void Bind(Gem gem) { + this.gem = gem; + this.gameObject.SetActive(true); + } - public void UpdatePosition(Vector2Int positionBasedOnIndex) { - if (Vector2.Distance(this.transform.position, positionBasedOnIndex) > 0.01f) { - this.transform.position = Vector2.Lerp(this.transform.position, positionBasedOnIndex, 0.1f); + public void Unbind() { + this.gem = null; + this.gameObject.SetActive(false); + this.isFalling = false; + } + + private async UniTask FallDelay() { + float randomDelay = Random.Range(0.05f, 0.5f); + await UniTask.WaitForSeconds(randomDelay); + this.isFalling = true; + } + + public async UniTaskVoid UpdatePosition(Vector2Int positionBasedOnIndex) { + if (!this.isFalling) { + await FallDelay(); + } + + if (!this.isFalling) + return; + + if (Vector2.Distance(this.transform.position, positionBasedOnIndex.ToVector2()) > 0.01f) { + this.transform.position = Vector2.Lerp(this.transform.position, positionBasedOnIndex.ToVector2(), 0.01f); } } }