Helping to understand OOP and Classes in Unity JS.

I’m new to Unity and Javascript and quite frankly I’m having some problem with using classes in Unity. I thought I understood it but Unity is not responding in the way I expect so I must be missing something. I’ve tried many tutorials and spent hours searching but I still haven’t found any examples of what I am doing wrong or how I should do something instead.

I’ve successfully written a block moving game in Unity without using any specific classes and I’m now trying to expand it and thought using classes would give the extra functionality I am looking for.

I’ve created the class called Block to control each block. I have attached this script to the Block Prefab.

public class Block extends MonoBehaviour
{
//Variables for Block	
    var obj : GameObject;
    var top : String;
    var right : String;
    var bottom : String;
    var left : String;
    var objID : int;
    var texture : Texture2D;	

            }
            
    function create(objID, x, y, top, right, bottom, left)
    {
    	obj = GameObject.Instantiate(UnityEngine.Resources.Load("Block"), Vector3 (x, y, 0), Quaternion.identity);

				var pepper = "Textures/B" + objID;
 				texture = UnityEngine.Resources.Load(pepper, Texture2D);
				obj.renderer.material.mainTexture = texture;
				objID = objID;
				top = top;
				right = right;
				bottom = bottom; 
				left = left;
				obj.name = 'block'+objID; 
  }

     function colliderOff ()
     {
     	obj.collider.enabled=false;
     } 

     function rotateCW ()
     {
             Debug.Log('one');

     	var temp = top;
     	top = left;
     	left = bottom;
     	bottom = right;
     	right = temp;
             Debug.Log('top = '+top+' left = '+left);

The initialisation script I have then creates a couple of blocks.

var block1 = new Block();
var block2 = new Block();
block1.create(1,1,1,"red","blue","null","null");
block2.create(2,0,1,"red","blue","blue","null");

I then have a line that modifies one of these blocks based on another function (colliderOff()) in the Block class.

block1.colliderOff();

This works fine, which is how I expected classes to work.

My main code (attached to the camera) then runs. I can select the block by mouse click and then move the block with the WASD keys, error checking etc. This is mainly taken from my first game and works fine.

The problem I have is when I try and run the rotateCW() function from the block class.

I’ve used the following snippet of code.

var myObject : Transform;
var selectedObject : GameObject;

\\ [on keypress] {
	myObject.Rotate( 0,0,90);
        selectedObject = GameObject.Find(myObject.name);
        selectedObject.GetComponent(Block).rotateCW();

I was surprised that I couldn’t just use myObject.rotateCW() as that how I would have expected it to work. Eventually cobbled up the above script that not only rotates the cube, but should rotate the variables associated to that specific object.

The problem is while it appears to work and kicks off the debug logs lists in the function the variables are blank, so I’m not sure if it works or not. It’s like it runs the code but doesn’t recognise the object I am running it on as being already created.

What am I doing wrong?

Please let me know if you need more code or I need to explain something more clearly.


HOW I EXPECT IT TO WORK.

  • Write Class
  • Create Object
  • Select Object
  • Run function on object that is selected.

But something seems to be wrong between point 3 and 4.

It’s UnityScript you’re talking about. JavaScript is not OO. OOP is not Unity specific. Not even language specific. It’s utilized by many different languages.

Nothing wrong with that reasoning.

You shouldn’t be “selecting” objects using Find. It’s unreliable and terribly slow.
You already have a reference to object you are looking for: myObject. Then you use it to get this object name and then you use that name to find reference of object of that name. In the end it gives the same reference you started with: myObject

You say that the Log messages work so you know the function is being called. So what makes you think that it doesn’t work?

I think I follow that. I think I forgot to mention that types of the variables.

var myObject : Transform;
var selectedObject : GameObject;

I’ve tried changing myObject to a GameObject at first but that broke the rest of the script such as myObject.Rotate as it’s not a transform. Hence the cludge.

The reason I don’t think it is working is that if I add Debug.Log('top = ‘+top+’ left = '+left); to the initialisatuin script it returns the strings for top and left for each block. But I’ve just noticed that if I add it to the rotateCW() script and run block1.rotateCW() straight after I create them then the strings are blank although the Debug lines are ran. I’m now even more confused.

var myObject : Transform;

You should call the variable myTransform si it’s no so confusing. If you have Transform you can get its GameObject:

myObject.gameObject

And the other way around:

This is smoewhat contradictory. Can you paste the exact output you’re getting in the console?

Thanks for the links. I pinched the script from another answer and adapted it. It mentioned that myObject isn’t the best name in it anyway.

block1.create(1,1,1,“red”,“blue”,“null”,“null”);
top = red left = null
UnityEngine.Debug:Log(Object)
Block:create(Object, Object, Object, Object, Object, Object, Object) (at Assets/Scripts/Block.js:37)
Scr_Setup_B1:Spline(Object) (at Assets/Scripts/Scr_Setup_B1.js:14)
Puzzle_B1:Start() (at Assets/Scripts/Puzzle_B1.js:14)

block1.rotateCW();
top = left =
UnityEngine.Debug:Log(Object)
Block:rotateCW() (at Assets/Scripts/Block.js:49)
Scr_Setup_B1:Spline(Object) (at Assets/Scripts/Scr_Setup_B1.js:22)
Puzzle_B1:Start() (at Assets/Scripts/Puzzle_B1.js:14)

The top and left variables don’t have values assign. They should be assigned inside create method but they’re not. I’m guessing what happens is you create objects of type Block but then you never call create method for them.

Try this: Scrap the create method all together and move it’s content to Block class constructor.

Unless there is some particular reason why you need your class initialization separate from constructor?

After a little bit of experimentation I have found the bit that really vexes me. Forgive me if you don’t quite understand this because I don’t understand it either.

I’ve done what you suggested and changed myObject to an Object rather than a transform. Using myObject.transform.Rotate(0,0,0) now works.

If I then try myObject.GetComponant(Block).rotateCW() the script that also runs, but doesn’t seem to do anything.

I am using the following code to select myObject

    if (Physics.Raycast (ray, hit, 600)) {
    
      myObject = hit.transform.gameObject;
      Debug.Log("Name = "+myObject.GetComponent(Block).name);
      Debug.Log("Left = "+myObject.GetComponent(Block).left);

I get the following Debug output.

Name = block1
UnityEngine.Debug:Log(Object)
Puzzle_B1:OnLeftMouseDown() (at Assets/Scripts/Puzzle_B1.js:77)
Puzzle_B1:LateUpdate() (at Assets/Scripts/Puzzle_B1.js:21)

Left =
UnityEngine.Debug:Log(Object)
Puzzle_B1:OnLeftMouseDown() (at Assets/Scripts/Puzzle_B1.js:78)
Puzzle_B1:LateUpdate() (at Assets/Scripts/Puzzle_B1.js:21)

So what puzzles me is how does Unity recognise that the GameObject “block1” is the same as the Object called “block1” instantiated in the Block script, because while that is what I would expect, that isn’t what it seems to be doing.

I thought they were assigned in the following part:

    function create(objID, x, y, top, right, bottom, left)

    {
        obj = GameObject.Instantiate(UnityEngine.Resources.Load("Block"), Vector3 (x, y, 0), Quaternion.identity);
                var pepper = "Textures/B" + objID;
                texture = UnityEngine.Resources.Load(pepper, Texture2D);
                obj.renderer.material.mainTexture = texture;
                objID = objID;
                top = top;
                right = right;
                bottom = bottom;
                left = left;
                obj.name = 'block'+objID;

  }

The reason that I have them separate is so I can reuse the Block script with multiple set-up scripts.

Read about inheritance and look at the documentation:

“Parent class: Object”

So as far as your changes go it seams to work fine. We still have the original problem though with left variable not having the value you expect it to have. Try what I suggested in previous post to fix this.

That’s fine you can pass arguments to constructor too:

var block1 = new Block(1,1,1,"red","blue","null","null");

So there’s no reason to initialize the Block class fields outside constructor. Yes you do it in the create(objID, x, y, top, right, bottom, left) method but I suspect you never call it so the left variable never gets assign value. So like I said: scrap create method and move it’s code to constructor. This way there’s no way the left variable won’t get initialized.

I don’t think I had a proper constructor which could be part of the fault. :sweat_smile: I think I mistook the create line for that.

So I’ve rename the create function to Block to make a proper constructor and changed the lines in the script to what you suggested.
Still doesn’t seem to work though. :sad:

When instantiating Block:

var block1 = new Block(1,1,1,"red","blue","null","null");

You pass as last argument value null and that’s what variable left gets assigned:

function Block(objID, x, y, top, right, bottom, left)
{
   left = left;

So then when you display it shows what you would expect: null;
Initialize the Block class objects with some values (not nulls) and see if it works

So I changed the constructor to the following and shortening the arguments seemed to work. I guess it is just picky.

 function Block(objID, x, y, t, r, b, l)
    {
    	obj = GameObject.Instantiate(UnityEngine.Resources.Load("Block"), Vector3 (x, y, 0), Quaternion.identity);

				var pepper = "Textures/B" + objID;
 				texture = UnityEngine.Resources.Load(pepper, Texture2D);
				obj.renderer.material.mainTexture = texture;
				objID = objID;
				top = t;
				right = r;
				bottom = b; 
				left = l;
				obj.name = 'block'+objID;

				
            }

After creating the object in setup script var block1 = new Block(1,1,1,“red”,“blue”,“null”,“null”); I run block1.rotateCW(); and I get the following results:

top = red left = null / / before “rotating” the vars.
top = blue left = red // after "rotating the vars.

So that seems to work and you can see that “null” does return “null” as it should be a string.

But when I click on the object :

      myObject = hit.transform.gameObject;

      Debug.Log("Name = "+myObject.GetComponent(Block).name);
      Debug.Log("Left = "+myObject.GetComponent(Block).top);

myObject.GetComponent(Block).top returns:

Top =

as if it is empty. (I have switched it to Top as it has a value).

It’s strange that changing the parameter names has helped.

This first line in costructor is dodgy:

obj = GameObject.Instantiate(UnityEngine.Resources.Load("Block"), Vector3 (x, y, 0), Quaternion.identity);

Not sure what was the intent behind this but here is what happens. You create new Block instance: new Block(…); and an instance of Block class is created. Now it’s constructor is called and the first line in it calls GameObject.Instantiate that creates another instance of Block class but this time no arguments are passed to constructor. As a result new Block produces two instances: one with fields instantiated and the other with fields not instantiated.
When you’re getting "Top = " you are probably referring the second one.

Now that I think about it: perhaps the original intent was that you should use the create method to crate Block isntances but then instead of:

var block1 = new Block();

you should have been doing:

var block1 = Block.create(1,1,1,"red","blue","null","null");

EDIT: I’m not JS guy so not sure but if this was true then create method should have had a return type defined yes? It didn’t.

So try this:

  1. Go back to create method.
  2. In create method make sure you set values of the newly created instance so:
    obj.top = top;
    instead of
    top = top;
  3. Make it so create method returns obj (that’s the Block instance we create using GameObject.Instantiate)
  4. Now when creating Block objects do it like in previous post (var block1 = Block.create(…))

EDIT: And of course make the create method static

Thanks for all of your help with this. I went away for the weekend and attacked it again today. Probably not perfect but it is now functioning as I expect. :smile:

Block.JS

function create(objID, x, y, t, r, b, l)
    {
    	obj = GameObject.Instantiate(UnityEngine.Resources.Load("Block"), Vector3 (x, y, 0), Quaternion.identity);

				var pepper = "Textures/B" + objID;
 				texture = UnityEngine.Resources.Load(pepper, Texture2D);
				obj.renderer.material.mainTexture = texture;
				obj.GetComponent(Block).objID = objID;
				obj.GetComponent(Block).top = t;
				obj.GetComponent(Block).right = r;
				obj.GetComponent(Block).bottom = b; 
				obj.GetComponent(Block).left = l;
				obj.name = 'block'+objID;

				return obj;
            }

Setup.JS

var block1 = new Block();
var block2 = new Block();
var finish = new Block();
block1.create(1,1,1,"red","blue","null","null");
block2.create(2,0,1,"red","blue","blue","null");

Now to tackle the rest of the project.