Set bool to different objects with 1 script Unity - c#

I read many pages on google and everything else I could find but nothing helped me, so I have to ask here: I made a script attached to 100 cubes it says:
using UnityEngine;
using System.Collections;
public class IfClose:MonoBehaviour{
public bool Gravitronned=false;
// Use this for initialization
void Start(){
}
// Update is called once per frame
void Update(){
}
void OnTriggerEnter(Collider col)
{
if(col.gameObject.name =="IfInScreenGrv"){
Gravitronned=true;
}
else
{
Gravitronned=false;
}
}
}
and then I have another script that says:
using UnityEngine;
using System.Collections;
public class TimeFreeze:MonoBehaviour{
static bool GravitronedTwice;
// Use this for initialization
void Start(){
}
void Update()
{
GravitronedTwice= gameObject.GetComponent<IfClose>().Gravitronned;
if(GravitronedTwice=true){
if(Input.GetKeyDown(KeyCode.V)){
Physics.gravity =newVector3(0,3.0F,0);
}
}
}
}
so when I press V I want the cube only in this area to get Physics.gravity = new Vector3 (0, 3.0F, 0);

You're calling the value of the bool variable Gravitronned in the second script using GetComponent<>() which is not the right way of going about this.
How about instead of having two bool variables, Gravitronned and GravitronnedTwice, you only have 1 variable in the first script, make it public [even though make it public a bad idea - but that's another topic altogether] and have it access to both the scripts, instead of what you're doing. That way you can change the bool value, as well as access it in both scripts.
Alternatively, you can make the bool Gravitronned private in Script 1, and access it in any other script via C# property.
Script 1
private bool Gravitronned;
public BOOL GRAVITY
{
get Gravitronned;
set Gravitronned;
}

Related

Unity: Even when the "player" game object gets destroyed, the text doesn't appear

I've been using Unity to create a simple 2D game but the problem is that even when the game object "player" gets destroyed, the gameobject "isDead" (text) doesn't appear.
This is my script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class youDied_Text : MonoBehaviour
{
private Transform player;
private Text isDead;
// public static bool isDead;
// Start is called before the first frame update
private void Start() {
isDead = GetComponent<Text>();
}
void checkForDeath()
{
if (player==false)
{
isDead.gameObject.SetActive(true);
}
else
{
isDead.gameObject.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
player = GameObject.FindWithTag("Player").transform;
checkForDeath();
}
}
This script is attached in the text which I need to display in UI element.
As was noted currently you would get a NullReferenceException which is definitely not what you want.
There is absolutely no need / redundancy going through Transform at all actually. Simply store the GameObject reference instead
You are currently setting the object to inactive which has the Text attached ... which is the same object your component is attached to as well!
=> As soon as you end up in the second case once you set it to inactive => from now on Update is never called anymore!
In general as it sounds like this should only happen once anyway I would use a more event driven approach and have a component on your player like e.g.
public class Player : MonoBehaviour
{
public UnityEvent onDied;
private void OnDestroy ()
{
onDied.Invoke();
}
}
And then simply attach a listener/callback to that event once without poll checking states. You can do this either via the Inspector directly (just like in e.g. Button.onClick) or via code like e.g.
public class youDied_Text : MonoBehaviour
{
// Already reference things via the Inspector if possible!
[SerializeField] private GameObject player;
[SerializeField] private Text isDead;
private void Awake()
{
if(!isDead) isDead = GetComponent<Text>();
isDead.gameObject.SetActive(false);
// If you want to rather set it via the Inspector remove all the rest here
//if(!player) player = GameObject.FindWithTag("Player"). GetComponent<Player>();
// or even simpler
if(!player) player = FindObjectOfType<Player>();
player.onDied.AddListener(OnPlayerDied);
}
// If you want to rather set it via the Inspector make this public
private void OnPlayerDied()
{
isDead.gameObject.SetActive(true);
}
}

Saving changed serialized values (Unity Editor C#) [duplicate]

So in my game I have an object that I need to move smoothly from Vector3 fromPosition to Vector3 toPosition at speed float speed, and then go back to where it started. All very simple, but to try and make life easier when setting up levels I decided to make a custom inspector for this script with buttons that allow me to set the target positions to the current position of the object, so I can just move it to where it needs to be and click a button, rather that typing in all the coordinates. Thought I had it all working but then started seeing some very strange behaviour, after playing around it seems to be as follows: The first time a button is used all is well. Every time the button is used after that, the values change properly in the inspector, but upon hitting Play the values of toPosition and fromPosition are reverted to what they were the first time the button was used. (They don't revert back again on Stop). However if I type the values in manually, it works perfectly. Very strange, does anyone have any idea what might be happening here? The code for the script and custom inspector are bellow.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class MovingGrapple : MonoBehaviour
{
public Vector3 fromPosition;
public Vector3 toPosition;
public float speed;
Rigidbody thisBody;
Grapple player;
// Start is called before the first frame update
void Start()
{
thisBody = GetComponent<Rigidbody>();
player = GameObject.Find("Head").GetComponent<Grapple>();
}
private void FixedUpdate()
{
thisBody.MovePosition(Vector3.MoveTowards(transform.position, toPosition, Time.fixedDeltaTime * speed));
if(transform.position == toPosition)
{
transform.position = fromPosition;
if (player.activeTarget != null && GetComponentsInChildren<Transform>().Contains(player.activeTarget.transform))
{
player.BreakGrapple();
GameObject.Destroy(player.activeTarget);
}
}
}
public void SetFromPosition()
{
fromPosition = transform.position;
}
public void SetToPosition()
{
toPosition = transform.position;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(MovingGrapple))]
public class MovingGrappleInspector : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
MovingGrapple myTarget = (MovingGrapple)target;
if (GUILayout.Button("Set From position."))
{
myTarget.SetFromPosition();
}
if (GUILayout.Button("Set To position."))
{
myTarget.SetToPosition();
}
}
}
Thanks.
This will not only happen if you press play .. your changes are never saved!
If possible you should never mix editor scripts with direct access to the target except you know exactly what you're doing!
You would especially need to "manually" mark your changed object as dirty. Otherwise the changes are only temporary until your object is deserialized again (Enter/Exit PlayMode or reload of the Scene or asset).
You could before the changes add a Undo.RecordObject
if (GUILayout.Button("Set From position."))
{
Undo.RecordObject(myTarget, "SetFromPosition");
myTarget.SetFromPosition();
}
if (GUILayout.Button("Set To position."))
{
Undo.RecordObject(myTarget, "SetToPosition");
myTarget.SetToPosition();
}
Also (sounds unlikely in your use-case but)
Important: To correctly handle instances where objectToUndo is an instance of a Prefab, PrefabUtility.RecordPrefabInstancePropertyModifications must be called after RecordObject.
In general rather always go through SerializedProperty where possible like e.g.
[CustomEditor(typeof(MovingGrapple))]
public class MovingGrappleInspector : Editor
{
private SerializedProperty fromPosition;
private SerializedProperty toPosition;
private MovingGrapple myTarget
private void OnEnable()
{
fromPosition = serializedObject.FindProperty("fromPosition");
toPosition = serializedObject.FindProperty("toPosition");
myTarget = (MovingGrapple)target;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
// This loads the current real values into the serialized properties
serializedObject.Update();
if (GUILayout.Button("Set From position."))
{
// Now go through the SerializedProperty
fromPosition.vector3Value = myTarget.transform.position;
}
if (GUILayout.Button("Set To position."))
{
toPosition.vector3Value = myTarget.transform.position;
}
// This writes back any changes properties into the actual component
// This also automatically handles all marking the scene and assets dirty -> saving
// And also handles proper Undo/Redo
serializedObject.ApplyModifiedProperties();
}
}

Unity can't acess prefab's script's variable

I am working on a 3D game on Unity but recently a problem occurred. I can't access a variable from another prefab's script. I tried it before when the object which has to be accessed wasn't a prefab and it worked correctly.
This is the script which tries to access the "slashtime" variable, but when I run it gives back 0 though in the other script the variable is changing continuously.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collision : MonoBehaviour
{
public GameObject sword;
public float slashtime;
private void Update()
{
slashtime=sword.GetComponent<movement>().slashtime;
}
private void OnTriggerEnter(Collider collider)
{
if (collider.tag == "sword" && slashtime+1f > Time.time)
{
Destroy(gameObject);
}
}
}
You can use this. It is also working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class collision : MonoBehaviour
{
public GameObject sword;
public float slashtime;
private void OnTriggerEnter(Collider collider)
{
sword = collider.gameObject;
slashtime=sword.GetComponent<movement>().slashtime;
if (collider.tag == "sword" && slashtime+1f > Time.time)
{
Destroy(gameObject);
}
}
}
Reason for your code is not working because when you write some code in update then it calls every frame. so when your object is destroyed, update call that code and it shown error.
Reworking your code a bit, with explanations below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collision : MonoBehaviour
{
[SerializeField]
private GameObject _sword;
private float _slashTime;
private void Start()
{
_slashTime = GameObject.Find("Movement").GetComponent<Movement>().GetSlashTime();
}
private void OnTriggerEnter(Collider collider)
{
if (collider.CompareTag("sword") && _slashTime + 1f > Time.time)
{
Destroy(gameObject); // What is gameObject in this context? the _sword? something else?
}
}
}
Use private properties. Why? Because it increases modularity and protects the object(s) you're working with from accidentally having their values overwritten.
In conjunction to this, use public methods to obtain/change the values in other objects. Public methods are your API.
It's convention to begin the names of private properties with an underscore.
Like I said in my comment above, you first need to Find() your desired object before you can attempt to GetComponent<T>().
Like others have said, attempting to GetComponent<T>() in Update() is computationally expensive. It's better to get that component at Start() and then talk to it as necessary (in this case, during collision).
CompareTag() is the more modern/accepted way to check a tag.
EDIT:
[SerializeField] will keep the property private while also making it available to the Unity editor. It's good for debugging and linking objects.

Detect collision from different objects?

I couldn't come up with a suitable title for this question but I'll explain.
I have a collision box, which holds a script. This script has an if statement that detects collision from object "Cube001" and sends a Debug.Log to console.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cubeDetect : MonoBehaviour {
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.name == "Cube001")
{
Debug.Log("Cube001 hit!");
}
}
}
With this method, the box collider knows what cube has touched it, as I have instructed so with
collision.gameObject.name == "Cube001"
But say if I have 10 cubes colliding with the collision box, how can I change the if statement so instead of writing another 9 if statements that check if it touches the collision box, I can just have 1 if statement that just first detects a collision from another cube, knows what cube hit the box, and with this knowledge, is able to do a Debug.Log to display the name of the cube that hit the box.
I've tried going through the documentation for OnCollisionEnter but couldn't find anything to help with this.
One option would be to tag all of your similar objects you want to collide with, with the same name. Say we give them the tag "Cubicle". Then we can do the following:
Gameobject myCube;
void OnCollisionEnter(Collision collision) {
if (collision.collider.tag == "Cubicle")
{
Debug.Log(collision.gameObject.name + " hit!");
myCube = collision.gameObject; // store colliding gameobject info for use elsewhere
}
}
You need to use Dictionary. This eliminates the need for all the if statements.
This is what the Dictionary should look like:
public static Dictionary<GameObject, System.Action> objToAction = new Dictionary<GameObject, Action>();
Then a function to add objects to the dictionary when they are instantiated or in the Start function if they already exist
public void registerObject(GameObject obj, System.Action action)
{
objToAction.Add(obj, action);
}
The key in the Dictionary is the GameObject(Cube), you can also use string(name of the GameObject) but using the GameObject is better. The value in the Dictionary stores what you want to to do when OnCollisionEnter is called. So the code that should've been inside that if statement should be placed here. This is done with Action and delegate. You can add as many GameObjects (Cubes) as you wish.
You must add those Cube Objects to the Dictionary with the function above:
public GameObject prefab;
void Start()
{
//Create new Cube
GameObject obj = Instantiate(prefab);
obj.name = "Cube001";
//Add it to the Dictionary
registerObject(obj, delegate { Debug.Log("Hit: " + obj.name); });
}
Your new OnCollisionEnter code is as below. No if statement required. If the Object exist in the Dictionary, execute that code we stored in the value of that Dictionary for each key GameObject.
void OnCollisionEnter(Collision collision)
{
Action action;
if (objToAction.TryGetValue(collision.gameObject, out action))
{
//Execute the approprite code
action();
}
}
Note that the objToAction variabel should either be made static or placed in another script attached to an empty GameObject so that you can access it. There should only be one instance of it.
What was working best for me was to use interfaces and components and check for that. This is working great if you have certain logic on collision but when you don't you can just use tag and set it to something like "collidable".
interfaces solution:
public interface ICollidableObject
{
void CollidedWith(ICollidableObject other);
}
public class CollidableBlock : MonoBehaviour, ICollidableObject
{
public void CollidedWith(ICollidableObject other)
{
Debug.Log((other as MonoBehaviour).gameObject.name);
}
}
public class CollidableSphere : MonoBehaviour, ICollidableObject
{
public void CollidedWith(ICollidableObject other)
{
Debug.Log("sphere001");
}
}
And just use it like such :
void OnCollisionEnter(Collision collision)
{
ICollidableObject collidedWith = collision.gameObject.GetComponent<ICollidableObject>();
if ( collidedWith != null )
collidedWith.CollidedWith(this);
}

How to check ints from other classes on separate scripts?

I'm making a game in Unity3D and I have a 2 different scripts. One called Rays (it checks what I'm clicking on and lowers its hp) and a script called colorChange (it changes the color of the object that I clicked on depending on its hp). I created the hp variable in colorChange and I need to check hp in Ray.
So the "colorChange" script depends on the "Rays" script, yes?
Then you can define the "colorChange" script to expect a "Rays" component on the same GameObject by using the [RequireComponent] tag, which is described
here: https://docs.unity3d.com/ScriptReference/RequireComponent.html
Then in the "Awake" function of "colorChange" you retrieve a reference to "Rays". If the "hp" variable in "Rays" has public get access then in "colorChange" you can use the acquired reference to the "Rays" script to check its current value.
Example for script "Rays":
using UnityEngine;
public class Rays : MonoBehaviour {
private int hp = 0;
public int Hitpoints {
get { return hp; }
}
// ... other methods ...
}
Example for script "colorChange":
using UnityEngine;
[RequireComponent (typeof (Rays))]
public class colorChange : MonoBehaviour {
private Rays raysReference = null;
protected void Awake() {
raysReference = GetComponent<Rays>();
}
protected int getRaysHitpoints() {
return raysReference.Hitpoints;
}
// ... other methods that may use getRaysHitpoints ...
}

Categories

Resources