I have a scene that contains a nested prefab.
The nested prefab contains multiple barrel prefabs (image below)
These barrel prefabs have components: a transform, animation, mesh filter and a mesh renderer
The problem is that i can't find the mesh filter & mesh renderer by code. Everything works fine in the Unity player! But when i build for pc i get a nullreference on `gobj.getComponent, but only on the objects that have the "barrel" tag, (oil works fine and has the same components):
private void EvaluateAll() //gets called from awake
{
EvaluateDissolve();
EvaluateFish();
EvaluateFloor();
EvaluateFogCol();
EvaluateFogDens();
EvaluatePlants();
EvaluateRocks();
}
private void EvaluateDissolve()
{
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("oil"))
{
gobj.GetComponent<Renderer>().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor + 0.2f);
}
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("barrel"))
{
gobj.GetComponent<Renderer().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor);
}
}
I tried logging all the components of my barrel objects, it logs only the animation and transform components, for some reason the mesh renderer and mesh filter are not logged (this code is called from the awake function):
foreach (GameObject gobj in GameObject.FindGameObjectsWithTag("barrel"))
{
Debug.Log("is object active? " + gobj.activeInHierarchy.ToString());
Debug.Log("barrel scene: " + gobj.scene.name.ToString());
Debug.Log("asking components of barrel, is barrel null?" + (gobj == null).ToString());
Component[] components = gobj.GetComponents(typeof(Component));
foreach (Component comp in components)
{
Debug.Log(comp.GetType().ToString());
}
gobj.GetComponent<Renderer>().sharedMaterial.SetFloat("_SliceAmount",m_dissolveFactor);
}
GetComponent will never return null unless the component is not attached to that Object or the component is destroyed.
Your GameObject.FindGameObjectsWithTag("oil") or GameObject.FindGameObjectsWithTag("barrel") is finding a GameObject with the "oil" or "barrel" tag respectively.
After this, GetComponent<Renderer() is used in a loop to access each Renderer returned by FindGameObjectsWithTag. According to your, it returns null sometimes and the the loop that returns null points to a GameObject called "Fish1".
The problem is that you have mistakenly changed the tag of Fish1 to either "oil" or "barrel". FindGameObjectsWithTag will then pick it up and try to check for its Renderer which is null.
Change the "Fish1" tag to Untagged so that GameObject.FindGameObjectsWithTag("oil") and GameObject.FindGameObjectsWithTag("barrel") won't find it.
Now, if you really meant to have "Fish1" to have "oil" or "barrel" tag then you must make sure that a MeshRender is attached to it.
Note:
There is a search bar in the Editor, use that to search for "Fish1" GameObjects in the scene and change the tag.
Also, make sure that you are not instantiating any GameObject named ""Fish1" with "oil" or "barrel" tag. This is very important. It's not magic. GameObject.FindGameObjectsWithTag is returning it because it exist in the scene.
Related
I'm attempting to dynamically add an FBX model into a scene and add the following components to the child objects of the main GameObject created from the FBX model:
MeshCollider (I Add a mesh to this using the sharedMesh variable)
ManipulationHandler
NearInteractionGrabbable
When I test the functionality in Unity it works flawlessly. When I deploy to the HoloLens 2 only the objects that wouldn't require "convex = true" are working.
It makes me feel like setting convex = true isn't working when deployed on the hololens 2.
Here's my code for your reference on how I'm getting things to work:
void Start()
{
//Grab object from Resources/Objects using provided unique identifier
//I'm instantiating like this because I'm setting the parent to another GameObject in the original code and I got errors if I didn't do it this way
GameObject go = Instantiate(Resources.Load<GameObject>("Objects/" + objectId));
//Loop through child objects of model
for (int i = 0; i < go.transform.childCount; i++)
{
AddManipulationComponents(go.transform.GetChild(i).gameObject);
}
}
void AddManipulationComponents(GameObject item)
{
//Get MeshFilter so I can set the mesh of it to the sharedMesh of the MeshCollider
MeshFilter meshFilter = item.GetComponent<MeshFilter>();
//Only Add MeshCollider components if there's a mesh on the object.
if (meshFilter != null)
{
Mesh mesh = item.GetComponent<MeshFilter>().mesh;
if (mesh != null)
{
//Add MeshCollider
MeshCollider collider = item.EnsureComponent<MeshCollider>();
//A lot of components are curved and need convex set to false
collider.convex = true;
//Add NearInteractionGrabbable
item.EnsureComponent<NearInteractionGrabbable>();
//Add ManipulationHandler
item.EnsureComponent<ManipulationHandler>();
//Set mesh to MeshCollider
collider.sharedMesh = mesh;
}
}
//If current component has children, then loop through those.
if (item.transform.childCount > 0)
{
for (int i = 0; i < item.transform.childCount; i++)
{
AddManipulationComponents(item.transform.GetChild(i).gameObject);
}
}
}
This works when I run it through Unity, but when I build it and deploy to the HoloLens 2 it doesn't completely work. By that I mean there's only 3 or 4 child objects that actually work and they happen to be flat objects. It makes me think the "convex" variable in MeshCollider is not behaving as I'd hope.
Anyone have a fix for this?
Here's a photo of it working through unity (I clipped out proprietary information):
Convex mesh colliders are limited to 255 triangles, perhaps this is why its not working?
When importing the FBX you need to select the checkbox for "Read/Write"
I enabled that and made sure to export all materials as well.
My animations enters a state and it's collision with enemy projectiles either defends himself or gets hurt depending on what animation he is currently in. I'm trying to detect collisions with the ONTRIGGERENTER function and boxcollider2D's but it's not having any affect. I want to figure out how to correctly facilitate trigger enters and collisions between these animations.
I've already tried giving my sprite a tag and calling that tag when the ONTRIGGERENTER function is called but it didn't work. I also tried a more complicated way of calling the tag of the collider but that didn't work either. How do you access the trigger when it's an animation?
string DefenceAnimTag = "Defending";
string DestroyEnemTag = "Eliminated";
//This code is connected to the enemy projectile and trying to
//sense when it's collided with the player
void OnTriggerEnter(Collider2D col)
{
if (col.tag == Defending)
{
GetComponent<Animator>().SetBool("Elim", true);
}
else { //deal damage to the player }
}
//The first method didn't work so I tried a second method.
//Here is an alternative attempt at detecting triggers in
//animations.
void OnCollisionStay2D(Collider2D col)
{
if (col.gameobject.CompareString("Defending"))
{
GetComponent<Animator>().SetBool("Elim", true);
}
}
//This method didn't work either even though the Animation
//WOULD be Defending
I expected enemy projectiles to collide with my player when he was defending himself and get defeated. I knew that it was working when the Enemy projectiles transition into their Elim state, where they get defeated by enemy defenses, however they made collisions and then continued unaffected.
Okay, okay, I figured it out. Because a Gameobject can have many animations to it, you cannot go by the tag that the GameObject in the inspector has. You have to go by the title of the Animations that is currently playing. In order to access the animation currently playing we must use the following code:
AnimatorClipInfo[] m_AnimClipInf;
string m_ClipName;
void Start()
{
//Set up our projectile for possible Elimination
//upon Collision
Getcomponent<Animator>().SetBool("Elim", false);
}
void OnTriggerEnter2D(Collider2D col)
{
m_AnimClipInf = col.GetComponent<Animator>
().GetCurrentAnimatorClipInfo(0);
m_ClipName = m_AnimClipInf[0].clip.name;
if (m_ClipName == "Defending")
{
GetComonent<Animator>().SetBool("Elim", true);
//Projectile gets eliminated
}
}
I have a problem with my script. I'm really looking for a solution without having to post code. ( I'm using Unity 2019 with C#. )
Here's the dilemma:
I have two scripts
1) EnemyDamage
2) EnemySpawn
Both scripts work fine until one of the enemies is killed.
Basically when my enemy('s) die I use Destroy(gameObject) which ultimately is causing the error on my spawn script because the EnemySpawn is still trying to access the destroyed enemy.
The way my spawn script works is multiple enemies can be chosen from a list. Then using coroutines, the enemies appear on screen and then disappear using SetActive(true/false). So even if I elect to set the enemy to false in EnemyDamage, the EnemySpawn will just set it back to true.
So what I need is another way to hide my enemy. ( And I can't just move it out of camera sight because the EnemySpawn will just put it back on a random spawn point in camera view again )
What are some alternatives to Destroy(), SetActive(), or moving out of camera range?
Try adding this in the the spawn enemy script. It should remove an enemy from the list when that enemy is destroyed .The enemysToRemove variable is there because you cant edit a list while iterating through it (thanks Hristo).
public List<GameObject> enemys;
List<GameObject> enemysToRemove = new List<GameObject>();
void Update () {
foreach(GameObject enemy in enemys){
if (enemy == null) {
enemysToRemove.Add (enemy);
}
}
foreach (GameObject item in enemysToRemove) {
enemys.Remove (item);
}
}
Alternatively, put try and catch in-front of everything you do to the enemy`s in the spawn enemy script. Like this:
public List<GameObject> enemys;
foreach(GameObject enemy in enemys){
try{
//thing I want to do to this enemy
}catch{
Debug.Log ("enemy destroyed");
}
}
I have a GameObject.Instantiate() method that create a Page game object. The Page game object then have a Start() method where it also instantiate some new game objects.
My issue is that the game objects that are created in the Start() method get created twice. First it get's created as children of the Page game object (which I want, that works correct), but then it also creates the same game objects in the root of the scene.
I've tried to add Debug.Log("Calling start method...") to Start() to see if the method gets called twice, however this does not seem to be the case. It does not output the text twice to the log.
Navigation.cs
void navigateTo(Page page) {
...
// Here we instantiate the Page.
this.currentPage = GameObject.Instantiate (page.gameObject, this.content.transform).GetComponent<Page>();
}
Page.cs
void Start () {
// Here we create a child GameObject. This get's created both as a child of
// the Page game object, but also in the root of the scene.
GameObject tableGO = new GameObject ("Table");
GameObject instance = GameObject.Instantiate(tableGO, this.gameObject.transform) as GameObject;
...
}
The problem is that new GameObject ("Table") creates a gameObject which is without parent and GameObject.Instantiate(tableGO, this.gameObject.transform) as GameObject; also creates a gameObject but in the second case you use the first gameobject as a template (or like prefab) and set a parent to it. So you can remove GameObject tableGO = new GameObject ("Table"); and use prefab to create gameobject or remove the second line and set tableGO.transform.parent to whatever you need.
Working on 2D mode, I have a script attached to a Sprite. The Update() part is as follow:
void Update () {
if (Input.GetKeyDown ("left")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedX);
} else if (Input.GetKeyDown ("right")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedX);
} else if (Input.GetKeyDown ("up")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedY);
} else if (Input.GetKeyDown ("down")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedY);
}
}
where cursor is a Prefab linked in Inspector. The cursor prefab has only 2 components, Sprite Renderer (to define the image), and the Rigidbody 2D, which setting is as follow:
Mass = 1
Linear Drag = 0
Angular Drag = 0
Gravity Scale = 0
Fixed Angle = true
Is Kinematic = false
Interpolate = None
Sleeping Mode = Start Awake
Collision Detection = Discrete
But when I press the arrow keys, the sprite showing on screen does not move. What did I miss?
Tried to add Debug.Log() inside the if case, it really enters the case. And no error occurred.
Note: speedX = new Vector2(1,0); and speedY = new Vector2(0,1);
You're trying to move an object that doesn't exist from the game's perspective. Assigning cursor a prefab by dragging it from the assets library, is like assigning a blueprint. Telling the prefab to move is like yelling at a car's blueprint to accelerate :)
There are two general solutions.
1) Save a reference
Instantiate the prefab and save the resulting reference to manipulate your new object. You can directly cast it to Rigidbody2D if you're mainly interested in using the rigidbody. The cast will only work if your prefab variable is of the same type, otherwise it will raise an exception. This way will ensure that your prefab always contains a Rigidbody2D or you can't even assign it in the editor.
public Rigidbody2D cursorPrefab; // your prefab assigned by the Unity Editor
Rigidbody2D cursorClone = (Rigidbody2D) Instantiate(cursorPrefab);
cursorClone.MovePosition (cursorClone.position - speedX);
2) Assign the script to the prefab
Depending on your game and what you want to achieve, you can also add a script directly to the prefab. This way every instance of it would just control itself.
void Update () {
if (Input.GetKeyDown ("left")) {
rigidbody2D.MovePosition (rigidbody2D.position - speedX);
} else { //...}
}