Stuck with classes and refferencing

Okay i have this script:

using UnityEngine;
using System.Collections;

public class PositionCheck{

    public int CanMove(string direction, Vector3 position)
    {
        switch (direction)
        {
            case "up":
                position.y += 1;
                break;
            case "down":
                position.y -= 1;
                break;
            case "left":
                position.x -= 1;
                break;
            case "right":
                position.x += 1;
                break;
        }

        switch (XYBlock((int)position.x, (int)position.y))
        {
            case 0:
                return 0;
            default:
                break;
        }
        return -1;
    }

    int XYBlock(int xposition, int yposition)
    {
        if (xposition >= 0 && xposition < WC.world.GetLength(1) && yposition >= 0 && yposition < WC.world.GetLength(0))
        {
            return WC.world[xposition, yposition];
        }
        return -1;
    }
}

then in player script i have this code:

PositionCheck posCheck; //this is in initialization

    void Update () {
        if (Input.GetKey(KeyCode.UpArrow) && canMove == true)
        {
            canMove = false;
            if (posCheck.CanMove("up", transform.position) != -1)
            {
                StartCoroutine(MoveTo(transform.position, (transform.position += new Vector3(0, 1, 0)), 0.5f));
            }
        }

(dont worry about parsing i just copied what matters)

How can i refference PositionCheck class to call CanMove function (without making everything static and without assigning to gameobject and assign that to player)
also i plan to add other classes from position check

I just tought i fully understand classes and now this :face_with_spiral_eyes:

Seems like a complicated way to do movement but I only see one ting obviously wrong.
In this line

StartCoroutine(MoveTo(transform.position, (transform.position += new Vector3(0, 1, 0)), 0.5f));

You can’t do an assignment when passing an argument. I think you meant

StartCoroutine(MoveTo(transform.position, (transform.position + new Vector3(0, 1, 0)), 0.5f));

but would need to see the code for MoveTo to be certain.

There are some other things that could be wrong but you don’t include all the script. What particular issue are you having?

OH :stuck_out_tongue:
my problem is part in player script

if (posCheck.CanMove("up", transform.position) != -1)

how to call CanMove from first script?
posCheck doesnt really refference that class (probably because it is in another script)

Okay I understand. You say you have this code in Initialization

PositionCheck posCheck; //this is in initialization

So I misunderstood and thought you were only showing part of the line…

Okay, so you need a reference to an instance of PositionCheck in order to place it in the variable posCheck. The way it is now, posCheck will be null, so you can’t call methods on it without getting a NullRefernceException.

You have two options for getting a reference. Either create a new instance of PositionCheck, or else get one from somewhere else.

To create a new instance use the new keyword

PositionCheck posCheck = new PositionCheck();

If you want to get an existing one, that is more complicated. Since PositionCheck does not extend Unity.Component, it cannot exist on a GameObject, so you can’t use GetComponent etc… You could use the singleton pattern to get access via a static method but that is usually considered a code smell and is probably why you didn’t want to use a static method in the first place.

The truth is that you are facing a common problem in Unity designs - how to get two scripts to communicate with each other. Personally, I think your PositionCheck class is pretty simple and since it does not extend MonoBehaviour or some other Unity.Component, you should probably just “new one up.”

However, there is space for improvement. I think you split your classes in a strange way. The PositionCheck class is doing some movement related tasks - determine if we can move somewhere. Meanwhile, your second, unnamed class is doing both movement related tasks and input related tasks - start the MoveTo coroutine and check key pressed.

Instead, consider splitting your classes so that one just checks for key downs, and the other does both the movement checking and call to MoveTo. You can then have these classes communicate using events.

The constructor was the solution i added new in initalization.
I dont know how can i add existing if its only script but i will discover with time i guess.

I separated classes so its little easier to organize (first time i organize with different classes). in player script in update i check keys (later will be for android) and if i press key it checks if i can move to that position. if i can i go otherwise nothing happens. i dont know how else should i make classes or functions because a lot of functions in one place is a mess same as many classes. i divide them to scripts without needed gameobject

thanks :slight_smile:

Splitting functions is a great technique to manage complexity. However, you obviously don’t want one class for each function, that will be worse than one script with all functions.

Over time, you will learn the difficult part is not splitting functions up, it is grouping them together. Which functions belong together and how will they communicate with those that are not.

The best heuristic - though extremely vague - is to group based on “responsibility”. create a class that encapsulates data and functionality which accomplishes one thing. Defining that one thing, and knowing when and how to break it into smaller things, is the tricky part.

To put my suggestion into context then, I think you should have one class whose “responsibility” is to detect key presses, and then send an event notification to any other classes that are interested in that kind of stuff. You would then have another class whose “responsibility” is to move the player. This class would know how to check for obstacles and actually do the moving.

Anyways, everyone will have their own idea of how the systems should be split up, so as long as you’re happy with it, keep at it!

1 Like

But I have to move object somehow and transform.position at player script seems easiest as there is corutine.

also one quick question, would be wise to make new class(struct) to manage variables like health score and such (not saving but managing), would that be better or to leave it with script and class normaly

I’m not sure what you mean here. I’m not familiar with the MoveTo command you are calling. There’s also no reason another class can’t do the movement. So long as it is a Component, it can be added to the player GameObject and have access to the transform.

A fundamental of object oriented programming is encapsulation. This word has two meanings: hiding of internal components and bundling of similar data and methods which operate on that data. I need more information to make a judgment on a situational basis, but in general I suggest you leave data within a class so long as the class’ methods make use of it.

1 Like

And how would i reference this code in my script?

using UnityEngine;
using System.Collections;

public class Block : MonoBehaviour {

    public void TooFar(Vector3 position)
    {
        print(transform.position);
        float a = Vector3.Distance(transform.position, position);
        print(a);
        if (a > 51)
        {
            WC.displayed[(int)transform.position.x, ((int)transform.position.y * -1)] = null;
            Destroy(gameObject);
        }
    }
}

It inherits monobehaviour and goes to object. it says something about addcomponentsomething but there is no such function.

thanks

If you can get a reference to the GameObject the script is attached to, you can access it ANYWHERE with gameObjectName.GetComponent<Block>().TooFar() but if you’re trying to access it from within a script attached to the same GameObject, just replace “gameObjectName” with “gameObject” (shortcut reference/keyword for the CURRENT GameObject).

didnt quite test but i need to acces that class.
I wil have 10-20 types of objects (blocks like grass etc etc) so everytime i move i check in objects if they are too far (everytime on scene are 150 objects and far ones are destroyed while new are added)
i cant say grass.getcomponent right?

if it is clearer can i use GameObject[ ] that is holding prefabs?

You can, but I think you’re considering the problem backwards. Either the player/avatar/camera/whatever needs to have constant access to dozens if not hundreds of locations of other GameObjects in the scene, or all of those GameObjects need to have access to the position of one object- the player (or whatever).

Make a tag called “player” for the player, and then use FindGameObjectWithTag in the update function of a new script that constantly checks the distance between the player object’s transform and the current (with the script attached) object’s transform, then when distance is greater than a set amount, have it destroy ITSELF. This will be a tiny little script that you put on everything that you want to make self-destructable, and it makes it their own job to manage themselves, rather than having to use some sort of central controller and making things confusing.

For future reference, though, if you gave everything a common tag (like “grass”) you could indeed use GameObject.GetGameObjectsWithTag(“grass”) to return a list of every GameObject with that tag in the scene, and cycle through them and destroy the ones furthest away. This is a really roundabout way of doing things, however, when you can much more easily have them manage themselves.

I thought about making too far in update function of block but tought that would take much more processing power so i thought i make player call that function when needed

void Update()
    {
        Vector3 position = GameObject.FindGameObjectWithTag("Player").transform.position;
        print(transform.position);
        print(position);
        float a = Vector3.Distance(transform.position, position);
        print(a);
        if (a > 7)
        {
            WC.displayed[(int)transform.position.x, ((int)transform.position.y * -1)] = null;
            Destroy(gameObject);
        }
    }

this code just killed my machine 3 fps

Prints and Debug.Logs are fantastic for debugging and absolutely horrendous for framerates, which is why we typically don’t use them in Update functions which are called multiple times per frame without some kind of conditional statement, or you could crash the program. Comment out the prints and/or add some conditions like only printing if the distance is great enough to cause the object to destroy itself and you’ll fix your framerate problem.

Also, you should define “GameObject player” as a class member instead of inside of the update function, so that FindGameObjectWithTag(), which is another expensive function, doesn’t need to be called every update. Just say

if(!player)
    player = GameObject.FindGameObjectWithTag("player);

in Update, otherwise just use the “player” value that’s already assigned.

Wow 3 prints in update went from 70fps to 3fps LOL i never would assume that anyway. this works quite good

but i would still like to know how to tell script to go through TooFar function. static flag doesnt work(just for 1 block)
so how to tell class to do that function (to every gameobject that have that script and not me defining ever gameobject)

:confused: thanks :slight_smile:

After you attach the “Block” script to a GameObject, it’s a “component” of that GameObject (scripts are components). To access the script from a different script that’s attached to the same GameObject, just use gameObject.GetComponent<Block>().TooFar(); which can basically be broken down into

GameObject thisGameObject = this.gameObject;
Block thisBlockScript = thisGameObject.GetComponent<Block>();
thisBlockScript.TooFar();

but all in one little tidy line.

If you want to access the script from a script on a different GameObject, then you need to do the same thing, but reference the other GameObject instead of this.gameObject. Does that make sense?

The script you’ve attached is nothing but a component for the GameObject, but to get the right component (the right script), you need to access the right GameObject. GameObject.FindGameObjectsWithTag() is one way to find the right GameObject, and another (more popular) way is to use the inspector to assign GameObjects to public members within your script. You do this by just saying public GameObject someObject; without assigning it, and then in the inspector, drag a GameObject into that slot. Then in the script, you can just call “someObject.GetComponent()”.

I may have misunderstood you a bit. In order to have every object with the “Block” script automatically run the TooFar() function, just add an Update() function to the same script.

using UnityEngine;
using System.Collections;

public class Block : MonoBehaviour
{
    private GameObject player;

    void Start()
    {
        if(!player)
            player = GameObject.FindGameObjectWithTag("player");
    }

    void Update()
    {
        if(player)
            TooFar();
    }

    public void TooFar()
    {
        Vector3 playerPosition = player.transform.position;
     
        //STUFF
    }
}

Like that? I would probably run it as a Coroutine instead, so that you can easily reduce the number of checks from “several times a frame” (which is what update is) to “once a second” or “five times a second” or w/e. Coroutines are a whole different bag of worms though, so focus on the simple solutions for now. If you’re interested, it’s a good topic to check up on though.

Okay to summary this. I know this to assign THIS gameObject. My player script when moves (one gameobject) when moves 1 tile needs to call TooFar() function of EVERY gameObject with Block script.
public Block block; and drag&drop prefab to player gameObject but then i get 2 errors

  1. Destroying assets is not permitted to avoid data loss.
    2.Blocks transform position is always 0,0,0 no matter what his real position is (that is what transform.position of Block returns)

If I put TooFar() function in Update() (to be processed in update) and that works perfect but checks COUPLE of times in 1 frame for all 150 gameobjects (150 * multiple times per frame).

So, what i would like:

//In player script (one gameobject)
if (input.blablablaabl)
{
    // do all move stuff
    call TooFar() for every gameObject that has Block script (which is other cca.150 gameobjects)
}

If I (in awake function of player) reference script as getcomponent that doesnt work because there are many gameobjects

If there is no other way I will make corutine like this:

IEnumator TooFar()
{
   //all checking stuff and deleting
   yield WaitForSeconds(1f);
   StartCorutne(TooFar());
}

or i will make InvokeRepeating every second (that reduces amount of calls A LOT!