Hi @mnemonicon,
This is a long post, sorry…
Its actually the exact opposite, I feel really bad that I keep bothering you about this problem
No need to feel bad at all, I enjoy helping people on the forum and it’s nice to be of use
I’m actually surprised I did as much as I did already
So, that sounds like something to be very proud of then
I am just - I dunno slow to learning this syntax I guess.
Everyone will have a different pace to on-boarding new information and I would say that what you are doing is spot on, you’ve got to a point where something isn’t quite making sense, so you have paused until you can work it out - much better in my opinion than ploughing on through and perhaps getting to a stage where you needed to already know this bit to do the next bit.
It often also takes a degree of courage to effectively put your hand up and ask a question, especially on a forum where you present your question to everyone, or at least feel that you are. The thing I normally remind people is that for every one person that has the courage to ask a question, there may be 10, 20 or a hundred other people that actually wanted to know the very same thing but hadn’t quite mustered the courage to ask yet - so potentially, any question by anyone I feel has a lot of value for the community
I didn’t want to take more time from you
It’s absolutely fine, I am on here most days and do what I can, if I am wrapped up in something else and can’t respond straight away I always try to get back to people as soon as I can - it’s my responsibility for how I spend my time, so not something you need to worry about
but until I even know what a class is, and why its different from a function
Well, lets tackle that now then, at least with enough information to get you started
Try to think of a class as a template or a cookie cutter. You define what your thing going to look like but it’s only a template, it’s not the actual thing (or a cookie!) at this time, but you can use it to create a thing . We call what it creates an instance, e.g. an instance of the thing.
Within the template (class) you will most likely define so items to store some values within the instance, we typically call these fields (you’ll see them referred to as class or members variables also). We may also want to expose some / all of those fields, a good practice for this would be via Properties, which allows us to not give direct access to our fields. Our template (class) will most likely also define some behaviours, e.g. what the thing can do, these behaviours are often referred to as methods (functions is also another term but there are some subtle differences which we can cover later).
So, that was all a bit generic, lets talk through a better example. Just for now, we’ll forget about Unity and just talk generically. We’ll have a real world example, something easier to relate to
We’ll create a class called Car.
public class Car {
}
There you go, that’s it. Bit boring. Doesn’t really do anything, but we have a template for a Car object. At the moment it isn’t an instance, e.g. there is no Car object, we’re just defining what a Car will look like. This is our car cookie cutter. All Cars will currently look the same.
But cars are not all the same, so, lets expand our class a little bit in order to add some variation;
public class Car {
// fields
private int _numberOfDoor = 2;
private int _numberOfWheels = 4;
}
I have added to private fields (class level variables) which can hold some information. Lets break each of these down so we know what they are doing.
Firstly, they have both been set as private, this is what is called an Access Modifier, it determines the scope of the item and it can be used on classes, fields, methods etc. In this case I have set them both to private which means nothing outside of this class will be able to see / access them.
The next part is the int, this is the Type, just as we are defining our own Type called Car, C# (.Net) has lots of Types that we can already use (int, string, bool etc), int is an abbreviate for Integer, thus, a whole number. Why did I choose an int? Well, our Car is going to need some wheels, and they will be counted in whole numbers (we’re not having half a wheel!), the same applies for doors, we will want at least 1 to get in!
Next is the name of the fields, you may wonder why when I named these fields I placed an underscore in front of them, literally, personal preference. It’s a naming convention I use when I declare fields (class level variables) and this makes them very identifiable when I use them in my code later on. You don’t have to do this, but it is worth sticking to whatever naming convention you use when programming consistently throughout your code, it just makes things easier for you (and anyone else who has the pleasure of reading your code).
Finally, I set the initial / default value of these fields, 2 doors and 4 wheels.
So, we now have a class called Car which, when we create an instance of Car we can store some information relating to that car in it, but we don’t have a way yet to create an instance. Lets expand it a bit more.
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
// constructor
public Car() {}
}
Our constructor is public, which means anything else will also be able to see it, e.g. other code / software libraries and so on.
So how do we create an instance of our car? Well we will need something to be our instance, an object, so lets create one and call our constructor;
Car aCar = new Car();
- In the above line, first we specify the Type for our variable, as per the int earlier, Car is a Type because we have defined it with our class.
- Next, we give our variable a name aCar, this can be anything you like, aCar, myCar, fastCar, fred - it really doesn’t matter, although it is best to name things in a way that makes sense, again, this makes things easier later.
- In C# constructors have the same name as the class, and the new keyword is used to invoke the constructor.
Ok, at this point we would have an actual Car object, an instance of our class, it would be called aCar in our code and that is how we could then reference it’s properties and behaviours. But at the moment it doesn’t have any properties, and it doesn’t have any behaviours and the two fields it has are not accessible because someone set them as private!
Lets make these values accessible by adding some properties;
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
// constructor
public Car() {}
// properties
public int NumberOfDoors
{
get { return _numberOfDoors; }
set { NumberOfDoors = value; }
}
public int NumberOfWheels
{
get { return _numberOfWheels; }
set { NumberOfWheels = value; }
}
}
Above you can see that I have added two properties, one called NumberOfDoors which corresponds with the field _numberOfDoors, and likes wise, NumberOfWheels which corresponds with the field _numberOfWheels.
Within each property you will see there is a get and a set, these are accessors which enable us to retrieve a value from the property, or, push a value through our property.
In both cases for the get you can see that we return the corresponding field, so, whatever the value of either doors or wheels will be returned when this property is called. Equally, we could set the number of doors or wheels using this property. Typically it is best practice to not add the set accessor initially, just keep your properties for read only access to your fields and only expose your fields when you really need to.
Examples of using the properties;
Car aCar = new Car();
int wheels;
wheels = aCar.NumberOfWheels;
In the above, we create a new instance of our Car, this is called instantiating. I have also defined a variable called wheels. I then populate wheels with the value of numberOfWheels which is returned from the get accessor of the NumberOfWheels property.
This is a bit of a nodding example as I am populating another variable to show it’s use where as you could just use this in some logic, or displaying to the screen or whatever.
Another example, this time for setting the value;
Car = aCar = new Car();
aCar.NumberOfWheels = 3;
In the above example we instantiate our car object, aCar, and we then set the numberOfWheels field using the NumberOfWheels property via it’s set accessor.
Woah! A three wheeled car!
Lets expand on our car again and give it a state for it’s engine, we can use an enum for this, you will be familiar with both states and enums from the Text101 game, we will also give it a speed and set it’s default state when our object is instantiated;
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
private int _engineState = 0;
private float _speed = 0f;
// enum
private enum EngineStates { Off, Idle, Accelerating, Decelerating };
// constructor
public Car() {}
// properties
public int NumberOfDoors
{
get { return _numberOfDoors; }
set { NumberOfDoors = value; }
}
public int NumberOfWheels
{
get { return _numberOfWheels; }
set { NumberOfWheels = value; }
}
}
The EngineStates enum has been set to private as I will only be using this within our Car object, not outside, for now at least, again, try to code defensively, e.g. if things don’t need to be accessible initially, don’t set them to be accessible initially, increase the scope as required or redesign etc.
I’ve added a private field _engineState which will hold the current state of the engine. I have also added another private field speed which is going to store our car’s current speed.
As you will see, our fields are starting to build up and we are setting quite a few default values there, I’m not a big fan of coding this way and personally prefer to use a method (behaviour) which will be responsible for setting the objects default state.
Additionally, in our current example, I have set the _engineState field to equal 0, I can do this because it is an int, but I couldn’t, for example, set it using our EngineStates enum because until our car has been instantiated our enum wont exist.
So I can’t have private int _engineState = EngineStates.Accelerating;
for example
So, we will expand our class again but now add some behaviour (a method) for managing the default state of our car when it is instantiated;
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
private int _engineState = 0;
private float _speed = 0f;
// enum
private enum EngineStates { Off, Idle, Accelerating, Decelerating };
// constructor
public Car() {
Initialise();
}
// properties
public int NumberOfDoors
{
get { return _numberOfDoors; }
set { NumberOfDoors = value; }
}
public int NumberOfWheels
{
get { return _numberOfWheels; }
set { NumberOfWheels = value; }
}
// behaviours (methods)
// Initialises the instance of our object
private void Initialise() {
_numberOfDoors = 2;
_numberOfWheels = 4;
_engineState = EngineStates.Off;
_speed = 0f;
}
}
We now have our first behaviour (method).
I have set it to be private because again, it only needs to be called (at the moment) from our car instance.
It is defined as void because it doesn’t return anything to the calling statement.
I’ve given it the name Initialise, again, this is personal preference, I tend to use the same name for all of my initialising methods, it could just as easily be called “MakeCar” but worth naming your method to something that makes sense from the perspective of that object.
Within our Initialise()
method I now set all of our fields, and now can use our EngineStates enum because we have an instance of car. How did we get an instance of the car? Well, if you look at our constructor you will see that I have added a call to the Initialise()
method with it. So, when ever our Car class is instantiated, Initialise()
will be called, it will now have an enum which we can use.
You would still instantiate it the same way as before;
Car aCar = new Car();
I am conscious of the length of this post, but, hopefully I have broken each part down into small enough chunks that it isn’t too overwhelming. We’ll do just a little bit more and then stop.
I would really like to make this car “go”, making the car move is a behaviour, in the real world you would have behaviours also, a driver would open the door, seat in the seat, fasten a seat belt, turn a key and so on. We will deal with just what the car is going to do from the perspective of the key and two peddles.
A key turning in the car will have the following behaviours; StartEngine, StopEngine, for brevity we will assume that a drive has been sensible and reduced the speed to zero before removing the key!
Expanding our code again;
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
private int _engineState = 0;
private float _speed = 0f;
// enum
private enum EngineStates { Off, Idle, Accelerating, Decelerating };
// constructor
public Car() {
Initialise();
}
// properties
public int NumberOfDoors
{
get { return _numberOfDoors; }
set { NumberOfDoors = value; }
}
public int NumberOfWheels
{
get { return _numberOfWheels; }
set { NumberOfWheels = value; }
}
// behaviours (methods)
// Initialises the instance of our object
private void Initialise() {
_numberOfDoors = 2;
_numberOfWheels = 4;
_engineState = EngineStates.Off;
_speed = 0f;
}
// starts engine
public void StartEngine() {
_engineState = EngineStates.Idle;
_speed = 0f;
}
// stops engine
public void StopEngine() {
_engineState = EngineStates.Off;
_speed = 0f;
}
}
StartEngine()
is a method which is responsible for starting the car, as such, it sets our engineState field to an appropriate value Idle. We don’t necessarily need to set the _speed to zero here, as we have already done that in our Initialise()
method, but that assumes someone using are class will be sensible! We are making assumptions that they would call StartEngine()
after Initialise()
and not whilst the car is perhaps driving! What this will do is certainly draw attention to what is hopefully a mistake, as the car would stop and be idle but these are some of the things you may need to think about during your design. You could add some logic to only set the engine to Idle if it isn’t already running and so on, but we will just keep this simple for now.
StopEngine()
is a method which is responsible for stopping the car, as a such it sets our engineState field to an appropriate value Off and sets the speed to zero. That makes sense, but again, it makes sense if we think about it in the order of “create a car, start the car, stop the car”, what if someone tries to turn it off whilst its moving?! Again, that’s something for another day
Both of these methods have been set as public because we would want other code to be able to access them, for example;
Car aCar = new Car();
aCar.StartEngine();
// brrrmm... brrrmm
aCar.StopEngine();
Ok, lets get this thing moving! Our real world car has an accelerator and a brake, so those behaviours can also be modelled in our class;
public class Car {
// fields (class level variables)
private int _numberOfDoors = 2;
private int _numberOfWheels = 4;
private int _engineState = 0;
private float _speed = 0f;
// enum
private enum EngineStates { Off, Idle, Accelerating, Decelerating };
// constructor
public Car() {
Initialise();
}
// properties
public int NumberOfDoors
{
get { return _numberOfDoors; }
set { NumberOfDoors = value; }
}
public int NumberOfWheels
{
get { return _numberOfWheels; }
set { NumberOfWheels = value; }
}
public float Speed
{
get { return _speed ; }
}
// behaviours (methods)
// Initialises the instance of our object
private void Initialise() {
_numberOfDoors = 2;
_numberOfWheels = 4;
_engineState = EngineStates.Off;
_speed = 0f;
}
// starts engine
public void StartEngine() {
_engineState = EngineStates.Idle;
_speed = 0f;
}
// accelerate
public void Accelerate(float speedIncrease) {
if (_engineState != EngineStates.Off) {
_engineState = EngineStates.Accelerating;
_speed += speedIncrease;
}
}
// decelerate
public void Decelerate(float speedDecrease) {
if (_engineState != EngineStates.Off && _engineState != EngineStates.Idle) {
_engineState = EngineStates.Decelerating;
_speed -= speedDecrease;
// car has stopped
if (_speed < 0) {
_speed = 0;
_engineState = EngineStates.Idle;
}
}
}
// stops engine
public void StopEngine() {
_engineState = EngineStates.Off;
_speed = 0f;
}
}
Ok, we’ve added a few more parts now. Two new methods; Accelerate and Decelerate, both public so that they can be accessed outside of our object, both voids because they do not return anything and both will take in a value through their respective parameters. A Type is defined for the parameter in both cases, float as this is used for our speed, and then a name for the parameter, speedIncrease and speedDecrease respectively.
We have also added a bit of logic to these methods;
Accelerate checks the current state of the engineState field, if it is anything other than Off we can accelerate - kinda makes sense. We then set its state to accelerating and increase _speed by the value of the parameter (speedIncrease) which was passed in.
Decelerate checks the current state of the engineState field to see if it is anything other than Off or Idle, as in both cases it wouldn’t make much sense to be able to decelerate. Again we change the engineState and decrease _speed by the value passed in through the speedDecrease parameter.
We also do an additional check in this method, we test to see if _speed is less than or equal to zero, if it is, the car has stopped, so we change the _engineState to Idle and also set the speed to zero (this caters for someone constantly breaking and making the speed negative).
Now, this is a great opportunity to point out refactoring, e.g. changing your code to make it better and more streamlined. That last if
statement in the Decelerate()
method could really be a separate method in it’s own right, as there may well be other times we want to check to see if the car has stopped, for now, I will leave it where it is because there isn’t, I would probably also want to make it a little more generic and check things like the current engine state also, but if gives you an idea as you can see that method has started to grow more than the others and this can be a good indication as when to refactor.
So… you have a Car class (template / cookie cutter) which you can use to create an instance of, through its constructor. You can use its properties to both get / set its fields. And you have given it some behaviours (methods) for starting and stopping the engine as well as accelerating and breaking.
The above model isn’t obviously a perfect model of what goes on in a real world car, but I hope what you can see is that you can start very small, as we did with;
public class Car {
}
…and turn it into something much more, fairly simply.
I hope the above helps to cover some of the basics for the things you weren’t too sure about, classes, methods etc.
For practice, take a look around you at some real world objects and see if you can use a simple approach like the above to recreate them in code. A bookcase (width, height, depth). A wall (number of windows, number of doors, colour), a shape (width, height, area), a person (name, age, gender, height, weight, eye colour) - some examples…
If there is anything which isn’t clear please do ask and when you are ready let me know and we will take a look at get that door to open with your security code