I added Unity Ads to my project.
At the first run at all works good and it show that an ad will show, but when I hit play the second time it give me this error
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
This is my script for the ads :
public class Monetization : MonoBehaviour, IUnityAdsListener
{
string GooglePlay_ID = "****";
bool GameMode = true;
string myPlacementId = "rewardedVideo";
void Start()
{
Advertisement.AddListener(this);
Advertisement.Initialize(GooglePlay_ID, GameMode);
}
public void ShowRewardedVideo()
{
// Check if UnityAds ready before calling Show method:
if (Advertisement.IsReady(myPlacementId))
{
Advertisement.Show(myPlacementId);
}
else
{
Debug.Log("Rewarded video is not ready at the moment! Please try again later!");
}
}
// Implement IUnityAdsListener interface methods:
public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)
{
// Define conditional logic for each ad completion status:
if (showResult == ShowResult.Finished)
{
// Reward the user for watching the ad to completion.
SceneManager.LoadScene(4);
}
else if (showResult == ShowResult.Skipped)
{
// Do not reward the user for skipping the ad.
}
else if (showResult == ShowResult.Failed)
{
Debug.LogWarning("The ad did not finish due to an error.");
}
}
public void OnUnityAdsReady(string placementId)
{
// If the ready Placement is rewarded, show the ad:
if (placementId == myPlacementId)
{
// Optional actions to take when the placement becomes ready(For example, enable the rewarded ads button)
}
}
public void OnUnityAdsDidError(string message)
{
// Log the error.
}
public void OnUnityAdsDidStart(string placementId)
{
// Optional actions to take when the end-users triggers an ad.
}
}
When you load a new scene, the game objects in the previous scene will be destroyed. To prevent this add the following function in your Monetization script.
void Awake() {
DontDestroyOnLoad(this.gameObject);
}
Related
I have three buttons, three players, and I want to know which player clicked on which button by assigning them a different popup UI color. Let's say player 1- red, player 2 - green, player 3 - white. So when player 1 clicks on button(1), the red panel will be visible for all other players telling them who did that.
Possible solution: I have been advised to call to the Host with a Cmd method and pass the connectionId of the player who click on it. Then check the list of players on the NetworkManager, and find the matching player object. Once it has that player object, it would get the name and Id, and calls a RPC to the clients to show the UI element, passing them the correct text and color.
What I Have: I have list of my players:
public List<MyNetworkPlayer> players { get; } = new List<MyNetworkPlayer>();
and i can use my player by
MyNetworkPlayer player = conn.identity.GetComponent<MyNetworkPlayer>();
I also can attatch my NetworkManager if that can help:
public class MyNetworkManager : NetworkManager
{
public static event Action ClientOnConnected;
public static event Action ClientOnDisconnected;
// players should't be able to join the lobby during the game.
private bool isGameInProgress = false;
// here, we create a list of players, in order to display them in the lobby later.
public List<MyNetworkPlayer> players { get; } = new List<MyNetworkPlayer>();
#region Serwer
public override void OnServerConnect(NetworkConnection conn )
{ //kick a player if the game is in progress
if (!isGameInProgress) { return; }
base.OnServerConnect (conn);
conn.Disconnect();
}
// here i write some own logic about what heppens when a client connects to a server. If the connection is successful
// the message about connection will be display in a console!
public override void OnServerDisconnect(NetworkConnection conn)
{ // when the server disconnects someone
// lets grab that player
MyNetworkPlayer player = conn.identity.GetComponent<MyNetworkPlayer>();
players.Remove(player);
base.OnServerDisconnect(conn);
}
public override void OnStopServer()
//what happens when we stop running the server
{
players.Clear();
isGameInProgress = false;
}
public void StartGame()
{ // the game won't start with the less than 2 players
if(players.Count <2){return;}
//if we have more than 2 we can start the game
isGameInProgress = true;
//also we change our scene here
ServerChangeScene("GameScene");
}
public override void OnServerAddPlayer(NetworkConnection conn)
{
base.OnServerAddPlayer(conn);
//reference to network player
MyNetworkPlayer player = conn.identity.GetComponent<MyNetworkPlayer>();
players.Add(player);
// here I assign player's name base on joining order
player.SetDisplayName($"Player {players.Count}");
Debug.Log("Player conn ID: " + conn.connectionId);
//here we dclare who will be a party owner
// if there is only one player he will be a party owner
player.SetPartyOwner(players.Count == 1);
Debug.Log($"There are now {numPlayers} players");
}
//when a client stops, we shold also clear the player's list and currently we only
// add to it on the server
public override void OnStopClient()
{
players.Clear();
}
#endregion
#region Client
[System.Obsolete]
public override void OnClientConnect(NetworkConnection conn)
{
base.OnClientConnect(conn);
Debug.Log("Client connected: " + conn.connectionId);
ClientOnConnected?.Invoke();
}
[System.Obsolete]
public override void OnClientDisconnect(NetworkConnection conn)
{
base.OnClientDisconnect(conn);
ClientOnDisconnected?.Invoke();
}
#endregion
}
And MyNetworkPlayer:
public class MyNetworkPlayer : NetworkBehaviour
{
//Here i refer to a player text and change it based on current player number.
[SerializeField] private TMP_Text displayNameText= null;
[SyncVar(hook = nameof(AuthorityHandlePartyOwnerStateUpdated))]
private bool isPartyOwner = false;
// syncvar that will allow other players to store our name
[SyncVar(hook = nameof(ClientHandleDisplayNameUpdated))]
public string displayName;
// this below is the event created in order to handle the changes of client info
// like it's colour and so on
public static event Action ClientOnInfoUpdated;
//public string which returns the display name
public string GetDisplayName()
{
return displayName;
}
public bool GetIsPartyOwner()
{
return isPartyOwner;
}
public static event Action<bool> AuthorityOnPartyOwnerStateUpdated;
#region Serwer
public override void OnStartServer()
{
DontDestroyOnLoad(gameObject);
}
[Server]
// checking who the party owner is
public void SetPartyOwner(bool state)
{
isPartyOwner = state;
}
[Server]
public void SetDisplayName(string newDisplayName)
{
displayName = newDisplayName;
}
#endregion
#region Commands
[Command]
private void CmdSetDisplayName(string newDisplayName)
{
//here I add the codition saying that the player length can not be shorter than 2 characters
//if(newDisplayName.Length <2 || newDisplayName.Length > 20){ return; }
RpcLogNewName(newDisplayName);
SetDisplayName(newDisplayName);
}
[Command]
// A command so client can tell the server that he wants to start the game
public void CmdStartGame()
{
if(!isPartyOwner) { return; }
//otherwise tell the NetworkManager to stop the game
((MyNetworkManager)NetworkManager.singleton).StartGame();
}
#endregion
#region Authorities
private void AuthorityHandlePartyOwnerStateUpdated(bool oldState, bool newState)
{
// it allows you to change who the party owner is during the lobby
// its goinna be a button to change the authority of the ownershit
if(!hasAuthority) {return; }
// this down below is for the UI button change its statement
AuthorityOnPartyOwnerStateUpdated?.Invoke(newState);
}
#endregion
#region Client
// Here we change the player's name
private void HandleDisplayNameUpdated(string oldName, string newName)
{
displayNameText.text = newName;
}
// Here we connect the reference from MyNetworkManager
public override void OnStartClient()
{
if (NetworkServer.active) {return;}
// whenever a client starts and we're not the host, if we just end up a client, we can
// be added to our list of the players
((MyNetworkManager)NetworkManager.singleton).players.Add(this);
// this below prevcents our player objects from being destroyed when
// changing the scanes
DontDestroyOnLoad(gameObject);
}
private void ClientHandleDisplayNameUpdated(string oldDisplayName, string newDisplayName)
{ // we need to display people's name when they got updated (in the UI)
ClientOnInfoUpdated?.Invoke();
}
public override void OnStopClient()
{
ClientOnInfoUpdated?.Invoke();
//if we are not the server we do this for everyone:
if (!isClientOnly) {return; }
// Here we remove a player from the list of players
((MyNetworkManager)NetworkManager.singleton).players.Remove(this);
//if we are the server we do this for everyone:
if (!hasAuthority) { return; }
}
[ClientRpc]
//rpc log new name is one of our new names being set
private void RpcLogNewName(string newDisplayName)
{
Debug.Log(newDisplayName);
}
#endregion
}
My problem: As I said in possible solution, I don't know how to send connectionId to host and then, based on that, assign corresponding UI element for each different player. I would be really thankful for any advise on how to code that.
I used the code from Unity and the rewards are multiples... first 1 then 2 then 3 and increasing... i tried deleting some code, but keep doing the same, then this happened.
I searched online and i couldn't find anything that explains clicking the rewarded ads button more than once.
Everything (apparently) is working fine only if i don't assing a button and leave the "SerializeField" empty, because if i remove the button, goes back to give more rewards... can somebody check this and tell me what's going on? i add the code Here
using UnityEngine;
using UnityEngine.Advertisements;
using UnityEngine.UI;
public class AdsInitializer : MonoBehaviour, IUnityAdsInitializationListener, IUnityAdsLoadListener, IUnityAdsShowListener
{
[SerializeField] string _androidGameId = "4634758";
[SerializeField] string _iOSGameId = "4634759";
[SerializeField] bool _testMode = false;
private string _gameId;
[SerializeField] Button _showAdButton;
[SerializeField] string rewardAndroidAdUnitId = "Rewarded_Android";
[SerializeField] string rewardiOSAdUnitId = "Rewarded_iOS";
string rewardAdUnitId = null; // This will remain null for unsupported platforms
void Awake()
{
_gameId = (Application.platform == RuntimePlatform.IPhonePlayer)
? _iOSGameId
: _androidGameId;
rewardAdUnitId = (Application.platform == RuntimePlatform.IPhonePlayer)
? rewardiOSAdUnitId
: rewardAndroidAdUnitId;
Advertisement.Initialize(_gameId, _testMode, this);
rewardAdUnitId = (Application.platform == RuntimePlatform.IPhonePlayer)
? rewardiOSAdUnitId
: rewardAndroidAdUnitId;
}
public void OnInitializationComplete()
{
Debug.Log("Unity Ads initialization complete.");
LoadRewardedAd();
}
public void OnInitializationFailed(UnityAdsInitializationError error, string message)
{
Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}");
}
#region REWARDED ADS
public void LoadRewardedAd()
{
Debug.Log("Loading Ad: " + rewardAdUnitId);
Advertisement.Load(rewardAdUnitId, this);
}
public void ShowRewardedAd()
{
Advertisement.Show(rewardAdUnitId, this);
}
#endregion
public void OnUnityAdsAdLoaded(string adUnitId)
{
if (adUnitId.Equals(rewardAdUnitId))
{
// Configure the button to call the ShowAd() method when clicked:
//_showAdButton.onClick.AddListener(ShowRewardedAd);
// Enable the button for users to click:
//_showAdButton.interactable = true;
Debug.Log("RewardedAds Loaded");
}
}
public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
{
if (adUnitId.Equals(rewardAdUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
{
Debug.Log("Unity Ads Rewarded Ad Completed");
// Grant a reward.
_showAdButton.onClick.RemoveAllListeners(); //with this line of code the problem is solved but shows the NullReference.
// Load another ad:
Advertisement.Load(rewardAdUnitId, this);
}
}
public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message)
{
if (adUnitId.Equals(rewardAdUnitId))
{
Debug.Log($"Error loading Ad Unit {adUnitId}: {error.ToString()} - {message}");
}
}
public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message)
{
if (adUnitId.Equals(rewardAdUnitId))
{
Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}");
}
}
public void OnUnityAdsShowStart(string adUnitId) { }
public void OnUnityAdsShowClick(string adUnitId) { }
void OnDestroy()
{
//with or without it doesn't change, it works only when the game is closed, and when reopen is working the same
//_showAdButton.onClick.RemoveAllListeners();
}
}
SOLVED!!
So, basically the script from Unity somehow has an unknown error, i couldn't explain myself how or from where... so the solution i fould was to add a bool in this case showAd = false and implemented it inside the showAd and the OnUnityAdsShowComplete functions, fortunatelly that was enough to solve the issue, now I can put the script in the button or in an AdManager and call the funtion from the button in the section OnClick() either way now is not showing neither error neither multiples with the rewards.
Hope it will be usefull for someone else.
using UnityEngine;
using UnityEngine.Advertisements;
using UnityEngine.UI;
public class AdsInitializer : MonoBehaviour, IUnityAdsInitializationListener, IUnityAdsLoadListener, IUnityAdsShowListener
{
[SerializeField] string _androidGameId = "4634758";
[SerializeField] string _iOSGameId = "4634759";
[SerializeField] bool _testMode = true;
private string _gameId;
[SerializeField] Button _showAdButton; //You can remove this if want to add the function manually from OnClick()
[SerializeField] string _androidAdUnitId = "Rewarded_Android";
[SerializeField] string _iOSAdUnitId = "Rewarded_iOS";
string _adUnitId = null; // This will remain null for unsupported platforms
private bool showAd = false;
void Awake()
{
InitializeAds();
Debug.Log("Awake");
_adUnitId = (Application.platform == RuntimePlatform.IPhonePlayer)
? _iOSAdUnitId
: _androidAdUnitId;
Debug.Log("the _adUnitId is: " + _adUnitId);
}
public void InitializeAds()
{
_gameId = (Application.platform == RuntimePlatform.IPhonePlayer)
? _iOSGameId
: _androidGameId;
Advertisement.Initialize(_gameId, _testMode, this);
}
public void OnInitializationComplete()
{
Debug.Log("Unity Ads initialization complete.");
LoadAd();
}
public void OnInitializationFailed(UnityAdsInitializationError error, string message)
{
Debug.Log($"Unity Ads Initialization Failed: {error.ToString()} - {message}");
}
public void LoadAd()
{
// IMPORTANT! Only load content AFTER initialization (in this example, initialization is handled on top of the script).
Debug.Log("Loading Ad: " + _adUnitId);
Advertisement.Load(_adUnitId, this);
}
// If the ad successfully loads, add a listener to the button and enable it:
public void OnUnityAdsAdLoaded(string adUnitId)
{
Debug.Log("Ad Loaded: " + adUnitId);
if (adUnitId.Equals(_adUnitId))
{
// Configure the button to call the ShowAd() method when clicked:
_showAdButton.onClick.AddListener(ShowAd); //You can remove this if want to add the function manually from OnClick()
// Enable the button for users to click:
_showAdButton.interactable = true; //You can remove this if want to add the function manually from OnClick()
}
}
// Implement a method to execute when the user clicks the button:
public void ShowAd()
{
if (showAd == false)
{
Debug.Log("Showing Ad");
// Disable the button:
_showAdButton.interactable = false; //You can remove this if want to add the function manually from OnClick()
// Then show the ad:
Advertisement.Show(_adUnitId, this);
_showAdButton.onClick.RemoveAllListeners(); //You can remove this if want to add the function manually from OnClick()
Debug.Log("All Listeners Removed");
showAd = true;
}
}
// Implement the Show Listener's OnUnityAdsShowComplete callback method to determine if the user gets a reward:
public void OnUnityAdsShowComplete(string adUnitId, UnityAdsShowCompletionState showCompletionState)
{
if (showAd == true)
{
if (adUnitId.Equals(_adUnitId) && showCompletionState.Equals(UnityAdsShowCompletionState.COMPLETED))
{
Debug.Log("Unity Ads Rewarded Ad Completed");
// Grant a reward.
// Load another ad:
Advertisement.Load(_adUnitId, this);
showAd = false;
}
}
}
public void OnUnityAdsFailedToLoad(string adUnitId, UnityAdsLoadError error, string message)
{
Debug.Log($"Error loading Ad Unit {adUnitId}: {error.ToString()} - {message}");
// Use the error details to determine whether to try to load another ad.
}
public void OnUnityAdsShowFailure(string adUnitId, UnityAdsShowError error, string message)
{
Debug.Log($"Error showing Ad Unit {adUnitId}: {error.ToString()} - {message}");
// Use the error details to determine whether to try to load another ad.
}
public void OnUnityAdsShowStart(string adUnitId) { }
public void OnUnityAdsShowClick(string adUnitId) { }
}
Trying to create a ready check using PUN2 so that all players will load into the game scene at the same time, but I do not understand how to check another players custom property and keep a count of how many players are currently ready and if all are ready then start the game. I think I have a custom property set up for every player that should be but I am unsure if it working at all.
public class HeroSelectController : MonoBehaviour
{
[HideInInspector]
public string selectedHero;
private PhotonView PV;
private bool PlayerReady = false;
private ExitGames.Client.Photon.Hashtable _playerCustomProperties = new ExitGames.Client.Photon.Hashtable();
private void Update()
{
Debug.Log("Player Ready = " + _playerCustomProperties["PlayerReady"]);
}
private void HeroSelect()
{
PlayerReady = true;
selectedHero = "PlayerTest";
PhotonNetwork.SetPlayerCustomProperties(_playerCustomProperties);
_playerCustomProperties["PlayerReady"] = PlayerReady;
}
public void OnClickHeroButton()
{
HeroSelect();
if (PhotonNetwork.IsMasterClient)
{
foreach (var photonPlayer in PhotonNetwork.PlayerList)
{
photonPlayer.CustomProperties["PlayerReady"] = true;
PhotonNetwork.LoadLevel(3);
}
}
}
}
What is currently happening is that the master client can start the game regardless of everyone else's state. Feel like I might be overthinking all of this and there is a much similar solution as I would expect a function like this to be common place as I would expect something similar to be used in many online games so if I am going about completely the wrong way please point me a more suitable direction
Don't know exactly how Photon works but I assume these properties are already synchronized and as far as I understand you simply want some kind of a check like e.g.
private bool AllPlayersReady
{
get
{
foreach (var photonPlayer in PhotonNetwork.PlayerList)
{
if(photonPlayer.CustomProperties["PlayerReady"] == false) return false;
}
return true;
}
}
Which you could probably also shorten using Linq like
using System.Linq;
...
private bool AllPlayersReady => PhotonNetwork.PlayerList.All(player => player.CustomProperties["PlayerReady"] == true);
And then use it like
public void OnClickHeroButton()
{
HeroSelect();
if (!PhotonNetwork.IsMasterClient) return;
if(!AllPlayersReady)
{
return;
}
PhotonNetwork.LoadLevel(3);
}
This can still be enhanced since now the master has to press repeatedly until maybe everyone is ready. I would additionally use a Coroutine like
public void OnClickHeroButton()
{
HeroSelect();
if (!PhotonNetwork.IsMasterClient) return;
StartCoroutine (WaitAllReady ());
}
private IEnumerator WaitAllReady()
{
yield return new WaitUntil (() => AllPlayersReady);
PhotonNetwork.LoadLevel(3);
}
Thanks to Iggy:
Instead of a routine which checks every frame you can use OnPlayerPropertiesUpdate in order to check for the ready state
void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
if (!PhotonNetwork.IsMasterClient) return;
// you can even limit the check to make it
// only if "PlayerReady" is among the changed properties
if(!changedProps.Contains("PlayerReady")) return;
if(!AllPlayersReady) return;
PhotonNetwork.LoadLevel(3);
}
Note: Typed on smartphone but I hope the idea gets clear
I am trying to show interstitial ads when the player clicks on specific button (hint button), but Unity is giving me this error:
> NullReferenceException: Object reference not set to an instance of an
> object ButtonManager.Hint()...
This is the code for showing the ad:
public class ButtonManager : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void Reload()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void Home()
{
SceneManager.LoadScene("MainMenu");
}
int contc;
public void Play()
{
SceneManager.LoadScene("LevelMenu");
}
public void Hint()
{
FindObjectOfType<AdMobManager>().Hint = true;
FindObjectOfType<AdMobManager>().showInterstitial();
}
Any idea how can I get around this problem?
That is a C# error, you are trying to use a null Object.
I think the problem is:
FindObjectOfType()
Check if you really get any object with that Find.
I'm using this code to create and load a Rewarded Video, when i click on the button, with, teorically it has to show, it doesn't. But, in my Debug shows that are instanced, loaded and, how i said, when i click on the button, the Debug says that is showing too. However, I've tested in my smartphone and an emulator and nothing appears.
public class AdRewardedVideo : MonoBehaviour {
string VideoAdID = "THE ID HERE";
RewardBasedVideoAd MyRewardedVideoAd;
void Start() {
MyRewardedVideoAd = RewardBasedVideoAd.Instance;
}
public void LoadVideoAd() {
if (!MyRewardedVideoAd.IsLoaded()) {
AdRequest request = new AdRequest.Builder().Build();
MyRewardedVideoAd.LoadAd(request,VideoAdID);
}
}
public void showVideoAd() {
if (MyRewardedVideoAd.IsLoaded()) {
MyRewardedVideoAd.Show();
} else {
LoadVideoAd();
}
}
}