Fixed Object Pooling and cascading
This commit is contained in:
@@ -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
|
||||
|
||||
3
Assets/Scripts/Presenter.meta
Normal file
3
Assets/Scripts/Presenter.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 126a4d7fcb1147ed869399287b5b9d18
|
||||
timeCreated: 1765696722
|
||||
30
Assets/Scripts/Presenter/GemPresenter.cs
Normal file
30
Assets/Scripts/Presenter/GemPresenter.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Presenter/GemPresenter.cs.meta
Normal file
3
Assets/Scripts/Presenter/GemPresenter.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a89c766a122b41faa5df785594db1b6e
|
||||
timeCreated: 1765696732
|
||||
@@ -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);
|
||||
|
||||
@@ -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<IScoreService, ScoreService>(Lifetime.Scoped);
|
||||
|
||||
builder.Register<IObjectPool<GemView>>(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<IGameBoardService, GameBoardService>(Lifetime.Scoped);
|
||||
builder.Register<IGameBoardService, GameBoardService>(Lifetime.Scoped).AsImplementedInterfaces();
|
||||
|
||||
builder.RegisterEntryPoint<LevelEntryPoint>();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<GemView> 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<GemView> objectPool;
|
||||
private readonly Transform gemsHolder;
|
||||
|
||||
private readonly List<GemPresenter> gemPresenters = new List<GemPresenter>();
|
||||
|
||||
public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IObjectPool<GemView> 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<GemView> 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<GemPresenter> presentersToRemove = this.gemPresenters.Where(p => p.GemView == gemView).ToList();
|
||||
foreach (GemPresenter presenter in presentersToRemove) {
|
||||
this.gemPresenters.Remove(presenter);
|
||||
}
|
||||
}
|
||||
|
||||
private List<GemView> GemsViews() {
|
||||
return this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace Services
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Debug.Log("Level Entry Point");
|
||||
this.gameBoardService.Setup();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<GemView> {
|
||||
private readonly GemView[] prefabs;
|
||||
private readonly Transform parent;
|
||||
private readonly int size;
|
||||
|
||||
private readonly Stack<GemView> pool = new Stack<GemView>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
14
Assets/Scripts/Utils/Vector2IntUtils.cs
Normal file
14
Assets/Scripts/Utils/Vector2IntUtils.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Utils/Vector2IntUtils.cs.meta
Normal file
3
Assets/Scripts/Utils/Vector2IntUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a5066db9b0b4aadba9aa22854bb4cf9
|
||||
timeCreated: 1765697266
|
||||
@@ -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;
|
||||
|
||||
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);
|
||||
private bool isFalling;
|
||||
|
||||
public void Bind(Gem gem) {
|
||||
this.gem = gem;
|
||||
this.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user