Button click does not work correctly - c#

I don't know what i am doing wrong it's a 2D project there are two objects.One has a RigidBody2D and BoxCollider2D component. Second object only has BoxCollider2D. And bottom there is a button when press button Object1 fall on Object2 and Destroy and Instantiate Object1 again. But when Object1 Instantiate then click on button does not work. And error came up like this :
The object of type Rigidbody2D 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.
Object 1:
Object 2:
Button Click:
Object 1 Script:
public class Object1 : MonoBehaviour {
public static Object1 instance;
[SerializeField]
private Rigidbody2D body;
[SerializeField]
private bool hasdropped;
[SerializeField]
private bool click;
[SerializeField]
private float PointerPos;
[SerializeField]
private float BorderX;
void Awake(){
if (instance == null) {
instance = this;
}
//take width of screen
Vector3 gameScreen = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height,0));
BorderX = gameScreen.x - 0.6f;
body.isKinematic = true;
click = true;
hasdropped = true;
}
void FixedUpdate () {
if (click) {
Vector3 temp = transform.position;
PointerPos = Camera.main.ScreenToWorldPoint(Input.mousePosition).x;
temp.x = Mathf.Clamp(PointerPos,-BorderX,BorderX);
body.position = temp;
if(hasdropped){
return;
}
}
}
public void ButtonClick(){
body.isKinematic = false;
}
}
Object 2 Script:
public class Object2 : MonoBehaviour {
[SerializeField]
private GameObject BallClone;
void OnCollisionEnter2D(Collision2D target){
Destroy (target.gameObject);
Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity);
}
}

The problem is that you referenced the Object1Prefab (the live object, not the prefab) on ClickButton On Click(). (But referencing the prefab will not work at all)
Since you destroy it, you missed the link. (Check it: Select the ClickButton object on the hierarchy, there is the script reference, right? Click the game button once. Now deselect and select ClickButton object on hierarchy again... it is gone.)
Try to make a listener or to put the ButtonClick() method inside the object2 (that you don't destroy):
using UnityEngine;
using System.Collections;
public class Object2 : MonoBehaviour {
[SerializeField]
private GameObject BallClone;
public GameObject mine;
void OnCollisionEnter2D(Collision2D target){
Destroy (target.gameObject);
mine = Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity) as GameObject;
}
public void ButtonClick(){
//this is just an example. You might wanna to cache this info for better practice and not call GetComponent all the time
mine.GetComponent<Rigidbody2D> ().isKinematic = false;
}
}
1- Drag GameObject1Prefab (live object) to the Object2 script on "mine" field. Reference to public GameObject mine.
The first click you gonna destroy it, and create a new one and programmatically referencing the new one again on the line:
mine = Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity) as GameObject;
On the ClickButton Object, you drag the GameObject2, not the 1, on the On Click().
Is this clear? Sorry my english. :~

Related

There is no Rigidbody 2D attached to the game object but the script is trying to access it

Here is the code I'm using. It throws a MissingComponentException: There is no Rigidbody 2D attached to "Bird" game object but the script is trying to access it.
using System.Collections;
public class Bird : MonoBehaviour
{
public float UpForce; //Upward force of the "flap".
private bool _isDead = false; //Has the player collided with a wall?
private Animator _anim; //Reference to the Animator component.
private Rigidbody2D _rb2d; //Holds a reference to the Rigidbody2D component of the bird.
void Start()
{
//Get reference to the Animator component attached to this GameObject.
_anim = GetComponent<Animator> ();
//Get and store a reference to the Rigidbody2D attached to this GameObject.
_rb2d = GetComponent<Rigidbody2D>();
}
void Update()
{
//Don't allow control if the bird has died.
if (_isDead == false)
{
//Look for input to trigger a "flap".
if (Input.GetMouseButtonDown(0))
{
//...tell the animator about it and then...
_anim.SetTrigger("Flap");
//...zero out the birds current y velocity before...
_rb2d.velocity = Vector2.zero;
// new Vector2(rb2d.velocity.x, 0);
//..giving the bird some upward force.
_rb2d.AddForce(new Vector2(0, UpForce));
}
}
}
void OnCollisionEnter2D(Collision2D other)
{
// Zero out the bird's velocity
_rb2d.velocity = Vector2.zero;
// If the bird collides with something set it to dead...
_isDead = true;
//...tell the Animator about it...
_anim.SetTrigger ("Die");
//...and tell the game control about it.
GameControl.instance.BirdDied ();
}
}
How do I provide the reference it wants?
The GameObject this script is attached to should have a RigidBidy2D component (because the script is using it to apply forces to GameObject). You can either add a Rigidbody2D in inspector (choose the GameObject the script is attached to and use "Add Component" menu), or make it so the script adds a component automatically when you attach it to GameObject by adding a RequireComponent attribute like this:
[RequireComponent(typeof(Rigidbody2D))]
public class Bird : MonoBehaviour
{
//your Bird class code here
}
See also these questions - that's the same problem.

Destroying clone of prefab with a button which is spawned by a different button

I'm working on a Unity Project in which 3D objects like Cube, Sphere & Cylinder spawn at the center of the screen when selected from a dropdown button list. The instantiate script is working fine but all 3 objects spawn and merge with each other. It is expected to destroy the other 2 when one of them is instantiated. The issue I'm facing is the object spawned is a clone and I can't get it to be destroyed. I'm a beginner to Unity and trying to learn. I've pasted the code below, I've created 3 of these for each button and changed the parameters as per the object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Cube_Inst : MonoBehaviour
{
public GameObject box;
public Transform pos;
public bool trigger;
public Button yourButton;
public GameObject Sphere_Destroy;
public GameObject Cylinder_Destroy;
void Start()
{
Button btn = yourButton.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
}
void TaskOnClick()
{
trigger = true;
Destroy(Sphere_Destroy);
Destroy(Cylinder_Destroy);
}
public void Update()
{
if (trigger == true)
{
Instantiate(box, pos.position, pos.rotation);
trigger = false;
}
}
}
You should rather implement the entire thing centralized ("Single responsibility pattern").
Don't let the individual buttons have individual scripts with a ton of cross references between them.
Rather have one central controller component and let the buttons pass in different parameters to a general spawn method.
Also you should do it event driven - you already know the moment a button is clicked - instead of poll checking a flag in the Update method.
I would use something like
// Put this on ONE object that is always active in the Scene
public class SpawnController : MonoBehaviour
{
private GameObject _currentClone;
private GameObject _currentPrefab;
public void SetInstance(GameObject prefab, Vector3 position, Quaternion rotation)
{
// Was the same prefab passed again? -> Ignore
if(prefab == _currentPrefab) return;
_currentPrefab = prefab;
// Already exists a clone? -> Destroy it
if(_currentClone) Destroy(_currentClone);
// Create and store the new clone
_currentClone = Instantiate (prefab, position, rotation);
}
}
So if SetInstance is called it automatically destroys any already existing instance.
And then have the same component on each button but only configure them accordingly
public class SpawnButton : MonoBehaviour
{
// Already reference this via the Inspector if possible
// If not we will get it on runtime as fallback
[SerializeField] private Button _button;
// For each different button reference a different prefab
[SerializeField] private GameObject _prefab;
// Reference the spawn point
[SerializeField] private Transform _spawnPoint;
// Here drag in the SpawnController from the scene if possible
// if not, we will find it on runtime as fallback
[SerializeField] private SpawnController _spawnController;
private void Awake()
{
if(!_button) _button = Get component<Button>();
_button.onClick.AddListener(DoSpawn());
if(!_spawnController) _spawnController = FindObjectOfType<SpawnController>();
}
private void DoSpawn()
{
// Only tell the SpawnController to do its thing
// This button doesn't have to know or care what that means for the Scene
_spawnController.SetInstance(_prefab, _spawnPoint.position, _spawnPoint.rotation);
}
}
You could even take a step beyond and don't let the buttons know the SpawnController and its secret SetInstance method at all and rather do something like
public class SpawnController : MonoBehaviour
{
private GameObject _currentClone;
private Button _lastClickedButton;
private void Awake()
{
SpawnButton.OnSpawnButtonClicked += SetInstance;
}
private void SetInstance(Button button, GameObject prefab, Vector3 position, Quaternion rotation)
{
// Was the same button pressed again? -> Ignore
if(button == _lastClickedButton) return;
_lastClickedButton = button;
// Already exists a clone? -> Destroy it
if(_currentClone) Destroy(_currentClone);
// Create and store the new clone
_currentClone = Instantiate (prefab, position, rotation);
}
}
and then
public class SpawnButton : MonoBehaviour
{
public static event Action<Button, GameObject, Vector3, Quaternion> OnSpawnButtonClicked;
// Already reference this via the Inspector if possible
// If not we will get it on runtime as fallback
[SerializeField] private Button _button;
// For each different button reference a different prefab
[SerializeField] private GameObject _prefab;
// Reference the spawn point
[SerializeField] private Transform _spawnPoint;
private void Awake()
{
if(!_button) _button = Get component<Button>();
_button.onClick.AddListener(DoSpawn());
}
private void DoSpawn()
{
// Only Invoke the event
// You don't care if or who is listening
OnSpawnButtonClicked?.Invoke(this, _prefab, _spawnPoint.position, _spawnPoint.rotation);
}
}

Changing data on click button

I have some functionalities within some GameObjects.
These functionalities need to be changed when an upgrade is purchased within the game. The problem is that each function is set on its own object.
The first problem is that the variables don't change when I click on the button. As you can see I have added an onclick value to the button, stating that when the button is clicked. The value should change.
The problem here is that "Object reference not set to an instance"
The second problem I face is that each projectile is fired independently. So if I change the static damage of 1 it won't be transferred to other projectiles.
UpgradeMenu
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UpgradeMenu : MonoBehaviour
{
[SerializeField]
private Text accuracyText;
[SerializeField]
private Text speedText;
[SerializeField]
private Text damageText;
[SerializeField]
private float accuracyMultiplier = 0.7f;
private Weapon weapon;
private Projectile projectile;
private Player player;
void OnEnable()
{
UpdateValues();
}
void UpdateValues ()
{
accuracyText.text = weapon.randomAngle.ToString();
damageText.text = projectile.DamageOnHit.ToString();
speedText.text = player.MaxRun.ToString();
}
public void UpgradeAccuracy ()
{
weapon.randomAngle = (int)weapon.randomAngle * accuracyMultiplier;
UpdateValues();
}
public void UpgradeDamage ()
{
projectile.DamageOnHit = (int)projectile.DamageOnHit + 1;
UpdateValues();
}
}
Projectile (DamageScript)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {
[Header ("Speed")]
public float baseSpeed;
public float randomSpeed;
public Vector2 SpeedV2;
public Vector2 Direction;
[Header ("Damage")]
public int DamageOnHit;
[Header ("Layers")]
public LayerMask solid_layer;
public LayerMask entities_layer;
[Header ("OnHit FX")]
public GameObject HitFxPrefab;
public GameObject DustFxPrefab;
[Header ("Bounce")]
public bool BounceOnCollide = false;
public int bouncesLeft = 0;
[HideInInspector]
public Health owner; // owner of the projectile
private Vector2 Position; // Current position
private Vector2 movementCounter = Vector2.zero; // Counter for subpixel movement
public BoxCollider2D myCollider;
List<Health> healthsDamaged = new List<Health>(); // List to store healths damaged
void OnCollideWith (Collider2D col, bool horizontalCol = true) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
return;
}
}
// if the projectile hit's a solid object, destroy it
if (col.gameObject.layer == (int)Mathf.Log(solid_layer.value, 2)) {
DestroyMeWall ();
return;
}
}
void OnCollideWithEntity(Collider2D col) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
}
}
}
First of all, change
[Header ("Damage")]
public int DamageOnHit;
to static
public static int DamageOnHit = /*your starting value*/;
This ensures that all projectiles will share the same damage it deals on a hit.
For instance, if you currently have 10 projectiles in a scene, and DamageOnHit is 2, they all will deal 2 damage.
Without the static, each of the projectile will have it's own DamageOnHit. This brings us to the next case too:
If each projectile had it's own DamageOnHit, and we want to modify DamageOnHit, we need to specify which projectile's damage to modify.
But if it's static, it becomes much simpler as ALL of the projectile shares the same DamageOnHit.
Now, if you wanted to change the DamageOnHit for ALL projectiles, just do
Projectile.DamageOnHit = /*Your new damage value*/
Also, your null reference exception occured due to the fact that you never did assign your projectile in UpgradeMenu.
(Notice how you never did projectile = /*your projectile*/ in UpgradeMenu.cs?)
By default, that will make the variable null. And trying to do null.DamageOnHit += 1 would make no sense.
Small Edit: Making a variable static would also mean that you can't expose it to the inspector. But you can assign a starting value like the code shown initially.

Unity Engine - Instantiate a prefab by collision

I have a GameObject called Player.
Attached to Player there is a script component called Player ( same )
Inside the Player's script, I have a field called _weaponPrefab ( GameObject type )
In the Inspector, I can easily drag & drop any prefabs from my Prefab folder, inside the _weaponPrefab variable.
All good so far. What I want to archive is: to be able to add my Prefabs based on a Collision2D. So if my Player collides with a Prefab, let's say a Sword, the prefab of that sword will be automatically attached and insert into the _weaponPrefab field inside the Player script.
Below my Player script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] private float _speed = 5.0f;
[SerializeField] private float _fireRate = 0.2f;
private bool _canFire = true;
[SerializeField] private GameObject _weaponPrefab; <- to populate runtime
// Use this for initialization
void Start ()
{
transform.position = Vector3.zero;
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space) && _canFire)
StartCoroutine(Shoot());
}
void OnTriggerEnter2D(Collider2D other)
{
//here i don't know how to continue.
}
public IEnumerator Shoot()
{
_canFire = false;
Instantiate(_weaponPrefab, transform.position + new Vector3(0, 1, 0), Quaternion.identity);
yield return new WaitForSeconds(_fireRate);
_canFire = true;
}
}
Edit: i just wrote a comment of where i don't know how to proceed.
There are many ways to accomplish what you desire.
My suggestion may be out of your comfort zone at first, but it will provide you with most flexibility and eventually easy of programming/ designing/ maintaining your game (in my opinion).
First make a scriptable object (what is a scriptable object and how do i use it?)
using UnityEngine;
using System.Collections;
using UnityEditor;
public class Item
{
[CreateAssetMenu(menuName = "new Item")]
public static void CreateMyAsset()
{
public GameObject prefab;
// you can add other variables here aswell, like cost, durability etc.
}
}
Create a new item (in Unity, Assets/new Item)
Then create a monobehaviour script which can hold the item. In your case let's name it "Pickup".
public class Pickup : MonoBehaviour
{
public Item item;
}
Finally in your player script, change your on TriggerEnter to:
void OnTriggerEnter2D(Collider2D other)
{
if(other.GetComponentInChildren<Pickup>())
{
var item = other.GetComponentInChildren<Pickup>().item;
_weaponPrefab = item.prefab;
}
}
When you instantiate a GameObject you need to pass basically 3 parameters (However not all of them are mandatory, check):
The prefab
The position
The rotation
Let's assume you have a placeholder in the hand or in the back of your character, to keep there the weapon once collected. This placeholder can be an empty gameobject (no prefab attached, but will have a transform.position component)
Now let's assume you have a list of weapons in the scene with their prefab and with a different tag each. Then you can do something like this:
GameObject weapon;
GameObject placeholder;
public Transform sword;
public Transform bow;
...
void OnTriggerEnter2D(Collider2D other)
{
//You can use a switch/case instead
if(other.gameObject.tag == "sword"){
Instantiate(sword, placeholder.transform.position, Quaternion.identity);
}else if(other.gameObject.tag == "bow"){
Instantiate(bow, placeholder.transform.position, Quaternion.identity);
}...
}
What I'll do is load the prefabs first in a Dictionary<string, GameObject> from the resources folder.
First I populate the dictionary with all the weapon prefabs in the Start method with the tag of the object as a key. Then in the OnTriggerEnter2D I check if the tag is in the dictionary then I instantiate it from there.
Take a look at the code :
private Dictionary<string, GameObject> prefabs;
// Use this for initialization
void Start () {
var sword = (GameObject) Resources.Load("Weapons/sword", typeof(GameObject));
prefabs.Add("sword", sword);
// Add other prefabs
};
void OnTriggerEnter2D(Collider2D other)
{
if (prefabs.ContainsKey(other.tag))
Instantiate(prefabs[other.tag]);
}
NOTE : your prefabs needs to be in the folder Assets/Resources/Weapons because I used Resources.Load("Weapons/sword", typeof(GameObject));

Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption

I checked this code with a friend that has a better knowledge of Unity, but we can't find the problem.
Basically, Unity says that i can't parent a prefab, but i'm trying to change the parent of an instanced object, not the prefab.
I can't understand the error (but I think that it's in the Update method)
public GameObject[] Weapons;
public float projectileSpeed;
public float bulletTime;
public Rigidbody bullet;
private bool canShoot = true;
private float t = 0f;
private int actualBullets;
private GameObject actualWeapon;
private void Update()
{
t += Time.deltaTime;
if (actualWeapon != null)
return;
actualWeapon = GameObject.Instantiate(Weapons[0], gunPosition.position, gunPosition.rotation) as GameObject;
actualWeapon.transform.parent = GameManager.instance.player.transform;
}
public virtual void Fire()
{
if (canShoot)
{
actualBullets--;
var nBullet = GameObject.Instantiate(bullet, bulletSpawn.position, Quaternion.identity) as Rigidbody;
nBullet.AddForce (new Vector3(Vector3.forward.x, Vector3.forward.y, projectileSpeed));
canShoot = false;
}
else if (t > bulletTime)
(canShoot, t) = (true, 0);
}
Your problem is that
actualWeapon.transform.parent = GameManager.instance.player.transform;
is trying to get a parent that is not instantiated. You need to go to the GameManager and actually instantiate the player GameObject. Then you can keep a reference to the Player and make weapons children of the Player.
In my case, when developing Glitch Garden in Unity 5.5
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = projectileParent.transform;
newProjectile.transform.position = gun.transform.position;
}
solved by Instantiate prefab projectileParent:
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = Instantiate(projectileParent).transform;
newProjectile.transform.position = gun.transform.position;
}
Then, It caused another problem that is multiple projectiles. Finally,
I removed that line and it worked with following code:
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.position = gun.transform.position;
}
Above solution is a temp solution. Finally next video it will be fixed and current code is as follows:
public class Shooter : MonoBehaviour {
public GameObject projectile;
public GameObject gun;
private GameObject projectileParent;
void Start ()
{
projectileParent = GameObject.Find ("Projectiles");
if (!projectileParent) {
projectileParent = new GameObject();
projectileParent.name = "Projectiles";
}
}
private void Fire (){
GameObject newProjectile = Instantiate(projectile) as GameObject;
newProjectile.transform.parent = projectileParent.transform;
newProjectile.transform.position = gun.transform.position;
}
}

Categories