C# in Unity, SetActive several gameObjects from player entering trigger area - c#

Brand new to C# and Unity.Please be nice I've been at this all night. Every tutorial I poor through says changing an object from invisible to visible is as simple as setting the game object to on. However Unity gives me an error when I declare a game object in this script. The objective is, when the trigger is entered, several game objects called 'spawn' will become visible.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collider : MonoBehaviour
{
public gameObject Spawn; // I get error On this line that type is expected,
//not property. It wants a Transform>
private Rigidbody rb;
void Start ()
{
rb = GetComponent<Rigidbody>();
}
void OnTriggerEnter(BoxCollider other)
{
if (other.gameObject.CompareTag("Player"))
{
Spawn.SetActive(true);
}
}
}

gameObject isn't a type, but GameObject is.
Get rid of public gameObject Spawn; and use public GameObject Spawn; to declare a GameObject property called Spawn

Related

Instantiated gameObject's self-written component is not working

I'm trying to implement chest mechanic in my game. When a user touches to chest, chest disappears and heart shows up. When player touches the instantiated heart, it must disappear and add 1 to life value, but the script is not working.
Chest.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chest : MonoBehaviour
{
public GameObject heart;
public GameObject _heart; //Instantiated heart.
void OnTriggerEnter2D(Collider2D col){
if (col.gameObject.tag == "Player"){
Vector3 insPos = transform.position;
_heart = Instantiate(heart,new Vector3(insPos.x,insPos.y+0.5f,insPos.z),Quaternion.identity);
_heart.AddComponent<Heart>();
_heart.GetComponent<Heart>().ls = GameObject.FindGameObjectWithTag("Player").GetComponent<LifeSystem>();
Destroy(gameObject);
}
}
}
Heart.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Heart : MonoBehaviour
{
public LifeSystem ls;
void OnTriggerEnter2D(Collider2D col){
if (col.gameObject.tag == "Player"){
SoundManager.PlaySound("CollectCoinSound");
ls.lifes += 1;
Destroy(gameObject);
}
}
}
I check the components of the instantiated heart when player touches the chest and I can see that life system reference is added and so heart script is.
Thanks for your time.
You need to meet these conditions in order for OnTriggerEnter2D to be called on the spawned Hearth:
At least one between Player and Hearth need to have a Rigidbody2D component
Both Player and Hearth need to have a collider with IsTrigger = true
Player and Hearth GameObjects need to be on two interacting layers in the collision matrix
The Hearth component needs to be either on the same GameObject with a collider or on the same GameObject with the RigidBody2D
I actually simplified rules 1 and 2 a bit to show the most common use-case: if you want to know the actual rules for collisions, you can find them here (see the matrix at the bottom).
Besides the general collision rules I would make sure your fields have the correct type in the first place and already attach the Heart component to your prefab.
Also there is no need for find .. You already know the reference to the player you are colliding with
public class Chest : MonoBehaviour
{
public Heart heartPrefab;
void OnTriggerEnter2D(Collider2D col)
{
if (col.CompareTag("Player"))
{
Instantiate(heart, transform.position + Vector3.up * 0.5f), Quaternion.identity);
Destroy(gameObject);
}
}
}
and then simply do
public class Heart : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D col)
{
if (col.TryGetComponent<LifeSystem>(out var ls))
{
SoundManager.PlaySound("CollectCoinSound");
ls.lifes += 1;
Destroy(gameObject);
}
}
}
and rather have the LifeSystem attached to your player object.
In general I would kind of expect both eventually triggered at the same time since the heart might be already triggered if spawning right where the player is

Finding a way for script to detect a child in a playerContainer game object

I'm currently making a 3D endless runner game with the choice to select different skins for the player. Everything's going smoothly until I come across a problem, in which I try to assign two collision game objects to each character prefab in the container that can detect the player's collision box when it collides with a powerup.
It only detects the first playerContainer's (Milo) 'Coin Detect' game object (eventhough it's been deactivated) and does not recognize the 'Coin Detect' collision game object in the Baddie playerContainer (which the player has chosen to play)
So my question is, how am I able to get the script to recognize the child that's active in the third playerContainer game object instead of it automatically detecting the first playerContainer game object's child?
From the Magnet powerup script I've made, the script detects the first playerContainer's 'Coin Detect' game object only. As shown in the attached picture below:
Here's my current script where the player is able to select their preferred characters.
using System.Collections.Generic;
using UnityEngine;
public class EnablePlayerSelector : MonoBehaviour
{
public GameObject[] players;
public int currPlayerCount;
void Start()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
foreach (GameObject player in players)
player.SetActive(false);
players[currPlayerCount].SetActive(true);
}
}
and here's the script where I deactivate the 'Coin Detect' collision game Object and activate it when the player collides with the powerup.
using System.Collections.Generic;
using UnityEngine;
public class Magnet : MonoBehaviour
{
public GameObject coinDetect;
void Start()
{
coinDetect = GameObject.FindGameObjectWithTag("CoinDetect");
coinDetect.SetActive(false);
Debug.Log("False");
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.tag == "Player")
{
StartCoroutine(coinActivate());
Destroy(transform.GetChild(0).gameObject);
}
}
IEnumerator coinActivate()
{
Debug.Log("Hi");
coinDetect.SetActive(true);
yield return new WaitForSeconds(5f);
coinDetect.SetActive(false);
}
}
Well your issue is rather that you are doing GameObject.FindGameObjectWithTag("CoinDetect") which will return whatever is the first encountered CoinDetect in your scene in this moment. So unless EnablePlayerSelector.Start is executed before the Magnet.Start it will always be the first one since they are still all active by then.
You could probably already solve your issue by following the general thumb-rule:
Do things where you do not depend on other components already in Awake
Do things where you o depend on other components in Start
This way you already cover most of the use cases and are sure that components are self-initialized (Awake) before anything is accessing them in Start.
So simply change the EnablePlayerSelector.Start into Awake and you should be fine.
In very specific cases you might still have to adjust the Script Execution Order or use events.
Instead of doing this at all though I would rather in the moment of the collision request the current one from the PlayerContainer directly:
// put this on each player root object
public class Player : MonoBehaviour
{
// reference these via the Inspector
[SerializeField] private GameObject coinDetector;
[SerializeField] private GameObject playerrange;
// ... and in general any information bout this Player that is relevant for other scripts
// Readonly accessor property
public GameObject CoinDetector => coinDetector;
}
and then rather have
public class EnablePlayerSelector : MonoBehaviour
{
[SerializeField] private Player[] players;
[SerializeField] private int currPlayerCount;
public Player CurrentPlayer => players[currPlayerCount];
// In general do things where you don't depend on other scripts already in Awake
// This way you could actually already solve your issue by keeping things where you do
// depend on others in Start
private void Awake()
{
currPlayerCount = PlayerPrefs.GetInt("SelectedPlayer", 0);
for(var i = 0; i < players.Length; i++)
{
players[i].gameObject.SetActive(i == currPlayerCount);
}
}
}
and then finally do e.g.
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
var playerSelector = other.GetComponentInParent<EnablePlayerSelector>();
if(!playerSelector) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = playerSelector.CurrentPlayer.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}
or alternatively if the other in this case refers to the object with the Player component anyway you could also directly do
public class Magnet : MonoBehaviour
{
public float effectDuration = 5f;
private IEnumerator OnTriggerEnter(Collider other)
{
if(!other.TryGetComponent<Player>(out var player)) yield break;
Debug.Log("Hi");
Destroy(transform.GetChild(0).gameObject);
var coinDetect = player.CoinDetector;
coinDetect.SetActive(true);
yield return new WaitForSeconds(effectDuration);
coinDetect.SetActive(false);
}
}

Reference to Animator component not appearing in the inspector

I have already tried to look into this... but no luck..
Sprite renderer, and mover references, work fine they appear in the inspector as I want them to.
However I am having issues in FILE 2 I am unsure of how to Serialize the Animator in the inspector.
FILE 1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "EnemyConfig", menuName = "Enemies/Enemy config", order = 0)]
public class EnemyConfig : ScriptableObject
{
public float moverSpeed;
//public float zRotation;
public Sprite sprite;
public Animator animator;
}
FILE 2
//EnemyController.CS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
[HideInInspector]
public EnemyConfig config;
[SerializeField]
private SpriteRenderer spriteRenderer;
//Want to get a reference to the animator component appear in the inspector
//This is my issue...--------------------------------------]
[SerializeField]
private GetComponent<Animator> animatorController;
//----------------------------
private Mover mover;
private void Start()
{
mover = GetComponent<Mover>();
if(mover != null)
{
mover.speed = config.moverSpeed;
}
if(config.sprite != null)
{
spriteRenderer.sprite = config.sprite;
}
// related to the animator
if(config.animator != null)
{
animatorController.animator = config.animator;
}
}
}
Here you can see how the Sprite reference, and mover reference appear in the inspector.
I am trying to do the same with the Animator
GetComponent<T> is a method, it is not a type. So, you can't declare a field with this as its type. I would be surprised if this code actually even compiles. What you want is simply
[SerializeField]
private Animator animator;
Note that AnimatorController is an Editor-only class; you can't include it in your game code. Hence why you want Animator here.
Another thing to consider is, is the Animator component going to be on the same game object? If so, you don't need to serialize the reference - that would only be useful if it's going to be on a different game object, and you need to store that link at edit time.
If the Animator component is on the same object, you can do this:
private Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
The advantage is that you won't have to make sure the reference stays up-to-date, it will be connected whenever the gameplay starts. This, by the way, is the correct use of GetComponent<T> - since it's a method, you can call it within another method, not where you had it in your File2.

How to fix "the object of type 'GameObject' has been destroyed, but you are still trying to access it" error in Unity?

I'm creating a 2.5D fighting game in Unity with C#. Currently, I'm trying to make a bumper appear around the player and disappear after a set amount of time. I've managed to make the bumper appear and disappear once, but after that, when I try to make the bumper appear again, Unity has an error for me: "The object of type 'GameObject' has been destroyed but you are still trying to access it."
I've tried using the "instantiate" and "destroy" commands, following a tutorial by "Brackeys" on 2D shooting. After also following some questions on forums about the same issue, I've altered my code again, but the problem persists.
The firePoint is an empty object, from where the BumperPrefab is instantiated.
using UnityEngine;
public class weapon: MonoBehaviour
{
public Transform firePoint;
public GameObject BumperPrefab;
public float lifetime = 0.2f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
attack();
}
}
void attack()
{
BumperPrefab = (GameObject) Instantiate(BumperPrefab, firePoint.position, firePoint.rotation);
Destroy(BumperPrefab, lifetime);
}
}
I expect the GameObject "BumperPrefab" to appear, stick around for 0.2 seconds and disappear. I should be able to repeat that as many times as I want, but what actually happens is that I can do this only once and then the error "The object of type 'GameObject' has been destroyed but you are still trying to access it" shows up and I can't make the BumperPrefab appear again.
Any help is much appreciated!
using UnityEngine;
public class weapon: MonoBehaviour
{
public Transform firePoint;
public GameObject BumperPrefab;
public float lifetime = 0.2f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
attack();
}
}
void attack()
{
var bumper = (GameObject) Instantiate(BumperPrefab, firePoint.position, firePoint.rotation);
Destroy(bumper, lifetime);
}
Right now you're overwriting your public field containing the prefab object with your instantiated object, then destroying it. Set the instantiated object as a variable and you should be fine.
The problem is that in your code you don't care about is your GameObject exist. So for example if (for some reason) the object BumperPrefab will not be created, Destory() will try to act on null.
You can try add to BumperPrefab script bumper.cs with:
float lifetime = 0.2f;
private void OnEnable()
{
Desroy(this, lifetime)
}
problem is you are destroying BumperPrefab
when you Instantiate a new GameObject you should add it to the local variable like this
var newbumper = (GameObject) Instantiate(BumperPrefab, firePoint.position,firePoint.rotation);
and you must destroy your local variable which contains newly created gameObject
Destroy(newbumper , lifetime);

Variables in script do not show in Inspector (Unity)

I'm trying to learn how to use Unity and following online tutorials but I am currently having a problem that I don't understand how to fix.
I have a Sprite in my scene and I have attached a script to it however in the Inspector it shows the script is there but I cannot see the variables inside? I had this problem previously and it sorted itself out.
What is the cause of this problem/how do I fix it?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpaceShip : MonoBehaviour {
public float speed = 30;
public GameObject theBullet;
private void FixedUpdate()
{
float horzMove = Input.GetAxisRaw("Horizontal");
GetComponent<Rigidbody2D>().velocity = new Vector2(horzMove, 0) *
speed;
}
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Jump"))
{
Instantiate(theBullet, transform.position, Quaternion.identity);
}
}
}
Edit: The problem was solved by reimporting.
You either need to declare the variables as Public or [SerializeField] for member variables to appear in the inspector. Note that by declaring something as public allows access to the variable from outside the class (from other scripts/classes for example). By default, private is assigned to member variables.
Example:
public class testscript : MonoBehaviour
{
public int foo; // shows up in inspector
[SerializeField] private int bar; // also shows up while still being private
void Start()
{
}
}
Not is a problem, You forget to do something surely.
It is common at first with Unity.
Start again.
In the scene create a new GameObject and add you script.
If the inspector shows not variable:
The varible do not is public (false, if is public in you script)
There is some syntax error in the script!
or
You were not adding the correct script to the GameObject.
There are not many secrets to that, if all is well enough that the variable is public and this outside of a method of the script so that it is seen in the inspector.
One tip, do not use a GetComponent or Instantiate inside a FixedUpdate or Update because they are expensive, save the Rigidbody2D in a variable in the Start and then use it.
Sorry for my English and good luck.

Categories