Runtime Collectables
Building on the systems we've established for loading and saving collectable data within the editor, we will now implement a runtime collectable spawner that dynamically generates collectables based on the data loaded from our CSV file. Additionally, we will cover how to save changes to collectable data at runtime, ensuring that your game remains flexible and data-driven. By the end of this chapter, you will have a comprehensive understanding of how to manage collectables effectively while your game is running.
Load CSV Data at Runtime
To facilitate the dynamic generation of collectables during gameplay, we will first focus on loading collectable data from our CSV file at runtime. This process involves creating a system that not only retrieves the data but also prepares it for use in the game environment. By leveraging the CollectableLoader
, we can easily pull in the collectable data that we have defined in our CSV file. This will serve as the foundation for our next step, where we will create a collectable spawner that utilizes this data to instantiate collectable objects in the scene.
Collectable Spawner
To begin, we'll create a simple collectable spawner that utilizes the CollectableLoader
to load data at runtime. This spawner will instantiate collectable objects based on the information stored in the CSV file. The spawner will need:
- Collectable Prefab: A reference to the collectable prefab that will be instantiated in the scene.
- Collectable Loader: a reference to the
CollectableLoader
component, which is responsible for loading the collectable data from the CSV file. - Start Method: On the
Start
method the class will load the data from theCollectableLoader
reference. For eachCollectableData
loaded, the data will be assigned to the collectable prefab game object and then instantiated at the spawner's position.
The following code illustrates how to create the CollectableSpawner:
using System.Collections.Generic;
using UnityEngine;
public class CollectableSpawner : MonoBehaviour
{
[SerializeField]
private CollectableLoader _collectableLoader; // Reference to the CollectableLoader
[SerializeField]
private GameObject _collectablePrefab; // Reference to the collectable prefab
private Vector3 _pos; //position of spawner
private float _offsetDistance = 2f; //an offset distance to avoid the objects instating on top of each other
// Start is called before the first frame update
void Start()
{
_pos = transform.position; // Get the initial position of the spawner
List<CollectableData> collectables = LoadCollectables(); // Load collectable data
//for each collectable in the list
foreach (CollectableData data in collectables)
{
SpawnCollectable(data); // Spawn and set up the collectable
}//end foreach (CollectableData data in collectables)
}//end Start()
//Load the collectables from the CollectableLoader and return the list
private List<CollectableData> LoadCollectables()
{
return _collectableLoader.LoadCollectables(); // Load and return collectable data
}//end LoadCollectables()
//SpwanCollectable
void SpawnCollectable(CollectableData data)
{
Vector3 spawnPosition = CalculateSpawnPosition(); //calculate the spawn position by offesting the x
// Instantiate the collectable GameObject at the spawner's position
GameObject collectable = Instantiate(_collectablePrefab, spawnPosition, Quaternion.identity);
SetupCollectable(collectable, data); // Set up the collectable with the loaded data
}//end SpawnCollectable
// Calculates the spawn position by updating the x-offset distance and returning the new position
private Vector3 CalculateSpawnPosition()
{
_offsetDistance++; // Increase the offset distance
// Return the new position, offsetting the x coordinate by _offsetDistance
return new Vector3(_pos.x + _offsetDistance, _pos.y, _pos.z);
}//end CalculateSpawnPosition()
//Setup the collectable for use in the game
void SetupCollectable(GameObject collectable, CollectableData data)
{
collectable.name = data.CollectableName; // Set the name of the instantiated GameObject
// Get the Collectable component from the instantiated GameObject
Collectable collectableComponent = collectable.GetComponent<Collectable>();
// Assign the loaded data to the collectable component
collectableComponent.CollectableData = data;
}//end SetupCollectable()
}
Setting Up the Spawner
Now that we have our CollectableSpaner
class we need to set it up in the scene to spawn collectables at runtime. To do this we will need to:
- Create an empty GameObject in the scene and name it "CollectableSpawner"
- Attach the
CollectableSpawner
component (script) to this GameObject. - Assign the collectable prefab to the collectablePrefab field in the Inspector.
- Press
Play
to see the objects created from the csv file into the game.
Save Data to CSV at Runtime
Next, we will implement the functionality to save changes made to collectable data during runtime back to the CSV file. This can be particularly useful for game design scenarios where you may want to modify collectable attributes based on player actions or other game mechanics.
CollectableCreator
We introduce the CollectableCreator
class, which is responsible for dynamically creating new collectables during runtime and saving them to a CSV file. This functionality allows for real-time modifications to collectable attributes based on player actions or other game events. Let's break down the components of the class:
-
Public Fields: We will need a reference to the
CollectableSpawner
, which handles spawning collectables in the game world. It also requires a reference to theCollectablePrefab
, which will be used to visually represent the collectable in the scene. Lastly, the class usesCollectableName
,PointValue
, andCollectedSound
to define the attributes of each new collectable, including its name, point value, and the sound played when collected. -
OnTriggerEnter() Method: We need a method that will trigger the creation of the new collectable. For now a simple
OnTriggerEnter
will work. When an object enters theCollectableCreator
object trigger zone. it will initiates the creation of a new collectable. When triggered, it calls theCreateNewCollectable()
method, passing the values ofCollectableName
,PointValue
, andCollectedSound
to define the properties of the new collectable. -
CreateNewCollectable() Method: This is the core function of the
CollectableCreator
class. When invoked, it performs the following steps:
- Creates a New Collectable Data Object: A new instance of
CollectableData
is created usingScriptableObject.CreateInstance
. This object will hold the data for the new collectable, including its name, point value, and sound effect. - Sets Collectable Properties: The
CollectableData
object's fields are populated with the values provided (name, point value, sound). - Saves the Data: The
CollectableSaver.SaveCollectableData()
method is then called to save the newly created collectable's data to a CSV file. This step ensures that changes to collectable attributes are stored persistently. - Spawns the Collectable: If a
CollectableSpawner
is linked, theCollectableSpawner.SpawnNewCollectable()
method is called to place the new collectable in the game world. - Logging: A debug log is generated to confirm that a new collectable has been successfully created, making it easier to track the process during development.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CollectableCreator : MonoBehaviour
{
public CollectableSpawner CollectableSpawner; // Reference to the CollectableSpawner
public GameObject CollectablePrefab; // Reference to the collectable prefab
public string CollectableName; //name for new collectable
public int PointValue; //new collectable point value
public AudioClip CollectedSound; //Sound clip for new collectable
//When triggered create new collectable
private void OnTriggerEnter(Collider other)
{
CreateNewCollectable(CollectableName, PointValue, CollectedSound);
}//end OnTriggerEnter()
//Creates a new collectable
public void CreateNewCollectable(string collectableName, int pointValue, AudioClip collectedSound)
{
// Create a new CollectableData instance
CollectableData newCollectableData = ScriptableObject.CreateInstance<CollectableData>();
// Set the properties for the new collectable
newCollectableData.CollectableName = collectableName;
newCollectableData.PointValue = pointValue;
newCollectableData.CollectedSound = collectedSound;
// Save the new collectable data to the CSV file
CollectableSaver.SaveCollectableData(newCollectableData);
// if there is a spawner the new collectable in the game world
if (CollectableSpawner != null) { CollectableSpawner.SpawnNewCollectable(newCollectableData); }
// Debg.Log the name of the newly created collectable
Debug.Log($"New collectable created: {collectableName}");
}//end CreateNewCollectable()
}
Spwan new Collectable
To save the new collectable we need to update the Collectable spawner to spawn the new collectable
using System.Collections.Generic;
using UnityEngine;
public class CollectableSpawner : MonoBehaviour
{
[SerializeField]
private CollectableLoader _collectableLoader; // Reference to the CollectableLoader
[SerializeField]
private GameObject _collectablePrefab; // Reference to the collectable prefab
private Vector3 _pos; //position of spawner
private float _offsetDistance = 2f; //an offset distance to avoid the objects instating on top of each other
// Start is called before the first frame update
void Start()
{
_pos = transform.position; // Get the initial position of the spawner
List<CollectableData> collectables = LoadCollectables(); // Load collectable data
//for each collectable in the list
foreach (CollectableData data in collectables)
{
SpawnCollectable(data); // Spawn and set up the collectable
}//end foreach (CollectableData data in collectables)
}//end Start()
//Load the collectables from the CollectableLoader and return the list
private List<CollectableData> LoadCollectables()
{
return _collectableLoader.LoadCollectables(); // Load and return collectable data
}//end LoadCollectables()
//SpwanCollectable
void SpawnCollectable(CollectableData data)
{
Vector3 spawnPosition = CalculateSpawnPosition(); //calculate the spawn position by offesting the x
// Instantiate the collectable GameObject at the spawner's position
GameObject collectable = Instantiate(_collectablePrefab, spawnPosition, Quaternion.identity);
SetupCollectable(collectable, data); // Set up the collectable with the loaded data
}//end SpawnCollectable
// Calculates the spawn position by updating the x-offset distance and returning the new position
private Vector3 CalculateSpawnPosition()
{
_offsetDistance++; // Increase the offset distance
// Return the new position, offsetting the x coordinate by _offsetDistance
return new Vector3(_pos.x + _offsetDistance, _pos.y, _pos.z);
}//end CalculateSpawnPosition()
//Setup the collectable for use in the game
void SetupCollectable(GameObject collectable, CollectableData data)
{
collectable.name = data.CollectableName; // Set the name of the instantiated GameObject
// Get the Collectable component from the instantiated GameObject
Collectable collectableComponent = collectable.GetComponent<Collectable>();
// Assign the loaded data to the collectable component
collectableComponent.CollectableData = data;
}//end SetupCollectable()
// Spawn a new collectable at runtime
public void SpawnNewCollectable(CollectableData newCollectableData)
{
SpawnCollectable(newCollectableData); // Use the existing method to spawn the new collectable
}//end SpawnNewCollectable()
}