Editor works fine - builds run with bugs

Im making an endless runner game.

Code bellow.

the GameManager runs 90% of the game, the rest is ui elements and player with rigidbody and simple jump script.

Test runs fine - player spawns, you can run infinitely the score runs fine - is being calculated.
Builds on windows and android - first 10 obstacles spawn, then no obstacles spawn. Score does not get updated and always shows YOUR SCORE.

I have failed to find any help on google.

using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    [SerializeField]
    private GameObject UIManager;
    [SerializeField]
    private GameObject MasterPool;
    [SerializeField]
    private Transform PoolerInUse;
    [SerializeField]
    private int currentLevel = 0;
    [SerializeField]
    private List<GameObject> activeObstacles = new List<GameObject>();
    [SerializeField]
    private GameObject Player;
    ManagerUI UIScript;
    public enum gameState { play, mainMenu, paused, playerDeath }
    private static gameState currGameState = gameState.mainMenu;
    gameState gameStateBeforePause;

    private void Start()
    {
        CreateLevelPools(TotalLevelsCount());
        PoolerInUse = MasterPool.transform.GetChild(currentLevel);
        UIScript = UIManager.GetComponent<ManagerUI>();
    }

    public static gameState GetGameState()
    {
        return currGameState;
    }

    private void FixedUpdate()
    {
        GenerateObstacles();
        DespawnObstacles();
        AddScore();

    }
        private int TotalLevelsCount() //TODO: Need to change this later on.
    {
        return 2;
    }



    private void CreateLevelPools(int lvlCount)
    {
        for (int i = 1; i < TotalLevelsCount() + 1; i++)
        {
            GameObject Pooler = new GameObject("pooler" + i);
            Pooler.transform.parent = MasterPool.transform;
            PopulateLevelPool(i, Pooler.transform);
        }
    }

    private void PopulateLevelPool(int lvlNumber, Transform parentPooler)
    {
        GameObject[] obstacles = Resources.LoadAll<GameObject>("Prefabs/Levels/Level" + lvlNumber);
        foreach (GameObject obstacle in obstacles)
        {
            for (int i = 0; i < 50; i++)
            {
                GameObject ObstacleClone = Instantiate(obstacle, parentPooler);
                ObstacleClone.SetActive(false);
            }
        }
    }


    private void GenerateObstacles()
    {
         if (activeObstacles.Count < 10)
        {
            Transform obstacleToSpawn = LookForInactiveObstaclesInPooler(PoolerInUse);
           
            GameObject lastObstacle = activeObstacles.Last(); // finds last obstacle.
            Vector2 posOfLastObs = new Vector2(lastObstacle.transform.position.x, lastObstacle.transform.position.y); // stores last obstacles position in Vector2;

            Collider2D obsToSpwnMainCol = obstacleToSpawn.transform.Find("MainCollider").GetComponent<Collider2D>();

            obstacleToSpawn.gameObject.SetActive(true); // sets the obstacle to spawn to active.


            obstacleToSpawn.position = new Vector2(posOfLastObs.x + (obsToSpwnMainCol.bounds.size.x /2) //measures the size of last spawned and next to spawn obstacles and calculates
            + (obsToSpwnMainCol.bounds.size.x /2), posOfLastObs.y); //  where to place the spawning obstacle depending on both obstacles collider sizes

            obstacleToSpawn.position = new Vector2 (obstacleToSpawn.position.x + UnityEngine.Random.Range(0.25f, 1.5f), obstacleToSpawn.position.y +
                UnityEngine.Random.Range(-0.5f, 0.5f));

            activeObstacles.Add(obstacleToSpawn.gameObject); // adds the spawned obstacle to the active obstacles list.
        }
    }

    private Transform LookForInactiveObstaclesInPooler(Transform poolToUse)
    {
        Transform obstacleToSpawn = PoolerInUse.GetChild(UnityEngine.Random.Range(0, PoolerInUse.childCount - 1));
        if (obstacleToSpawn.gameObject.activeInHierarchy)
        {
            obstacleToSpawn = LookForInactiveObstaclesInPooler(PoolerInUse); // TODO: improve this so it looks for an actual inactive platform.
        }
        return obstacleToSpawn;
    }

    private void DespawnObstacles()
    {
        GameObject oldestObstacle = activeObstacles.First();
        if (activeObstacles.Count == 10 && Vector2.Distance(oldestObstacle.transform.position, Player.transform.position) > oldestObstacle.transform.Find("MainCollider").GetComponent<BoxCollider2D>().size.x)
        {
            activeObstacles.Remove(oldestObstacle);
            oldestObstacle.SetActive(false);
        }
    }


    private void ChangeGameState ( gameState _gameState)
    {
        currGameState = _gameState;
        PlayerScript playerScript = Player.GetComponent<PlayerScript>();

        switch (_gameState)
        {
            case gameState.play:
                playerScript.ChangePlayerSpeed(3f);
                print(currGameState);
                UIScript.SwitchButton(UIScript.runButton, false);
                UIScript.SwitchButton(UIScript.pauseButton, true);
                UIScript.SwitchButton(UIScript.restartButton, true);

                break;
            case gameState.mainMenu:
                playerScript.ChangePlayerSpeed(0f);
                UIScript.SwitchButton(UIScript.runButton, true);
                UIScript.SwitchButton(UIScript.pauseButton, false);
                UIScript.SwitchButton(UIScript.restartButton, false);
                break;
            case gameState.paused:
                playerScript.ChangePlayerSpeed(0f);
                UIScript.SwitchButton(UIScript.runButton, false);
                UIScript.SwitchButton(UIScript.pauseButton, true);
                UIScript.SwitchButton(UIScript.restartButton, false);
                break;
            case gameState.playerDeath:
                playerScript.ChangePlayerSpeed(0f);
                UIScript.SwitchButton(UIScript.runButton, false);
                UIScript.SwitchButton(UIScript.pauseButton, false);
                UIScript.SwitchButton(UIScript.restartButton, true);
                break;
            default:
                playerScript.ChangePlayerSpeed(3f);
                UIScript.SwitchButton(UIScript.runButton, false);
                UIScript.SwitchButton(UIScript.pauseButton, false);
                UIScript.SwitchButton(UIScript.restartButton, true);
                break;
        }

    }

    public void ClickedRunButton()
    {
        ChangeGameState(gameState.play);
        Time.timeScale = 1f;
    }

    public void ClickedPauseButton()
    {
        if (currGameState == gameState.paused) //resume game
        {
            Time.timeScale = 1f;
            ChangeGameState(gameStateBeforePause);
        }
        else //pause game
        {
            Time.timeScale = 0f;
            gameStateBeforePause = currGameState;
            ChangeGameState(gameState.paused);
        }
    }

    public void ClickedRestartButton()
    {
        RestartGame();
    }

    private void RestartGame()
    {
        Time.timeScale = 1f;
        currGameState = gameState.mainMenu;
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    // ~~~~~~~~~~~~ SCORE CALCULATION ~~~~~~~~~~~~~~~~

    private float currScore = 0;

    private void AddScore()
    {
        if (currGameState == gameState.play)
        {
            currScore = currScore + 1 * Time.deltaTime + UnityEngine.Random.Range(0, 3);
        }
    }

    public int GetScore()
    {
        int i = Convert.ToInt32(currScore);
        return i;
    }

}

Check your player logs. It sounds like a exception in runtime. Here they are Unity - Manual: Log files
On android build there’s an easy way. Connect the device to your PC via usb cable. Go to platform-tools folder in your android sdk, bring up the cmd and issue the command

> adb logcat -s Unity

Hey, thanks for your help.

I’m afraid i can’t really follow your instructions clearly.

Whenever i try to launch cmd it returns
‘logcat’ is not recognized as an internal or external command, operatable program or batch file.

Also - didin’t really get where to navigate to - my android device?

The build runs with exact same issue on the PC build in the exact same way - maybe it would be easier to debug pc build?

How would i go about that? Im sorry im asking for so much - it’s first time i encounter such problems and i have no idea how to fight them :expressionless:

Yes, check the link i’ve posted, it says where are pc player logs.

Okay, for anyone else encountering problem:
C:\Users\UserName\AppData\LocalLow\CompanyNameHere\ProjectNameHere

If you google for player logging it might show some answers that tell you to look for “output.txt” file in some different location, but for me i was able to find it the location i provided.

The name is also “Player” for some reason and not log.txt or output.txt - which I honestly don’t find a sensible filename at all but…

After investigating the logs i was easily able to find the problem. The log itself was actualy very easy to understand. I like it.

Problem was in DespawnObstacles();

    private void DespawnObstacles()
    {
        GameObject oldestObstacle = activeObstacles.First();
        if (activeObstacles.Count == 10 && Vector2.Distance(oldestObstacle.transform.position, Player.transform.position) > oldestObstacle.transform.Find("MainCollider").GetComponent<BoxCollider2D>().size.x)
        {
            activeObstacles.Remove(oldestObstacle);
            oldestObstacle.SetActive(false);
        }
    }

I think the problem was that i was using a quick-solution for testing purposes and also was using “Find”. It might be something else, but after i changed the

if (activeObstacles.Count == 10 && Vector2.Distance(oldestObstacle.transform.position, Player.transform.position) > oldestObstacle.transform.Find(“MainCollider”).GetComponent().size.x)

To a simple

if (activeObstacles.Count == 10 && Vector2.Distance(oldestObstacle.transform.position, Player.transform.position) > 15) it worked like a charm :slight_smile: I will just see whats the largest platform in build and add that number for now.

thanks @palex-nx for the support in fighting this ugly problem.