Welcome to our tutorial on custom map creation for our dynamic 2D portrait game! Our game offers pre-made levels with obstacles, all presented in a unique portrait orientation. But the real excitement lies in the ability for users to design their own maps, placing obstacles and challenges as they see fit. With our grid-based editor, creating custom maps is intuitive and fun. Save your creations for later. Join us as we explore the process of crafting your own adventure in our immersive 2D game!
CameraController.cs
using UnityEngine;
using UnityEngine.UI;
public class CameraController : MonoBehaviour
{
public Slider slider; // Reference to the UI Slider
public float minY = 0f; // Minimum y position
public float maxY = 38f; // Maximum y position
public float sensitivity = 0.1f; // Touch or mouse sensitivity
private Vector2 lastInputPosition;
void Start()
{
// Subscribe to the slider's OnValueChanged event
slider.onValueChanged.AddListener(OnSliderValueChanged);
// Set initial slider value based on the camera's initial position
UpdateSliderValue();
}
void Update()
{
if (Input.touchSupported && Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
lastInputPosition = touch.position;
}
else if (touch.phase == TouchPhase.Moved)
{
Vector2 delta = touch.position - lastInputPosition;
float deltaY = delta.y * sensitivity;
// Invert deltaY to match touch direction
deltaY *= -1;
MoveCamera(deltaY);
lastInputPosition = touch.position;
}
}
else if (Input.mousePresent)
{
if (Input.GetMouseButtonDown(0))
{
lastInputPosition = Input.mousePosition;
}
else if (Input.GetMouseButton(0))
{
Vector2 delta = (Vector2)Input.mousePosition - lastInputPosition;
float deltaY = delta.y * sensitivity;
MoveCamera(deltaY);
lastInputPosition = Input.mousePosition;
}
}
}
void MoveCamera(float deltaY)
{
// Invert deltaY to reverse camera movement
//deltaY *= -1;
// Update camera position within range
Vector3 newPosition = transform.position + new Vector3(0f, deltaY, 0f);
newPosition.y = Mathf.Clamp(newPosition.y, minY, maxY);
transform.position = newPosition;
// Update slider value based on camera's new position
UpdateSliderValue();
}
void UpdateSliderValue()
{
// Invert camera's Y-position to reverse slider value
float invertedY = maxY - (transform.position.y - minY);
// Map inverted camera's Y-position to slider value within range
float sliderValue = Mathf.InverseLerp(minY, maxY, invertedY);
slider.value = sliderValue;
}
void OnSliderValueChanged(float value)
{
// Invert the slider value to reverse the movement
value = 1f - value;
// Map inverted slider value to camera's Y-position within range
float newY = Mathf.Lerp(minY, maxY, value);
Vector3 newPosition = transform.position;
newPosition.y = newY;
transform.position = newPosition;
}
}
This script is a CameraController
class responsible for handling camera movement in a 2D environment using touch input or mouse input. It also syncs the camera’s position with a UI Slider to provide visual feedback of the camera’s vertical position and allows the user to control it.
Here’s a breakdown of its functionalities:
- Slider Syncing: The script synchronizes the camera’s vertical position with a UI Slider’s value, providing visual feedback to the user about the camera’s position in the scene.
- Touch and Mouse Input Handling: The script detects touch or mouse input and calculates the delta movement of the input device.
- Camera Movement: It moves the camera vertically based on the input received, ensuring it stays within specified bounds (minimum and maximum Y positions).
- Inverted Movement: The script provides an option to invert the camera’s movement direction by uncommenting the respective lines.
- Slider Value Update: When the camera moves, the script updates the Slider’s value to reflect the new camera position, allowing users to interactively control the camera’s position.
- Slider Value Change Handling: The script also listens for changes in the Slider’s value and adjusts the camera’s position accordingly.
Overall, this script provides a simple and intuitive way to control the camera’s movement in a 2D environment, enhancing the user experience in your game or application.
GridSpawner.cs
using UnityEngine;
public class GridSpawner : MonoBehaviour
{
public GameObject objectToSpawn; // The object to spawn
public Vector3 startingPosition; // Starting position for spawning
public float gridWidth = 10f; // Width of the grid area
public float gridHeight = 10f; // Height of the grid area
public int rows = 5; // Number of rows in the grid
public int columns = 5; // Number of columns in the grid
void OnDrawGizmosSelected()
{
// Calculate the starting position based on gridHeight
Vector3 adjustedStartingPosition = startingPosition + new Vector3(0f, gridHeight, 0f);
// Draw wireframe cube for the grid area
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(adjustedStartingPosition + new Vector3(gridWidth / 2f, -gridHeight / 2f, 0f), new Vector3(gridWidth, gridHeight, 0f));
// Calculate row and column spacing
float rowSpacing = gridHeight / (float)(rows - 1);
float columnSpacing = gridWidth / (float)(columns - 1);
// Draw grid lines for rows
for (int i = 0; i < rows; i++)
{
Vector3 rowStart = adjustedStartingPosition + new Vector3(0f, -i * rowSpacing, 0f);
Vector3 rowEnd = rowStart + new Vector3(gridWidth, 0f, 0f);
Gizmos.DrawLine(rowStart, rowEnd);
}
// Draw grid lines for columns
for (int i = 0; i < columns; i++)
{
Vector3 columnStart = adjustedStartingPosition + new Vector3(i * columnSpacing, 0f, 0f);
Vector3 columnEnd = columnStart + new Vector3(0f, -gridHeight, 0f);
Gizmos.DrawLine(columnStart, columnEnd);
}
}
void Awake()
{
SpawnGrid();
}
void SpawnGrid()
{
// Calculate row and column spacing
float rowSpacing = gridHeight / (float)(rows - 1);
float columnSpacing = gridWidth / (float)(columns - 1);
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < columns; col++)
{
// Calculate the x, y, and z positions for the current grid cell
float xPosition = startingPosition.x + col * columnSpacing;
float yPosition = startingPosition.y + row * rowSpacing;
float zPosition = startingPosition.z;
// Spawn the object at the calculated position
Vector3 spawnPosition = new Vector3(xPosition, yPosition, zPosition);
GameObject newObj = Instantiate(objectToSpawn, spawnPosition, Quaternion.identity);
newObj.transform.SetParent(this.gameObject.transform);
// Attach a ClickableObject component to the instantiated object
ClickableObject clickableObj = newObj.AddComponent<ClickableObject>();
clickableObj.OnClick += (clickableObj,position) => MapManager.Instance.HandleClick(clickableObj,position);
}
}
}
}
This script, GridSpawner
, is responsible for spawning a grid layout of objects in a 2D environment. It provides a visual representation of the grid in the Unity Editor using Gizmos and spawns objects at each grid point based on specified parameters.
Here’s a breakdown of its functionalities:
- Gizmos Drawing: The
OnDrawGizmosSelected
method draws a wireframe cube representing the grid area in the Unity Editor. It also draws grid lines for rows and columns within this area. - Object Spawning: The
SpawnGrid
method calculates the positions for each grid cell based on the specified number of rows and columns. It then instantiates theobjectToSpawn
GameObject at each calculated position, creating a grid layout of objects. - ClickableObject Component: Upon instantiation of each object, a
ClickableObject
component is attached. This component allows for interaction with the spawned objects by detecting clicks on them. When an object is clicked, it triggers a callback to theMapManager
instance, passing the clicked object and its position. - Grid Parameters: The script allows customization of the grid’s width, height, number of rows, and number of columns, providing flexibility in designing various grid layouts.
Overall, this script provides a convenient way to spawn a grid layout of objects in Unity, which can be used for various purposes such as placing obstacles or creating game levels with structured layouts.
ClickableObject.cs
using UnityEngine;
public class ClickableObject : MonoBehaviour
{
public event System.Action<GameObject,Vector3> OnClick;
void OnMouseDown()
{
Debug.Log("Clicked on obstacle");
OnClick?.Invoke(gameObject,transform.position);
}
}
The ClickableObject
script enables interaction with GameObjects by detecting mouse clicks on them. It utilizes Unity’s OnMouseDown
method to trigger an event when a GameObject is clicked.
Here’s a breakdown of its functionality:
- OnClick Event: The script defines an event named
OnClick
usingSystem.Action<GameObject,Vector3>
. This event allows other scripts to subscribe to it and be notified when a GameObject is clicked. - OnMouseDown Method: When a GameObject with this script attached is clicked, Unity calls the
OnMouseDown
method. Within this method, it logs a debug message indicating that the object has been clicked and then invokes theOnClick
event. It passes two parameters: the clicked GameObject itself (gameObject
) and its position (transform.position
). - Event Invocation: The
OnClick
event is invoked using the null-conditional operator (?.
). This ensures that the event is only invoked if there are subscribers to it, preventing null reference errors.
Overall, the ClickableObject
script provides a simple and efficient way to handle mouse clicks on GameObjects in Unity, making them interactive and responsive to user input.
MapManager.cs
using System.Collections;
using System.Collections.Generic;
using UMGS;
using UnityEngine;
using UnityEngine.Serialization;
public class MapManager : SingletonPersistent<MapManager>
{
public GameObject obstaclesPanel;
private GameObject pointObject;
private Vector3 position;
public GameObject grid;
public GameObject obstaclesHolder;
public GameObject mapsGallery;
public GameObject galleryMapButton;
public void HandleClick(GameObject pointObject,Vector3 position)
{
Debug.Log("Clicked object name: "+pointObject.name+"\n"+"Clicked object position: " + position);
obstaclesPanel.SetActive(true);
this.pointObject = pointObject;
this.position = position;
}
public void SpawnObstacle(GameObject objectToSpawn,bool isNone)
{
Destroy(pointObject);
GameObject newObj = Instantiate(objectToSpawn, position, Quaternion.identity);
if (isNone)
{
newObj.GetComponent<SpriteRenderer>().sortingOrder = 1;
newObj.transform.SetParent(grid.transform);
}
else
{
newObj.GetComponent<SpriteRenderer>().sortingOrder = 2;
newObj.transform.SetParent(obstaclesHolder.transform);
}
ClickableObject clickableObj = newObj.AddComponent<ClickableObject>();
clickableObj.OnClick += (clickableObj,position) => HandleClick(clickableObj,position);
obstaclesPanel.SetActive(false);
}
public void ShowLevelEditor()
{
grid.SetActive(true);
}
public void hideLevelEditor()
{
grid.SetActive(false);
}
}
The MapManager
script is responsible for managing the creation and manipulation of maps in your game environment. It handles user clicks on grid points, spawns obstacles, manages obstacle layers, and provides functionality to show/hide the level editor.
Here’s a breakdown of its functionalities:
- HandleClick Method: This method is invoked when a grid point is clicked. It logs the name and position of the clicked object, activates the obstacles panel for further interaction, and stores the clicked object and its position for future use.
- SpawnObstacle Method: This method spawns obstacles at the clicked grid point. It destroys the clicked point object, instantiates the obstacle object at the stored position, and assigns it to either the grid or obstacles holder based on the
isNone
parameter. It also adjusts the sorting order of the obstacle object to ensure proper layering. Additionally, it attaches aClickableObject
component to the spawned obstacle for further interaction. - ShowLevelEditor and hideLevelEditor Methods: These methods respectively show and hide the level editor grid in the game environment.
- Public Fields: The script declares public GameObject fields for various game objects such as the obstacles panel, grid, obstacles holder, maps gallery, and gallery map button. These fields are likely assigned in the Unity Editor for easy reference and manipulation.
Overall, the MapManager
script serves as a central hub for managing map-related interactions and elements within your game, providing essential functionality for map creation and editing.
Obstacles.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Obstacles : MonoBehaviour
{
public GameObject obstacleToSpawn;
public Button button;
public bool none=false;
private void Start()
{
button.onClick.AddListener(SetSpawnInfo);
}
private void SetSpawnInfo()
{
MapManager.Instance.SpawnObstacle(obstacleToSpawn,none);
}
}
The Obstacles
script handles the spawning of obstacles in your game environment. It allows users to select an obstacle type from a UI button and then spawns that obstacle onto the map.
Here’s a breakdown of its functionalities:
- Public Fields: The script declares public GameObject and Button fields for the obstacle prefab to spawn (
obstacleToSpawn
) and the UI button (button
) respectively. These fields are likely assigned in the Unity Editor to link the script with the corresponding game objects. - none Field: This boolean field indicates whether the spawned obstacle should be placed on the grid directly (
none = true
) or on an obstacle layer (none = false
). - Start Method: This method is called when the script instance is initialized. It subscribes the
SetSpawnInfo
method to the button’sonClick
event listener. This means that when the button is clicked, theSetSpawnInfo
method will be invoked. - SetSpawnInfo Method: This method is called when the UI button is clicked. It notifies the
MapManager
instance to spawn the selected obstacle (obstacleToSpawn
) based on thenone
flag indicating whether it should be placed directly on the grid or on an obstacle layer.
Overall, the Obstacles
script provides a mechanism for users to interactively spawn obstacles onto the map by selecting them from a UI button interface. It facilitates easy customization and manipulation of the game environment.
ObstacleManager.cs
using System;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using UnityEngine.UI;
public class ObstacleManager : MonoBehaviour
{
// Data structure to hold information about spawned obstacles
[System.Serializable]
public class ObstacleData
{
public string name;
public Vector3 position;
public Quaternion rotation;
public string spriteName; // Additional data for SpriteRenderer
// Add any other relevant data fields here
}
// List to hold spawned obstacles' data
public List<ObstacleData> spawnedObstaclesData = new List<ObstacleData>();
// List to hold JSON data strings
private List<string> jsonDataList = new List<string>();
// Key to save/load jsonDataList using PlayerPrefs
private string jsonDataListKey = "ObstacleDataList";
// private void Start()
// {
// LoadJsonDataList();
// }
public void InitMaps()
{
foreach (Transform child in MapManager.Instance.mapsGallery.transform)
{
// Destroy the child GameObject
Destroy(child.gameObject);
}
LoadJsonDataList();
Debug.Log(jsonDataList.Count);
if (MapManager.Instance.galleryMapButton == null)
{
Debug.LogError("Obstacle prefab is not assigned!");
return;
}
if (jsonDataList == null)
{
Debug.LogWarning("No obstacle data available to spawn!");
return;
}
// foreach (string jsonData in jsonDataList)
// {
// // Instantiate the obstacle prefab using the data from each ObstacleData object
// GameObject newMap = Instantiate(MapManager.Instance.galleryMapButton, MapManager.Instance.mapsGallery.transform, true);
// newMap.GetComponent<Button>().onClick.AddListener(() => LoadObstacles(jsonDataList.IndexOf(jsonData)));
// }
for (int i = 0; i < jsonDataList.Count; i++)
{
int index = i; // Capture the index in a local variable to avoid closure issues
// Instantiate the obstacle prefab using the data from each ObstacleData object
GameObject newMap = Instantiate(MapManager.Instance.galleryMapButton, MapManager.Instance.mapsGallery.transform, true);
newMap.GetComponent<Button>().onClick.AddListener(() => LoadObstacles(index));
newMap.GetComponent<Button>().onClick.AddListener(ClosePanel);
}
}
private void ClosePanel()
{
MapManager.Instance.mapsGallery.SetActive(false);
}
// Spawn a new obstacle
public void SpawnObstacle(GameObject obstacle)
{
SpriteRenderer spriteRenderer = obstacle.GetComponent<SpriteRenderer>();
string spriteName = (spriteRenderer != null) ? spriteRenderer.sprite.name : ""; // Get sprite name if SpriteRenderer exists
// Record obstacle data
spawnedObstaclesData.Add(new ObstacleData
{
name = obstacle.name,
position = obstacle.transform.position,
rotation = obstacle.transform.rotation,
spriteName = spriteName // Additional data for SpriteRenderer
});
}
// Save spawned obstacles' data
public void SaveObstacles()
{
// Clear the existing list
spawnedObstaclesData.Clear();
// Populate the list with data from children
foreach (Transform child in transform)
{
SpawnObstacle(child.gameObject);
}
// Serialize the list of ObstacleData objects
string jsonData = JsonUtility.ToJson(new ObstacleDataWrapper { obstacles = spawnedObstaclesData });
// Save the JSON data to the file
File.WriteAllText(Application.persistentDataPath + "/obstaclesData.json", jsonData);
// Add the JSON data to the list
jsonDataList.Add(jsonData);
// Save jsonDataList using PlayerPrefs
SaveJsonDataList();
// Debug log to indicate successful saving
Debug.Log("Obstacles data saved successfully.");
MapManager.Instance.hideLevelEditor();
}
// Load spawned obstacles' data and recreate obstacles
public void LoadObstacles(int index)
{
// Load jsonDataList from PlayerPrefs
LoadJsonDataList();
if (index >= 0 && index < jsonDataList.Count)
{
string jsonData = jsonDataList[index];
// Deserialize the JSON data into a ObstacleDataWrapper object
ObstacleDataWrapper wrapper = JsonUtility.FromJson<ObstacleDataWrapper>(jsonData);
// Clear existing obstacles
foreach (Transform child in transform)
{
Destroy(child.gameObject);
}
// Recreate obstacles...
foreach (ObstacleData obstacleData in wrapper.obstacles)
{
GameObject newObstacle = new GameObject(obstacleData.name);
newObstacle.transform.position = obstacleData.position;
newObstacle.transform.rotation = obstacleData.rotation;
// Add SpriteRenderer component if sprite name exists
if (!string.IsNullOrEmpty(obstacleData.spriteName))
{
SpriteRenderer spriteRenderer = newObstacle.AddComponent<SpriteRenderer>();
// Load sprite by name
Sprite sprite = Resources.Load<Sprite>(obstacleData.spriteName);
if (sprite != null)
{
spriteRenderer.sprite = sprite;
}
else
{
Debug.LogWarning("Sprite not found with name: " + obstacleData.spriteName);
}
}
newObstacle.AddComponent<CircleCollider2D>();
ClickableObject clickableObj = newObstacle.AddComponent<ClickableObject>();
clickableObj.OnClick += (clickableObj,position) => MapManager.Instance.HandleClick(clickableObj,position);
newObstacle.GetComponent<SpriteRenderer>().sortingOrder = 2;
newObstacle.transform.SetParent(transform);
}
// Debug log to indicate successful loading
Debug.Log("Obstacles data loaded successfully.");
}
else
{
// Debug log to indicate invalid index
Debug.LogWarning("Invalid index provided for loading obstacles.");
}
}
// Save jsonDataList using PlayerPrefs
private void SaveJsonDataList()
{
string jsonDataListString = JsonUtility.ToJson(new StringListWrapper { dataList = jsonDataList });
PlayerPrefs.SetString(jsonDataListKey, jsonDataListString);
PlayerPrefs.Save();
}
// Load jsonDataList from PlayerPrefs
private void LoadJsonDataList()
{
if (PlayerPrefs.HasKey(jsonDataListKey))
{
string jsonDataListString = PlayerPrefs.GetString(jsonDataListKey);
StringListWrapper wrapper = JsonUtility.FromJson<StringListWrapper>(jsonDataListString);
jsonDataList = wrapper.dataList;
}
}
// Wrapper class to serialize and deserialize the list of ObstacleData objects
[System.Serializable]
private class ObstacleDataWrapper
{
public List<ObstacleData> obstacles;
}
// Wrapper class to serialize and deserialize a list of strings
[System.Serializable]
private class StringListWrapper
{
public List<string> dataList;
}
}
Let’s break down this code. Its for saving and loading the maps..
using System;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using UnityEngine.UI;
public class ObstacleManager : MonoBehaviour
{
// Data structure to hold information about spawned obstacles
[System.Serializable]
public class ObstacleData
{
public string name;
public Vector3 position;
public Quaternion rotation;
public string spriteName; // Additional data for SpriteRenderer
// Add any other relevant data fields here
}
// List to hold spawned obstacles' data
public List<ObstacleData> spawnedObstaclesData = new List<ObstacleData>();
// List to hold JSON data strings
private List<string> jsonDataList = new List<string>();
// Key to save/load jsonDataList using PlayerPrefs
private string jsonDataListKey = "ObstacleDataList";
// private void Start()
// {
// LoadJsonDataList();
// }
- The script starts by declaring a class named
ObstacleData
insideObstacleManager
. This class holds information about spawned obstacles, such as name, position, rotation, and sprite name. spawnedObstaclesData
is a list that holds instances ofObstacleData
.jsonDataList
is another list used to hold JSON data strings.jsonDataListKey
is a string used as a key for saving and loading data using PlayerPrefs.
public void InitMaps()
{
foreach (Transform child in MapManager.Instance.mapsGallery.transform)
{
// Destroy the child GameObject
Destroy(child.gameObject);
}
LoadJsonDataList();
Debug.Log(jsonDataList.Count);
if (MapManager.Instance.galleryMapButton == null)
{
Debug.LogError("Obstacle prefab is not assigned!");
return;
}
InitMaps()
method initializes the map.- It first clears any existing map objects in the gallery.
- Then, it loads JSON data list.
- It checks if the obstacle prefab is assigned. If not, it logs an error and returns.
if (jsonDataList == null)
{
Debug.LogWarning("No obstacle data available to spawn!");
return;
}
for (int i = 0; i < jsonDataList.Count; i++)
{
int index = i; // Capture the index in a local variable to avoid closure issues
// Instantiate the obstacle prefab using the data from each ObstacleData object
GameObject newMap = Instantiate(MapManager.Instance.galleryMapButton, MapManager.Instance.mapsGallery.transform, true);
newMap.GetComponent<Button>().onClick.AddListener(() => LoadObstacles(index));
newMap.GetComponent<Button>().onClick.AddListener(ClosePanel);
}
}
- The method checks if there is no obstacle data available, and if so, logs a warning and returns.
- It then iterates through the JSON data list and instantiates the obstacle prefab for each entry.
- It adds a listener to the instantiated button to load obstacles for each map.
- It also adds a listener to close the panel.
private void ClosePanel()
{
MapManager.Instance.mapsGallery.SetActive(false);
}
ClosePanel()
method simply deactivates the map gallery panel.
// Spawn a new obstacle
public void SpawnObstacle(GameObject obstacle)
{
SpriteRenderer spriteRenderer = obstacle.GetComponent<SpriteRenderer>();
string spriteName = (spriteRenderer != null) ? spriteRenderer.sprite.name : ""; // Get sprite name if SpriteRenderer exists
// Record obstacle data
spawnedObstaclesData.Add(new ObstacleData
{
name = obstacle.name,
position = obstacle.transform.position,
rotation = obstacle.transform.rotation,
spriteName = spriteName // Additional data for SpriteRenderer
});
}
SpawnObstacle()
method is responsible for recording data of newly spawned obstacles.- It gets the sprite name if the obstacle has a SpriteRenderer component attached.
- Then, it adds a new instance of
ObstacleData
to thespawnedObstaclesData
list containing the name, position, rotation, and sprite name of the obstacle.
// Save spawned obstacles' data
public void SaveObstacles()
{
// Clear the existing list
spawnedObstaclesData.Clear();
// Populate the list with data from children
foreach (Transform child in transform)
{
SpawnObstacle(child.gameObject);
}
// Serialize the list of ObstacleData objects
string jsonData = JsonUtility.ToJson(new ObstacleDataWrapper { obstacles = spawnedObstaclesData });
// Save the JSON data to the file
File.WriteAllText(Application.persistentDataPath + "/obstaclesData.json", jsonData);
// Add the JSON data to the list
jsonDataList.Add(jsonData);
// Save jsonDataList using PlayerPrefs
SaveJsonDataList();
// Debug log to indicate successful saving
Debug.Log("Obstacles data saved successfully.");
MapManager.Instance.hideLevelEditor();
}
SaveObstacles()
method is responsible for saving spawned obstacles’ data.- It first clears the existing list of obstacles’ data.
- Then, it populates the list with data from the spawned obstacles.
- Next, it serializes the list of
ObstacleData
objects into JSON format. - It writes the JSON data to a file named “obstaclesData.json” in the persistent data path.
- It adds the JSON data to the
jsonDataList
. - It saves the
jsonDataList
using PlayerPrefs. - Finally, it logs a message indicating successful saving and hides the level editor.
// Load spawned obstacles' data and recreate obstacles
public void LoadObstacles(int index)
{
// Load jsonDataList from PlayerPrefs
LoadJsonDataList();
if (index >= 0 && index < jsonDataList.Count)
{
string jsonData = jsonDataList[index];
// Deserialize the JSON data into a ObstacleDataWrapper object
ObstacleDataWrapper wrapper = JsonUtility.FromJson<ObstacleDataWrapper>(jsonData);
// Clear existing obstacles
foreach (Transform child in transform)
{
Destroy(child.gameObject);
}
// Recreate obstacles...
foreach (ObstacleData obstacleData in wrapper.obstacles)
{
GameObject newObstacle = new GameObject(obstacleData.name);
newObstacle.transform.position = obstacleData.position;
newObstacle.transform.rotation = obstacleData.rotation;
// Add SpriteRenderer component if sprite name exists
if (!string.IsNullOrEmpty(obstacleData.spriteName))
{
SpriteRenderer spriteRenderer = newObstacle.AddComponent<SpriteRenderer>();
// Load sprite by name
Sprite sprite = Resources.Load<Sprite>(obstacleData.spriteName);
if (sprite != null)
{
spriteRenderer.sprite = sprite;
}
else
{
Debug.LogWarning("Sprite not found with name: " + obstacleData.spriteName);
}
}
newObstacle.AddComponent<CircleCollider2D>();
ClickableObject clickableObj = newObstacle.AddComponent<ClickableObject>();
clickableObj.OnClick += (clickableObj,position) => MapManager.Instance.HandleClick(clickableObj,position);
newObstacle.GetComponent<SpriteRenderer>().sortingOrder = 2;
newObstacle.transform.SetParent(transform);
}
// Debug log to indicate successful loading
Debug.Log("Obstacles data loaded successfully.");
}
else
{
// Debug log to indicate invalid index
Debug.LogWarning("Invalid index provided for loading obstacles.");
}
}
LoadObstacles()
method is responsible for loading obstacles’ data and recreating them.- It first loads the
jsonDataList
from PlayerPrefs. - Then, it checks if the provided index is valid and within the range of
jsonDataList
. - If the index is valid, it retrieves the JSON data at that index.
- It deserializes the JSON data into an
ObstacleDataWrapper
object. - It clears existing obstacles.
- It recreates obstacles using the data from the
ObstacleDataWrapper
. - If a sprite name exists for an obstacle, it adds a
SpriteRenderer
component and loads the sprite by name from the Resources folder. - It adds a
CircleCollider2D
component to each obstacle for interaction. - It attaches a
ClickableObject
component to each obstacle for handling clicks. - It sets the sorting order and parent of each obstacle.
- Finally, it logs a message indicating successful loading or an invalid index.
// Save jsonDataList using PlayerPrefs
private void SaveJsonDataList()
{
string jsonDataListString = JsonUtility.ToJson(new StringListWrapper { dataList = jsonDataList });
PlayerPrefs.SetString(jsonDataListKey, jsonDataListString);
PlayerPrefs.Save();
}
SaveJsonDataList()
method serializes thejsonDataList
into a JSON string usingJsonUtility.ToJson()
method.- It then stores this JSON string in PlayerPrefs using a specified key.
- Finally, it saves the PlayerPrefs to disk using
PlayerPrefs.Save()
.
// Load jsonDataList from PlayerPrefs
private void LoadJsonDataList()
{
if (PlayerPrefs.HasKey(jsonDataListKey))
{
string jsonDataListString = PlayerPrefs.GetString(jsonDataListKey);
StringListWrapper wrapper = JsonUtility.FromJson<StringListWrapper>(jsonDataListString);
jsonDataList = wrapper.dataList;
}
}
LoadJsonDataList()
method checks if the PlayerPrefs contains data with the specified key.- If data exists, it retrieves the JSON string from PlayerPrefs using the key.
- It then deserializes this JSON string into a
StringListWrapper
object usingJsonUtility.FromJson()
method. - Finally, it assigns the
dataList
from the wrapper object tojsonDataList
.
// Wrapper class to serialize and deserialize the list of ObstacleData objects
[System.Serializable]
private class ObstacleDataWrapper
{
public List<ObstacleData> obstacles;
}
// Wrapper class to serialize and deserialize a list of strings
[System.Serializable]
private class StringListWrapper
{
public List<string> dataList;
}
}
ObstacleDataWrapper
is a wrapper class used to serialize and deserialize a list ofObstacleData
objects.StringListWrapper
is a wrapper class used to serialize and deserialize a list of strings.
These wrapper classes provide a convenient way to serialize and deserialize lists of objects and strings using Unity’s JsonUtility
. They encapsulate the list data and make it easier to work with during serialization and deserialization processes.
Overall, the ObstacleManager
script provides functionality to manage obstacles in the game environment, including spawning, saving, and loading obstacle data. It utilizes PlayerPrefs and JSON serialization for data persistence and provides methods to interact with obstacles dynamically during gameplay.