Problem
The Unity project I build is targeting iOS, Android and Windows X64. I have two scenes, A and B, whereas A is the main menu scene of my game and scene B is kind of a level selection scene in which the user can choose a level to play. From scene A I can navigate to scene B and back again. When running the game in the Unity Editor, everything behaves as expected. The problem arises, when I run the game on the target platforms (real devices). Then, when navigating like A --> B --> A, I end up in scene A being rendered as a black screen, except the FPSIndicator game object which is still rendered and doing its job. The FPSIndicator game object is a small piece of code which draws itself to the scene in the OnGUI callback. Nothing else is displayed.
Setup of Scene A
I have a Unity UI button there ("Drag and Drop"), which, when clicked, loads scene B using this code:
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.EventSystems;
public class GameTypeButtonController : MonoBehaviour, IPointerClickHandler
{
public ButtonSounds ButtonSounds;
public string SceneNameToLoad;
public GameType GameType;
public void OnPointerClick(PointerEventData eventData)
{
StartCoroutine(Do());
}
private IEnumerator Do()
{
var animator = gameObject.GetComponent<Animator>();
if (animator != null)
{
animator.SetTrigger("Clicked");
}
var audioSource = gameObject.GetComponent<AudioSource>();
if (audioSource != null)
{
var clip = GetRandomAudioClip(ButtonSounds);
audioSource.clip = clip;
audioSource.Play();
yield return new WaitWhile(() => audioSource.isPlaying);
}
Logger.LogInfo("[GameTypeButtonController.Do] Setting game type " + GameType);
GameManager.Instance.CurrentGameType = GameType;
SceneManager.LoadScene(SceneNameToLoad);
}
private AudioClip GetRandomAudioClip(ButtonSounds buttonSounds)
{
var numberOfAudioClips = buttonSounds.AudioClips.Length;
var randomIndex = Random.Range(0, numberOfAudioClips);
return buttonSounds.AudioClips[randomIndex];
}
}
The scene looks like this:
Setup of Scene B
In scene B, I have a button in the lower left which brings me back to scene A when clicked. This is not a Unity UI button, but a regular sprite with a CircleCollider2D attached. The script on that button looks like this:
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
public class HomeButtonController : MonoBehaviour
{
public ButtonSounds ButtonSounds;
public string SceneNameToLoad;
void OnMouseDown()
{
StartCoroutine(Do());
}
private IEnumerator Do()
{
var animator = gameObject.GetComponent<Animator>();
if (animator != null)
{
animator.SetTrigger("Clicked");
}
var audioSource = gameObject.GetComponent<AudioSource>();
if (audioSource != null)
{
var clip = GetRandomAudioClip(ButtonSounds);
audioSource.clip = clip;
audioSource.Play();
yield return new WaitWhile(() => audioSource.isPlaying);
}
SceneManager.LoadScene(SceneNameToLoad);
}
private AudioClip GetRandomAudioClip(ButtonSounds buttonSounds)
{
var numberOfAudioClips = buttonSounds.AudioClips.Length;
var randomIndex = UnityEngine.Random.Range(0, numberOfAudioClips);
return buttonSounds.AudioClips[randomIndex];
}
}
The scene looks like this:
General Notes
Two objects use DontDestroyOnLoad: GameManager and MusicPlayer.
What I have checked so far
Scenes are properly referenced in Build Settings
As I use Unity Cloud Build, I have disabled the Library Caching feature to avoid issues with old build artifacts (so every time I build, I do a proper, clean build)
I can locally build all three platforms (Unity reports it as "Build successful"). So no build errors.
I am using LoadSceneMode.Single (default)
I am using the same Unity version locally and in Unity Cloud Build: 2018.3.0f2
Update 2019-02-19:
When I navigate from a third scene C back to scene A using the same mechanism (a sprite button calling a coroutine), I also end up on the very same black screen. So the issue probably exists within scene A?
Update 2 from 2019-02-19:
Here's my GameManager code:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public EventHandler<LevelStartedEventArgs> LevelStarted;
public EventHandler<LevelFinishedEventArgs> LevelFinished;
// General
public GameType CurrentGameType;
public GameScene CurrentScene;
public int CurrentLevel;
public static GameManager Instance;
public GameLanguage Language;
public bool IsMusicEnabled;
private string gameStateFile;
void Start()
{
if (Instance == null)
{
gameStateFile = Application.persistentDataPath + "/gamestate.dat";
Load(gameStateFile);
DontDestroyOnLoad(gameObject);
Instance = this;
}
else if (Instance != this)
{
Destroy(gameObject);
}
}
public void Save()
{
Logger.LogInfo("[GameManager.Save] Saving game state to " + gameStateFile);
var bf = new BinaryFormatter();
var file = File.Create(gameStateFile);
var gameState = new GameState();
gameState.Language = Language;
gameState.IsMusicEnabled = IsMusicEnabled;
bf.Serialize(file, gameState);
file.Close();
Logger.LogInfo("[GameManager.Save] Successfully saved game state");
}
public void Load(string gameStateFile)
{
Logger.LogInfo("[GameManager.Load] Loading game state from " + gameStateFile);
if (File.Exists(gameStateFile))
{
var bf = new BinaryFormatter();
var file = File.Open(gameStateFile, FileMode.Open);
var gameState = (GameState)bf.Deserialize(file);
file.Close();
Language = gameState.Language;
IsMusicEnabled = gameState.IsMusicEnabled;
}
Logger.LogInfo("[GameManager.Load] Successfully loaded game state");
}
[Serializable]
class GameState {
public GameLanguage Language;
public bool IsMusicEnabled;
}
}
Thanks for any hints!
So, I finally managed to solve the issue on my own. Here's a list of steps I went through in order to eliminate my issue:
I installed the "Game development with Unity" workload in Visual Studio 2017 in order to be able to use the Visual Studio Debugger. That gave me the possibility to properly debug my scripts (setting breakpoints, inspect values, ...) and go through them, step by step. See this link for some details on how to accomplish this. Unfortunately, this did not solve my problem yet, but gave a good basis for troubleshooting my issue.
As the issue described above, also arised on Windows x64 builds, I've decided to troubleshoot my issue on a windows build. So I reconfigured my Windows build in the Build Player Settings, by setting Script Debugging to true, Development build to true, Copy PDB filesto true. Then I've ran another windows build.
If you now run your game, it will show a development console in the lower left screen, giving a lot of useful information (exceptions thrown, etc...). Also you can open the log file directly from within the game, which provides a LOT of useful informations to tackle down the real issue.
So, I had some PlatformNotSupportedExceptions in the logs, which I was able to fix and some other errors. Now it is working.
Although, I've answered my own question, I hope this will be useful to some of you.
Related
i followed the official unity tutorial on how to put bannner ads on my game, i set everything up in the unity dashboard, and in the editor i see a rectangle that says the ad should be there, but when i build the game nothing shows up, here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Advertisements;
public class bannerad : MonoBehaviour
{
bool testMode = false;
public string gameId = "******" (my game id is here i just dot want to share it);
// Start is called before the first frame update
IEnumerator Start()
{
Advertisement.Initialize(gameId, testMode);
while (!Advertisement.IsReady("Lost_Ad"))
{
yield return null;
}
Advertisement.Banner.SetPosition(BannerPosition.BOTTOM_CENTER);
Advertisement.Banner.Show("Lost_Ad");
}
}
anyone knows what is the solution?
Are you ever calling this function? If you just replaced the Start function with this IEnumerator it will not get called unless you call it.
private void Start()
{
Advertisement.Initialize(gameId, testMode);
StartCoroutine(StartAds());
}
private IEnumerator StartAds()
{
// your code here
}
This was originally coded in unity 2018 then ported to unity 2019 because my main project is on that version. It works perfectly in unity 2018 (aside from that button.clicked += used to be button.AddListener())
The only relevant bit of the code is lines 12 - 17 but who knows.
Script is attached to object with the Button component.
Unity 2019.4.11f1
Returns the following error with the setup listed above:
ArgumentException: GetComponent requires that the requested component 'Button' derives from MonoBehaviour or Component or is an interface.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEngine.Events;
public class ButtonProperties : MonoBehaviour {
public ButtonType buttonType;
public string dest;
Button button;
void Start()
{
button = GetComponent<Button>();
//perform different functions dependant on the button type
if (buttonType == ButtonType.Scene)
{
//add ButtonLoadScene as a listener to the the button
button.clicked += ButtonLoadScene;
}
else if (buttonType == ButtonType.Menu)
{
button.clicked += ButtonLoadMenu;
}
//change button text to "Load Scene" + the name of the scene
string str = "Load " + dest;
GetComponentInChildren<TextElement>().text = str;
}
//private scene loader because event listeners cant take arguments >:(
void ButtonLoadScene()
{
SceneManager.LoadScene(dest);
}
void ButtonLoadMenu()
{
//array of all canvases in the scene
Canvas[] canvases = GameObject.FindObjectsOfType<Canvas>();
//foreach canvas
foreach (Canvas canvas in canvases)
{
//if the canvases name is the desired name
if (canvas.name == dest)
{
//turn all canvases off
foreach (Canvas c in canvases)
{
c.enabled = false;
}
//turn current canvas on
canvas.enabled = true;
//break loop
break;
}
}
}
}
public enum ButtonType
{
Scene,
Menu
};
I also tested this by adding a NewBehaviourScript to the button with Button button; and, in the start function, Start() {button = GetComponent<Button>();}
Same error
I believe you are using the wrong namespace for your button. Try changing:
using UnityEngine.UIElements;
to
using UnityEngine.UI;
There is a Button in both namespaces, but the one in UnityEngine.UI is likely the component you're working with.
Had somebody else on the team test the build, as I started to think from the comment thread, I was missing the UI library. Changing it to the UI library was the solution but not for me until I restarted the computer. Thank you Thomas Finch for the help.
I want to make an example of shot,
then I wrote this in the handle button event,
using UnityEngine;
using System.Collections;
public class fire : MonoBehaviour {
public GameObject bullet;
SteamVR_TrackedObject trackedObj;
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}
void Update() {
var device = SteamVR_Controller.Input((int)trackedObj.index);
if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger)) {
GameObejct obj = Instantiate(bullet,transform.position);
Vector3d fwd = transform.TransformDirection(Vector3.forward);
obj.GetComponent.<Rigidbody>().AddForce(fwd*2800);
}
}
}
but when debugging and I press handle button ,it didn't produce a bullet,and had erred at the line
var device = SteamVR_Controller.Input((int)trackedObj.index);,
the error is:
Object reference not set to an instance of an object.
First You should need to confirm that your fire script is attached to your controller object and your controller object also attached SteamVR_TrackedObject script (which provide by steam plugin).
Then, lastly ensure this line is executing
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}
This is related to happyfuntimes plugin if you have used it .
I am trying to make a game on it and stuck at a point of displaying score along with name to display on large screen while user is playing on his mobile.(i have already tried to display name and score on mobile screens have seen in sample do not need that ). Please suggest how can this be done if you have used happyfuntimes plugin.
I could see the HFTgamepad input having public GameObject player name which I am trying to access ,do I have to make array ?
public string playerName;
I am trying to put these name on array.
Displaying anything in unity is really normal unity issue and not special to happyfuntimes. Games display highscore lists, item lists, inventory lists, etc... A list of players is no different
Use the GUI system display text.
According to the docs if you want to generate UI dynamically you should make a prefab
So basically you'd make a UI hierarchy in the scene. When a player is added to the game Instantiate your name-score prefab, search for the UI hierarchy gameObject (do that at init time), then set the parent of the preafab you just instantiated so it's in the UI hierarchy.
Look up the UI.Text components that represent name and score and set their text properties.
There's various auto layout components to help layout your scores
When a player disconnects Destroy the prefab you instantiated above.
OR ...
Conversely you can use the old immediate mode GUI. You'd make a GameObject, give it a component that has a OnGUI function. Somewhere keep a list of players. Add players to the list as they connect and remove them from the list as they disconnect. In your OnGUI function loop over the list of players and call the GUI.Label function or similar to draw each name and score
Here's a hacked up example.
Assuming you have a PlayerScript script component that's on each player that has a public accessor PlayerName for getting the player's name then you can make a GameObject and put a script like this on it.
using UnityEngine;
using System.Collections.Generic;
public class ExamplePlayerListGUI : MonoBehaviour
{
static ExamplePlayerListGUI s_instance;
List<PlayerScript> m_players = new List<PlayerScript>();
static public ExamplePlayerListGUI GetInstance()
{
return s_instance;
}
void Awake()
{
if (s_instance != null)
{
Debug.LogError("there should only be one ExamplePlayerListGUI");
}
s_instance = this;
}
public void AddPlayer(PlayerScript bs)
{
m_players.Add(bs);
}
public void RemovePlayer(PlayerScript bs)
{
m_players.Remove(bs);
}
public void OnGUI()
{
Rect r = new Rect(0, 0, 100, m_players.Count * 20);
GUI.Box(r, "");
r.height = 20;
foreach(var player in m_players)
{
GUI.Label(r, player.PlayerName);
r.y += 20;
}
}
}
PlayerScript can then call ExamplePlayerListGUI.GetInstance().AddPlayer in its Start function and ExamplePlayerListGUI.GetInstance().RemovePlayer in it's OnDestroy function
public PlayerScript : MonoBehaviour
{
private string m_playerName;
...
public string PlayerName
{
get
{
return m_playerName;
}
}
void Start()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
// Check if it exists so we can use it with or without the list
if (playerListGUI != null)
{
playerListGUI.AddPlayer(this);
}
}
void OnDestroy()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
if (playerListGUI != null)
{
playerListGUI.RemovePlayer(this);
}
}
}
I have an AnimatorController and want to override it at runtime with another clip. The project should run as WebGL project so it is not possbile to create a new AnimatorController because it is in the UnityEditor namespace which is not exported when building the project as WebGL project (if you have an solution for that you can post it too).
So i tried to use the AnimatorOverrideController but I don't know why the animation isn't playing...
Basic_Run_02 is loaded by default before pressing play and it should be overridden with the clip in the SadWalk.fbx.
Anyone an idea why the animation is not playing?
public class AnimationHelper : MonoBehaviour
{
public AnimationClip state;
public AnimatorOverrideController overrideController;
void Start(){
state = Resources.Load("SadWalk", typeof(AnimationClip)) as AnimationClip;
PlayMotion (state.name);
}
void PlayMotion(string name){
RuntimeAnimatorController myController = gameObject.GetComponent<Animator>().runtimeAnimatorController;
overrideController = new AnimatorOverrideController ();
overrideController.name = "Test";
overrideController.runtimeAnimatorController = gameObject.GetComponent<Animator>().runtimeAnimatorController;
overrideController ["Basic_Run_02"] = state;
//Debug.Log (overrideController.clips.ToString);
gameObject.GetComponent<Animator>().runtimeAnimatorController = overrideController;
gameObject.GetComponent<Animator> ().StartPlayback ();
}
}
The Animator and RuntineAnimatorController classes are also available in the UnityEngine namespace, so you should do a couple of changes like below:
gameObject.GetComponent<UnityEngine.Animator>().runtimeAnimatorController = overrideController;
And the overrideController should be of type UnityEngine.RuntimeAnimatorController, example:
public AnimatorOverrideController overrideController;
Now if you export to WebGL you will have your code working.