Right now the game shows ads each and every time the player dies. I want the ads to appear after the third death of the player. Please guide me as I am a beginner with no real knowledge of coding. Please also specify if I need to attach the script to a specific game object.
I have put the ads code below. Thanks.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public GameObject gameOverCanvas;
public GameObject Interstitial;
public int DeathCount = 0;
private void Start()
{
Time.timeScale = 1;
}
public void GameOver()
{
gameOverCanvas.SetActive(true);
Time.timeScale = 0;
DeathCount++;
}
private bool DeathCountCheck()
{
if (DeathCount == 3)
{
// Reset the variable, otherwise it will increase indefinitely and the condition will not be met in the future
Interstitial.GetComponent<InterstitialAds>().ShowInterstitial();
DeathCount = 0;
return true;
}
return false;
}
public void Replay()
{
SceneManager.LoadScene(0);
}
if "deathCount" increases each time the player dies, then when the ad is meant to show you just check that variable and reset it appropriately.
// Function that checks if there were 3 deaths
private bool DeathCountCheck()
{
if (deathCount == 3)
{
// Reset the variable, otherwise it will increase indefinitely and the condition will not be met in the future
deathCount = 0;
return true;
}
return false;
}
Then in the "ShowInterstitialAd" just add another condition
public void ShowInterstitial()
{
if (Advertisement.IsReady(interstitialID) && DeathCountCheck())
{
Advertisement.Show(interstitialID);
}
}
Related
I need help with my script. I really can't understand how to make it. I have a script that counts time but I don't know how to make it so that when the game is over the time appears in my Game Over scene as the highest score. I only need to save this high score for one time. After the game is closed the score can be removed. In the game, I have a countdown timer, when it reaches 0 the game is over.
This is my TimeCounter script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimeCounter: MonoBehaviour
{
public Text timerText;
private float startTime;
bool finishCountTime = false;
public string minutes, seconds;
// Use this for initialization
void Start()
{
}
public void startTheTimer()
{
Invoke("startTime", 10);
startTime = Time.time;
}
public void stopTheTimer()
{
finishCountTime = true;
}
// Update is called once per frame
void Update()
{
float t = Time.time - startTime;
minutes = ((int)t / 60).ToString();
seconds = (t % 60).ToString("f2");
timerText.text = minutes + ":" + seconds;
}
}
and this is GameManager script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Threading;
public class GameManager: MonoBehaviour
{
[SerializeField] private float timer = 30;
private void Awake()
{
pop.onClicked += PopClicked;
}
private void OnDestroy()
{
pop.onClicked -= PopClicked;
}
private void PopClicked()
{
if (pop.Unclicked.Count == 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
private void Update()
{
if (timer <= 0)
{
SceneManager.LoadScene("GameOver");
}
}
}
Here is an example snippet of how to use DontDestroyOnLoad along with the SceneLoaded delegates.
using UnityEngine;
using UnityEngine.SceneManagement;
public class ExampleScript : MonoBehaviour
{
private float score;
private void Awake()
{
var objs = FindObjectsOfType(typeof(ExampleScript));
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if(scene.name == "gameOver")
{
// display the time here as we are showing the score
Debug.Log(score);
}
else
{
// we loaded into another scene - presumably your game scene, so reset the score
score = 0;
}
}
}
As it is DontDestroyOnLoad, you will need to check if the current scene contains the object already and destroy it. As you have two scripts running your logic, you might need to slightly tweak it so that one of the managers is able to communicate to one another to pull your score or move it to the same script. I have greatly over-generalized your issue as I was not sure which part you did not understand.
Edit: Here is the more in-depth answer on how to achieve what you would like to do.
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class TestScript : MonoBehaviour
{
private float score; // our current score
private bool updateScore = false; // whether or not we should update our score UI every frame
private Text ScoreText = null; // our text object that updates our score
private void Awake()
{
// as the object is DontDestroyOnLoad, we need to check if another instance exists
// if one does, we need to delete it as if multiple exist in one scene, there could be issues
// if a player continually replays your scene where this object starts, then multiple will exist
var objs = FindObjectsOfType(typeof(TestScript));
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
private void OnEnable()
{
// add the callback for when a scene is loaded
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
// remove the callback from when a scene is loaded
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// grab the new reference to out score UI
ScoreText = GameObject.Find("Score").GetComponent<Text>();
// when we have the gameover scene, display our final score
if (scene.name == "gameover")
{
// display the time here as we are showing the score
ScoreText.text = "Final Score: " + score.ToString("#");
updateScore = false;
}
else
{
// we loaded into another scene - presumably your game scene, so reset the score
score = 0;
updateScore = true;
}
}
private void Update()
{
// if we are in the game scene and we should update score, update the score however you like
if(updateScore)
{
score += Time.deltaTime;
ScoreText.text = "Current Score: " + score.ToString("#");
}
// this is here solely for testing - instead of a real game just hit space to end the game and restart it
if(Input.GetKeyDown(KeyCode.Space))
{
if(SceneManager.GetActiveScene().name == "gameover")
{
SceneManager.LoadScene(0);
}
else
{
SceneManager.LoadScene("gameover");
}
}
}
}
Here is an example of the script working as well as the two scene hierarchies. For this to work exactly as I have it, you will need two scenes. In your build settings, the first scene will be your game scene, and then there needs to be some other scene in the build settings with the name gameover. As well, you will need to have some sort of UnityEngine.UI.Text component that is called Score in both scenes.
All of this can be changed, but this is what you need to do to get it to work as I have it set up.
I have in the Hierarchy one scene and 3 parent objects. The 3 parent objects are :
Game Data (Where all the gameplay objects are childs of it)
Main Menu
Game Manager (That control on pausing/unpausing the game in some cases)
The idea the main goal is when the game start it's starting with the main menu then when clicking the start new game button it will start the game when hit the escape key it will be back to the main menu and again I can start a new game or resuming the game. I also did that while the game is running if you hit the P button it will pause/unpause the game.
First the script that is attached to the Game Manager :
Here I'm doing a reference for the Dialogue System, Depth Of Field, Main Menu And in the bottom I'm using find to find back the Depth Of Field and the Dialogue System since I'm destroying the Game Data object when starting a new game :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameController : MonoBehaviour
{
public GameObject dialogueSystem;
public DepthOfField depthOfField;
public static bool gamepaused = false;
public GameObject mainMenu;
private void Start()
{
Time.timeScale = 0f;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
gamepaused = !gamepaused;
if (gamepaused)
{
Time.timeScale = 0f;
}
else
{
Time.timeScale = 1f;
}
}
if (Input.GetKeyDown(KeyCode.Escape))
{
if(depthOfField == null || dialogueSystem == null)
{
depthOfField = GameObject.Find("Player Camera").GetComponent<DepthOfField>();
dialogueSystem = GameObject.Find("Dialogue System");
}
depthOfField.DepthOfFieldInit(3f);
Time.timeScale = 0f;
dialogueSystem.SetActive(false);
mainMenu.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
}
The problem is that the Dialogue System and Depth Of Field are in the Game Data and when I destroy the Game Data and instantiate the Game Data using a prefab over again to make a new game to start the variables in the GameController script lost reference and I need to use find. But using find so many times is a good idea ?
This script the next one is attached to the Main Menu :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject gameDataPrefab;
public GameObject gameData;
public GameObject mainMenu;
public void StartNewGame()
{
var gdata = GameObject.Find("Game Data");
if (gdata != null)
{
Destroy(gdata);
}
GameObject go = Instantiate(gameDataPrefab);
go.name = "Game Data";
mainMenu.SetActive(false);
GameController.gamepaused = false;
Time.timeScale = 1f;
}
public void ResumeGame()
{
}
public void QuitGame()
{
Application.Quit();
}
}
Here when I click the UI button to start a new game in the function StartNewGame I destroy the Game Data and create a new one. This make the variables in the GameController script to lost references.
The last script is attached to the Game Data this script I'm calling the method DepthOfFieldInit to make effect when I hit the escape key back to the Main Menu.
The problem is that some variables are lost references since the Game Data is destroyed. For example I'm getting null on :
playerLockMode.PlayerLockState(true, true);
Since playerLockMode is also on the Game Data that have been destroyed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;
public class DepthOfField : MonoBehaviour
{
public UnityEngine.GameObject player;
public PostProcessingProfile postProcessingProfile;
public bool dephOfFieldFinished = false;
public LockSystem playerLockMode;
private Animator playerAnimator;
private float clipLength;
private Coroutine depthOfFieldRoutineRef;
// Start is called before the first frame update
void Start()
{
if (depthOfFieldRoutineRef != null)
{
StopCoroutine(depthOfFieldRoutineRef);
}
playerAnimator = player.GetComponent<Animator>();
AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
foreach (AnimationClip clip in clips)
{
clipLength = clip.length;
}
DepthOfFieldInit(clipLength);
// Don't forget to set depthOfFieldRoutineRef to null again at the end of routine!
}
public void DepthOfFieldInit(float duration)
{
var depthOfField = postProcessingProfile.depthOfField.settings;
depthOfField.focalLength = 300;
StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, duration));
postProcessingProfile.depthOfField.settings = depthOfField;
}
public IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
{
playerLockMode.PlayerLockState(true, true);
float counter = 0f;
while (counter < duration)
{
var dof = postProcessingProfile.depthOfField.settings;
counter += Time.deltaTime;
float val = Mathf.Lerp(fromVal, toVal, counter / duration);
dof.focalLength = val;
postProcessingProfile.depthOfField.settings = dof;
yield return null;
}
playerAnimator.enabled = false;
dephOfFieldFinished = true;
depthOfFieldRoutineRef = null;
}
}
But I don't understand if I instantiate right away back the Game Data when starting a new game why the variables lost references ? And is there a better way to use Find at any place many times after the Game Data have been destroyed ? And maybe the whole code in the StartNewGame is wrong ?
My main goal is when clicking the StartNewGame start over a new game. When hit the escape key pause the game and go back to the main menu.
Here is a screenshot of my Hierarchy :
I have created a game where when the user breaks all the blocks he is taken to the next scene but this is not happening despite adding all of the scenes I have in the build settings. I have no errors whatsoever and the scene is written correctly. Can someone help me resolve this, please?
This is the build settings
Bricks script : (where the scene is called)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public static int brickCount = 0;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
if(this.gameObject.tag == "BrickHit")
{
brickCount++;
}
if(this.gameObject.tag == "BrickHitTwice")
{
brickCount++;
}
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
brickCount--;
Destroy(this.gameObject);
}
if(brickCount == 0)
{
myLevelManager.LoadLevel("Level1.2"); //THIS SCENE IS NOT LOADING
}
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
LevelManager Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
I suspect that your bug may lie in the fact that you're Destroy()ing the gameObject before it can load the next scene; you get a race condition on what will finish first; LoadScene or Destroy() - which would explain why it sometimes work. You should never assume it is a bug in the framework before understanding your problem.
Try putting the Destroy() after the LoadScene() or with a delay to understand if this is your issue.
Also, your LevelManager can be made static and doesn't need to inherit from MonoBehaviour since it doesn't use gameObject functionality.
public static class LevelManager {
public static void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Used by doing LevelManager.LoadLevel("MyLevel");, but then you may question what is more effective, doing LevelManager.LoadLevel or SceneManager.LoadLevel, as they will do the exact same thing.
The main issue that you're having is not having a single source to check brickCount instead each individual brick is maintaining its own count. I would recommend moving the brick counting logic into a separate class. It would seem like LevelManager would a good place for it. So in LevelManager add:
private int brickCount = 0;
public void AddBrick()
{
brickCount++;
}
public void RemoveBrick()
{
brickCount--;
// Check if all bricks are destroyed
if (brickCount == 0)
{
LoadLevel("Level1.2");
}
}
And then in your brick script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
myLevelManager.AddBrick();
// I'm not sure why you were checking the tag here, since the result was the same
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
myLevelManager.RemoveBrick();
Destroy(this.gameObject);
}
/* This looks like the player is getting score whether the brick is destroyed or not. Also, it would appear the player won't get scored on the final brick */
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
}
I have been doing a RPG game in Unity with C # and when doing a system of quests, specifically those of killing a certain number of enemies, I found the problem of having 3 enemies in the scene and being the target of the quest: Kill 3 enemies. If I kill them before activating the quest and later active the quest does not give me the reward (in this case experience). How can I tell the enemies and make that if the quest detects that I have already killed the necessary enemies to get the quest give me the reward equally?
Here the two needed scripts i think:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QuestObject : MonoBehaviour {
public int questNumber;
public QuestManager qManager;
public string startText;
public string endText;
public bool isItemQuest;
public string targetItem;
public bool isEnemyQuest;
public string targetEnemy;
public int enemiesToKill;
private int enemyKillCount;
private PlayerStats playerStats;
public int EXPToGive;
void Start () {
playerStats = FindObjectOfType <PlayerStats> ();
}
void Update () {
if (isItemQuest) {
if (qManager.itemCollected == targetItem) {
qManager.itemCollected = null;
EndQuest ();
}
}
if (isEnemyQuest) {
if (qManager.enemyKilled == targetEnemy) {
qManager.enemyKilled = null;
enemyKillCount++;
}
if (enemyKillCount >= enemiesToKill) {
EndQuest ();
}
}
}
public void StartQuest (){
qManager.ShowQuestText (startText);
}
public void EndQuest (){
qManager.ShowQuestText (endText);
playerStats.AddEXP (EXPToGive);
qManager.questCompleted [questNumber] = true;
gameObject.SetActive (false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyHealth : MonoBehaviour {
public int startingHealth;
public int currentHealth;
public GameObject damageBurst;
private PlayerStats playerStats;
public int EXPToGive;
public string enemyQuestName;
private QuestManager qManager;
void Start ()
{
// Setting up the references.
//anim = GetComponent <Animator> ();
//enemyAudio = GetComponent <AudioSource> ();
//enemyMovement = GetComponent <EnemyMovement> ();
//enemyAttacking = GetComponentInChildren <EnemyAttack> ();
// Set the initial health of the player.
currentHealth = startingHealth;
playerStats = FindObjectOfType <PlayerStats> ();
qManager = FindObjectOfType <QuestManager> ();
}
void Update ()
{
if (currentHealth <= 0) {
qManager.enemyKilled = enemyQuestName;
Destroy (gameObject);
playerStats.AddEXP (EXPToGive);
}
}
public void TakeDamage (int amountDamage)
{
// Reduce the current health by the damage amount.
currentHealth -= amountDamage;
Instantiate (damageBurst, transform.position, transform.rotation);
}
public void SetMaxHelth () {
currentHealth = startingHealth;
}
}
One Aproach would be to create some type of "WorldManager" which counts every Enemy which has been slain. And when Starting a quest this quest could check the WorldManagers kill count and add it to it's own count.
public void StartQuest (){
qManager.ShowQuestText (startText);
this.enemyKillCount += worldManager.GetKillCount();
}
In your enemy class you have to add a kill to your worldManager.
void Update ()
{
if (currentHealth <= 0) {
qManager.enemyKilled = enemyQuestName;
this.worldManager.AddKill(this)
Destroy (gameObject);
playerStats.AddEXP (EXPToGive);
}
}
Alternative:
Make your QManager be aware of every kill in a Scene.
You can achieve this through many ways.
One of them is passing your EnemyObject an reference of your Qmanager and do the same as with the "WorldManager" provided above, or you use Messaging and fire a Message targeting the QManager when an enemy is slain.
Alternative 2:
Throw an Event when an enemy has been slain and subscribe to it on your QManager/WorldManager. This way u can reuse your enemy class in every game. From my point of view static dependencies are evil, but there are many discussions and SO and everywhere on the internet about that.
You can several approach. The most straight-forward is to use static.
The purpose of static is for the variable/method to belong to the class instead of an instance of the class.
In your case, you want each enemy to have its own health, this cannot be static.
And you want to count how many instances there are in the scene from the class. So static is fine.
public class Enemy:MonoBehaviour
{
private static int enemyCount = 0;
public static int EnemyCount {get{ return enemyCount;} }
public event Action<int> RaiseEnemyDeath;
public static void ResetEnemyCount(){
enemyCount = 0;
}
private int health;
public void Damage(int damage)
{
CheckForDamage(); // here you check that damage is not neg or too big...
this.health -= damage;
if(this.health <= 0)
{
OnDeath();
}
}
void OnActivate()
{
enemyCount++;
this.health = 20;
}
void OnDeath()
{
enemyCount--;
RaiseEnemyDeath(enemyCount); // Should check for nullity...
}
}
This one is fairly simple. The first part is all static and is relevant to the class. The second part is relevant to the instance. If you use a pool of enemy and then reuse the same instance multiple times, the OnActivate method is called when you make the enemy alive in the scene (it may have been there for a while as inactive). Then when the health is down, kill the enemy (there are not all the required actions there...) and trigger the event.
Using the public static property, you can know what is the enemy count from a GameManager (Enemy should not affect the gameplay, only takes care of the enemy).
public class GameManager:MonoBehaviour
{
void Start()
{
Enemy.RaiseEnemyDeath += Enemy_RaiseEnemyDeath;
}
void Enemy_RaiseEnemyDeath(int count)
{
if(count < 0){ // End of level }
// You can also access enemyCount
int count = Enemy.EnemyCount;
}
}
The good point of using this principle is that Enemy has no clue about GameManager and can be reused in another game without any modification. The GameManager is a higher level entity and knows about it.
I need the gameobject to pause on its own in my scene for 7f or 8f and un-pause at 2f on its own. The script I have is letting my pause by key. Here is my script :
{
sing UnityEngine;
using System.Collections;
public class star : MonoBehaviour {
GameObject[] pauseObjects;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
}
void pauseGameobject()
{
if()
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
void pauseGameobject()
{
if()
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
}
You can use coroutines to insert delays in the update() loop. Coroutines use generators, which "yield", rather than functions/methods which "return". What this allows for is code that operates asynchronously while still being written in a linear fashion.
The built in coroutine you're most likely looking for is WaitForSeconds. To start a coroutine you simply call StarCoroutine() and pass in any method of type IEnumerator. This method will yield periodically. In the following example, WaitForSeconds(5) will yield after 5 seconds. Fractions of a second can also be used, represented by floats, for example 2.5 would be two and a half seconds.
using UnityEngine;
using System.Collections;
public class WaitForSecondsExample : MonoBehaviour {
void Start() {
StartCoroutine(Example());
}
IEnumerator Example() {
Debug.Log(Time.time); // time before wait
yield return new WaitForSeconds(5);
Debug.Log(Time.time); // time after wait
}
}
It's not too clear what you mean by pause on it's one, however I'm going to answer broadly, to try and help you.
If you whant to pause a single game object externally you can deactivate it and activate it accordingly with this code: gameObject.SetActive(false);
Instead if you want to pause the game object internally you can make a bool and in the update test wether or not it's true:
using UnityEngine;
using System.Collections;
bool update = false
public class ActiveObjects : MonoBehaviour
{
void Start ()
{
//Do stuff
}
void Update ()
{
if(update){
//Do stuff
}
//decide wether or not to pause the game object
}
}
If you want to pause the game you can set the Time.timeScale to 0, or just pause all game objects.
Here you can find how to make a timer, all you need to do is count down a variable using timeLeft -= Time.deltaTime;.
Hope I helped you,
Alex
Edit:
Ok, here is the script, keep in mind I have no way to test it ;)
using UnityEngine;
using System.Collections;
public class star : MonoBehaviour {
GameObject[] pauseObjects;
public float timer = 7;
float t = 0;
bool pause = false;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
t = timer;
}
void Update() {
if(pause){
if(t<0){
t=timer;
pause = false;
time.timescale = 1;
}else{
t -= Time.deltaTime;
time.timescale = 0;
}
}
}