I made a Project in Unity, a game to be clear. So the player has a Sci-fi car and he tries to avoid obstacles-rocks.
When the car hits a rock, the Game Manager load the "credits" screen to choose between quit or restart.
My problem is that I want to make the rock explode when the car goes on it and then the Game Manager will load the "credits" screen.
Some of my code:
For player collision:
public class PlayerCollision : MonoBehaviour {
public PlayerMovement movement;
public static bool y = true;
public void OnCollisionEnter (Collision collisionInfo)
{
// We check if the object we collided with has a tag called "Obstacle".
if (collisionInfo.collider.tag == "Obstacle")
{
movement.enabled = false; // Disable the players movement.
y = false;
FindObjectOfType<GameManager>().EndGame();
}
}
}
Game Manager:
public class GameManager : MonoBehaviour {
bool gameHasEnded = false;
public float restartDelay = 1f;
public GameObject completeLevelUI;
public void CompleteLevel ()
{
completeLevelUI.SetActive(true);
}
public void EndGame ()
{
if (gameHasEnded == false)
{
gameHasEnded = true;
Debug.Log("GAME OVER");
Invoke("Restart", restartDelay);
}
}
void Restart ()
{
SceneManager.LoadScene("Credits");
//SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
The endTrigger:
public class EndTrigger : MonoBehaviour {
public GameManager gameManager;
void OnTriggerEnter ()
{
gameManager.CompleteLevel();
}
}
LevelComplete:
public class LevelComplete : MonoBehaviour {
public void LoadNextLevel ()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
Menu:
public class Menu : MonoBehaviour {
public void StartGame ()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
I have written other scripts. If you want anything let me know. Thanks guys.
Add a particle system to your rocks. Uncheck "looping" and "play on awake". Then you can play by adding the line below.
public void OnCollisionEnter (Collision collisionInfo)
{
// We check if the object we collided with has a tag called "Obstacle".
if (collisionInfo.collider.tag == "Obstacle")
{
movement.enabled = false; // Disable the players movement.
y = false;
collisionInfo.gameObject.GetComponent<ParticleSystem>().Play(); // play the explosion
FindObjectOfType<GameManager>().EndGame();
}
}
I would change your approach. As I understand you already have an endTrigger [With a Collider Trigger] that ends the game if the player goes inside. So uncheck the trigger in the player collider and instead add that trigger to the rocks collider, so when the player crashes it will:
Activate the Particle System
Makes the rock invisible
End the Game
You can add this script to your rock and see how it works
public class RockTrigger : MonoBehaviour {
public GameManager gameManager;
ParticleSystem myParticleSystem;
void Awake()
{
myParticleSystem = GetComponent<ParticleSystem>();
}
void OnTriggerEnter(Collider other)
{
myParticleSystem.Play();
GetComponent<MeshRenderer>.enabled = false;
gameManager.GetComponent<GameManager>().EndGame();
}
}
Note about the OnTriggerEnter: In case you only have the player moving in the scene this is fine like this (I use the same pattern you used in EndGame),
but in case there were other GameObject with a RigidBody moving in the
scene you should check if other is indeed the player. Usually tagging the player and the checking if(other.tag == "Player")
Related
I'm making a game right now where the enemies become alerted to the player and chase them indefinetely until the player stand on a "safe plate" in game. When the player stands here the enemies should then return to their original position.
The issue is that whenever the player stands on the plate I get a null reference exception error and I can see the original guard position has been reset to 0,0,0 in the console. I'm assuming the reason for the null reference exception is because the world origin isn't on the nav mesh I'm using, although I could easily be wrong about that. I just can't seem to figure out why the vector3 value has changed at all since the guardPosition variable is initiated on Start and never touched again.
I've included both my enemyAi class (script attached to enemy) and my class associated with stepping on plates. If there's anything more needed to include let me know. If anyone has any ideas, help would be appreciated. Cheers.
Screenshot on console after stepping on plate
public class EnemyAI : MonoBehaviour
{
DeathHandler dh;
[SerializeField] Transform target;
[SerializeField] float chaseRange = 5f;
[SerializeField] float killRange = 2f;
[SerializeField] GameObject explosionEffect;
NavMeshAgent navMeshAgent;
float distanceToTarget = Mathf.Infinity;
bool isDead = false;
bool isAlert = false;
Vector3 guardPosition;
void Start()
{
GameObject gob;
gob = GameObject.Find("Player");
dh = gob.GetComponent<DeathHandler>();
guardPosition = transform.position;
navMeshAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
distanceToTarget = Vector3.Distance(target.position, transform.position);
print(guardPosition + " guard position during update");
//alert a dude
if (distanceToTarget <= chaseRange && !isDead)
{
isAlert = true;
}
//chase a dude
if (isAlert == true)
{
ChasePlayer();
print(isAlert);
}
}
public void ChasePlayer()
{
navMeshAgent.SetDestination(target.position);
//explode a dude
if (distanceToTarget <= killRange)
{
Explode();
dh.KillPlayer();
}
}
public void SetAlertStatus(bool status)
{
isAlert = status;
}
public void ReturnToPost()
{
//isAlert = false;
print(guardPosition + " guard position after stepping on plate");
navMeshAgent.SetDestination(guardPosition);
}
void Explode()
{
Instantiate(explosionEffect, transform.position, transform.rotation);
isDead = true;
Destroy(gameObject);
}
void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, chaseRange);
}
}
public class SafeSpots : MonoBehaviour
{
EnemyAI enemyAi;
void Start()
{
GameObject gob;
gob = GameObject.FindGameObjectWithTag("Enemy");
enemyAi = gob.GetComponent<EnemyAI>();
}
public void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player")
{
enemyAi.SetAlertStatus(false);
enemyAi.ReturnToPost();
}
}
}
You are getting a disabled instance of EnemyAI. Instead, use FindGameObjectsWithTag then iterate through all of them and add their EnemyAI to a list which you can iterate through when necessary. By the way, it's better to use CompareTag when possible to reduce garbage:
public class SafeSpots : MonoBehaviour
{
List<EnemyAI> enemyAis;
void Start()
{
GameObject[] enemies= GameObject.FindGameObjectsWithTag("Enemy");
enemyAis = new List<EnemyAI>();
foreach (GameObject enemy in enemies)
{
enemyAis.Add(enemy.GetComponent<EnemyAI>());
}
}
public void OnTriggerStay(Collider other)
{
if (other.CompareTag("Player"))
{
foreach(EnemyAI enemyAi in enemyAis)
{
enemyAi.SetAlertStatus(false);
enemyAi.ReturnToPost();
}
}
}
}
I have 2 monsters and 1 player. when players shoot, i get this mistake and they stop shooting. I have the same code for players, but players can keep shooting. I think the problem is with "destroy" but I can not find a solution enter image description here
public class SpawnBulletController : MonoBehaviour
{
public GameObject bullet;
public float interval = 1F;
// Use this for initialization
void Start () {
InvokeRepeating("ShootBullet", interval, interval);
}
void ShootBullet()
{
GameObject g = Instantiate(bullet, transform.position, Quaternion.identity);
}
}
public class BorderCollision : MonoBehaviour
{
private void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag != "Player")
Destroy(other.gameObject);
}
}
When your collider hits any GameObject that has no "Player" tag the GameObject will be destroyed this is wrong.
you should try
private void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Monster")
Destroy(collider.gameObject);
}
And if you want to destroy more with the same code but not with the same tag you should try.
private void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Monster" || collider.gameObject.tag == "Monster2")
Destroy(collider.gameObject);
}
When my Player (GameObject) meets Lava, they should respawn in a specific scene.
This is the code I have assigned to the Player:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Lava")
{
GameObject.Find("Controller").GetComponent<Controller>().Respawn();
}
}
Controller is a GameObject, that I don't want to Destroy by Changing level, so this is the code for my Controller GameObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Controller : MonoBehaviour
{
private static bool created = false;
public static Controller instance;
GameObject Player;
Vector3 respawnPoint;
void Awake()
{
if (instance = null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
Player = GameObject.Find("Player");
respawnPoint = GameObject.Find("RespawnPoint").transform.position;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
Player.transform.position = respawnPoint;
}
}
RespawnPoint is just an invisible Cube GameObject, where I want the player to respawn.
Let's say the Game Starts with Scene "0" (this is where the RespawnPoint is, too.)
Then the Player goes to Scene "1" and dies (meets Lava). Then I want the Game to change back to Scene "0" and teleport the Player to the RespawnPoint.
The Scene-Change works good, but the player always starts at the same position, where he starts the first time and he's not teleported to the RespawnPoint.
What am I doing wrong?!
First of all you lack the "==" in the first 'if' from the Awake: if (instance == null
The code is fine or it does seem so to me, but the RespawnPoint should be in the Scene your Player meets the lava not in the Scene you are loading. If not the starting position of the player will always be (0,0,0).
I would recommend coming to this in a completely different way. I would make a public Transform[] Spawnpoints. Since the transform is public you can assign different objects to it. Make an empty game object and position it where you want to spawn. Then use
Void OnTriggerEnter(collider2D, other){
if(other.gameObject.tag == lava) {
transform.position = spawnpoints[0].position;
}
}
In the inspector set the Transform to be a size of 1 and set the respawn GameObject as the one transform.
Thanks to your answers, they helped me solving this problem.
I added a "DontDestroyOnLoad" to the RespawnPoint and than i changed the Controller Code to this:
{
private static bool created = false;
public static Controller instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
return;
}
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
}
}
public void Respawn()
{
SceneManager.LoadScene(0);
GameObject.Find("Player").transform.position = GameObject.Find("RespawnPoint").transform.position;
}
}
now the player gets teleported to the correct RespawnPoint. Thanks for your help!
In the game I am creating a 'Game Over' scene which loads once the player loses the game. During the game, the score is counted by the player's position on the screen and increases as the player's y-axis position increases.
I used this code to display the score on the scene the game was actually played:
using UnityEngine;
using UnityEngine.UI;
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
}
}
I tried to use the same coding to display the final score of the player on the 'Game Over' screen. The problem I faced was that I was not able to identify the player on the 'Game Over' scene as the player was not an object or sprite on that scene.
Is there an easy way to reference the player sprite from the previous scene into the final ('Game Over') scene so I can use it to determine the final score?
This is the script I tried with a game object of the 'EndScene' within the playing scene:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class EndMenu: MonoBehaviour
{
public Text scoreText;
public Transform player;
public static bool GameEnds = false;
public GameObject endMenuUI;
void OnCollisionEnter2D(Collision2D exampleCol)
{
if(exampleCol.collider.tag == "Obstacle")
{
endMenuUI.SetActive(true);
Time.timeScale = 0.0001f;
GameEnds = true;
}
}
public void Retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
SceneManager.LoadScene("Menu");
}
public void Quit()
{
Debug.Log("Quitting game...");
Application.Quit();
}
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
}
}
You can keep the player using DontDestroyOnLoad
You could add this to the player:
void Awake() {
DontDestroyOnLoad(transform.gameObject);
}
You could do other stuff like keeping just the data... but the easier way should be this one.
Keep in mind that the player will remain in you gameover screen.
In my opinion the best idea could be creating a new gameobject in your game scene called "GameOverScreen" and disable it until you need it.
I think the easiest solution would be to simply store the score as a static variable that will persist across scene loads, then manually reset it when you start over. For example:
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
public static string scoreString;
// Update is called once per frame
void Update()
{
scoreString = player.position.y.ToString("0");
scoreText.text = scoreString;
}
}
Now you will be able to access scoreString from anywhere in your code and it will be whatever it was when the PlayerScore component last ran its Update(). Then in your EndMenu class, you would simply update your Retry() and BackToMenu() methods like so:
public void Retry()
{
PlayerScore.scoreString = "0";
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
PlayerScore.scoreString = "0";
SceneManager.LoadScene("Menu");
}
And your EndMenu.Update() method becomes the following:
// Update is called once per frame
void Update()
{
scoreText.text = PlayerScore.scoreString;
}
//try to use playerprefs to save the score
public class PlayerScore : MonoBehaviour
{
public Transform player;
public Text scoreText;
// Update is called once per frame
void Update()
{
scoreText.text = player.position.y.ToString("0");
PlayerPrefs.SetInt("CurrentScore", player.position.y);
}
}
//for another scene get the value of saved score using playerprefs.getInt
public class EndMenu: MonoBehaviour
{
public Text scoreText;
public Transform player;
public static bool GameEnds = false;
public GameObject endMenuUI;
void OnCollisionEnter2D(Collision2D exampleCol)
{
if(exampleCol.collider.tag == "Obstacle")
{
endMenuUI.SetActive(true);
Time.timeScale = 0.0001f;
GameEnds = true;
}
}
public void Retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}
public void BackToMenu()
{
SceneManager.LoadScene("Menu");
}
public void Quit()
{
Debug.Log("Quitting game...");
Application.Quit();
}
// Update is called once per frame
void Update()
{
scoreText.text = PlayerPrefs.GetInt("Score",0);
}
}
I've been trying to make a sliding door for my unity level and I've managed to set up the animations but the scripting that's supposed to link up the functions with the objects isn't working.
Here's the script for the key card:
using UnityEngine;
using System.Collections;
public class Key_Pickup_1 : MonoBehaviour {
public GameObject player;
private Player_inventory playerinventory;
void Awake ()
{
playerinventory = player.GetComponent<Player_inventory>();
}
// Update is called once per frame
void onTriggerEnter()
{
if (gameObject == player)
{
playerinventory.hasKey_1 = true;
Destroy(gameObject);
}
}
}
Here's the script for the Door animation:
using UnityEngine;
using System.Collections;
public class Door_Animation_1 : MonoBehaviour {
public string Open;
private Animator anim_1;
public GameObject player;
private Player_inventory playerInventory;
void Start()
{
anim_1 = GetComponent<Animator>();
player = GameObject.FindGameObjectWithTag("Player");
playerInventory = player.GetComponent<Player_inventory>();
}
void OntriggerEnter (Collider other)
{
if(other.gameObject == player)
{
if (playerInventory.hasKey_1)
{
anim_1.SetTrigger(Open);
}
}
}
Any Ideas?
You don't have the proper capitalization for the OnTriggerEnter methods in your code. You have two different spellings and they are both wrong. It must be exactly OnTriggerEnter (or OnTriggerEnter2D for objects with a Collider2D instead of a Collider).