3
\$\begingroup\$

I'm working on a Wall-runner reaction based game. Whilst I know my way around a lot of the features Unity provides, I'm still in the beginner phase when it comes to working with several classes and functions simultaneously.

I have currently got the two following classes that work together:

Obstacle class

public class ObstacleSpawn : MonoBehaviour {

    //Reaction times: 1.0(4F), 0.9(3.6F), 0.8(3.2F), 0.7(2.8F), 0.6(2.4F), 0.5(2F), 0.4(1.6F), 0.35(1.4F), 0.3(1.2F), 0.25(1F);

    public PlayerScript pScript;

    public GameObject player;
    public GameObject obstacle;
    public GameObject endGamePanel;
    public GameObject infoText;
    public Text Average;
    public Text BestReaction;

    public GameObject instantiatedObstacle;

    public float randomSpawnMin;
    public float randomSpawnMax;
    public float endingSpeed;
    public float speed;
    public float canvasMoveTowardsSpeed;
    public float spawnTime;
    public float maxReactionTime;
    public float reactionTime;

    float obstacleDimensionY;
    float spawnTimeDistance;
    float reactionClick;
    float amountOfTries;
    float panelAlpha = 0F;
    float gamePanelAlpha = 1F;
    private float[] reactionTimeArray = new float[10];

    public int maxClicks;

    public bool canSpawn = true;

    bool reactionCap;
    bool pointOneChange;

    int fade = 0;

    // Use this for initialization
    void Start () {
        spawnTimeDistance = 4F;
        maxReactionTime = 1F;

        InvokeRepeating ("Spawn", Random.Range (randomSpawnMin, randomSpawnMax), Random.Range (randomSpawnMin, randomSpawnMax));

        Vector2 sprite_size = obstacle.GetComponent<SpriteRenderer> ().sprite.rect.size;
        Vector2 spriteScale = obstacle.transform.localScale;
        float sizeAndScaleY = sprite_size.y * spriteScale.y;
        float obstacle_local_sprite_sizeY = (sizeAndScaleY / obstacle.GetComponent<SpriteRenderer> ().sprite.pixelsPerUnit) * 0.5F;
        obstacleDimensionY = obstacle_local_sprite_sizeY;
    }

    // Update is called once per frame
    void Update () {
        endGamePanel.GetComponent<CanvasRenderer> ().SetAlpha (panelAlpha);
        Average.GetComponent<CanvasRenderer> ().SetAlpha (panelAlpha);
        BestReaction.GetComponent<CanvasRenderer> ().SetAlpha (panelAlpha);
        infoText.GetComponent<CanvasRenderer> ().SetAlpha (gamePanelAlpha);

        if (instantiatedObstacle != null) {
            if (instantiatedObstacle.transform.position.x > 1.78F) {
                instantiatedObstacle.transform.position += new Vector3 (-1, -1, 0) * speed * Time.deltaTime;
            } else if (instantiatedObstacle.transform.position.x < -1.78) {
                instantiatedObstacle.transform.position += new Vector3 (1, -1, 0) * speed * Time.deltaTime;
            } else {
                instantiatedObstacle.transform.position += new Vector3 (0, -1, 0) * speed * Time.deltaTime;
            }  

            if (instantiatedObstacle.GetComponent<Renderer> ().isVisible != true ) {
                Destroy (instantiatedObstacle);
                pScript.thereHaveBeenClicked = false;
            }
        }

        if (amountOfTries == 10) {
            float moveTowardsSpeed = endingSpeed * Time.deltaTime;
            player.transform.position = Vector2.MoveTowards (player.transform.position, new Vector2 (0, -2), moveTowardsSpeed);
            if (fade < 100) {
                panelAlpha += 0.01F;   
                gamePanelAlpha -= 0.1F;
                fade++;
            }

        }

        if (maxReactionTime >= 0.4F) {
            pointOneChange = true;
        } else if (maxReactionTime < 0.4F) {
            pointOneChange = false;
        }

        if (maxReactionTime == 1F) {
            reactionCap = true;
        } else {
            reactionCap = false;
        }

        if (pScript.mouseClickedTimeCheck == true && amountOfTries <= 10 && maxClicks != 10) {
            reactionClick = Time.unscaledTime;
            amountOfTries += 1;
            maxClicks += 1;

            reactionTime = reactionClick - spawnTime;

            if (reactionTimeArray [0] == 0) {
                reactionTimeArray [0] = reactionTime;
            } else if (reactionTimeArray [1] == 0) {
                reactionTimeArray [1] = reactionTime;
            } else if (reactionTimeArray [2] == 0) {
                reactionTimeArray [2] = reactionTime;
            } else if (reactionTimeArray [3] == 0) {
                reactionTimeArray [3] = reactionTime;
            } else if (reactionTimeArray [4] == 0) {
                reactionTimeArray [4] = reactionTime;
            } else if (reactionTimeArray [5] == 0) {
                reactionTimeArray [5] = reactionTime;
            } else if (reactionTimeArray [6] == 0) {
                reactionTimeArray [6] = reactionTime;
            } else if (reactionTimeArray [7] == 0) {
                reactionTimeArray [7] = reactionTime;
            } else if (reactionTimeArray [8] == 0) {
                reactionTimeArray [8] = reactionTime;
            } else if (reactionTimeArray [9] == 0) {
                reactionTimeArray [9] = reactionTime;
            }

            float averageReaction = (reactionTimeArray[0] + reactionTimeArray [1] + reactionTimeArray [2] + reactionTimeArray [3] + reactionTimeArray [4] + reactionTimeArray [5] + reactionTimeArray [6] + reactionTimeArray [7] + reactionTimeArray [8] + reactionTimeArray [9]) / 10;

            BestReaction.text = "Best reaction: " + Mathf.Min(reactionTimeArray[0], reactionTimeArray[1], reactionTimeArray[2], reactionTimeArray[3], reactionTimeArray[4], reactionTimeArray[5], reactionTimeArray[6], reactionTimeArray[7], reactionTimeArray[8], reactionTimeArray[9]);
            Average.text = "Average reaction: " + averageReaction;

            if (reactionTime <= maxReactionTime && pointOneChange == true) {
                maxReactionTime -= 0.1F;
                spawnTimeDistance -= 0.4F;
            } else if (reactionTime <= maxReactionTime && pointOneChange == false) {
                maxReactionTime -= 0.05F;
                spawnTimeDistance -= 0.2F;
            } else if (reactionTime > maxReactionTime && pointOneChange == true && reactionCap == false) {
                maxReactionTime += 0.1F;
                spawnTimeDistance += 0.4F;
            } else if (reactionTime > maxReactionTime && pointOneChange == false && reactionCap == false) {
                maxReactionTime += 0.05F;
                spawnTimeDistance += 0.2F;
            }
            Debug.Log (string.Format ("ReactionClick: " + reactionClick));
            Debug.Log (string.Format ("ReactionTime: " + reactionTime));
            Debug.Log (string.Format ("Max Reaction Time: " + maxReactionTime));
        }
    }

    public void Spawn() {
        if (pScript.isRight == true && pScript.inAir == false) {
            obstacle.transform.localScale = new Vector3 (-1, 1, 1);
            spawnTime = Time.unscaledTime;
            instantiatedObstacle = (GameObject)Instantiate (obstacle, player.transform.position + new Vector3(0.50F , (spawnTimeDistance + obstacleDimensionY + pScript.playerDimensionY), -1), Quaternion.identity);
            Debug.Log (string.Format ("Spawn time: " + spawnTime));

        } else if (pScript.isRight == false && pScript.inAir == false) {
            obstacle.transform.localScale = new Vector3 (1, 1, 1);
            spawnTime = Time.unscaledTime;
            instantiatedObstacle = (GameObject)Instantiate (obstacle, player.transform.position + new Vector3(-0.50F , (spawnTimeDistance + obstacleDimensionY + pScript.playerDimensionY), -1), Quaternion.identity);
            Debug.Log (string.Format ("Spawn time: " + spawnTime));
        }
    }
}

Player class

public class PlayerScript : MonoBehaviour {

    public ObstacleSpawn oScript;

    public GameObject player;

    public Text warningText;

    public float playerDimensionY;
    float warningFade;
    float warningAlpha;

    public bool isRight = true;
    public bool inAir = false;
    public bool mouseClicked = false;
    public bool mouseClickedTimeCheck;
    public bool thereHaveBeenClicked = false;
    bool warningTextAlive;

    public int flyingSpeed;

    float timeStamp1;
    float timeStamp2;

    // Use this for initialization
    void Start () {

        Vector2 sprite_size = GetComponent<SpriteRenderer> ().sprite.rect.size;
        Vector2 spriteScale = transform.localScale;
        float sizeAndScaleY = sprite_size.y * spriteScale.y;
        float player_local_sprite_sizeY = (sizeAndScaleY / GetComponent<SpriteRenderer> ().sprite.pixelsPerUnit) * 0.5F;
        playerDimensionY = player_local_sprite_sizeY;
    }

    // Update is called once per frame
    void Update () {
        warningText.GetComponent<CanvasRenderer> ().SetAlpha (warningAlpha);
        mouseClickedTimeCheck = false;

        if (oScript.maxClicks != 10) {

            if (isRight == true && mouseClicked == true) {
                transform.position += Vector3.right * flyingSpeed * Time.deltaTime;
            } else if (isRight == false && mouseClicked) {
                transform.position += Vector3.left * flyingSpeed * Time.deltaTime;
            }

        if (warningTextAlive == true) {
                if (warningFade < 400F) {
                    warningAlpha = 1F;
                    warningFade++;
                } else if (warningFade < 500F) {
                    warningAlpha -= 0.01F;
                    warningFade++;
                } else if (warningFade == 500F) {
                    warningText.text = "";
                    warningAlpha = 1F;
                    warningTextAlive = false;
                    warningFade = 0F;
                }
            }

            if (Input.GetMouseButtonDown (0) && oScript.instantiatedObstacle == null) {
                warningText.text = "Slow down - do not click without an obstacle visible" + "\nObstacle spawn-time reset";
                warningTextAlive = true;
                oScript.CancelInvoke ();
                oScript.InvokeRepeating ("Spawn", Random.Range (oScript.randomSpawnMin, oScript.randomSpawnMax), Random.Range (oScript.randomSpawnMin, oScript.randomSpawnMax));
            } else if (Input.GetMouseButtonDown (0) && inAir == false && oScript.instantiatedObstacle != null) {
                mouseClicked = true;
                inAir = true;
                mouseClickedTimeCheck = true;
                oScript.canSpawn = true;
                thereHaveBeenClicked = true;

                if (isRight == true) {
                    isRight = false;
                } else if (isRight == false) {
                    isRight = true;
                } 
            } else if (Input.GetMouseButtonDown (0) && inAir == true && oScript.instantiatedObstacle != null) {
                warningText.text = "Slow down - do not click without an obstacle visible" + "\nObstacle spawn-time reset";
                warningTextAlive = true;
                oScript.CancelInvoke ();
                oScript.InvokeRepeating ("Spawn", Random.Range (oScript.randomSpawnMin, oScript.randomSpawnMax), Random.Range (oScript.randomSpawnMin, oScript.randomSpawnMax));
            }
        }
    }

    void OnCollisionEnter2D(Collision2D coll) {
        inAir = false;
        mouseClicked = false;
    }
}

As you can see my code is rather entangled with shared variables and if statements. I know I need to divide this information into functions and classes, to make it easier to read in the future , however I'm unaware of exactly how.

Are there any suggestions about how I can untangle the code.

Screenshot of my game for reference:

enter image description here

\$\endgroup\$
4
\$\begingroup\$

The thing that sticks out to me the most is there is a lot of similar or duplicate code. Let's look at the code for assigning the reactionTime:

if (reactionTimeArray [0] == 0) {
    reactionTimeArray [0] = reactionTime;
} else if (reactionTimeArray [1] == 0) {
    reactionTimeArray [1] = reactionTime;
// etc up to 9

The only thing that changes for each if statement is the index is incremented, so we can replace all of the if statements with a for loop:

for (int i = 0; i < 10; i++)
{
    if(reactionTimeArray[i] == 0)
    {
        reactionTimeArray[i] = reactionTime;
        break;
    }
}

Calculating averageReaction and bestReaction can also be done using loops. There are LINQ methods to make this even easier. This requires using System.Linq;.

averageReaction = reactionTimeArray.Sum() / 10;
float bestReaction = reactionTimeArray.Min();

Jumping up there's this chunk of code:

if (maxReactionTime >= 0.4F) {
    pointOneChange = true 
} else if (maxReactionTime < 0.4F) {
    pointOneChange = false;
}

If maxReactionTime is not >= 0.4F, then it has to be < 0.4F, so you can turn the else if into an else. You can then refactor it to one line:

pointOneChange = maxReactionTime >= 0.4F;

The following:

if (maxReactionTime == 1F) {
    reactionCap = true;
} else {
    reactionCap = false;
}

Can also be reduced to one line:

reactionCap = maxReactionTime == 1F;

In your Player class, the following:

if (isRight == true) {
    isRight = false;
} else if (isRight == false) {
    isRight = true;
}

can be reduced to:

isRight = !isRight;

You probably don't want to call GetComponent() in Update(). Instead you can declare a class level variable and assign to it using GetComponent() in Start().

It's your choice if you want to refactor the code into more methods. The main reason I declare more methods is if I can use a method multiple times, reducing duplication. It can also help with readability.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.