Script code only seems to work on single instance of prefb - unity3d

I am experiencing the strangest issue
I have a ray cast and when it touches a certain layer it calls my function which does a small animation.
The problem is, this only works on a single object, I have tried duplicating, copying the prefab, dragging prefab to the scene, it doesn't work.
Now I have this code below, and as you can see I have this line which allows me to access the script on public PlatformFall platfall; so I can call platfall.startFall();
Something I've noticed, If I drag a single item from the hierarchy to the public PlatFall in Inspector then that SINGLE object works as it should. ( in that it animates when startFall is called). HOWEVER, if I drag the prefab from my project to the inspector then they do not work. (Even if debug log shows that the method is called animation does not occur).
public class CharacterController2D : MonoBehaviour {
//JummpRay Cast
public PlatformFall platfall;
// LayerMask to determine what is considered ground for the player
public LayerMask whatIsGround;
public LayerMask WhatIsFallingPlatform;
// Transform just below feet for checking if player is grounded
public Transform groundCheck;
/*....
...*/
Update(){
// Ray Casting to Fallingplatform
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall.startFall();
}
Debug.Log(isFallingPlatform);
}
}
Platform Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformFall : MonoBehaviour
{
public float fallDelay = 0.5f;
Animator anim;
Rigidbody2D rb2d;
void Awake()
{
Debug.Log("Awake Called");
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
}
private void Start()
{
Debug.Log("Start Called");
}
//void OnCollisionEnter2D(Collision2D other)
//{
// Debug.Log(other.gameObject.tag);
// GameObject childObject = other.collider.gameObject;
// Debug.Log(childObject);
// if (other.gameObject.CompareTag("Feet"))
// {
// anim.SetTrigger("PlatformShake");
// Invoke("Fall", fallDelay);
// destroy the Log
// DestroyObject(this.gameObject, 4);
// }
//}
public void startFall()
{
anim.SetTrigger("PlatformShake");
Invoke("Fall", fallDelay);
Debug.Log("Fall Invoked");
// destroy the Log
// DestroyObject(this.gameObject, 4);
}
void Fall()
{
rb2d.isKinematic = false;
rb2d.mass = 15;
}
}

I understood from your post that you are always calling PlatformFall instance assigned from inspector. I think this changes will solve your problem.
public class CharacterController2D : MonoBehaviour {
private PlatformFall platfall;
private RaycastHit2D isFallingPlatform;
void FixedUpdate(){
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall = isFallingPlatform.transform.GetComponent<PlatformFall>();
platfall.startFall();
}
}
}
By the way, i assume that you put prefab to proper position to cast. And one more thing, you should make physics operations ,which affect your rigidbody, in FixedUpdate.

Related

I can not instantiate prefab of gameobject in Unity

I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
public class PozicijaButona : MonoBehaviour
{
Vector2 pozicijaV;
float x, y;
Text tekstPozicije;
List<Vector2> lista = new List<Vector2>();
List<Sprite> slike1t, slike2t, slike3t, slike4t;
public GameObject instantacija;
private GameObject oznacenaSl;
Vector3 skala;
// Start is called before the first frame update
void Start()
{
tekstPozicije = GameObject.Find("Canvas/pozicija").GetComponent<Text>();
List<Vector2> lista = new List<Vector2>();
slike1t= new List<Sprite>();
for (int i = 0; i < HintoviMale.slikeT.Count; i++)
{
slike1t.Add(HintoviMale.slikeT[i]);
}
skala = transform.localScale;
instantacija.transform.localScale = skala;
Debug.Log("sjaa" + skala);
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
pozicijaV = transform.position;
x = transform.position.x;
y = transform.position.y;
string xT = x.ToString();
string yT = y.ToString();
// tekstPozicije.text=xT+","+yT;
tekstPozicije.text = pozicijaV.ToString();
Destroy(oznacenaSl);
}
public void klikNaPoziciju()
{
Debug.Log("broj itema u slici"+HintoviMale.slikeT.Count);
oznacenaSl = Instantiate (instantacija, new Vector2(-0.7f, -3.4f), Quaternion.identity);
// oznacenaSl.transform.localScale = skala;
oznacenaSl.GetComponent<SpriteRenderer>().sprite=HintoviMale.slikeT[0];
}
}
I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
You never call your function named "klikNaPoziciju", which contains the Instantiate call. You also have commented out the line that changes the localScale of the object named "oznacenaSl".
Here is a simple example of instantiating an object and modifying it's localScale:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void Awake()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = new Vector3(2f, 2f, 1f);
}
}
In the Unity Hierarchy, create an empty gameObject and attach the script to it. Then in the Inspector for the gameObject you just created, drag your prefab into the "Object to Instantiate" field.
EDIT:
OP mentioned they are calling their public method from an OnClick method in the Unity Editor. I'm not familiar with that approach, but another approach would be to use the OnMouseDown() function in your script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void OnMouseDown()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
Debug.Log("Creating instance!");
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = transform.localScale;
}
}
For this to work, make sure the object you attach the script to has a collider attached to it. Clicking on the collider will trigger the OnMouseDown() function.

Change Bullets as power

I am doing a 2d game and I want my player to be able to change bullets when he collides with an object(power) and destroy that object. I have a script and I was thinking that I need to implement 2 variables prefab ON/Off but now thinking much more I want to change with the help of a tag ( My player has in his script a public Rigidbody2D bullet) and this function
void Fire()
{
if (photonView.IsMine)
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
this is the script that I was working on for switching bullets but I think it will not work to change a bullet that I add in the inspector for the Character script , to disable from this script and add other bullet . How I can make it by tag?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponSwitching : MonoBehaviour
{
[SerializeField] private GameObject pickupEffect;
public GameObject[] DisablePrefab;
public GameObject[] EnablePrefab;
public int selectBullet = 0;
// Start is called before the first frame update
// Update is called once per frame
public void Bullet(Character bullet)
{
var effect = Instantiate(pickupEffect, transform.position, transform.rotation);
foreach (GameObject disable in DisablePrefab)
{
disable.SetActive(false);
}
foreach (GameObject enable in EnablePrefab)
{
enable.SetActive(true);
}
Destroy(gameObject);
Destroy(effect, 3.0f);
}
}
and I try this think with a BulletSwitch script to call the function from Weapon Switching script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSwitch : MonoBehaviour
{
[SerializeField] private Character bullet;
// Start is called before the first frame update
private void Awake()
{
if (!bullet) bullet = GetComponent<Character>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("Bullet"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var Bullet = other.GetComponent<WeaponSwitching>();
if (!Bullet)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
Bullet.Bullet(bullet);
}
}
inspector character
after my player collides with an object the bullets disappear.
You could make an array of prefabs and change to the correct bullet type when you hit a powerup by setting a CurrentBulletType variable to one of the types from the array. So when you hit powerup X, change bullet type to one of the types from the array.
public GameObject currentPrefab;
public GameObject[] bulletPrefabs;
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
bool foundObject = true;
switch (tagString)
{
case "standard bullet":
currentPrefab = bulletPrefabs[0];
break;
case "fancy bullet":
currentPrefab = bulletPrefabs[1];
break;
case "even fancier bullet":
currentPrefab = bulletPrefabs[2];
break;
default:
foundObject = false;
break;
}
if (foundObject) other.gameObject.SetActive(false);
}
You can instead use a dictionary to directly reference the correct prefab by string name (tag) to make it easy to add more bullet types.
//dictionairy to reference bullet types quickly
public Dictionary<string, BulletTypes> bulletLibrary = new Dictionary<string, BulletTypes>();
//set these in the inspector
public BulletTypes[] bulletTypes;
[System.Serializable]
public struct BulletTypes
{
public string bulletName;
public GameObject prefab;
//public int bulletPower; //more data if you wish
}
private void Start()
{
//fill the dictionary based on the filled bullet type array in inspector
for (int i = 0; i < bulletTypes.Length; i++)
{
if (bulletTypes[i].bulletName != "")
bulletLibrary.Add(bulletTypes[i].bulletName, bulletTypes[i]);
else
print("bullet added, but name empty");
}
}
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
//if the collided tag is in the dictionary, we can reference the bullet type from that dictionairy
if (bulletLibrary.ContainsKey(tagString))
{
print("spawn: " + bulletLibrary[tagString].bulletName);
print("prefab: " + bulletLibrary[tagString].prefab);
//all the data you need is in: bulletLibrary[tagString];
//Bullet.Bullet(bulletLibrary[tagString]);
other.gameObject.SetActive(false);
}
}
Sidenote: If you spawn a lot of bullets, you can use pooling. SimplePool is nice and easy for this. Instead of calling: instantiate and destroy, you call: SimplePool.Spawn() and SimplePool.DeSpawn().
If you have any more questions on this feel free to ask ;)

Destroy GameObject when its clicked on

I'm making a building mechanic in my game and I want to be able to clear out certain objects around the map (trees, other decor) so I have room to build. I've tried using a ray cast to find what object is being clicked on and destroy it but that doesn't work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectDestroy : MonoBehaviour {
// Start is called before the first frame update
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
Debug.Log (Input.mousePosition);
if (Physics.Raycast (ray, out hit)) {
if (hit.collider.gameObject == gameObject) Destroy (gameObject);
}
}
}
}
Here is a little example script:
public class Destroyable : MonoBehaviour
{
private void OnMouseDown()
{
Destroy(gameObject);
}
}
You can attach this script to the GameObject you want to destroy and then during Play-Mode you can click on it to destroy it. It is modifiable if you just need it in your In-Game-Editor.
Note: You need an active Collider on the same Gameobject.
Edit:
The following script shows an example for changing the color of the object:
public class Destroyable : MonoBehaviour
{
public Color mouseHoverColor = Color.green;
private Color previousColor;
private MeshRenderer meshRenderer;
private void Start()
{
meshRenderer = GetComponent<MeshRenderer>();
previousColor = meshRenderer.material.color;
}
private void OnMouseDown()
{
Destroy(gameObject);
}
private void OnMouseOver()
{
meshRenderer.material.color = mouseHoverColor;
}
private void OnMouseExit()
{
meshRenderer.material.color = previousColor;
}
}
You don't need to add this script on every object, just add it to a manager and also I think you are missing Raycast parameters.
To see where you ray is going you can use Debug.Ray()
Also, I would prefer you use #MSauer way since is much cleaner for what you want, just be sure the object contains a collider, I think they can be a trigger and the click will still happen.

Control object rotation by mouse click in unity

I need help with my project in unity
I want to stop each object by clicking on it.
What I did so far:
all my objects rotate but when I click anywhere they all stop, I need them to stop only if I click on each one.
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EarthScript : MonoBehaviour
{
public bool rotateObject = true;
public GameObject MyCenter;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if(rotateObject == true)
{
rotateObject = false;
}
else
{
rotateObject = true;
}
}
if(rotateObject == true)
{
Vector3 axisofRotation = new Vector3(0,1,0);
transform.RotateAround(MyCenter.transform.position,axisofRotation, 30*Time.deltaTime);
transform.Rotate(0,Time.deltaTime*30,0,Space.Self);
}
}
}
Theres two good ways to achieve this. Both ways require you to have a collider attatched to your object.
One is to raycast from the camera, through the cursor, into the scene, to check which object is currently under the cursor.
The second way is using unity's EventSystem. You will need to attach a PhysicsRaycaster on your camera, but then you get callbacks from the event system which simplifies detection (it is handled by Unity so there is less to write)
using UnityEngine;
using UnityEngine.EventSystems;
public class myClass: MonoBehaviour, IPointerClickHandler
{
public GameObject MyCenter;
public void OnPointerClick (PointerEventData e)
{
rotateObject=!rotateObject;
}
void Update()
{
if(rotateObject == true)
{
Vector3 axisofRotation = new Vector3(0,1,0);
transform.RotateAround(MyCenter.transform.position,axisofRotation, 30*Time.deltaTime);
transform.Rotate(0,Time.deltaTime*30,0,Space.Self);
}
}

Unity - NullReferenceException in my GameOverManager

Iam trying to load my "PlayerHealth" Script to my "GameOverManager" to check my currentHealth from the PlayerHealth-Script.
If the current health is "0" - I want to trigger an animation.
The problem is, that Unity gives me an error with following message:
"NullReferenceException: Object reference not set to an instance of an object
GameOverManager.Update () (at Assets/GameOverManager.cs:32)"
Here is the piece of Code of my GameOverManager:
public class GameOverManager : MonoBehaviour {
public PlayerHealth playerHealthScript;
public float restartDelay = 5f;
Animator anim;
float restartTimer;
private void Awake()
{
anim = GetComponent<Animator>();
}
private void Update()
{
playerHealthScript = GetComponent<PlayerHealth>();
if (playerHealthScript.currentHealth <= 0) {
anim.SetTrigger("GamerOver");
restartTimer += Time.deltaTime;
if (restartTimer >= restartDelay) {
SceneManager.LoadScene(2);
}
}
}
}
The error is triggered on the following line:
if (playerHealthScript.currentHealth <= 0)
Here is the hierarchy - FPSController holds "PlayerHealth" - HUDCanvas holds "GameOverManager:
Here are the inspectors:
Here is the code of "PlayerHealth":
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.SceneManagement;
public class PlayerHealth : MonoBehaviour
{
public int startingHealth = 100; // The amount of health the player starts the game with.
public int currentHealth; // The current health the player has.
public Slider healthSlider; // Reference to the UI's health bar.
public Image damageImage; // Reference to an image to flash on the screen on being hurt.
public AudioClip deathClip; // The audio clip to play when the player dies.
public float flashSpeed = 5f; // The speed the damageImage will fade at.
public Color flashColour = new Color(1f, 0f, 0f, 0.1f); // The colour the damageImage is set to, to flash.
public float restartDelay = 5f;
//Animator anim; // Reference to the Animator component.
public AudioSource playerAudio; // Reference to the AudioSource component.
// PlayerMovement playerMovement; // Reference to the player's movement.
// PlayerShooting playerShooting; // Reference to the PlayerShooting script.
bool isDead; // Whether the player is dead.
bool damaged; // True when the player gets damaged.
void Awake()
{
// Setting up the references.
// anim = GetComponent<Animator>();
// playerAudio = GetComponent<AudioSource>();
// playerMovement = GetComponent<PlayerMovement>();
// playerShooting = GetComponentInChildren<PlayerShooting>();
// Set the initial health of the player.
currentHealth = startingHealth;
}
void Update()
{
// If the player has just been damaged...
if (damaged)
{
// ... set the colour of the damageImage to the flash colour.
damageImage.color = flashColour;
}
// Otherwise...
else
{
// ... transition the colour back to clear.
damageImage.color = Color.Lerp(damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
}
// Reset the damaged flag.
damaged = false;
}
public void TakeDamage(int amount)
{
// Set the damaged flag so the screen will flash.
damaged = true;
// Reduce the current health by the damage amount.
currentHealth -= amount;
playerAudio.Play();
Debug.Log("PLayer Health: " + currentHealth);
// Set the health bar's value to the current health.
healthSlider.value = currentHealth;
// Play the hurt sound
playerAudio.Play();
// If the player has lost all it's health and the death flag hasn't been set yet...
if (currentHealth <= 0 && !isDead)
{
// ... it should die.
Death();
}
}
void Death()
{
// Set the death flag so this function won't be called again.
isDead = true;
Debug.Log("In der Death Funktion");
First of all, you don't need, or better, you SHOULD NOT, use GetComponent inside Update, it's a very slow method and it impacts a lot the performance.
So, change your code to this:
public class GameOverManager : MonoBehaviour {
public PlayerHealth playerHealthScript;
public float restartDelay = 5f;
private Animator anim;
private float restartTimer;
private void Awake() {
anim = GetComponent<Animator>();
//No need for GetComponent<PlayerHealth>() if you assign it in the Inspector
//playerHealthScript = GetComponent<PlayerHealth>();
}
private void Update() {
if (playerHealthScript.currentHealth <= 0) {
anim.SetTrigger("GamerOver");
restartTimer += Time.deltaTime;
if (restartTimer >= restartDelay) {
SceneManager.LoadScene(2);
}
}
}
}
Moreover, your bug happens most probably because in the Inspector you assigned to the playerHealthScript variable the game object containing the PlayerHealth script. But, since you try in Update to get the script component again but this time from the game object that has the GameOverManager script (and I assume it doesn't have the PlayerHealth script), you get the NullReference since that script can't be found on this game object.
So, as you can see from the two lines commented out in my code, you actually don't need to get that component from script, just assign it via Inspector and you're good to go.