Devlog 2 - Autosave, Skills, Stage/Level System - v0.7
Here is the second devlog of Isominer - Casual space mining game. A lot of features added in game, here some of them:
-Autosave
-Skills
-Rush Stage
-Stage/Level system
-Events and delegates
1 - Autosave
I entegrate free asset from Unity's Asset Store, here is the link. I use its' binary save and load system and develop a save and load system for my project, a generic system for save/load processes and event system for generalizing the procedure for different classes. Here is a screenshot of my generic class BaseData:
public class BaseData<T> : MonoBehaviour where T : Data, new() { [SerializeField] public T data = new T(); /// <summary> /// Bound events to save/data processes to all inherited classes /// </summary> protected virtual void Awake() { GameEvents.current.OnSaveGameData += SaveData; GameEvents.current.OnGameStarting += LoadData; LoadData(); } /// <summary> /// Load data if load enabled and data exist /// </summary> protected void LoadData() { if (GameInfo.current.IsGameLoadEnabled && !string.IsNullOrWhiteSpace(data.SaveIdentifier) && SaveGame.Exists(identifier: data.SaveIdentifier)) { data = SaveGame.Load<T>(data.SaveIdentifier); GameEvents.current.LoadGameDataCompleted(); } } /// <summary> /// Save data if save enabled and identifier exist /// </summary> protected void SaveData() { if (GameInfo.current.IsGameSaveEnabled && !string.IsNullOrWhiteSpace(data.SaveIdentifier)) { SaveGame.Save<T>(data.SaveIdentifier, data); GameEvents.current.SaveGameDataCompleted(); } } } /// <summary> /// Base class for requirement of identifier /// </summary> public class Data { public string SaveIdentifier; }
I use BaseData class everywhere required. It enable that save and load system work without any code and give ability to save and load only that class. Here is a usage example of that class:
[System.Serializable] public class CollectorSoftwareData : Data { public int collectedAsteroidsCountSinceStageUp = 0; public int storedAsteroidsCount; public float storedMetalCount; public float storedSilliconCount; public float storedTritiumCount; } public class CollectorSoftware : BaseData<CollectorSoftwareData> { protected override void Awake() { base.Awake(); } }
2 - Skills
I developed a generic Skill class for my skills and inherit this class to my skills. This generic class storing all information and doing all stuff which is common to all of my skills. Codes are not complete here since they are very long and special to my project, but you can understand what my skill system like. Here is a my generic skill class template.
public enum SkillType { Offensive, //Mixed, Supportive } [System.Serializable] public class SkillData : Data { public float SkillDuration; public int SkillLevel = 1; public float DisableDuration; public float Delay = 0f; public int ActiveSubSkillId; public float EarnedSkillPoints; public bool IsUnlocked; public bool IsActive; public bool IsSkillUpgradeAvailableEventSent; } public class Skill : BaseData<SkillData> { #region Properties public string SystemName; public string Name; public string Description; public SkillType ElementType; public Action SkillMethod; public int UnlockLevel; //Only for skills that //public bool IsUnlockedByLevel; public int UnlockSkillPointCount; public int UpgradeSkillPointCount => (int)Mathf.Pow(data.SkillLevel, 2) * 25; public int MaxLevel => SubSkills.Count; [HideInInspector] public List<SubSkill> SubSkills; public Skill ParentSkill; public int LayerCount; public Sprite SkillImage; [HideInInspector] public SubSkill ActiveSubSkill; [HideInInspector] public bool IsDisabled; #region Usable Change Event [HideInInspector] public void UsableChangeEvent(bool isUsable, float? disabledDuration = null) { if (OnUsableChangeEvent != null) OnUsableChangeEvent(isUsable, disabledDuration); } [HideInInspector] public event Action<bool, float?> OnUsableChangeEvent; #endregion [HideInInspector] public Action SubSkillReceiverMethod; [HideInInspector] public Action SkillLevelReceiverMethod; #endregion protected override void Awake() { GameEvents.current.OnLevelIncrese += CheckToUnlock; ShipInfo.current.SkillTree.Add(this); base.Awake(); } protected virtual void Start() { CheckToUnlock(); ArrangeSubSkill(); if (SkillLevelReceiverMethod != null) { GameEvents.current.OnSkillUpgraded += (Skill skill) => SkillLevelReceiverMethod(); SkillLevelReceiverMethod.Invoke(); } if (SubSkillReceiverMethod != null) SubSkillReceiverMethod.Invoke(); } }
And an example for a skill class:
public class LaserShotSkill : Skill { public GameObject laserSkillObject; //public float duration = 6.0f; public float DemageInterval = 0.5f; [HideInInspector] public DemageInfo LaserShotDemage; public GameObject LaserDestroyEffect; public static LaserShotSkill current; public ParticleSystem[] particleSystems; float particleZVelocity = 50; Vector3 colliderCenter = new Vector3(0, -1, 18); Vector3 colliderSize = new Vector3(2, 6, 25); public int ExplodedAsteroidCount; public bool IsDoubleShotSubSkillActive = false; public int DoubleShotActivationMinNumber = 5; public bool IsDoubleShotActivated = false; public AudioClip LaserShotActivationAudioClip; protected override void Awake() { current = this; SkillMethod = UseLaserShotSkill; //UsableChangeEvent = GameEvents.current.LaserShotSkillIsUsableChange; SubSkillReceiverMethod = SubSkillArranger; SkillLevelReceiverMethod = ChangeLaserLengthBasedOnLevel; GameEvents.current.OnAsteroidDestroyedByLaserShot += DoubleShotController; GameEvents.current.OnLevelIncrese += SkillDemageArranger; GameEvents.current.OnSkillUpgraded += (Skill skill) => { if (skill == this) SkillDemageArranger(); }; base.Awake(); } protected override void Start() { LaserShotDemage = new DemageInfo() { DestroyEffect = LaserDestroyEffect, IsRangeDemage = true, Type = DemageType.Fire, DestroyDelay = 0.1f, }; SkillDemageArranger(); base.Start(); } }
My skill game objects are permanent in screen but not active, since they are permanent, I do not use prefabs for them. I attach skill scripts to main object and sub scripts to sub game objects such as Laser Shot Skill has more than one game object that harm asteroids with their colliders, so I coded another script to harm asteroids when they entered the trigger collider and attach it to all objects with same collider but at different positions.
3 - Rush Stage
As a surprise element, I add a rush stage that initiated by small possibility at the end of every stage with a warning. It simply change asteroid spawn rate to spawn faster, so player try to survive with more harmful asteroids but collects more asteroid too.
Here is how rush stage decided by probability:
var random = UnityEngine.Random.Range(0, data.probabilityOfRush); if (data.stage > 1 && random == 0) { StartCoroutine(ActivateRushStage()); } else { ActivateUpgradeStage(); }
4 - Events and Delegates
I didn't use events and delegates in my web applications and know about their details. So, I followed CodeMonkey's tutorial (I strongly recommend this channel for coding tutorials on Unity) and use them a lot. It mainly give ability to sync tasks across all methods based on events. I use them as trigger for listener methods to run when an event occur. Here is a example of code snippet for a event and delegate with or without parameters:
public event Action OnNormalStageCompleted; public void NormalStageCompleted() { if (OnNormalStageCompleted != null) OnNormalStageCompleted(); } //Notify collector storage is/is not full public event Action<bool> OnCollectorStorageFull; public void CollectorStorageFull(bool isFull) { if (OnCollectorStorageFull != null) OnCollectorStorageFull(isFull); } public event Action<float, float, float> OnAsteroidRefined; public void AsteroidRefined(float metalCount, float silliconCount, float tritiumCount) { if (OnAsteroidRefined != null) OnAsteroidRefined(metalCount, silliconCount, tritiumCount); }
And here is a usage example:
For listeners:
void Start() { GameEvents.current.OnHardpointShootIsUsableChange += (bool isUsable) => ShootButton.isEnabled = isUsable; GameEvents.current.OnHardpointReloadIsUsableChange += (bool isUsable) => ReloadButton.isEnabled = isUsable; GameEvents.current.OnCollectorRefineStorageIsUsableChange += (bool isUsable) => RefineButton.isEnabled = isUsable; GameEvents.current.OnUpgradeStageCompleted += ResetButtons; GameEvents.current.OnStageRestarted += ResetButtons; }
For triggers:
MetalInventoryCount += metalCount; SilliconInventoryCount += silliconCount; TritiumInventoryCount += tritiumCount; //Send inventory updated signal GameEvents.current.InventoryUpdated(); GameEvents.current.AsteroidRefined(metalCount, silliconCount, tritiumCount);
The End
I added lots of minor features, enhanced the ui too. This is really a enjoy to see the progress I did in videos and pictures.
I am creating these devlogs since other devlogs are very helpful for me while I am developing this project. I hope it is helpful you too. Please feel free to ask.
Donations also really appreciated since this is a side project for me and I am a undergraduate student too.
Get Isominer - Casual Space Mining Game
Isominer - Casual Space Mining Game
Isominer is the extraordinary arcade space mining game with hand crafted content.
Status | In development |
Author | bataylan |
Genre | Action |
Tags | 3D, Arcade, Casual, Singleplayer, Space, Unity |
Languages | English, Turkish |
Accessibility | Interactive tutorial |
More posts
- Devlog 5 - Manual Ship Controls and Special AsteroidsJan 02, 2021
- Devlog 4 - Tutorial System Design, Main Menu and Localization,Dec 20, 2020
- Devlog 3 - Skill Tree SystemDec 20, 2020
- Devlog 1 - Announcement of Isominer Project - v0.5Dec 18, 2020
Leave a comment
Log in with itch.io to leave a comment.