Instantiate a prefab? - c#

I am making a crossy road game with 4 prefab "chunks" that I want to create when the player touches the collider named . My code randomizes which chunk is loaded when the player hits it and then spawns the next chunk. For some reason though, it doesn't recognize my prefab and I want to load the chunk 25 z-value ahead of the last one. Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChunkLoader : MonoBehaviour {
int level = 0;
public Transform chickenpos;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void spawnChunk()
{
float chunkload = Random.Range(0, 2);
Debug.Log(chunkload);
if (level <= 10)
{
if(chunkload <= 1 )
{
Instantiate (ChunkA1, chickenpos);
}
}
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Loader")
{
level = level++;
Debug.Log(level);
spawnChunk();
}
}
}

You first need to declare a variable for your prefab - I will assume it is of type Gameobject here.
int level = 0;
public Transform chickenpos;
public Gameobject ChunkA1; //declare variable for the prefab
// Use this for initialization
void Start () {}
You can then either drag the prefab in your Assets folder to the field in the inspector, or locate it through your script using Resources.Load().

Related

Unity (C#) - How do I single out GameObject being detected by Raycast in a destroy / respawn system

I'm trying to make a Destroy gameobject, wait x seconds, respawn gameobject system. I have 2 scripts and I'm destorying then instantiating it again. I want to use multiples of the same prefab called "Breakable" but have only the one I'm aiming at being destroyed. Similar to games like Minecraft, aim and only the aimed at the block is destroyed.
BlockBreakItem script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlockBreakItem : MonoBehaviour
{
RaycastHit hit;
int layerMask = 1;
public GameObject breakableObject;
public bool isObjectDestoryed = false;
public int score = 0;
// Update is called once per frame
void Update()
{
breakableDetection();
}
void breakableDetection()
{
Ray rayLocation = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(rayLocation, out hit, 1000, layerMask))
{
print("Detected");
if (Input.GetKey(KeyCode.Mouse0))
{
breakableObject = GameObject.Find("Breakable");
Destroy(breakableObject);
isObjectDestoryed = true;
score = score +1 ;
}
}
}
}
RespawnBrokenObject script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RespawnBrokenObject : MonoBehaviour
{
private BlockBreakItem BlockBreakItem;
public GameObject breakablePrefab;
// Start is called before the first frame update
void Start()
{
BlockBreakItem = GameObject.FindObjectOfType<BlockBreakItem>();
}
// Update is called once per frame
void Update()
{
if (BlockBreakItem.isObjectDestoryed == true)
{
Invoke("respawnObject", 5.0f);
BlockBreakItem.isObjectDestoryed = false;
}
}
void respawnObject()
{
GameObject tempObjName = Instantiate(breakablePrefab);
tempObjName.name = breakablePrefab.name;
BlockBreakItem.breakableObject = tempObjName;
}
}
I hope the code isn't too messy! Always worried it won't be understood xD
Fortunately it's easy and basic in Unity!
You don't do this:
breakableObject = GameObject.Find("Breakable");
The good news is the cast will tell you what object you hit! Great, eh?
It's basically:
hit.collider.gameObject
You can use hit.collider.gameObject.name in a Debug.Log for convenience.
It's that easy!
Note you then (if you need it) get your component on that object with something like .GetComponent<YourBreakishBlock>()... easy.
Note that you can CHECK if the object hit, is one of the "YourBreakishBlock" objects, by simply checking if that component is present.
Note simply google something like "unity physics raycast out hit, which object was hit ?" for endless examples,
https://forum.unity.com/threads/getting-object-hit-with-raycast.573982/
https://forum.unity.com/threads/changing-properties-of-object-hit-with-raycast.538819/
etc.
--
Make this simple class:
public class ExamplePutThisOnACube: MonoBehavior
{
public void SAYHELLO() { Debug.Log("hello!"); }
}
Now put that on SOME cubes but NOT on OTHER cubes.
In your ray code:
ExamplePutThisOnACube teste =
hit.collider.gameObject.GetComponent<ExamplePutThisOnACube>();
and then check this out:
if (teste != null) { teste.SAYHELLO(); }
Now run the app and try pointing at the various cubes!

Unity3D playing sound when Player collides with an object with a specific tag

I using Unity 2019.2.14f1 to create a simple 3D game.
In that game, I want to play a sound anytime my Player collides with a gameObject with a specific tag.
The MainCamera has an Audio Listener and I am using Cinemachine Free Look, that is following my avatar, inside the ThridPersonController (I am using the one that comes on Standard Assets - but I have hidden Ethan and added my own character/avatar).
The gameObject with the tag that I want to destroy has an Audio Source:
In order to make the sound playing on the collision, I started by creating an empty gameObject to serve as the AudioManager, and added a new component (C# script) to it:
using UnityEngine.Audio;
using System;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
// Start is called before the first frame update
void Awake()
{
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
}
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
}
And created the script Sound.cs:
using UnityEngine.Audio;
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume;
[Range(.1f, 3f)]
public float pitch;
[HideInInspector]
public AudioSource source;
}
After that, in the Unity UI, I went to the Inspector in the gameObject AudioManager, and added a new element in the script that I named: CatchingPresent.
On the Third Person Character script, in order to destroy a gameObject (with a specific tag) when colliding with it, I have added the following:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
It is working properly as that specific object is disappearing on collision. Now, in order to play the sound "CatchingPresent" anytime the Player collides with the object with the tag, in this case, Present, I have tried adding the following to the if in the OnCollisionEnter:
FindObjectOfType<AudioManager>().Play("CatchingPresent");
But I get the error:
The type or namespace name 'AudioManager' could not be found (are you
missing a using directive or an assembly reference?)
AudioManager.instance.Play("CatchingPresent");
But I get the error:
The name 'AudioManager' does not exist in the current context
As all the compiler errors need to be fixed before entering the Playmode, any guidance on how to make the sound playing after a collision between the player and the gameObject with the tag Present is appreciated.
Edit 1: Assuming that it is helpful, here it goes the full ThirdPersonUserControl.cs:
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (ThirdPersonCharacter))]
public class ThirdPersonUserControl : MonoBehaviour
{
public Text countText;
public Text winText;
private int count;
private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
private Transform m_Cam; // A reference to the main camera in the scenes transform
private Vector3 m_CamForward; // The current forward direction of the camera
private Vector3 m_Move;
private bool m_Jump; // the world-relative desired move direction, calculated from the camForward and user input.
private void Start()
{
count = 20;
SetCountText();
winText.text = "";
// get the transform of the main camera
if (Camera.main != null)
{
m_Cam = Camera.main.transform;
}
else
{
Debug.LogWarning(
"Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
// we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
}
// get the third person character ( this should never be null due to require component )
m_Character = GetComponent<ThirdPersonCharacter>();
}
private void Update()
{
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
// Fixed update is called in sync with physics
private void FixedUpdate()
{
// read inputs
float h = CrossPlatformInputManager.GetAxis("Horizontal");
float v = CrossPlatformInputManager.GetAxis("Vertical");
bool crouch = Input.GetKey(KeyCode.C);
// calculate move direction to pass to character
if (m_Cam != null)
{
// calculate camera relative direction to move:
m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
m_Move = v*m_CamForward + h*m_Cam.right;
}
else
{
// we use world-relative directions in the case of no main camera
m_Move = v*Vector3.forward + h*Vector3.right;
}
#if !MOBILE_INPUT
// walk speed multiplier
if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
#endif
// pass all parameters to the character control script
m_Character.Move(m_Move, crouch, m_Jump);
m_Jump = false;
}
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
//FindObjectOfType<AudioManager>().Play("CatchingPresent");
AudioManager.instance.Play("CatchingPresent");
}
}
void SetCountText()
{
countText.text = "Missing: " + count.ToString();
if (count == 0)
{
winText.text = "You saved Christmas!";
}
}
}
}
Edit 2: Hierarchy in Unity:
Reformulated the approach that I was following and solved the problem by simply adding an Audio Source to the ThirdPersonController (with the AudioClip that I wanted to call) and added GetComponent<AudioSource>().Play(); to the if statement as it follows:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
GetComponent<AudioSource>().Play();
}
}
Importing your scripts myself works without any issues when using FindObjectOfType<AudioManager>().Play("CatchingPresent");. Try reimporting your scripts from the editor (right click in the project folder > reimport all. this might take a while depending on the size of your project)
to use AudioManager.instance.Play("CatchingPresent"); you would first need to create a static variable that holds instance like this (this only works as a singleton, and will break if multiple AudioManager's are in the scene):
public class AudioManager : MonoBehaviour
{
//Create a static AudioManager that will hold the reference to this instance of AudioManager
public static AudioManager Instance;
public Sound[] sounds;
//Assign Instance to the instance of this AudioManager in the constructor
AudioManager()
{
Instance = this;
}
// Rest of the AudioManager code
}
Doing it like this, and using the rest of your code also works for me.

Unity3D Shooter: Using tags to switch level after killing all enemies

I am new to Unity and was trying, after some suggestions, to use tags to know the number of enemies i have in each level and move to the next scene right after eliminating all enemies. This is the script i use on enemy gameobjects. I've also tagged each of them with the "enemy" tag in unity inspector but it still doesn't work when i run the game. After killing all the enemies, it didnĀ“t change to next scene (Success!). Any ideas on what I'm doing wrong? Any other suggestions?
Thanks a lot for the help.
Enemies Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BadguyScript : MonoBehaviour
{
public GameObject[] enemies;
public int maxHealth;
public int curHealth;
private Animator myAnimator;
private bool isDead;
[SerializeField]
private float DespawnTime = 2.5f;
[SerializeField]
private string DeathAnimHash = "isDead";
void Start()
{
myAnimator = GetComponent<Animator>();
myAnimator.enabled =true;
myAnimator.SetBool (DeathAnimHash ,isDead);
maxHealth = 1;
curHealth = maxHealth;
}
void Update()
{
if (curHealth < 1)
{
isDead = true;
myAnimator.SetBool (DeathAnimHash ,isDead);
Destroy(gameObject,DespawnTime);
}
enemies = GameObject.FindGameObjectsWithTag("enemy"); // Checks if enemies are available with tag "Enemy".
if (enemies.Length == 0)
{
SceneManager.LoadScene("SucessScene"); // Load the scene with name "SucessScene"
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (isDead)
return;
if (col.tag == "bullet")
{
curHealth -= 1;
Destroy(col.gameObject);
}
}
}
I would create a script holder gameobject for this and put a GameManager script inside it. And inside GameManager.cs which should be a singleton class you can have a property like this:
int _enemyNumber;
public int EnemyNumber{
get{
return _enemyNumber;
}
set{
_enemyNumber = value;
}
}
And when you need to change these values, use some functions you will create inside this game controller such as:
public void DecreaseEnemyCount(){
//do the logic here
}
public void SetEnemyCount(){
//do the logic here
}
Also you can find information about creating a singleton class here
You create a list with all enemies, its a good practice, cause you'll gain performance. But you're verifing if enemies.Lenght == 0, what will never occur, because before you are adding the gameObject in the list enemies = GameObject.FindGameObjectsWithTag("enemy");
In the start method, you can search for all enemies and add then in your array, and in the update or onTriggerEnter you remove it from your array and validate the array lenght. I think it'll be more easy.
Instead of adding the script to a new gameManager script attached to an empty game object cause now once all enemies are killed the script will not be in work but if added to an empty gameobject it will be working always.

Next Scene not loading when requested to

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);
}
}
}

Meeting a condition before allowing level to change

I'm trying to add a coin to my game. If the coin isn't touched then the level won't be able to switch until the player touches the coin. My scripts are trying to set a value in a variable then when the value increases to 1 then it allowed the level to change.
How do I fix my scripts?
Coin script:
using UnityEngine;
using System.Collections;
public class Coin : MonoBehaviour {
public GameObject destroyCoin;
public static int coinWorth = 0;
void OnCollisionEnter(Collision other)
{
if (other.transform.tag == "Coin")
{
Destroy(destroyCoin);
coinWorth = 1;
}
}
}
GameManager script:
using UnityEngine;
using System.Collections;
public class GameManager4 : MonoBehaviour {
Coin coinValue = GetComponent<Coin>().coinWorth;
void Update ()
{
coinValue = Coin.coinWorth;
}
void OnCollisionEnter(Collision other){
if (other.transform.tag == "Complete" && coinValue > 0) {
Application.LoadLevel(1);
}
}
}
It might be simpler to have the Coin send its value directly to the GameManager upon collision.
Should your coin perhaps be searching for a 'Player' tag rather than a 'Coin' tag (I am assuming that the Coin.cs script will be attached to a coin object which will have the 'Coin' tag).
So in you scripts it would look like this:
using UnityEngine;
using System.Collections;
public class Coin : MonoBehaviour {
// Drag your Game Manager object into this slot in the inspector
public GameObject GameManager;
public static int coinWorth = 1;
void OnCollisionEnter(Collision other)
{
// If the coin is collided into by an object tagged 'player'
if (other.transform.tag == "Player")
{
// retrieve the gamemanager component from the game manager object and increment its value
GameManager.GetComponent<GameManager4>().coinValue++;
// Destroy this instance of the coin
Destroy(gameObject);
}
}
}
Then your second script
using UnityEngine;
using System.Collections;
public class GameManager4 : MonoBehaviour {
// Declare the coinValue as a public int so that it can be accessed from the coin script directly
public int coinValue = 0;
void Update ()
{
// This shouldn't be necessary to check on each update cycle
//coinValue = Coin.coinWorth;
}
void OnCollisionEnter(Collision other){
if (other.transform.tag == "Complete" && coinValue > 0) {
Application.LoadLevel(1);
}
}
}
Of course if you are instancing the coin from a prefab then you would need to do this differently as you wouldn't be able to drag the game menager in the inspector. If thats the case then it might be worthwhile to use a singleton class for the game manager. Let me know if that is the case and I'll show you how to do this :)

Categories