Hello everyone. I am a newbie and I am trying to make a 2D farm game and I am following a tutorial on how to make it, where unfortunately many steps are missing. I have encountered a problem in developing a store in the game. My store works on the principle that the player drags an object to the playing field and the object is placed if the player has enough money (money is taken away) or the object disappears if the player does not have enough money. I already have one similar mechanism for opening new territories in my game and I wanted to use its likeness this time. I found the most suitable option to put this check (if the player has enough money or not) in public void CheckPlacement() - all scripts will be below. But when I implement a similar system for buying from the store, either the installation of objects gives bugs (multiple installation, or items disappear) or I just get the error “KeyNotFoundException: The given key ‘EnoughCurrencyGameEvent’ was not present in the dictionary.
System.Collections.Generic.Dictionary2[TKey,TValue].get_Item (TKey key)”, although it seems to me that I register events and listeners correctly. In any case, I will be very grateful if someone helps me fix my mistake or suggests a more suitable solution for the store. Thanks!
Part of Territory sctipt, which I used like expample:
public class Territory : MonoBehaviour, IPointerClickHandler
{
...
//amount of currency needed to unlock the territory
public int gemsAmount;
....
public void OnPointerClick(PointerEventData eventData)
{
...
window.Find("Buy Button").GetComponent<Button>().onClick.AddListener(delegate
{
EventManager.Instance.AddListenerOnce<EnoughCurrencyGameEvent>(OnEnoughCurrency);
EventManager.Instance.AddListenerOnce<NotEnoughCurrencyGameEvent>(OnNotEnoughCurrency);
CurrencyChangeGameEvent info = new CurrencyChangeGameEvent(-gemsAmount, CurrencyType.Gems);
EventManager.Instance.QueueEvent(info);
Destroy(holder);
isWindowOpen = false;
Deselect();
});
window.Find("Amount Text").GetComponent<TextMeshProUGUI>().text = gemsAmount.ToString();
PanZoom.current.Focus(transform.position);
Select();
}
...
}
private void OnEnoughCurrency(EnoughCurrencyGameEvent info)
{
//unlock territory if the player has enough currency
UnlockTerritory();
//remove listener of the opposite event
EventManager.Instance.RemoveListener<NotEnoughCurrencyGameEvent>(OnNotEnoughCurrency);
}
private void OnNotEnoughCurrency(NotEnoughCurrencyGameEvent info)
{
//remove listener of the opposite event
EventManager.Instance.RemoveListener<EnoughCurrencyGameEvent>(OnEnoughCurrency);
}
...
}
Here a script of PlaceableObject, where I tried implemet similar system but bugs were everywhere
public class PlaceableObject : MonoBehaviour
{
//if the building is placed or not
public bool Placed { get; private set; }
//position on which an object was placed
//(save it if the new position is not available)
private Vector3 origin;
//area under the house - stores position and building size
public BoundsInt area;
public int Price;
public CurrencyType Currency;
//Check if the building can be placed at its current position
public bool CanBePlaced()
{
//create an area under the building
Vector3Int positionInt = BuildingSystem.current.gridLayout.LocalToCell(transform.position);
BoundsInt areaTemp = area;
areaTemp.position = positionInt;
//call the GridBuildingSystem to check the area
return BuildingSystem.current.CanTakeArea(areaTemp);
}
//Make the building placed
public void Place()
{
//create an area under the building
Vector3Int positionInt = BuildingSystem.current.gridLayout.LocalToCell(transform.position);
BoundsInt areaTemp = area;
areaTemp.position = positionInt;
//set the bool
Placed = true;
//save position
origin = transform.position;
//call the system to
BuildingSystem.current.TakeArea(areaTemp);
PanZoom.current.UnfollowObject();
}
public void CheckPlacement()
{
// If the object is new and hasn't been placed yet
if (!Placed)
{
// Subscribe to the event if there is enough currency
EventManager.Instance.AddListenerOnce<EnoughCurrencyGameEvent>(OnEnoughCurrency);
// Subscribe to the event if there isn't enough currency
EventManager.Instance.AddListenerOnce<NotEnoughCurrencyGameEvent>(OnNotEnoughCurrency);
// Create an event to attempt spending currency
CurrencyChangeGameEvent currencyChangeEvent = new CurrencyChangeGameEvent(-Price, Currency);
// Trigger the event to change the amount of currency
EventManager.Instance.QueueEvent(currencyChangeEvent);
// Check if the object still exists and can be placed
if (this != null && CanBePlaced())
{
// Place the object at the desired location
Place();
}
else if (this != null)
{
// If the placement is not possible, destroy the object
Destroy(transform.gameObject);
}
// After placing the object or destroying it, open the shop
ShopManager.current.ShopButton_Click();
}
// If the object was previously placed and is now being edited
else
{
// If the object cannot be placed at the new location
if (!CanBePlaced())
{
// Check if the object still exists
if (this != null)
{
// Return the object to its original position
transform.position = origin;
}
}
// Place the object again (at the new location)
if (this != null)
{
Place();
}
}
}
private void OnEnoughCurrency(EnoughCurrencyGameEvent info)
{
// Check if the object still exists and can be placed
if (this != null && CanBePlaced())
{
// Place the object at the desired location
Place();
}
else if (this != null)
{
// If the placement is not possible, destroy the object
Destroy(transform.gameObject);
}
// Unsubscribe from events after the operation is completed
EventManager.Instance.RemoveListener<EnoughCurrencyGameEvent>(OnEnoughCurrency);
EventManager.Instance.RemoveListener<NotEnoughCurrencyGameEvent>(OnNotEnoughCurrency);
}
private void OnNotEnoughCurrency(NotEnoughCurrencyGameEvent info)
{
// Check if the object still exists before destroying it
if (this != null)
{
// Destroy the object because there isn't enough currency
Destroy(transform.gameObject);
}
// Unsubscribe from events after the operation is completed
EventManager.Instance.RemoveListener<EnoughCurrencyGameEvent>(OnEnoughCurrency);
EventManager.Instance.RemoveListener<NotEnoughCurrencyGameEvent>(OnNotEnoughCurrency);
}
....
}
and the last one is a script of CurrencySystem, where, I guess, also could be a problem:
public class CurrencySystem : MonoBehaviour
{
//all player's treasures
private static Dictionary<CurrencyType, int> CurrencyAmounts = new Dictionary<CurrencyType, int>();
//currency texts
[SerializeField] private List<GameObject> texts;
//currency texts in a dictionary (for easier access)
private Dictionary<CurrencyType, TextMeshProUGUI> currencyTexts =
new Dictionary<CurrencyType, TextMeshProUGUI>();
private void Awake()
{
//initialize dictionaries
for (int i = 0; i < texts.Count; i++)
{
CurrencyAmounts.Add((CurrencyType)i, 0);
currencyTexts.Add((CurrencyType)i, texts[i].transform.GetChild(1).GetComponent<TextMeshProUGUI>());
}
}
private void Start()
{
//give the player some currency
CurrencyAmounts[CurrencyType.Mana] = 200;
CurrencyAmounts[CurrencyType.Coins] = 1000;
CurrencyAmounts[CurrencyType.Gems] = 10;
//update UI texts to reflect the right amount
UpdateUI();
//add listeners for currency change events and not enough currency
EventManager.Instance.AddListener<CurrencyChangeGameEvent>(OnCurrencyChange);
EventManager.Instance.AddListener<NotEnoughCurrencyGameEvent>(OnNotEnough);
}
private void UpdateUI()
{
//set new currency amounts
for (int i = 0; i < texts.Count; i++)
{
currencyTexts[(CurrencyType)i].text = CurrencyAmounts[(CurrencyType)i].ToString();
}
}
private void OnCurrencyChange(CurrencyChangeGameEvent info)
{
//if the player's trying to spend currency
if (info.amount < 0)
{
if (CurrencyAmounts[info.currencyType] < Math.Abs(info.amount))
{
EventManager.Instance.QueueEvent(new NotEnoughCurrencyGameEvent(info.amount, info.currencyType));
return;
}
EventManager.Instance.QueueEvent(new EnoughCurrencyGameEvent());
}
//change currency amount
CurrencyAmounts[info.currencyType] += info.amount;
//update currency texts
currencyTexts[info.currencyType].text = CurrencyAmounts[info.currencyType].ToString();
UpdateUI();
}
private void OnNotEnough(NotEnoughCurrencyGameEvent info)
{
//display that the player doesn't have any currency
Debug.Log($"You don't have enough of {info.amount} {info.currencyType}");
}
}
public enum CurrencyType
{
Mana,
Coins,
Gems
}
If you have scrolled all the way to the end of my question, then thank you very much for your patience!