Classes and How to use Them

Hi, all! My game project was going along really well and I was making steady progress, but then the variables got out of control, and I started sinking in all of the bugfixing and confusing scripts.

Today I discovered classes. I have NO idea how I could have been programming with Unity for several years and not discover them.

In my game project, I have 8 guns, all with 6 different variables (ints) denoting the level for Damage, Recharge Rate, Ammo, etc.
Earlier, I had been handling this using a nested array of
Guns[7][5].
Well that is extremely impractical.
Then I discovered what classes are and Objects and Class Functions and such and I know for a fact that they will make my life easier.

So here are my questions:

How would you save the set of variables belonging to an object of a class?

How can you make a class function accessible from other scripts?

Any pointers / tips / tutorials on how to make really good use of classes? I’ve read several articles and tuts already but I don’t want to miss anything.

Anything else I should know about that might make my life easier?

Thanks - YA

General object oriented programming is such a broad topic that I can’t imagine how could I help you in just one post. To not being useless though I can tell you about one concept that you probably didn’t hear about. It’s about keeping all or most of your scripts under only one GameObject, let’s call it ScriptsContainer. Doing this you can avoid having to search through entire hierarchy to know where or whether any particular script is used. The code is decoupled and structured in more manageable manner what favours good object oriented practices. I haven’t tried this approach in any big project yet, I am not good at design patterns either so try to find out what people smarter than me have to say on that matter xD

He asked about classes… not OO programming or structure.

this is the standard script (class)

using MonoBehaviour;

public class MySuperScript : MonoBehaviour
{
   void Start()
   {

   }

   void Update() 
   {


   }
}

To insert a new class type…

using MonoBehaviour;


public class WeaponClass
{
   public float FireSpeed;
   public float ReloadSpeed;
   public float Damage;

   public void Reload()
   {
      //do reload stuff
   }

}
 

public class MySuperScript : MonoBehaviour
{


   public WeaponClass weaponClass;
   void Start()
   {
      weaponClass = new WeaponClass();
      weaponClass.FireSpeed = 0.1f;
      weaponClass.ReloadSpeed = 2;
      weaponClass.Damage = 5;
   }

   void Update() 
   {
     weaponClass.Fire();

   }
}

Im not going to get into the accessibility of everything, but the above class shows and example of some properties and a function.

Note: The custom classes you create dont need to be in the same file as ive done. You could create a new file and for the class(es)

if you wanted to access the class from another script…

MySuperScript myScript = (MySuperScript)GetComponent(typeof(MySuperScript));

myScript.weaponClass...

No, he asked about how to use objects in right manner. Talking about classes in isolation from OOP is pointless. Creating classes is trivial, the idea behind it is not. As far as I know every university course on programming begins with the concept first, when you don’t even know how to make classes or what programming language will be used to instantiate objects. He’s read several articles and tuts already so he probably knows how to do this technically.

OO is hardly going to be helpful for someone that just discovered classes, despite using them for the last few years.

Fair enough, I’ve just tried to point out the right direction.

I know, Im not trying to ride you, just felt it might be too much info for OP

(of course you are right ;))

No no you have both been extremely helpful. And yes I do indeed understand the creation, modification, and extension of classes, class functions, Objects, etc.
@JamesLeeNZ- I’m assuming your example scripts are in C#. I am using Javascript, but they still make sense to me.
What has not been answered is my inquiry to taking the variables of a class (FireSpeed, ReloadSpeed, and Damage in your case), and saving them to PlayerPrefs or any other database. As I am looking to replace my previous system with the use of classes (simply because my previous system was cluttered and HIGHLY impractical (I was saving wayyyy too many variables, condensing them in to a string, and then FOR looping through the string and splitting it up back into arrays on the load)).

So can I save those variables that belong to their respective objects? Or would I have to save them out into other capsule variables, and then load them into the Objects?

class variables can be assigned by playerPrefs just like any other variables.

You will still have as many playerPrefs though.

You don’t have to create your class from MonoBehaviour (eg)

var myToilet : tp

class tp {
var toiletPaper : GameObject;
var toiletBrush : Transform;
var bowlCleaner : int;
}

…Which would take FOREVER to save individually. Is there a way to save an entire Object?

What kind of stuff are you saving?

And why lol?

Well, for each of the 3 save slots, I have 8 guns each with 6 variables attached to them. I need to save them because there are 3 save slots. I would need to save them if there was one save slot, regardless.

It is for a web game I am developing.

So would i be able to save those variables in bulk, or would it come right back to the array?

Is the player able to create/get custom guns during the game? If not, then you could just save the gun type (as a string or int), and create one of that type when you loaded it.

Or do you mean there are six state variables (such as current ammo) for each gun? That you would need to save, but 48 variables is not that large an amount; PlayerPrefs should be able to handle it fine.

Each gun has 5 variables (all ints of 1-4), and one boolean (locked or unlocked) that denote what level it is at for specific features such as Damage. Now that I think about it, I’m going to need to somehow incorporate a Cost variable for every one of those, but that will be easy to do by simply changing some of them into Objects, no?

Oh yes I guess you should understand the premise of the game.

You can save the game (there is an autosave) but not in the middle of a round. It autosaves when you die. The only things I need to save are current loadout, player name, unlocked guns, and levels for each of the guns.

This would be easily achieved through classes and class functions (Since all guns have same set of variables, it would be one class, no?).

So how would I save those attributes?

Just like Errorsatz said you should hold the gun info in a class array and then most of your gun info can be referenced from there. By using a index in the array you can reference anything from the class array.

var myGuns : gN[];
private var gIndex : int
///======================//
class gN {
//Gun vars
var icon : Texture;
}
///======================//
function OnGUI() {
    if(myGuns[gIndex].icon!=null) {
    GUI.DrawTexture(Rect(0,0,0,0), myGuns[gIndex].icon);
    }
}
///======================//

Woah. What is this magic?
Are you saying you just made an array from the class variables by creating an array with the Class Name? 0_o

More a JEDI force thing. lol

You may want to use a int for the bool/unlock idea because default prefs do not use bool. So you would just need to alter the int - 0 for false by default and 1 for true.

So I would create my class like so:

var Guns : Gun[];

class Gun{
    var Damage : int;
    var Overheat : int;
    var Special : int;
    var Unlocked : int;
    }
    
class Machine extends Gun{
//Load and save stuff here
}

Now how would my Loading/Saving code go?

And by the way I am not using UnityGUI because it sucks xP (I am coding my own GUI system using textured planes (But the same concepts still apply))

You can but its over my head. The scripts for it is on the wiki called arrayPrefs or something like that.
If your array is loaded in the inspector then you will have it there to reference or instantiate from when ever you need. You only need to save the class vars of the elements you want (ie)
If element one of your array was unlocked save the int and unlock it again on Start(). If you wanted to say change the actual gun Obj but not the info you can load/Instantiate the new gun, assign it to the Obj var and on save, save its name to a string, then on load load the Object with that name instead of the original Obj.

Oh yes that all makes sense. Kind of. But now I see no conceivable advantage of using classes for this except for, like, Class functions…

I didn’t want to have to do this, but here is my original Save/Load code (Check out lines 22, and line 72-90)

I know it looks complicated, but I’m simply piling the Guns array into a string, and then loading it by splitting it using a For loop and loading it into an array. From there, would I assign all of these gun variables to separate Objects (i.e Machine, Classic, Ray, etc) of class Gun?

/*This script will keep track of all universally accessible variables. 
It will also keep track of save data in the form of arrays and
will be referenced each time a game is saved / loaded*/
//The beginning of this script will be the save and slot-independent variables that need to be accessed more than the others.
static var CurrentSlot : int;
static var GameStage : int;
//GameStage works like so: Main Menu = 1, Garage = 2, In Flight = 3, Boss Battle = 4, Game Over = 5, Credits =6, Tutorial = 7

/*And now we begin the save stuff
SLOT ONE IS INDEXED AS 0, SLOT 2 IS 1, AND SLOT 3 IS 2*/
//The name variable
static var Name  : String;
//The Color variable
static var  Col : Color;
//the Ore number 
static var Ore : int;
//Keeps track of the unlocked specials (1 = unlocked, 0 = locked)
static var Specials = new int[6];
//Now onto creating the Ship upgrade variables and their respective arrays
static var ShipUps : int[] = new int[4];
//Time for the guns
static var Guns : int[,] = new int[8,7];

//Functions
function Awake(){
    GameStage = 1;
}
//Save Function
static function SaveSlot(){
    var SaveStr = (Name + "|" + Ore + "|" + Specials + "|" + ShipUps + "|" + Guns).ToString();
    PlayerPrefs.SetString("Slot"+CurrentSlot, SaveStr);
    PlayerPrefsX.SetColor("Color" + CurrentSlot, Col);
    PlayerPrefs.Save();
    
    //Load Function
}
static function LoadSlot(){
    var LoadStr = PlayerPrefs.GetString("Slot"+CurrentSlot);
    //Splitting
    var LoadArr = LoadStr.Split("|"[0]);
    //Loading Name
    Name = LoadArr[0];
    Debug.Log("Loaded Name as " + Name);
    //Loading the color
    Col = PlayerPrefsX.GetColor("Color" + CurrentSlot);
    Debug.Log("Loaded Color as " + Col );
    //Loading Ore
    Ore = parseInt(LoadArr[1]);
    Debug.Log("Loaded Ore as " + Ore);
    
    //Splitting up the Specials String
    
    var SpecStr = LoadArr[2];
    for(i=0; i < SpecStr.length; i++){
    var SpecAdd = parseInt(SpecStr.Substring(i,1));
    Specials[i] = SpecAdd;
    }
    Debug.Log("Loaded Specials as " + "[  " + Specials[0] + " , " +Specials[1] + " , " +Specials[2] + " , " +Specials[3] + " , " +Specials[4] + " , " +Specials[5] + " ]");
    
    //End Splitting up Specials String
    //Splitting up ShipUpgrades string
    
    var ShipStr = LoadArr[3];
    for(s=0; s < ShipStr.length; s++){
    var ShipAdd = parseInt(ShipStr.Substring(s,1));
    ShipUps[s] = ShipAdd;
    }
    Debug.Log("Loaded Ship Upgrades as " + "[  " + ShipUps[0] + " , " + ShipUps[1] + " , " + ShipUps[2] + " , " + ShipUps[3] + " ]");
    
    //End Splitting ShipUpgrades String
    //Splitting up THE GUNS, BITCHES
    
    var GunStr = LoadArr[4];
    //First Layer (Divided into 7 parts)
    for(g=0; g < 8; g++){
    //Second Layer
    for(p=0; p< 7; p++){
    var GunAttrAdd = parseInt(GunStr.Substring(((g*7)+(p)),1));
    Guns[g,p] = GunAttrAdd;
    }
    }
    Debug.Log("Loaded Classic Stats as " + "Plasma Drain: Level  " + Guns[0,0]+ ", " + "Plasma Recharge: Level  " + Guns[0,1]+ ", " + "Energy Drain: Level " + Guns[0,2] + ", " + "Damage: Level" + Guns[0,3] + ", " + "Special: Level "  + Guns[0,4] + ", " + "Unlocked (1/0): " + Guns[0,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[0,6]);
    Debug.Log("Loaded Machine Stats as " + "Plasma Drain: Level  " + Guns[1,0]+ ", " + "Plasma Recharge: Level  " + Guns[1,1]+ ", " + "Energy Drain: Level " + Guns[1,2] + ", " + "Damage: Level" + Guns[1,3] + ", " + "Special: Level "  + Guns[1,4] + ", " + "Unlocked (1/0): " + Guns[1,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[1,6]);
    Debug.Log("Loaded BFG Stats as " + "Plasma Drain: Level  " + Guns[2,0]+ ", " + "Plasma Recharge: Level  " + Guns[2,1]+ ", " + "Energy Drain: Level " + Guns[2,2] + ", " + "Damage: Level" + Guns[2,3] + ", " + "Special: Level "  + Guns[2,4] + ", " + "Unlocked (1/0): " + Guns[2,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[2,6]);
    Debug.Log("Loaded Whip Stats as " + "Plasma Drain: Level  " + Guns[3,0]+ ", " + "Plasma Recharge: Level  " + Guns[3,1]+ ", " + "Energy Drain: Level " + Guns[3,2] + ", " + "Damage: Level" + Guns[3,3] + ", " + "Special: Level "  + Guns[3,4] + ", " + "Unlocked (1/0): " + Guns[3,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[3,6]);
    Debug.Log("Loaded Ray Stats as " + "Plasma Drain: Level  " + Guns[4,0]+ ", " + "Plasma Recharge: Level  " + Guns[4,1]+ ", " + "Energy Drain: Level " + Guns[4,2] + ", " + "Damage: Level" + Guns[4,3] + ", " + "Special: Level "  + Guns[4,4] + ", " + "Unlocked (1/0): " + Guns[4,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[4,6]);
    Debug.Log("Loaded Spray Stats as " + "Plasma Drain: Level  " + Guns[5,0]+ ", " + "Plasma Recharge: Level  " + Guns[5,1]+ ", " + "Energy Drain: Level " + Guns[5,2] + ", " + "Damage: Level" + Guns[5,3] + ", " + "Special: Level "  + Guns[5,4] + ", " + "Unlocked (1/0): " + Guns[5,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[5,6]);
    Debug.Log("Loaded Spear Stats as " + "Plasma Drain: Level  " + Guns[6,0]+ ", " + "Plasma Recharge: Level  " + Guns[6,1]+ ", " + "Energy Drain: Level " + Guns[6,2] + ", " + "Damage: Level" + Guns[6,3] + ", " + "Special: Level "  + Guns[6,4] + ", " + "Unlocked (1/0): " + Guns[6,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[6,6]);
    Debug.Log("Loaded Wave Stats as " + "Plasma Drain: Level  " + Guns[7,0]+ ", " + "Plasma Recharge: Level  " + Guns[7,1]+ ", " + "Energy Drain: Level " + Guns[7,2] + ", " + "Damage: Level" + Guns[7,3] + ", " + "Special: Level "  + Guns[7,4] + ", " + "Unlocked (1/0): " + Guns[7,5] + ", " + "Equipped (0 = no, 1,2,3): " + Guns[7,6]);
}