Unity modify component property after collision - c#

I'm trying to modify a property of a class component after collision,
but the property doesn't seem to be set.
void OnCollisionExit2D (Collision2D myCollision) {
Debug.Log ("OnCollisionExit2D in Player:" + myCollision);
CompoMyClass compo = myCollision.gameObject.GetComponent<CompoMyClass>();
if (compo.collideOnce == true)
return;
compo.collideOnce = true;
// it always goes here :(
}
Do you know why?
public class CompoMyClass : MonoBehaviour {
public bool collideOnce = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}

Make sure that you're looking at the same instance of the Component per collision. If this component were attached to multiple objects, each would have their own collideOnce variable to worry about.
A good way to do this, which will unambiguously tag each object, is to assign an ID such as
Guid ID = Guid.NewGuid();
Also make sure that the object you're colliding with has the right component, to prevent a NullReferenceException, and that you're exiting the collision properly. You may want to switch to OnCollisionEnter.
finally, make sure you're not setting that value false anywhere else.

Related

Game Text Inputfield and Button not responding

I am trying to build a word guessing game. My input field is supposed to accept a string and go to the next page if right or return back to the menu page if not right. Currently, my scene is going to the next scene for both the right and wrong answer.
public class anser : MonoBehaviour {
private string secretWord = "eje";
private string guessWord;
public void GetInput(string guessWord) {
//Invoke ("CompareGuesses", 1f);
if (secretWord == guessWord) {
SceneManager.LoadScene ("Front Page");
} else if (secretWord != guessWord) {
SceneManager.LoadScene ("Question2");
}
}
}
I'm not sure what the guessWord field is for, all you need is the guessWord parameter for your GetInput() method.
Also, you need to make sure the scene names are correct (check for capitals and spaces), and make sure that some other script is actually calling the GetInput() method (you didn't post the code for anything that calls this method).
Here is a cleaned up version of your code that should do what you want:
// The class name "anser" was misspelled. Also you typically use PascalCase for class names.
public class Answer : MonoBehaviour
{
private string secretWord = "eje";
public void GetInput(string guessWord)
{
if (secretWord == guessWord.ToLower())
{
SceneManager.LoadScene("Question2");
return; // eliminates the need for an else clause
}
SceneManager.LoadScene("Front Page");
}
}
Edit: Per Scriven's suggestions in the comments, I changed the LoadScene() arguments to reflect the OP's desired behavior. Also added a little bit of input validation for the guessWord parameter.

Find localPlayer when all players have same name

Unity makes me confused when it comes to referencing players to another object's script. I have used several ways to reference a localPlayer object:
Comparing Tags(not so good in some situations)
gameObject.Name(never used it but it is also not reliable)
Assigning public GameObject Player; in the inspector Drag and Drop (face problems when the player is a prefab)
Making public static [PlayerControllerClass] instance; and setting instance = thisin void Start() function.In order to access playerSpeed from another script I do PlayerControllerClass.instance.playerSpeed = 10;
Is there other solutions for referencing players in a proper way.
Note: I know my question might not be relating to a specific problem but I can explain more if somebody will help.
Edit1: for example I want to find localPlayer in a NetworkGame where all players have same name, same tag but only one player is a localPlayer. Can I access the localPlayer from another script?
Edit2 : The answer I accepted is what I felt the best.I will use the answer provided in previewing score text of the localPlayerby accessing it from a set of players [localPlayer]
Your question wasn't clear until your edit then I realized that this is a networking question.
I need to find localPlayer in a NetworkGame where all players have
same name, same tag but only one player is a localPlayer can I access
the localPlayer from another script?
You can find players with NetworkConnection.playerControllers which retruens List of PlayerController then loop over it and check if it is valid. After that, get NetworkBehaviour from it check the isLocalPlayer property. That's really it.
Something like this:
GameObject FindLocalNetworkPlayer()
{
NetworkManager networkManager = NetworkManager.singleton;
List<PlayerController> pc = networkManager.client.connection.playerControllers;
for (int i = 0; i < pc.Count; i++)
{
GameObject obj = pc[i].gameObject;
NetworkBehaviour netBev = obj.GetComponent<NetworkBehaviour>();
if (pc[i].IsValid && netBev != null && netBev.isLocalPlayer)
{
return pc[i].gameObject;
}
}
return null;
}
If you have NetworkIdentity attached to the player instead of NetworkBehaviour, just get the NetworkIdentity and check the isLocalPlayer property. You can also find the GameObject then check the NetworkIdentity and the isLocalPlayer property.
if (obj.GetComponent<NetworkIdentity>().isLocalPlayer){}
Other useful player operation you may need:
Find all players:
List<GameObject> ListPlayers()
{
NetworkManager networkManager = NetworkManager.singleton;
List<PlayerController> pc = networkManager.client.connection.playerControllers;
List<GameObject> players = new List<GameObject>();
for (int i = 0; i < pc.Count; i++)
{
if (pc[i].IsValid)
players.Add(pc[i].gameObject);
}
return players;
}
Find player by name(Can also be changed to by tag or layer):
GameObject FindNetworkPlayer(string name)
{
NetworkManager networkManager = NetworkManager.singleton;
List<PlayerController> pc = networkManager.client.connection.playerControllers;
for (int i = 0; i < pc.Count; i++)
{
if ((pc[i].IsValid) && (name == pc[i].gameObject.name))
return pc[i].gameObject;
}
return null;
}
OLD answer before your edit:
Is there other solutions for referencing players in a proper way.
Yes, you Find the GameObject with GameObject.Find, after this, you can use the GetComponent function to get the script that is attached to it.
I notice you mentioned you want to reference prefabs in the scene too. There is a difference between prefabs and objects already the scene and they both have different ways to reference them.
Here is an example of Objects in the Scene already. You can see them in the Hierarchy tab:
Let's reference the "Canvas" GameObject by name:
GameObject canvasObj = GameObject.Find("Canvas");
Let's reference the Canvas script attache to the "Canvas" GameObject:
GameObject canvasObj = GameObject.Find("Canvas");
Canvas canvas = canvasObj.GetComponent<Canvas>();
Prefabs:
Here is an example of prefabs in the Project tab:
Put the prefabs in a folder named "Resources". You must so that you can reference them with the Resources API.
Let's reference the "Capsule" prefab GameObject:
GameObject prefab = Resources.Load<GameObject>("Capsule");
To use it, you have to instantiate it:
GameObject instance = Instantiate(prefab);
You can only get components on prefabs after when they are instantiated:
CapsuleCollider cc = instance.GetComponent<CapsuleCollider>();
I recommend the Singleton Pattern. I implement it in almost all my Unity Projects for objects that only have 1 class, yet is a monobehaviour.
using UnityEngine;
public abstract class SingletonBehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
public bool dontDestroyOnLoad = true;
private bool isInitialized = false;
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
if (instance == null)
{
var obj = new GameObject
{
name = typeof(T).Name
};
instance = obj.AddComponent<T>();
}
}
return instance;
}
}
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
if (dontDestroyOnLoad)
{
DontDestroyOnLoad(this.transform.root.gameObject);
}
}
else if (instance != this)
{
Destroy(gameObject);
}
}
private void OnEnable()
{
if (!isInitialized)
{
OnInitialize();
isInitialized = true;
}
}
public abstract void OnInitialize();
}
You can then create a class like so:
public class MyClass : SingletonBehaviour<MyClass>
{
public void OnInitialize()
{
// You can use this instead of awake or start
}
public void DoStuff();
}
And call it like so:
MyClass.Instance.DoStuff()
I have always sought usage of GameObject.FindGameObjectsWithTag when I need multiple references or else GameObject.FindWithTag when I need just the one reference to a specific GameObject available in the system.
To reference a script on said GameObject (found using the API reference above), you simply use `` as described in GameObject.GetComponent API reference.
A complete example would be:
Assign a tag to your Player GameObject e.g. PlayerA.
GameObject player = GameObject.FindWithTag("PlayerA");
PlayerScript playerScript = player.GetComponent<PlayerScript>();
I hope this helps and should you require further clarification, I'll be more than happy to help.
EDIT: Another solution:
Concern: 50 or x amount of players would be too mundane to assign tags.
Solution: If you have a set of 50 players:
I would group them under a parent GameObject.
Assign the parent GameObject a tag e.g. ParentPlayer
Use the GetComponentsInChildren reference.

How to set initial values for custom inspector fields in unity

I have two similar classes ClassA and ClassB. Both classes contain a bool:
In Class A:
[SerializeField]
private bool _overwrite = true;
public bool overwrite
{
get { return _overwrite; }
set { _overwrite = value; }
}
In Class B:
[SerializeField]
private bool _hide = true;
public bool hide
{
get { return _hide; }
set { _hide = value; }
}
Both scripts have a CustomEditor script. In both Editor scripts, inside the OnInspectorGUI() method the following two lines are used to add the respective bool's to the Inspector.
ClassA.overwrite = EditorGUILayout.ToggleLeft("Overwrite", ClassA.overwrite);
ClassB.hide = EditorGUILayout.ToggleLeft("Hide", ClassB.hide);
When I add ClassA to a GameObject, the "Overwrite" field is unchecked, however when I add ClassB to a GameObject, the "Hide" field is checked.
I don't understand what is different, or what other factor is involved in setting a default / initial value for a property.
Ideally I want them both to be checked by default.
Any ideas what I might be missing?
Thanks for your time,
Liam
The Reset method of MonoBehaviours seems that it will provide the functionality you are looking for. It will look something like this:
void Reset()
{
_overwrite = true;
}
What is grabObject?
If it is the component variable which you have found a reference to in the OnEnable method using the target variable of the Editor script, then simply changing your editor scripts to:
grabObject.overwrite = EditorGUILayout.ToggleLeft("Overwrite", grabObject.overwrite);
and:
grabObject.hide = EditorGUILayout.ToggleLeft("Hide", grabObject.hide);
should solve your problem.

Having Issue with Setting Up multiple Input Field in Unity3d

I'm currently trying to set up multiple Input Field in Unity and having issue do so. I can only get one input to work (intPressure) but don't know how to add a second one (drillpipe). It seem to me that unity only allow one Input Field at a time. I would think that just changing it to InputField2, ect will do the trick, but that doesn't seem to work. Other have mentioned creating an empty gameobject and inserting the inputfield to each gameobject.
To be honest, I still quite confuse about how to setup the second input field.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class UserInput : MonoBehaviour {
InputField input;
int intPressure = 0;
int drillpipe = 0;
public DataManager data;
void Start ()
{
var input = gameObject.GetComponent<InputField>();
var se= new InputField.SubmitEvent();
se.AddListener(SubmitName);
input.onEndEdit = se;
}
private void SubmitName(string pressure)
{
var pressureScript =
GameObject.FindObjectOfType(typeof(DataManager)) as DataManager;
if (int.TryParse(pressure, out intPressure))
{
pressureScript.pressure = intPressure;
}
else
{
// Parse fail, show an error or something
}
}
}
I am not sure about the way you want to implement this (using single GameObject), however you most certainly would be able to do this if you had a couple of GameObjects (One object per control) nested within another GameObject (let's call it UserInputRoot) like this:
UserInputRoot (has UserInputController script)
|
+--- InputPressure (has InputField component)
|
+--- InputDrillpipe (has InputField component)
The controlling script would then have either a couple of public InputField's or a couple of private ones, initialized within the Start() or Awake() method:
class UserInputController : MonoBehaviour {
//These can be set from the inspector and controlled from within the script
public InputField PressureInput;
public InputField DrillpipeInput;
// It would be better to pass this reference through
// the inspector instead of searching for it every time
// an input changes
public DataManager dataManager;
private void Start() {
// There is no actual need in creating those SubmitEvent objects.
// You can attach event handlers to existing events without a problem.
PressureInput.onEndEdit.AddListener(SubmitPressureInput);
DrillpipeInput.onEndEdit.AddListener(SubmitDrillpipeInput);
}
private void SubmitDrillpipeInput(string input) {
int result;
if (int.TryParse(input, out result)) {
dataManager.drillpipe = result;
}
}
private void SubmitPressureInput(string input) {
int result;
if (int.TryParse(input, out result)) {
dataManager.pressure = result;
}
}
}
And by the way, the formatting of your code is absolutely atrocious. You MUST fix it.
It looks like the script you are showing is attached to an input field gameobject is that correct? You have a member variable in your object named input but then you create a new variable input in your Start method. Rather than have a script attached to your first InputField, create an empty gameobject in the editor and attach a script to that. In the script add two public members:
public class UserInput : MonoBehaviour {
public InputField PressureInput;
public InputField DrillPipeInput;
Now pop back to the editor, you should see the two input fields showing up when you select your empty gameobject. Drag and drop your two input fields into the slots for each inputfield. Now when the scene starts your UserInput script will be set with the input fields and you can use them both.

this is null in the inside method, how is this even possible?

I have a class Star that inherits from Object. It has a GameObject field.
Here is code:
public class Star : Object {
public GameObject starObject;
// code omitted
}
It has a method that I use for selection.
public void selectStar () {
Debug.Log ("Inside star selection");
mapManager.selectionStarDidChange(this);
selected = true;
}
I have another script from which I call that method, it detects input.
public class StarObjectHandler : MonoBehaviour {
public Star starScript;
void OnMouseOver()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log ("Mouse registered star selection");
starScript.selectStar();
}
}
}
But when I call the first method, somehow this becomes null. How can I even call methods on null and they work?
What is wrong here? I checked the starScript in second class, it is not null.
In the log I get "Mouse registered.." and then "Inside star selection" as expected.
EDIT1:
Here is the method that is getting invoked from this:
public void selectionStarDidChange(Star newlySelected)
{
if (newlySelected) {
starSelector.transform.position = newlySelected.position;
} else {
starSelector.transform.position = new Vector2(-10, -10);
}
if (lastSelectedStar) {
lastSelectedStar.deselectStar();
}
if (lastSelectedShip) {
lastSelectedShip.deselectShip();
}
lastSelectedStar = newlySelected;
}
It always goes the else route, and logging here says that newlySelected is null.
EDIT2:
Here is the project if anyone feels interested to see what is wrong:
https://www.dropbox.com/s/napsqknlw3hjuku/Orion2.zip?dl=0
EDIT3:
I managed to localize the error to this code:
Star star = new Star();
Debug.Log ("Is star null? __" + (star == null) + "__");
Somehow this returns true, I will have to look up object instantiation.
Yes looks like you have declared variable without assigning it
Star star = new Star();
Will solve the issue
Apparently the problem was that this code returned null instead of an object. I have no idea how the rest of the program worked with this being null, and I can't understand why it returns null.
It is a subclass of Object class. I changed it to no superclass and it worked.
Star star = new Star();

Categories