
Description
This project represents a simple 3D game prototype. It's an endless game, the player should collect objects on two levels. Each object has it's own type and some gold attached to it. Once all objects are collected, new objects are spawned. The game progress can be saved and loaded.
Features
- Very simple project with simple scripts
- Code generation usage example
- Data binders usage example
- Save Load addon usage example
- Text tutorial available (read below)
Setup:
- Download zip archive, unzip it and open the project in Unity Editor (version >= 2019 LTS)
- Import BGDatabase asset
- If you use Unity 2022, you may see compilation errors in the console. Import "Unity UI" package using Unity package manager
- Run example scene (BGDatabaseBasic3DExample\Scenes\BGExample1.unity)
Database schema
Here is the database schema, we use for that example.
-
Scene
- is linked to Unity scene, it's name should be equal to Unity scene name. It has following fields:spawnPosition [Vector3]
- position, the player will be moved then he's spawned in this scenespawnRotation [Vector3]
- rotation, the player will be rotated then he's spawned in this scenebounds [Bounds]
- area for spawning objects
-
Collectable
- is Scene's nested field, meaning it defines it's own table (meta), which is connected toScene
table (meta). It stores all collectable objects in the scene and have following fields:position [Vector3]
- position of the objectgold [int]
- amount of gold, attached to the objecttype [relationSingle]
- type of the object
-
CollectableType
- type of theCollectable
prefab [unityPrefab]
- prefab to use as an object to spawnaudio [audioClip]
- sound to play then object is collected
-
Player
- single entity table (meta), which store info about playergold [int]
- amount of gold player gatheredposition [Vector3]
- player's position, used by SaveLoad addon to save and load player's positionrotation [Quaternion]
- player's rotation, used by SaveLoad addon to save and load player's positionscene [string]
- player's scene, used by SaveLoad addon to save and load player's position
Save/Load settings
Let's look at the save/load setting to understand how they can be used to save/load your data during gameplay. You can find more information about Save/Load hereAbout Code Generation
We use code generation in this example (and we strongly recommend you to use it too).
The long story short- instead of using basic API like this entity.Set("gold", entity.Get<int>("gold")
+1));
code generation allows you to use code like this entity.gold=entity.gold+1;
.
As you can see code generation provides huge benefit, you can learn more about code generation here .
To distinct database fields from normal fields we use f_
, m_
or d_
field
prefixes in the examples below.
We use all 3 types of code generation (for illustrative purpose), each prefix correspond to code generation
type:
BGM_ClassName and m_FieldName
- MonoBehaviour generated classes (generated classes extends from MonoBehaviour and can be attached to GameObject)BGE_ClassName and f_FieldName
- Extension classes (instead of creating basic BGEntity class, the database populated with classes which inherited from BGEntity and have all additional properties for all fields)BGD_ClassName and d_FieldName
- Dna generated classes (this is a special case and can be avoided)
Code explanation
Some non relevant code is omitted.
BGCollectable.cs
- This script is attached to every collectable object. This script is hooked to a database by extending from BGM_Collectable class (generated by code generator), which in turn is extended from BGEntityGo- our component we use to connect GameObjects to database. When new collectable object is spawned in the scene byBGSpawner.cs
, Entity field is assigned- and object become linked to a particular table row.
This script has 4 tasks:- Show gold amount on the TextMesh component. This is done by this code
Text.text = "Gold:" + m_gold;
.m_gold
field reads/write data from/to database - Add some gold to the player
- Play sound effect when object's collected.
- Remove the object when it's collected from scene and from database.
public class BGCollectable : BGM_Collectable { //this callback is called then connected entity is changed public override void EntityChanged() { Text.text = "Gold:" + m_gold; } private void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Player")) { var player = other.GetComponent<BGPlayer>(); //add gold player.m_gold += m_gold; //play sound var audioSource = player.GetComponent<AudioSource>(); audioSource.clip = m_type.f_audio; audioSource.Play(); //remove from database & scene Entity.Delete(); Destroy(gameObject); } } }
- Show gold amount on the TextMesh component. This is done by this code
BGGoToScene.cs
- this script is attached to a portal to another scene. When the player collides with this portal, he's moved to another scene. This script is also hooked up to a database, namely toScene
row, pointing to a scene to load. When the player collides with this object, script read data about this scene from database, updates Player table with position and rotation from Scene table and loads target scene.public class BGGoToScene : BGM_Scene { private void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Player")) { //get reference to player var player = other.GetComponent<BGPlayer>(); //set player position & rotation to scene's spawn position & rotation player.m_position = m_spawnPosition; player.m_rotation = Quaternion.Euler(m_spawnRotation); //load the scene SceneManager.LoadScene(Entity.Name); } } }
BGSpawner.cs
- this script fills database with new objects and also spawn them to the scene. It hooked up toScene
table. It uses database events, to find the moment all objects are collected.public class BGSpawner :BGM_Scene { public override void Start() { base.Start(); Spawn(); //use database events to keep track the moment, when all collectables are gathered BGRepo.I.Events.AddAnyEntityDeletedListener(BGE_Collectable.MetaDefault.Id, CheckForNewSpawns); } public override void OnDestroy() { base.OnDestroy(); BGRepo.I.Events.RemoveAnyEntityDeletedListener(BGE_Collectable.MetaDefault.Id, CheckForNewSpawns); } //spawn unity's gameobjects - corresponding to Repo objects private void Spawn() { //fetch collectables from database var collectables = m_Collectable; //no collectables- no luck if (BGUtil.IsEmpty(collectables)) return; //spawn collectables objects foreach (BGEntity collectable in collectables) { //create collectable GameObject var newCollectable = Instantiate(collectable.f_type.f_prefab, collectable.f_position, Quaternion.identity); //we know - collectable prefab has BGCollectable script attached // so hook it up to table's row newCollectable.GetComponent<BGCollectable>().Entity = collectable; } } //This method create new collectables randomly for all scenes if all objects are collected public void CheckForNewSpawns(object sender, BGEventArgsAnyEntity args) { //there are still some collectables if (BGE_Collectable.CountEntities > 0) return; //ok, we gathered all objects- lets spawn new ones BGE_Scene.ForEachEntity(scene => { //number of collectables for one scene var count = Random.Range(3, 6); for (var i = 0; i < count; i++) { //nested meta has utility method, which auto assign new collectable to owner entity (scene) var newCollectable = BGE_Collectable.NewEntity(scene); //set gold newCollectable.f_gold = Random.Range(1, 10); //bounds determines scene's frontiers var bounds = scene.f_bounds; //set position newCollectable.f_position = new Vector3(Random.Range(bounds.min.x, bounds.max.x), bounds.center.y, Random.Range(bounds.min.z, bounds.max.z)); //set type newCollectable.f_type = BGE_CollectableType.GetEntity(Random.Range(0, BGE_CollectableType.CountEntities)); } }); //spawn GameObjects for current scene Spawn(); } }
BGPlayer.cs
- this script is attached to the player and hooked up to a first row ofPlayer
table via extending from BGM_Player.- It initialize it's position and rotation from database in the Start method
- It implements
BGAddonSaveLoad.BeforeSaveReciever
interface to ensureOnBeforeSave
method is called before saving. In this method it saves it's own position and amount of gold to the database, so while saving those values will be put to the save file.
public class BGPlayer : BGM_Player, BGAddonSaveLoad.BeforeSaveReciever { public override void Start() { base.Start(); //get pos and rotation from the table transform.position = m_position; transform.rotation = m_rotation; } //this method is called before saving void BGAddonSaveLoad.BeforeSaveReciever.OnBeforeSave() { //save current position, rotation and scene to the database m_position = transform.position; m_rotation = transform.rotation; m_scene = BGE_Scene.FindEntity(scene => string.Equals(scene.Name, SceneManager.GetActiveScene().name)); } }
-
BGSavingLoading.cs
It Provides Save/Load functions via SaveLoad addon. More information about Save/Load addon can be found herepublic class BGSavingLoading : MonoBehaviour { //... some code is omitted public void Save() { //save var bytes = BGRepo.I.Addons.Get<BGAddonSaveLoad>().Save(); File.WriteAllBytes(SaveFilePath, bytes); } public void Load() { //load if (!HasSavedFile) return; var content = File.ReadAllBytes(SaveFilePath); BGRepo.I.Addons.Get<BGAddonSaveLoad>().Load(content); //load saved scene SceneManager.LoadScene(R.Player.d_scene.Get(PlayerEntity).Name); } }
Releases
Click to see all releases
Version | Release date | Log |
---|---|---|
1.1 | Nov 10, 2021 | Graph binder adn action field examples are added |
1.0 | March 30, 2020 | Initial release |