diff --git a/.idea/.idea.Match3/.idea/.gitignore b/.idea/.idea.Match3/.idea/.gitignore
new file mode 100644
index 0000000..9b4f605
--- /dev/null
+++ b/.idea/.idea.Match3/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/modules.xml
+/contentModel.xml
+/.idea.Match3.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.Match3/.idea/indexLayout.xml b/.idea/.idea.Match3/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.Match3/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.Match3/.idea/vcs.xml b/.idea/.idea.Match3/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.Match3/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assets/GameVariables.asset b/Assets/GameVariables.asset
index 8fab45d..98fbe4b 100644
--- a/Assets/GameVariables.asset
+++ b/Assets/GameVariables.asset
@@ -14,15 +14,43 @@ MonoBehaviour:
m_EditorClassIdentifier:
bgTilePrefabs: {fileID: 2914066502361773997, guid: 3f39182b81f944a4d93213431acb41c3,
type: 3}
- bombPrefab: {fileID: 5652386976359944012, guid: aa0291e650b382941875040db9e8a232,
- type: 3}
gemsPrefabs:
- - {fileID: 3808538059049426536, guid: 724e93e48c6cc0b4ab3d44e5ea34f2ec, type: 3}
- - {fileID: 4490600519223577409, guid: 784323496d719684cb6201b200b95864, type: 3}
- - {fileID: 6213027626580313688, guid: 473855e3d0d3c8143836b678c9a1b8b5, type: 3}
- - {fileID: 1845825807271331471, guid: 91ba2370328500d4db689dad894b1602, type: 3}
- - {fileID: 745607475630438949, guid: 93bd623174244c047af9ce43cc254c32, type: 3}
- destroyEffectPrefabs: []
+ - type: 0
+ gemPrefab: {fileID: 3808538059049426536, guid: 724e93e48c6cc0b4ab3d44e5ea34f2ec,
+ type: 3}
+ explosionPrefab: {fileID: 8904178830182364799, guid: d794be08823edd34da1790efd1739074,
+ type: 3}
+ scoreValue: 10
+ - type: 1
+ gemPrefab: {fileID: 4490600519223577409, guid: 784323496d719684cb6201b200b95864,
+ type: 3}
+ explosionPrefab: {fileID: 4157327684024108653, guid: e5bba4196a7d43441bf62bd125171449,
+ type: 3}
+ scoreValue: 10
+ - type: 2
+ gemPrefab: {fileID: 6213027626580313688, guid: 473855e3d0d3c8143836b678c9a1b8b5,
+ type: 3}
+ explosionPrefab: {fileID: 5902279534240010874, guid: 072620c89288cc14e9872d103c2b2fbe,
+ type: 3}
+ scoreValue: 10
+ - type: 3
+ gemPrefab: {fileID: 1845825807271331471, guid: 91ba2370328500d4db689dad894b1602,
+ type: 3}
+ explosionPrefab: {fileID: 4897855320600288343, guid: 7fcedfb0407bb5d438de37da2d61df30,
+ type: 3}
+ scoreValue: 10
+ - type: 4
+ gemPrefab: {fileID: 745607475630438949, guid: 93bd623174244c047af9ce43cc254c32,
+ type: 3}
+ explosionPrefab: {fileID: 1865769399618337668, guid: 986dd822e2e6d4b45b2a054259f1241b,
+ type: 3}
+ scoreValue: 10
+ - type: 5
+ gemPrefab: {fileID: 5652386976359944012, guid: aa0291e650b382941875040db9e8a232,
+ type: 3}
+ explosionPrefab: {fileID: 8968486364681163996, guid: 05c754e3d4f9fd349ac1def58d17670f,
+ type: 3}
+ scoreValue: 10
bonusAmount: 0.5
bombChance: 2
dropHeight: 1
diff --git a/Assets/Scenes/Scene_Submission.unity b/Assets/Scenes/Scene_Submission.unity
index 25a9f62..2c74cd5 100644
--- a/Assets/Scenes/Scene_Submission.unity
+++ b/Assets/Scenes/Scene_Submission.unity
@@ -154,6 +154,55 @@ Transform:
- {fileID: 259844899}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &109682480
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 109682481}
+ - component: {fileID: 109682482}
+ m_Layer: 0
+ m_Name: ScoreView
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &109682481
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 109682480}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 786657518}
+ m_Father: {fileID: 1450061022}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 1}
+ m_AnchorMax: {x: 0.5, y: 1}
+ m_AnchoredPosition: {x: 0, y: -245}
+ m_SizeDelta: {x: 500, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &109682482
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 109682480}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 86d965b2c3d5474082911bdd360847ce, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
--- !u!1 &259844896
GameObject:
m_ObjectHideFlags: 0
@@ -507,17 +556,17 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 786657517}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
- m_Father: {fileID: 1450061022}
+ m_Father: {fileID: 109682481}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
- m_AnchorMin: {x: 0.5, y: 1}
- m_AnchorMax: {x: 0.5, y: 1}
- m_AnchoredPosition: {x: 0, y: -245}
- m_SizeDelta: {x: 500, y: 100}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &786657519
MonoBehaviour:
@@ -858,7 +907,7 @@ RectTransform:
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children:
- - {fileID: 786657518}
+ - {fileID: 109682481}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
diff --git a/Assets/Scripts/GameBoard.cs b/Assets/Scripts/GameBoard.cs
deleted file mode 100644
index 23f5764..0000000
--- a/Assets/Scripts/GameBoard.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-// using System.Collections;
-// using System.Collections.Generic;
-// using System.Linq;
-// using UnityEngine;
-// using Enums;
-//
-// //Done, moved to MatchService and GameBoard
-// public class GameBoard
-// {
-// // - sets size of the grid
-// // - handles the score
-// // - holds the current matches
-// #region Variables
-//
-// private int height = 0;
-// public int Height { get { return this.height; } }
-//
-// private int width = 0;
-// public int Width { get { return this.width; } }
-//
-// private SC_Gem[,] allGems;
-// // public Gem[,] AllGems { get { return allGems; } }
-//
-// private int score = 0;
-// public int Score
-// {
-// get { return this.score; }
-// set {
-// this.score = value; }
-// }
-//
-// private List currentMatches = new List();
-// public List CurrentMatches { get { return this.currentMatches; } }
-// #endregion
-//
-// public GameBoard(int _Width, int _Height)
-// {
-// this.height = _Height;
-// this.width = _Width;
-// this.allGems = new SC_Gem[this.width, this.height];
-// }
-//
-// //checks if there are 3 gems of the same type next to each other
-// //used during setup to avoid matches on game start
-// //MatchService
-// public bool MatchesAt(Vector2Int _PositionToCheck, SC_Gem _GemToCheck)
-// {
-// if (_PositionToCheck.x > 1)
-// {
-// if (this.allGems[_PositionToCheck.x - 1, _PositionToCheck.y].type == _GemToCheck.type && this.allGems[_PositionToCheck.x - 2, _PositionToCheck.y].type == _GemToCheck.type)
-// return true;
-// }
-//
-// if (_PositionToCheck.y > 1)
-// {
-// if (this.allGems[_PositionToCheck.x, _PositionToCheck.y - 1].type == _GemToCheck.type && this.allGems[_PositionToCheck.x, _PositionToCheck.y - 2].type == _GemToCheck.type)
-// return true;
-// }
-//
-// return false;
-// }
-//
-// //places the gem in the 2d array
-// public void SetGem(int _X, int _Y, SC_Gem _Gem)
-// {
-// this.allGems[_X, _Y] = _Gem;
-// }
-//
-// public SC_Gem GetGem(int _X,int _Y)
-// {
-// return this.allGems[_X, _Y];
-// }
-//
-// //MatchService
-// public void FindAllMatches()
-// {
-// this.currentMatches.Clear();
-//
-// for (int x = 0; x < this.width; x++)
-// for (int y = 0; y < this.height; y++)
-// {
-// SC_Gem currentGem = this.allGems[x, y];
-// if (currentGem != null)
-// {
-// if (x > 0 && x < this.width - 1)
-// {
-// SC_Gem leftGem = this.allGems[x - 1, y];
-// SC_Gem rightGem = this.allGems[x + 1, y];
-// //checking no empty spots
-// if (leftGem != null && rightGem != null)
-// {
-// //Match
-// if (leftGem.type == currentGem.type && rightGem.type == currentGem.type)
-// {
-// currentGem.isMatch = true;
-// leftGem.isMatch = true;
-// rightGem.isMatch = true;
-// this.currentMatches.Add(currentGem);
-// this.currentMatches.Add(leftGem);
-// this.currentMatches.Add(rightGem);
-// }
-// }
-// }
-//
-// if (y > 0 && y < this.height - 1)
-// {
-// SC_Gem aboveGem = this.allGems[x, y - 1];
-// SC_Gem bellowGem = this.allGems[x, y + 1];
-// //checking no empty spots
-// if (aboveGem != null && bellowGem != null)
-// {
-// //Match
-// if (aboveGem.type == currentGem.type && bellowGem.type == currentGem.type)
-// {
-// currentGem.isMatch = true;
-// aboveGem.isMatch = true;
-// bellowGem.isMatch = true;
-// this.currentMatches.Add(currentGem);
-// this.currentMatches.Add(aboveGem);
-// this.currentMatches.Add(bellowGem);
-// }
-// }
-// }
-// }
-// }
-//
-// if (this.currentMatches.Count > 0) this.currentMatches = this.currentMatches.Distinct().ToList();
-//
-// CheckForBombs();
-// }
-//
-// public void CheckForBombs()
-// {
-// for (int i = 0; i < this.currentMatches.Count; i++)
-// {
-// SC_Gem gem = this.currentMatches[i];
-// int x = gem.posIndex.x;
-// int y = gem.posIndex.y;
-//
-// if (gem.posIndex.x > 0)
-// {
-// if (this.allGems[x - 1, y] != null && this.allGems[x - 1, y].type == GemType.Bomb)
-// MarkBombArea(new Vector2Int(x - 1, y), this.allGems[x - 1, y].blastSize);
-// }
-//
-// if (gem.posIndex.x + 1 < this.width)
-// {
-// if (this.allGems[x + 1, y] != null && this.allGems[x + 1, y].type == GemType.Bomb)
-// MarkBombArea(new Vector2Int(x + 1, y), this.allGems[x + 1, y].blastSize);
-// }
-//
-// if (gem.posIndex.y > 0)
-// {
-// if (this.allGems[x, y - 1] != null && this.allGems[x, y - 1].type == GemType.Bomb)
-// MarkBombArea(new Vector2Int(x, y - 1), this.allGems[x, y - 1].blastSize);
-// }
-//
-// if (gem.posIndex.y + 1 < this.height)
-// {
-// if (this.allGems[x, y + 1] != null && this.allGems[x, y + 1].type == GemType.Bomb)
-// MarkBombArea(new Vector2Int(x, y + 1), this.allGems[x, y + 1].blastSize);
-// }
-// }
-// }
-//
-// public void MarkBombArea(Vector2Int bombPos, int _BlastSize)
-// {
-// string _print = "";
-// for (int x = bombPos.x - _BlastSize; x <= bombPos.x + _BlastSize; x++)
-// {
-// for (int y = bombPos.y - _BlastSize; y <= bombPos.y + _BlastSize; y++)
-// {
-// if (x >= 0 && x < this.width && y >= 0 && y < this.height)
-// {
-// if (this.allGems[x, y] != null)
-// {
-// _print += "(" + x + "," + y + ")" + System.Environment.NewLine;
-// this.allGems[x, y].isMatch = true;
-// this.currentMatches.Add(this.allGems[x, y]);
-// }
-// }
-// }
-// }
-//
-// this.currentMatches = this.currentMatches.Distinct().ToList();
-// }
-// }
-//
diff --git a/Assets/Scripts/GameBoard.cs.meta b/Assets/Scripts/GameBoard.cs.meta
deleted file mode 100644
index c6830de..0000000
--- a/Assets/Scripts/GameBoard.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 05610b3b492499a49bd43c2cc8260ab1
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Models/Gem.cs b/Assets/Scripts/Models/Gem.cs
index 8289e44..7d6788b 100644
--- a/Assets/Scripts/Models/Gem.cs
+++ b/Assets/Scripts/Models/Gem.cs
@@ -14,9 +14,10 @@ namespace Services {
public bool isMatch = false;
- public Gem(GemType type, Vector2Int position) {
+ public Gem(GemType type, Vector2Int position, int scoreValue) {
this.type = type;
this.position = position;
+ this.scoreValue = scoreValue;
}
public void SetPosition(Vector2Int position) {
diff --git a/Assets/Scripts/Presenter/ScorePresenter.cs b/Assets/Scripts/Presenter/ScorePresenter.cs
new file mode 100644
index 0000000..579c5bf
--- /dev/null
+++ b/Assets/Scripts/Presenter/ScorePresenter.cs
@@ -0,0 +1,30 @@
+using System;
+using Services.Interfaces;
+using VContainer.Unity;
+using Views;
+
+namespace Presenter {
+ public class ScorePresenter : IDisposable{
+ private IScoreService scoreService;
+ private ScoreView scoreView;
+ public ScorePresenter(IScoreService scoreService, ScoreView scoreView) {
+ this.scoreService = scoreService;
+ this.scoreView = scoreView;
+
+ this.scoreService.OnScoreChanged += OnScoreChanged;
+ this.scoreView.SetScore(this.scoreService.Score);
+ }
+
+ public void Tick() {
+ this.scoreView.UpdateScore();
+ }
+
+ private void OnScoreChanged(int score) {
+ this.scoreView.SetScore(score);
+ }
+
+ public void Dispose() {
+ this.scoreService.OnScoreChanged -= OnScoreChanged;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Presenter/ScorePresenter.cs.meta b/Assets/Scripts/Presenter/ScorePresenter.cs.meta
new file mode 100644
index 0000000..690ff59
--- /dev/null
+++ b/Assets/Scripts/Presenter/ScorePresenter.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: c48fea73cb7c491295823cea6675795d
+timeCreated: 1765733852
\ No newline at end of file
diff --git a/Assets/Scripts/SC_GameLogic.cs b/Assets/Scripts/SC_GameLogic.cs
deleted file mode 100644
index 01a755e..0000000
--- a/Assets/Scripts/SC_GameLogic.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-// using System.Collections;
-// using System.Collections.Generic;
-// using TMPro;
-// using UnityEngine;
-// using Enums;
-//
-// public class SC_GameLogic : MonoBehaviour
-// {
-// private Dictionary unityObjects; //this is a dictionary of all game objects in the scene
-// private int score = 0; //current score
-// private float displayScore = 0; //for animation, i think
-// private GameBoard gameBoard; //game board object
-// private GameState currentState = GameState.Move; //current game state
-// public GameState CurrentState { get { return this.currentState; } }
-//
-// #region MonoBehaviour
-// private void Awake()
-// {
-// Init();
-// }
-//
-// private void Start()
-// {
-// StartGame();
-// }
-//
-// private void Update()
-// {
-// this.displayScore = Mathf.Lerp(this.displayScore, this.gameBoard.Score, SC_GameVariables.Instance.scoreSpeed * Time.deltaTime);
-// this.unityObjects["Txt_Score"].GetComponent().text = this.displayScore.ToString("0");
-// }
-// #endregion
-//
-// #region Logic
-// private void Init()
-// {
-// this.unityObjects = new Dictionary();
-// GameObject[] _obj = GameObject.FindGameObjectsWithTag("UnityObject");
-// foreach (GameObject g in _obj) this.unityObjects.Add(g.name,g);
-//
-// this.gameBoard = new GameBoard(7, 7);
-// Setup();
-// }
-//
-// //GameBoardService
-// private void Setup()
-// {
-// for (int x = 0; x < this.gameBoard.Width; x++)
-// for (int y = 0; y < this.gameBoard.Height; y++)
-// {
-// Vector2 _pos = new Vector2(x, y);
-// GameObject _bgTile = Instantiate(SC_GameVariables.Instance.bgTilePrefabs, _pos, Quaternion.identity);
-// _bgTile.transform.SetParent(this.unityObjects["GemsHolder"].transform);
-// _bgTile.name = "BG Tile - " + x + ", " + y;
-//
-// int _gemToUse = Random.Range(0, SC_GameVariables.Instance.gems.Length);
-//
-// int iterations = 0;
-// while (this.gameBoard.MatchesAt(new Vector2Int(x, y), SC_GameVariables.Instance.gems[_gemToUse]) && iterations < 100)
-// {
-// _gemToUse = Random.Range(0, SC_GameVariables.Instance.gems.Length);
-// iterations++;
-// }
-// SpawnGem(new Vector2Int(x, y), SC_GameVariables.Instance.gems[_gemToUse]);
-// }
-// }
-// public void StartGame()
-// {
-// this.unityObjects["Txt_Score"].GetComponent().text = this.score.ToString("0");
-// }
-//
-// //GameBoardService
-// private void SpawnGem(Vector2Int _Position, SC_Gem _GemToSpawn)
-// {
-// if (Random.Range(0, 100f) < SC_GameVariables.Instance.bombChance)
-// _GemToSpawn = SC_GameVariables.Instance.bomb;
-//
-// SC_Gem _gem = Instantiate(_GemToSpawn, new Vector3(_Position.x, _Position.y + SC_GameVariables.Instance.dropHeight, 0f), Quaternion.identity);
-// _gem.transform.SetParent(this.unityObjects["GemsHolder"].transform);
-// _gem.name = "Gem - " + _Position.x + ", " + _Position.y;
-// this.gameBoard.SetGem(_Position.x,_Position.y, _gem);
-// _gem.SetupGem(this,_Position);
-// }
-//
-// //GameBoardService
-// public void SetGem(int _X,int _Y, SC_Gem _Gem)
-// {
-// this.gameBoard.SetGem(_X,_Y, _Gem);
-// }
-//
-// //GameBoardService
-// public SC_Gem GetGem(int _X, int _Y)
-// {
-// return this.gameBoard.GetGem(_X, _Y);
-// }
-// public void SetState(GameState _CurrentState)
-// {
-// this.currentState = _CurrentState;
-// }
-//
-// //GameBoardService
-// public void DestroyMatches()
-// {
-// for (int i = 0; i < this.gameBoard.CurrentMatches.Count; i++)
-// if (this.gameBoard.CurrentMatches[i] != null)
-// {
-// ScoreCheck(this.gameBoard.CurrentMatches[i]);
-// DestroyMatchedGemsAt(this.gameBoard.CurrentMatches[i].posIndex);
-// }
-//
-// StartCoroutine(DecreaseRowCo());
-// }
-//
-// //I think, this moves gems down after a match?
-// //MoveGemsDown
-// //GameBoardService
-// private IEnumerator DecreaseRowCo()
-// {
-// yield return new WaitForSeconds(.2f);
-//
-// int nullCounter = 0;
-// for (int x = 0; x < this.gameBoard.Width; x++)
-// {
-// for (int y = 0; y < this.gameBoard.Height; y++)
-// {
-// SC_Gem _curGem = this.gameBoard.GetGem(x, y);
-// if (_curGem == null)
-// {
-// nullCounter++;
-// }
-// else if (nullCounter > 0)
-// {
-// _curGem.posIndex.y -= nullCounter;
-// SetGem(x, y - nullCounter, _curGem);
-// SetGem(x, y, null);
-// }
-// }
-// nullCounter = 0;
-// }
-//
-// StartCoroutine(FilledBoardCo());
-// }
-//
-// //IScoreService
-// public void ScoreCheck(SC_Gem gemToCheck)
-// {
-// this.gameBoard.Score += gemToCheck.scoreValue;
-// }
-//
-// //GameBoardService - DestroyMatchedGems
-// private void DestroyMatchedGemsAt(Vector2Int _Pos)
-// {
-// SC_Gem _curGem = this.gameBoard.GetGem(_Pos.x,_Pos.y);
-// if (_curGem != null)
-// {
-// Instantiate(_curGem.destroyEffect, new Vector2(_Pos.x, _Pos.y), Quaternion.identity);
-//
-// Destroy(_curGem.gameObject);
-// SetGem(_Pos.x,_Pos.y, null);
-// }
-// }
-//
-// //GameBoardService - FillBoard
-//
-// private IEnumerator FilledBoardCo()
-// {
-// yield return new WaitForSeconds(0.5f);
-// RefillBoard();
-// yield return new WaitForSeconds(0.5f);
-// this.gameBoard.FindAllMatches();
-// if (this.gameBoard.CurrentMatches.Count > 0)
-// {
-// yield return new WaitForSeconds(0.5f);
-// DestroyMatches();
-// }
-// else
-// {
-// yield return new WaitForSeconds(0.5f);
-// this.currentState = GameState.Move;
-// }
-// }
-//
-// //GameBoardService
-// private void RefillBoard()
-// {
-// for (int x = 0; x < this.gameBoard.Width; x++)
-// {
-// for (int y = 0; y < this.gameBoard.Height; y++)
-// {
-// SC_Gem _curGem = this.gameBoard.GetGem(x,y);
-// if (_curGem == null)
-// {
-// int gemToUse = Random.Range(0, SC_GameVariables.Instance.gems.Length);
-// SpawnGem(new Vector2Int(x, y), SC_GameVariables.Instance.gems[gemToUse]);
-// }
-// }
-// }
-// CheckMisplacedGems();
-// }
-// //gets all gem game objects
-// //if the gem is not in GameBoard, destroy it
-// private void CheckMisplacedGems()
-// {
-// List foundGems = new List();
-// foundGems.AddRange(FindObjectsOfType());
-// for (int x = 0; x < this.gameBoard.Width; x++)
-// {
-// for (int y = 0; y < this.gameBoard.Height; y++)
-// {
-// SC_Gem _curGem = this.gameBoard.GetGem(x, y);
-// if (foundGems.Contains(_curGem))
-// foundGems.Remove(_curGem);
-// }
-// }
-//
-// foreach (SC_Gem g in foundGems)
-// Destroy(g.gameObject);
-// }
-// public void FindAllMatches()
-// {
-// this.gameBoard.FindAllMatches();
-// }
-//
-// #endregion
-// }
diff --git a/Assets/Scripts/SC_GameLogic.cs.meta b/Assets/Scripts/SC_GameLogic.cs.meta
deleted file mode 100644
index c061d6b..0000000
--- a/Assets/Scripts/SC_GameLogic.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: db185b18195d3814aab9ba4ac5f95047
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/SC_GameVariables.cs b/Assets/Scripts/SC_GameVariables.cs
deleted file mode 100644
index 18eca71..0000000
--- a/Assets/Scripts/SC_GameVariables.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// using System.Collections;
-// using System.Collections.Generic;
-// using UnityEngine;
-//
-// //Done
-// //This is basically just the settings, this can maybe be a ScriptableObject
-// public class SC_GameVariables : MonoBehaviour
-// {
-// public GameObject bgTilePrefabs;
-// public SC_Gem bomb;
-// public SC_Gem[] gems;
-// public float bonusAmount = 0.5f;
-// public float bombChance = 2f;
-// public int dropHeight = 0;
-// public float gemSpeed;
-// public float scoreSpeed = 5;
-//
-// [HideInInspector]
-// public int rowsSize = 7;
-// [HideInInspector]
-// public int colsSize = 7;
-//
-// #region Singleton
-//
-// static SC_GameVariables instance;
-// public static SC_GameVariables Instance
-// {
-// get
-// {
-// if (instance == null)
-// instance = GameObject.Find("SC_GameVariables").GetComponent();
-//
-// return instance;
-// }
-// }
-//
-// #endregion
-// }
diff --git a/Assets/Scripts/SC_GameVariables.cs.meta b/Assets/Scripts/SC_GameVariables.cs.meta
deleted file mode 100644
index 60c61bc..0000000
--- a/Assets/Scripts/SC_GameVariables.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 3b3b891bcdf21df4ab82ea34c3afc99e
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/SC_Gem.cs b/Assets/Scripts/SC_Gem.cs
deleted file mode 100644
index 8bf17f2..0000000
--- a/Assets/Scripts/SC_Gem.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-// using System.Collections;
-// using System.Collections.Generic;
-// using Enums;
-// using UnityEngine;
-//
-// //This is a SINGULAR gem
-// public class SC_Gem : MonoBehaviour
-// {
-// [HideInInspector]
-// public Vector2Int posIndex;
-//
-// private Vector2 firstTouchPosition;
-// private Vector2 finalTouchPosition;
-// private bool mousePressed;
-// private float swipeAngle = 0;
-// private SC_Gem otherGem;
-//
-// public GemType type;
-// public bool isMatch = false;
-// private Vector2Int previousPos;
-// public GameObject destroyEffect;
-// public int scoreValue = 10;
-//
-// public int blastSize = 1;
-// private SC_GameLogic scGameLogic;
-//
-// void Update()
-// {
-// //if the current position doesnt match the index in GameBoard, animate it to move to the correct position
-// //else, we update the GameBoard with the current possition, do we need this every frame????
-// if (Vector2.Distance(this.transform.position, this.posIndex) > 0.01f)
-// this.transform.position = Vector2.Lerp(this.transform.position, this.posIndex, SC_GameVariables.Instance.gemSpeed * Time.deltaTime);
-// else
-// {
-// this.transform.position = new Vector3(this.posIndex.x, this.posIndex.y, 0);
-// this.scGameLogic.SetGem(this.posIndex.x, this.posIndex.y, this);
-// }
-//
-// if (this.mousePressed && Input.GetMouseButtonUp(0))
-// {
-// this.mousePressed = false;
-// if (this.scGameLogic.CurrentState == GameState.Move)
-// {
-// this.finalTouchPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
-// CalculateAngle();
-// }
-// }
-// }
-//
-// public void SetupGem(SC_GameLogic _ScGameLogic,Vector2Int _Position)
-// {
-// this.posIndex = _Position;
-// this.scGameLogic = _ScGameLogic;
-// }
-//
-// private void OnMouseDown()
-// {
-// if (this.scGameLogic.CurrentState == GameState.Move)
-// {
-// this.firstTouchPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
-// this.mousePressed = true;
-// }
-// }
-//
-// private void CalculateAngle()
-// {
-// this.swipeAngle = Mathf.Atan2(this.finalTouchPosition.y - this.firstTouchPosition.y, this.finalTouchPosition.x - this.firstTouchPosition.x);
-// this.swipeAngle = this.swipeAngle * 180 / Mathf.PI;
-//
-// if (Vector3.Distance(this.firstTouchPosition, this.finalTouchPosition) > .5f)
-// MovePieces();
-// }
-//
-// private void MovePieces()
-// {
-// this.previousPos = this.posIndex;
-//
-// if (this.swipeAngle < 45 && this.swipeAngle > -45 && this.posIndex.x < SC_GameVariables.Instance.rowsSize - 1)
-// {
-// this.otherGem = this.scGameLogic.GetGem(this.posIndex.x + 1, this.posIndex.y);
-// this.otherGem.posIndex.x--;
-// this.posIndex.x++;
-//
-// }
-// else if (this.swipeAngle > 45 && this.swipeAngle <= 135 && this.posIndex.y < SC_GameVariables.Instance.colsSize - 1)
-// {
-// this.otherGem = this.scGameLogic.GetGem(this.posIndex.x, this.posIndex.y + 1);
-// this.otherGem.posIndex.y--;
-// this.posIndex.y++;
-// }
-// else if (this.swipeAngle < -45 && this.swipeAngle >= -135 && this.posIndex.y > 0)
-// {
-// this.otherGem = this.scGameLogic.GetGem(this.posIndex.x, this.posIndex.y - 1);
-// this.otherGem.posIndex.y++;
-// this.posIndex.y--;
-// }
-// else if (this.swipeAngle > 135 || this.swipeAngle < -135 && this.posIndex.x > 0)
-// {
-// this.otherGem = this.scGameLogic.GetGem(this.posIndex.x - 1, this.posIndex.y);
-// this.otherGem.posIndex.x++;
-// this.posIndex.x--;
-// }
-//
-// this.scGameLogic.SetGem(this.posIndex.x, this.posIndex.y, this);
-// this.scGameLogic.SetGem(this.otherGem.posIndex.x, this.otherGem.posIndex.y, this.otherGem);
-//
-// StartCoroutine(CheckMoveCo());
-// }
-//
-// public IEnumerator CheckMoveCo()
-// {
-// this.scGameLogic.SetState(GameState.Wait);
-//
-// yield return new WaitForSeconds(.5f);
-// this.scGameLogic.FindAllMatches();
-//
-// if (this.otherGem != null)
-// {
-// if (this.isMatch == false && this.otherGem.isMatch == false)
-// {
-// this.otherGem.posIndex = this.posIndex;
-// this.posIndex = this.previousPos;
-//
-// this.scGameLogic.SetGem(this.posIndex.x, this.posIndex.y, this);
-// this.scGameLogic.SetGem(this.otherGem.posIndex.x, this.otherGem.posIndex.y, this.otherGem);
-//
-// yield return new WaitForSeconds(.5f);
-// this.scGameLogic.SetState(GameState.Move);
-// }
-// else
-// {
-// this.scGameLogic.DestroyMatches();
-// }
-// }
-// }
-// }
diff --git a/Assets/Scripts/SC_Gem.cs.meta b/Assets/Scripts/SC_Gem.cs.meta
deleted file mode 100644
index bc8671f..0000000
--- a/Assets/Scripts/SC_Gem.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: c5d1e13e319aa044e9776fbd351e9d03
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Scopes/LevelLifetimeScope.cs b/Assets/Scripts/Scopes/LevelLifetimeScope.cs
index 7432a41..55a10f0 100644
--- a/Assets/Scripts/Scopes/LevelLifetimeScope.cs
+++ b/Assets/Scripts/Scopes/LevelLifetimeScope.cs
@@ -21,6 +21,8 @@ namespace Scopes
builder.RegisterInstance(this.gameVariables);
builder.RegisterInstance(this.gemsHolder);
+ builder.RegisterComponentInHierarchy();
+
builder.Register(c =>
new GameBoard(this.gameVariables.width, this.gameVariables.height),
Lifetime.Scoped);
@@ -32,8 +34,9 @@ namespace Scopes
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 10d3073..b0bf637 100644
--- a/Assets/Scripts/ScriptableObjects/GameVariables.cs
+++ b/Assets/Scripts/ScriptableObjects/GameVariables.cs
@@ -1,5 +1,7 @@
+using System;
using System.Collections.Generic;
using Enums;
+using Structs;
using UnityEngine;
using Views;
@@ -8,9 +10,7 @@ namespace ScriptableObjects {
[CreateAssetMenu(fileName = "GameVariables", menuName = "Game Variables")]
public class GameVariables : ScriptableObject {
public GameObject bgTilePrefabs;
- public GemView bombPrefab;
- public GemView[] gemsPrefabs;
- public GameObject[] destroyEffectPrefabs;
+ public GemTypeValues[] gemsPrefabs;
public float bonusAmount = 0.5f;
public float bombChance = 2f;
public int dropHeight = 1;
diff --git a/Assets/Scripts/Services/GameBoardService.cs b/Assets/Scripts/Services/GameBoardService.cs
index bdfc4a1..e3e34e8 100644
--- a/Assets/Scripts/Services/GameBoardService.cs
+++ b/Assets/Scripts/Services/GameBoardService.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
@@ -14,31 +15,38 @@ using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace Services {
- public class GameBoardService : IGameBoardService, ITickable {
+ public class GameBoardService : IGameBoardService, ITickable, IDisposable {
+ #region Inject
private readonly IGameBoard gameBoard;
private readonly GameVariables gameVariables;
private readonly IMatchService matchService;
private readonly IScoreService scoreService;
private readonly IObjectPool objectPool;
private readonly Transform gemsHolder;
+ #endregion
+ #region Variables
private readonly List gemPresenters = new List();
+ private readonly ScorePresenter scorePresenter;
+ private GameState currentState = GameState.Move;
+ #endregion
- public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IObjectPool objectPool, Transform gemsHolder) {
+ public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IObjectPool objectPool, Transform gemsHolder, ScorePresenter scorePresenter) {
this.gameBoard = gameBoard;
this.gameVariables = gameVariables;
this.matchService = matchService;
this.scoreService = scoreSerivce;
this.objectPool = objectPool;
this.gemsHolder = gemsHolder;
+ this.scorePresenter = scorePresenter;
}
public void Tick() {
- int i = 0;
foreach (GemPresenter gemPresenter in gemPresenters) {
gemPresenter.Tick();
- i++;
}
+
+ this.scorePresenter.Tick();
}
//Instantiates background tiles and calls SpawnGems
@@ -61,18 +69,20 @@ namespace Services {
iterations++;
}
- SpawnGem(new Vector2Int(x, y), this.gameVariables.gemsPrefabs[gemToUse], (GemType)gemToUse);
+ SpawnGem(new Vector2Int(x, y), (GemType)gemToUse);
}
+
+ this.currentState = GameState.Move;
}
//Uses the ObjectPool to spawn a gem at the given position
- public void SpawnGem(Vector2Int position, GemView gemPrefab, GemType gemType) {
+ private void SpawnGem(Vector2Int position, GemType gemType) {
if (Random.Range(0, 100f) < this.gameVariables.bombChance)
- gemPrefab = this.gameVariables.bombPrefab;
+ gemType = GemType.Bomb;
GemView gemView = this.objectPool.Get(gemType, position, this.gameVariables.dropHeight);
- gemView.name = "Gem - " + position.x + ", " + position.y;
- Gem gem = new Gem(gemType, position);
+ gemView.name = "Gem - " + position.x + ", " + position.y + ' ' + gemType;
+ Gem gem = new Gem(gemType, position, 50);
gemView.Bind(gem);
this.gemPresenters.Add(new GemPresenter(gem, gemView));
@@ -80,17 +90,65 @@ namespace Services {
}
//Sets the gem on the GameBoard
- public void SetGem(Vector2Int position, Gem gem) {
+ private 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) {
+ private Gem GetGem(Vector2Int position) {
return this.gameBoard.GetGemAt(position);
}
+
+ //Listens to InputService OnSwapRequest
+ public async UniTask TrySwap(Vector2Int from, Vector2Int to) {
+ if (this.currentState != GameState.Move)
+ return false;
+
+ if (!InBounds(from) || !InBounds(to))
+ return false;
+
+ if (!AreAdjacentCardinal(from, to))
+ return false;
+
+ Gem fromGem = GetGem(from);
+ Gem toGem = GetGem(to);
+
+ if(fromGem == null || toGem == null)
+ return false;
+
+ this.currentState = GameState.Wait;
+
+ ApplySwap(from, to, fromGem, toGem);
+
+ await UniTask.Delay(600);
+
+ this.matchService.FindAllMatches();
+ bool hasMatch = this.matchService.CurrentMatches.Count > 0;
+
+ if (!hasMatch) {
+ ApplySwap(to, from, fromGem, toGem);
+ await UniTask.Delay(600);
+ this.currentState = GameState.Move;
+ return false;
+ }
+
+ DestroyMatches();
+ this.currentState = GameState.Move;
+ return true;
+ }
+
+ private void ApplySwap(Vector2Int posA, Vector2Int posB, Gem gemA, Gem gemB) {
+ // swap their stored positions
+ gemA.SetPosition(posB);
+ gemB.SetPosition(posA);
+
+ // update grid
+ SetGem(posA, gemB);
+ SetGem(posB, gemA);
+ }
//If there are matches, destroys them and moves the gems down
- public void DestroyMatches() {
+ private void DestroyMatches() {
for (int i = 0; i < this.matchService.CurrentMatches.Count; i++)
if (this.matchService.CurrentMatches[i] != null)
{
@@ -101,8 +159,8 @@ namespace Services {
MoveGemsDown();
}
- public async UniTask MoveGemsDown() {
- await UniTask.Delay(2);
+ private async UniTask MoveGemsDown() {
+ await UniTask.Delay(50);
int nullCounter = 0;
for (int x = 0; x < this.gameBoard.Width; x++)
@@ -127,7 +185,7 @@ namespace Services {
await FillBoard();
}
- public async UniTask FillBoard() {
+ private async UniTask FillBoard() {
await UniTask.Delay(5);
RefillBoard();
await UniTask.Delay(5);
@@ -140,11 +198,11 @@ namespace Services {
else
{
await UniTask.Delay(5);
- // currentState = GameState.Move;
+ this.currentState = GameState.Move;
}
}
- public void RefillBoard() {
+ private void RefillBoard() {
for (int x = 0; x < this.gameBoard.Width; x++)
{
for (int y = 0; y < this.gameBoard.Height; y++)
@@ -152,39 +210,13 @@ namespace Services {
Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x,y));
if (currentGem == null) {
int gemToUse = RandomUtils.RandomGemTypeAsInt();
- SpawnGem(new Vector2Int(x, y), this.gameVariables.gemsPrefabs[gemToUse], (GemType)gemToUse);
+ SpawnGem(new Vector2Int(x, y), (GemType)gemToUse);
}
}
}
- CheckMisplacedGems();
}
- //Checks if there are gems that are not in the board
- public void CheckMisplacedGems() {
- List gemsViews = GemsViews();
-
- for (int x = 0; x < this.gameBoard.Width; x++)
- {
- for (int y = 0; y < this.gameBoard.Height; y++)
- {
- Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x,y));
- GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem);
-
- if (gemView != null) {
- gemsViews.Remove(gemView);
- }
- }
- }
-
- foreach (GemView g in gemsViews) {
- RemovePresenterFor(g);
-
- g.Unbind();
- this.objectPool.Release(g);
- }
- }
-
- public void DestroyMatchedGems(Vector2Int position) {
+ private void DestroyMatchedGems(Vector2Int position) {
List gemsViews = GemsViews();
Gem currentGem = this.gameBoard.GetGemAt(position);
if (currentGem != null)
@@ -194,30 +226,38 @@ namespace Services {
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);
-
- RemovePresenterFor(gemView);
- gemView.Unbind();
this.objectPool.Release(gemView);
+ RemovePresenterFor(gemView);
SetGem(position, null);
}
}
+ #region Utils
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);
- }
+
+ GemPresenter presenter = this.gemPresenters.FirstOrDefault(p => p.GemView == gemView);
+ this.gemPresenters.Remove(presenter);
}
private List GemsViews() {
return this.gemsHolder.GetComponentsInChildren().ToList();
}
+
+ private bool InBounds(Vector2Int p) {
+ return p.x >= 0 && p.x < this.gameBoard.Width && p.y >= 0 && p.y < this.gameBoard.Height;
+ }
+
+ private static bool AreAdjacentCardinal(Vector2Int a, Vector2Int b) {
+ Vector2Int d = b - a;
+ return (Mathf.Abs(d.x) == 1 && d.y == 0) || (Mathf.Abs(d.y) == 1 && d.x == 0);
+ }
+ #endregion
+
+ public void Dispose() {
+ this.objectPool.Clear();
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/InputService.cs b/Assets/Scripts/Services/InputService.cs
index 83c3766..9d802de 100644
--- a/Assets/Scripts/Services/InputService.cs
+++ b/Assets/Scripts/Services/InputService.cs
@@ -5,31 +5,105 @@ using UnityEngine;
namespace Services {
public class InputService : MonoBehaviour, IInputService
{
- public event Action OnPointerDown;
- public event Action OnPointerUp;
+ public event Action OnSwapRequested;
- private bool wasDown;
+ private Camera inputCamera;
+ private Vector2 pointerDownScreenPos;
+ private bool isPointerDown;
+
+ private readonly Vector2 boardOrigin = Vector2.zero;
+ private readonly float minDrag = 0.35f;
+ private readonly float cellSize = 1f;
+
+ private void Awake() {
+ this.inputCamera = Camera.main;
+ }
private void Update()
{
- // Mouse
- var isDown = Input.GetMouseButton(0);
- if (!wasDown && isDown)
- OnPointerDown?.Invoke(Input.mousePosition);
+ if (TryGetPrimaryPointerState(out var isDown, out var screenPos))
+ {
+ if (!this.isPointerDown && isDown) {
+ pointerDownScreenPos = screenPos;
+ }
- if (wasDown && !isDown)
- OnPointerUp?.Invoke(Input.mousePosition);
+ if (this.isPointerDown && !isDown) {
+ TryEmitSwap(pointerDownScreenPos, screenPos);
+ }
- wasDown = isDown;
+ this.isPointerDown = isDown;
+ }
+ else
+ {
+ // No pointer available this frame (rare). Ensure we don't get stuck.
+ this.isPointerDown = false;
+ }
+ }
- // Optional: Touch (if you want both, you can merge logic more carefully)
- if (Input.touchCount <= 0) return;
+ private void TryEmitSwap(Vector2 downScreen, Vector2 upScreen)
+ {
+ if (this.inputCamera == null) return;
- var t = Input.GetTouch(0);
- if (t.phase == TouchPhase.Began)
- OnPointerDown?.Invoke(t.position);
- else if (t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled)
- OnPointerUp?.Invoke(t.position);
+ Vector2 downWorld = this.inputCamera.ScreenToWorldPoint(downScreen);
+ Vector2 upWorld = this.inputCamera.ScreenToWorldPoint(upScreen);
+ Vector2 dragWorld = upWorld - downWorld;
+
+ if (dragWorld.magnitude < this.minDrag)
+ return;
+
+ Vector2Int fromCell = WorldToCell(downWorld);
+ Vector2Int dir = DragToCardinalDirection(dragWorld);
+ Vector2Int toCell = fromCell + dir;
+
+ // Adjacency is guaranteed by construction (to = from + dir).
+ // Debug.Log($"Swap {fromCell} -> {toCell}");
+ OnSwapRequested?.Invoke(fromCell, toCell);
+ }
+
+ private Vector2Int WorldToCell(Vector2 worldPos)
+ {
+ // Convert to board-local coords first
+ Vector2 local = (worldPos - this.boardOrigin) / this.cellSize;
+
+ // Assumes cell centers lie on integer coordinates in local space
+ int x = Mathf.FloorToInt(local.x + 0.5f);
+ int y = Mathf.FloorToInt(local.y + 0.5f);
+
+ return new Vector2Int(x, y);
+ }
+
+ private static Vector2Int DragToCardinalDirection(Vector2 dragWorld)
+ {
+ if (Mathf.Abs(dragWorld.x) >= Mathf.Abs(dragWorld.y))
+ return dragWorld.x >= 0 ? Vector2Int.right : Vector2Int.left;
+
+ return dragWorld.y >= 0 ? Vector2Int.up : Vector2Int.down;
+ }
+
+ private static bool TryGetPrimaryPointerState(out bool isDown, out Vector2 screenPosition)
+ {
+ // Prefer touch when present (mobile)
+ if (Input.touchCount > 0)
+ {
+ Touch touch = Input.GetTouch(0);
+ screenPosition = touch.position;
+
+ // Treat "Moved/Stationary" as still down, and "Began" as down.
+ isDown = touch.phase == TouchPhase.Began
+ || touch.phase == TouchPhase.Moved
+ || touch.phase == TouchPhase.Stationary;
+
+ // Ended/Canceled => not down (we still report position)
+ if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
+ isDown = false;
+
+ return true;
+ }
+
+ // Fallback to mouse (editor/desktop)
+ screenPosition = Input.mousePosition;
+ isDown = Input.GetMouseButton(0);
+ return true;
}
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/Interfaces/IGameBoardService.cs b/Assets/Scripts/Services/Interfaces/IGameBoardService.cs
index 0d19414..1b8f724 100644
--- a/Assets/Scripts/Services/Interfaces/IGameBoardService.cs
+++ b/Assets/Scripts/Services/Interfaces/IGameBoardService.cs
@@ -6,14 +6,7 @@ using Views;
namespace Services.Interfaces {
public interface IGameBoardService {
void Setup();
- void SpawnGem(Vector2Int position, GemView gemPrefab, GemType gemType);
- void SetGem(Vector2Int position, Gem gem);
- Gem GetGem(Vector2Int position);
- void DestroyMatches();
- UniTask MoveGemsDown();
- UniTask FillBoard();
- void RefillBoard();
- void CheckMisplacedGems();
- void DestroyMatchedGems(Vector2Int position);
+
+ UniTask TrySwap(Vector2Int from, Vector2Int to);
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/Interfaces/IInputService.cs b/Assets/Scripts/Services/Interfaces/IInputService.cs
index 9f1c6ce..07cf9cb 100644
--- a/Assets/Scripts/Services/Interfaces/IInputService.cs
+++ b/Assets/Scripts/Services/Interfaces/IInputService.cs
@@ -3,7 +3,6 @@ using UnityEngine;
namespace Services.Interfaces {
public interface IInputService {
- event Action OnPointerDown;
- event Action OnPointerUp;
+ event Action OnSwapRequested;
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/Interfaces/IMatchService.cs b/Assets/Scripts/Services/Interfaces/IMatchService.cs
index 717105f..cb59130 100644
--- a/Assets/Scripts/Services/Interfaces/IMatchService.cs
+++ b/Assets/Scripts/Services/Interfaces/IMatchService.cs
@@ -7,7 +7,6 @@ namespace Services.Interfaces {
List CurrentMatches { get; }
bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck);
void FindAllMatches();
- void CheckForBombs();
void MarkBombArea(Vector2Int bombPosition, int blastSize);
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/Interfaces/IScoreService.cs b/Assets/Scripts/Services/Interfaces/IScoreService.cs
index 89aa549..401de0d 100644
--- a/Assets/Scripts/Services/Interfaces/IScoreService.cs
+++ b/Assets/Scripts/Services/Interfaces/IScoreService.cs
@@ -1,5 +1,9 @@
+using System;
+
namespace Services.Interfaces {
public interface IScoreService {
+ event Action OnScoreChanged;
+ int Score { get; }
void ScoreCheck(int value);
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/LevelEntryPoint.cs b/Assets/Scripts/Services/LevelEntryPoint.cs
index fb19533..4e56199 100644
--- a/Assets/Scripts/Services/LevelEntryPoint.cs
+++ b/Assets/Scripts/Services/LevelEntryPoint.cs
@@ -9,16 +9,23 @@ namespace Services
{
private readonly IObjectPool gemViewPool;
private readonly IGameBoardService gameBoardService;
-
- public LevelEntryPoint(IObjectPool gemViewPool, IGameBoardService gameBoardService)
+ private readonly IInputService inputService;
+
+ public LevelEntryPoint(IObjectPool gemViewPool, IGameBoardService gameBoardService, IInputService inputService)
{
this.gemViewPool = gemViewPool;
this.gameBoardService = gameBoardService;
+ this.inputService = inputService;
}
public void Start()
{
this.gameBoardService.Setup();
+ this.inputService.OnSwapRequested += HandleSwapRequest;
+ }
+
+ private void HandleSwapRequest(Vector2Int from, Vector2Int to) {
+ this.gameBoardService.TrySwap(from, to);
}
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/MatchService.cs b/Assets/Scripts/Services/MatchService.cs
index 951efbe..24f1290 100644
--- a/Assets/Scripts/Services/MatchService.cs
+++ b/Assets/Scripts/Services/MatchService.cs
@@ -89,12 +89,61 @@ namespace Services {
CheckForBombs();
}
- public void CheckForBombs() {
- throw new System.NotImplementedException();
+ private void CheckForBombs() {
+ Gem[,] gems = this.gameBoard.GemsGrid;
+ int width = this.gameBoard.Width;
+ int height = this.gameBoard.Height;
+
+ for (int i = 0; i < this.currentMatches.Count; i++)
+ {
+ Gem gem = this.currentMatches[i];
+ int x = gem.Position.x;
+ int y = gem.Position.y;
+
+ Vector2Int[] directions =
+ {
+ Vector2Int.left,
+ Vector2Int.right,
+ Vector2Int.down,
+ Vector2Int.up
+ };
+
+ foreach (Vector2Int direction in directions)
+ {
+ int newX = x + direction.x;
+ int newY = y + direction.y;
+
+ if (newX < 0 || newX >= width || newY < 0 || newY >= height)
+ continue;
+
+ Gem neighbor = gems[newX, newY];
+ if (neighbor?.Type == GemType.Bomb)
+ MarkBombArea(new Vector2Int(newX, newY), 1);
+ }
+ }
}
public void MarkBombArea(Vector2Int bombPosition, int blastSize) {
- throw new System.NotImplementedException();
+ Gem[,] gems = this.gameBoard.GemsGrid;
+ int width = this.gameBoard.Width;
+ int height = this.gameBoard.Height;
+
+ for (int x = bombPosition.x - blastSize; x <= bombPosition.x + blastSize; x++)
+ {
+ for (int y = bombPosition.y - blastSize; y <= bombPosition.y + blastSize; y++)
+ {
+ if (x >= 0 && x < width && y >= 0 && y < height)
+ {
+ if (gems[x, y] != null)
+ {
+ gems[x, y].isMatch = true;
+ this.currentMatches.Add(gems[x, y]);
+ }
+ }
+ }
+ }
+
+ this.currentMatches = this.currentMatches.Distinct().ToList();
}
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/ObjectPoolService.cs b/Assets/Scripts/Services/ObjectPoolService.cs
index 481ec79..ccf7393 100644
--- a/Assets/Scripts/Services/ObjectPoolService.cs
+++ b/Assets/Scripts/Services/ObjectPoolService.cs
@@ -1,50 +1,65 @@
using System.Collections.Generic;
using Enums;
using Services.Interfaces;
+using Structs;
using UnityEngine;
+using Utils;
using Views;
using Object = UnityEngine.Object;
namespace Services {
public class ObjectPoolService:IObjectPool {
- private readonly GemView[] prefabs;
+ private readonly GemTypeValues[] gemValues;
private readonly Transform parent;
- private readonly Stack pool = new Stack();
+ private readonly Dictionary> gemTypeToPools = new Dictionary>();
- public ObjectPoolService(GemView[] prefabs, Transform parent) {
- this.prefabs = prefabs;
+ public ObjectPoolService(GemTypeValues[] gemValues, Transform parent) {
+ this.gemValues = gemValues;
this.parent = parent;
}
public GemView Get(GemType type, Vector2Int position, float dropHeight) {
- int typeAsInt = (int) type;
+ if (!this.gemTypeToPools.ContainsKey(type)) {
+ this.gemTypeToPools.Add(type, new Stack());
+ }
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();
+ if (this.gemTypeToPools[type].Count > 0) {
+ gemView = this.gemTypeToPools[type].Pop();
gemView.transform.localPosition = vector2Position;
return gemView;
}
- gemView = Object.Instantiate(this.prefabs[typeAsInt], vector2Position, Quaternion.identity, this.parent);
+ gemView = Object.Instantiate(GemUtils.GetGemValues(type, this.gemValues).gemPrefab, vector2Position, Quaternion.identity, this.parent);
return gemView;
}
public void Release(GemView gemView) {
- if (gemView == null)
+ if (gemView is null)
return;
+
+ Object.Instantiate(GemUtils.GetGemValues(gemView.Gem.Type, this.gemValues).explosionPrefab, gemView.transform.position, Quaternion.identity, this.parent);
+
+ if (!this.gemTypeToPools.ContainsKey(gemView.Gem.Type)) {
+ this.gemTypeToPools.Add(gemView.Gem.Type, new Stack());
+ }
gemView.gameObject.SetActive(false);
- this.pool.Push(gemView);
+ this.gemTypeToPools[gemView.Gem.Type].Push(gemView);
+ gemView.Unbind();
}
public void Clear() {
- this.pool.Clear();
+ foreach (Stack pool in this.gemTypeToPools.Values) {
+ pool.Clear();
+ }
+
+ this.gemTypeToPools.Clear();
}
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Services/ScoreService.cs b/Assets/Scripts/Services/ScoreService.cs
index 09af96a..35a20c7 100644
--- a/Assets/Scripts/Services/ScoreService.cs
+++ b/Assets/Scripts/Services/ScoreService.cs
@@ -1,11 +1,16 @@
using System;
using Services.Interfaces;
+using UnityEngine;
namespace Services {
public class ScoreService : IScoreService {
private int score = 0;
+ public int Score => this.score;
+ public event Action OnScoreChanged;
public void ScoreCheck(int value) {
this.score += value;
+
+ OnScoreChanged?.Invoke(this.score);
}
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Structs.meta b/Assets/Scripts/Structs.meta
new file mode 100644
index 0000000..a0f2a95
--- /dev/null
+++ b/Assets/Scripts/Structs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f21342694e084644b1e157c9c4292b4f
+timeCreated: 1765732834
\ No newline at end of file
diff --git a/Assets/Scripts/Structs/GemTypeValues.cs b/Assets/Scripts/Structs/GemTypeValues.cs
new file mode 100644
index 0000000..9b54b04
--- /dev/null
+++ b/Assets/Scripts/Structs/GemTypeValues.cs
@@ -0,0 +1,14 @@
+using System;
+using Enums;
+using UnityEngine;
+using Views;
+
+namespace Structs {
+ [Serializable]
+ public struct GemTypeValues {
+ public GemType type;
+ public GemView gemPrefab;
+ public GameObject explosionPrefab;
+ public int scoreValue;
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Structs/GemTypeValues.cs.meta b/Assets/Scripts/Structs/GemTypeValues.cs.meta
new file mode 100644
index 0000000..b8c637c
--- /dev/null
+++ b/Assets/Scripts/Structs/GemTypeValues.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 91d203259e7b4e62843f564a556aca72
+timeCreated: 1765732844
\ No newline at end of file
diff --git a/Assets/Scripts/Utils/GemUtils.cs b/Assets/Scripts/Utils/GemUtils.cs
new file mode 100644
index 0000000..e5ad539
--- /dev/null
+++ b/Assets/Scripts/Utils/GemUtils.cs
@@ -0,0 +1,14 @@
+using Enums;
+using Structs;
+
+namespace Utils {
+ public static class GemUtils {
+ public static GemTypeValues GetGemValues(GemType type, GemTypeValues[] gemValues) {
+ foreach (GemTypeValues gemValue in gemValues) {
+ if(gemValue.type == type) return gemValue;
+ }
+
+ return default;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Utils/GemUtils.cs.meta b/Assets/Scripts/Utils/GemUtils.cs.meta
new file mode 100644
index 0000000..2481863
--- /dev/null
+++ b/Assets/Scripts/Utils/GemUtils.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 23b7012d4cd64b99be4170c1f0ea9fdb
+timeCreated: 1765733100
\ No newline at end of file
diff --git a/Assets/Scripts/Views/GemView.cs b/Assets/Scripts/Views/GemView.cs
index b479354..d9a1c4c 100644
--- a/Assets/Scripts/Views/GemView.cs
+++ b/Assets/Scripts/Views/GemView.cs
@@ -37,7 +37,7 @@ namespace Views {
return;
if (Vector2.Distance(this.transform.position, positionBasedOnIndex.ToVector2()) > 0.01f) {
- this.transform.position = Vector2.Lerp(this.transform.position, positionBasedOnIndex.ToVector2(), 0.01f);
+ this.transform.position = Vector2.Lerp(this.transform.position, positionBasedOnIndex.ToVector2(), 0.05f);
}
}
}
diff --git a/Assets/Scripts/Views/ScoreView.cs b/Assets/Scripts/Views/ScoreView.cs
new file mode 100644
index 0000000..f33dbf4
--- /dev/null
+++ b/Assets/Scripts/Views/ScoreView.cs
@@ -0,0 +1,24 @@
+using System;
+using TMPro;
+using UnityEngine;
+
+namespace Views {
+ public class ScoreView : MonoBehaviour {
+ private TextMeshProUGUI scoreText;
+ private float displayScore = 0;
+ private int actualScore = 0;
+
+ private void Awake() {
+ this.scoreText = GetComponentInChildren();
+ }
+
+ public void UpdateScore() {
+ this.displayScore = Mathf.Lerp(this.displayScore, this.actualScore, 5 * Time.deltaTime);
+ this.scoreText.text = this.displayScore.ToString("0");
+ }
+
+ public void SetScore(int score) {
+ this.actualScore = score;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Views/ScoreView.cs.meta b/Assets/Scripts/Views/ScoreView.cs.meta
new file mode 100644
index 0000000..7f73981
--- /dev/null
+++ b/Assets/Scripts/Views/ScoreView.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 86d965b2c3d5474082911bdd360847ce
+timeCreated: 1765733889
\ No newline at end of file