# Unity 2D Point-and-Click System
Below is a summary of the Unity 2D point-and-click system I created, including all scripts and setup instructions. This system features a score system, a limited inventory with sprites, mouse-based pickups, sound effects, and save/load functionality using `PlayerPrefs`.
## Summary of the Unity 2D Point-and-Click System
This is a 2D point-and-click game built in Unity 6 where players:
- Click to pick up **Point Items** (e.g., coins) to increase a score.
- Click to collect **Collectable Items** (e.g., keys) into a limited inventory (5 slots) with sprite visuals.
- Hear sound effects on pickup (`pointPickup.wav` and `collectablePickup.wav`).
- See the score and inventory displayed via UI (TextMeshPro for score, sprite slots for inventory).
- Persist score and inventory between sessions using `PlayerPrefs`.
### Scripts Overview
1. **GameManager.cs**
- Manages the score, persists it with `PlayerPrefs`, and provides a singleton for global access.
- *Key Methods*: `AddScore`, `GetScore`, `SaveScore`, `LoadScore`.
2. **Item.cs**
- A serializable class to store an item’s name and sprite for the inventory.
3. **Inventory.cs**
- Manages a list of `Item` objects with a 5-slot limit, saves/loads via `PlayerPrefs`, and uses `Resources.Load` for sprites.
- *Key Methods*: `AddItem`, `GetItems`, `SaveInventory`, `LoadInventory`.
4. **ScoreUI.cs**
- Updates a TextMeshPro UI element to display the current score.
- *Key Method*: Updates `scoreText.text` in `Update`.
5. **PointItem.cs**
- Attached to point-giving items; adds points and plays a sound on click, then destroys itself.
- *Key Method*: `OnMouseDown`.
6. **CollectableItem.cs**
- Attached to collectable items; adds to inventory with its sprite, plays a sound, updates UI, and destroys itself.
- *Key Method*: `OnMouseDown`.
7. **InventoryUI.cs**
- Displays inventory items as sprites in 5 UI Image slots, updating when items are added or loaded.
- *Key Method*: `UpdateInventoryUI`.
8. **AudioManager.cs**
- Plays sound effects for pickups using an `AudioSource` and two `AudioClip`s.
- *Key Methods*: `PlayPointPickup`, `PlayCollectablePickup`.
---
## All Scripts
### GameManager.cs
```csharp
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
private int score = 0;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
LoadScore();
}
else
{
Destroy(gameObject);
}
}
public void AddScore(int points)
{
score += points;
SaveScore();
Debug.Log("Score: " + score);
}
public int GetScore()
{
return score;
}
void SaveScore()
{
PlayerPrefs.SetInt("PlayerScore", score);
PlayerPrefs.Save();
}
void LoadScore()
{
score = PlayerPrefs.GetInt("PlayerScore", 0);
}
}
```
---
### Item.cs
```csharp
using UnityEngine;
[System.Serializable]
public class Item
{
public string itemName;
public Sprite itemSprite;
public Item(string name, Sprite sprite)
{
itemName = name;
itemSprite = sprite;
}
}
```
---
### Inventory.cs
```csharp
using UnityEngine;
using System.Collections.Generic;
public class Inventory : MonoBehaviour
{
public static Inventory Instance;
public int maxSlots = 5;
private List<Item> items = new List<Item>();
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
LoadInventory();
}
else
{
Destroy(gameObject);
}
}
public bool AddItem(string itemName, Sprite itemSprite)
{
if (items.Count < maxSlots)
{
items.Add(new Item(itemName, itemSprite));
SaveInventory();
Debug.Log("Collected: " + itemName + " | Inventory: " + items.Count + "/" + maxSlots);
return true;
}
else
{
Debug.Log("Inventory full!");
return false;
}
}
public List<Item> GetItems()
{
return items;
}
void SaveInventory()
{
PlayerPrefs.SetInt("InventoryCount", items.Count);
for (int i = 0; i < items.Count; i++)
{
PlayerPrefs.SetString("ItemName" + i, items[i].itemName);
}
PlayerPrefs.Save();
}
void LoadInventory()
{
items.Clear();
int count = PlayerPrefs.GetInt("InventoryCount", 0);
for (int i = 0; i < count; i++)
{
string itemName = PlayerPrefs.GetString("ItemName" + i, "");
Sprite itemSprite = Resources.Load<Sprite>("Items/" + itemName);
if (itemSprite != null)
items.Add(new Item(itemName, itemSprite));
}
InventoryUI inventoryUI = FindObjectOfType<InventoryUI>();
if (inventoryUI != null) inventoryUI.UpdateInventoryUI();
}
}
```
---
### ScoreUI.cs
```csharp
using UnityEngine;
using TMPro;
public class ScoreUI : MonoBehaviour
{
public TextMeshProUGUI scoreText;
void Update()
{
scoreText.text = "Score: " + GameManager.Instance.GetScore();
}
}
```
---
### PointItem.cs
```csharp
using UnityEngine;
public class PointItem : MonoBehaviour
{
public int pointsValue = 10;
void OnMouseDown()
{
GameManager.Instance.AddScore(pointsValue);
AudioManager.Instance.PlayPointPickup();
Destroy(gameObject);
}
}
```
---
### CollectableItem.cs
```csharp
using UnityEngine;
public class CollectableItem : MonoBehaviour
{
public string itemName = "Key";
public Sprite itemSprite;
void Awake()
{
if (itemSprite == null && GetComponent<SpriteRenderer>() != null)
itemSprite = GetComponent<SpriteRenderer>().sprite;
}
void OnMouseDown()
{
if (Inventory.Instance.AddItem(itemName, itemSprite))
{
AudioManager.Instance.PlayCollectablePickup();
InventoryUI inventoryUI = FindObjectOfType<InventoryUI>();
if (inventoryUI != null) inventoryUI.UpdateInventoryUI();
Destroy(gameObject);
}
}
}
```
---
### InventoryUI.cs
```csharp
using UnityEngine;
using UnityEngine.UI;
public class InventoryUI : MonoBehaviour
{
public Image[] inventorySlots;
void Start()
{
if (inventorySlots.Length != Inventory.Instance.maxSlots)
{
Debug.LogError("Inventory slots in UI (" + inventorySlots.Length + ") must match maxSlots (" + Inventory.Instance.maxSlots + ")!");
}
UpdateInventoryUI();
}
public void UpdateInventoryUI()
{
var items = Inventory.Instance.GetItems();
for (int i = 0; i < inventorySlots.Length; i++)
{
if (i < items.Count)
{
inventorySlots[i].sprite = items[i].itemSprite;
inventorySlots[i].color = Color.white;
}
else
{
inventorySlots[i].sprite = null;
inventorySlots[i].color = new Color(1, 1, 1, 0);
}
}
}
}
```
---
### AudioManager.cs
```csharp
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance;
public AudioSource audioSource;
public AudioClip pointPickupSound;
public AudioClip collectablePickupSound;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
audioSource = GetComponent<AudioSource>();
}
public void PlayPointPickup()
{
if (pointPickupSound != null)
audioSource.PlayOneShot(pointPickupSound);
}
public void PlayCollectablePickup()
{
if (collectablePickupSound != null)
audioSource.PlayOneShot(collectablePickupSound);
}
}
```
---
## Directions for Setting Up in a Unity Scene
### Scene Setup
1. **Create Managers**:
- Create an empty GameObject named "GameManager".
- Attach `GameManager.cs` and `Inventory.cs`.
- Create an empty GameObject named "AudioManager".
- Attach `AudioManager.cs`.
- Add an `AudioSource` component.
- Assign `pointPickupSound` and `collectablePickupSound` (e.g., "Assets/Sounds/pointPickup.wav" and "Assets/Sounds/collectablePickup.wav").
- see [[Creating Audio (Tools)]] about how to create custom audio.
2. **Set Up UI**:
- **Score UI**:
- Go to **GameObject > UI > Text - TextMeshPro**. Name it "ScoreTextTMP".
- Position it (e.g., top-left).
- Attach `ScoreUI.cs` and assign the `TextMeshProUGUI` component to `scoreText`.
- **Inventory UI**:
- Go to **GameObject > UI > Panel**. Name it "InventoryPanel".
- Position it (e.g., bottom of screen, Height: 100).
- Inside "InventoryPanel", create 5 **GameObject > UI > Image** objects (e.g., "Slot1" to "Slot5").
- Size each slot (e.g., 64x64) and arrange horizontally.
- Attach `InventoryUI.cs` to "InventoryPanel" and drag the 5 Image objects into the `inventorySlots` array.
3. **Create Item Prefabs**:
- **Point Item**:
- Create a 2D sprite (e.g., a coin) in the scene.
- Add a `BoxCollider2D` (or similar 2D collider).
- Attach `PointItem.cs` and set `pointsValue` (e.g., 10).
- Drag to "Assets/Prefabs" to make a prefab.
- **Collectable Item**:
- Create a 2D sprite (e.g., a key).
- Add a `BoxCollider2D`.
- Attach `CollectableItem.cs`, set `itemName` (e.g., "Key"), and optionally assign `itemSprite` (or it auto-grabs from SpriteRenderer).
- Drag to "Assets/Prefabs".
- Place sprites in "Assets/Resources/Items/" (e.g., "Key.png") for `PlayerPrefs` loading.
4. **Add Items to Scene**:
- Drag instances of the Point Item and Collectable Item prefabs into your scene.
5. **Camera Setup**:
- Ensure the Main Camera is Orthographic (default for 2D) and covers the play area.
### Testing
- **Run the Game**:
- Click a Point Item: Score increases, sound plays, item disappears.
- Click a Collectable Item: Adds to inventory (sprite appears in UI), sound plays, item disappears (stops at 5 items).
- Quit and restart: Score and inventory persist via `PlayerPrefs`.