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.
Related
First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;
I have a Controller class which has a list of GameObjects which are indeed prefabs (in my code called turtlesTypePrefab).
As you can see here, the prefabs I use have sprites inside them with animations (what I'm saying is FirstTurtle has an animator, SecondTurtle as well and so on, and they are all part of my prefab called ThreeTurtles).
So now in my code I want to change the value of a boolean inside one of these animators of my prefab.
Inside of my controller I have:
public GameObject[] turtlesTypePrefab;
And then in my update method I want to do something like:
void Update()
{
for (int i = 0; i < turtles.Length; i++)
{
for (int j = 0; j < turtles[i].Count; j++)
{
GameObject turtle = turtles[i][j];
if (turtle != null)
{
MoveTurtle(turtle, i);
// THIS DOESNT WORK
anim = turtle.GetComponent<FirstTurtle>().GetComponent<Animator>();
anim.SetBool("diving", true);
}
}
}
}
Any ideas?
If each turtle gameobject has its own animator component what you can do is initialize the parent animator from treeturtle gameobject GetComponentInChildren().
What you want to do is to access the one of the children animator via code from the parent. Here is an example
// this script will be attached to the parent
anim = GetComponentInChildren<Animator>();
You used this line of code which the one below:
anim = turtle.GetComponent<FirstTurtle>().GetComponent<Animator>();
The problem with it, is that FirstTurtle is a child gameobject not a component. So you can't really get FirstTurtle as a component. Using GetComponent() only work if you want to use the animator the gameobject you are working on. So I'll suggest you use GetComponentinChildren.
I have code that lerps Sphere instance positions from the vertices of a 1st mesh, to the vertices of a 2nd mesh.
The sphere instances are moved via a Lerp that uses transform data positions. The problem is that apparently moving the instances this way does not work with Unity's Physics very well, so other objects that collide with the sphere instances don't collide with them properly.
How can I change the code to correctly make the Sphere instances move with maybe Vector3 velocity (or whatever would make Unity physics work right), rather than just moving via setting explicit transform positions?
here is the code. I have these variables:
public List<TransformData> meshVertices1 = new List<TransformData>();
public List<TransformData> meshVertices2 = new List<TransformData>();
which are then being filled later with data like:
meshVertices1.Add(new TransformData(position, rotation, scaleVector));
meshVertices2.Add(new TransformData(position, rotation, scaleVector));
then I instantiate sphere instances based on above data:
spheres = new GameObject[24];
for (int i = 0; i < mesh.vertexCount; i++)
{
spheres[i] = Instantiate(spherePrefab, meshVertices1[i].position, Quaternion.identity, transform);
}
and lastly put a Lerp for the sphere instance positions in Update method:
for (int i = 0; i < 24; i++)
{
spheres[i].transform.localPosition = Vector3.Lerp(meshVertices1[i].position, meshVertices2[i].position, t);
}
Thanks. One of the things I tried was changing the Update method to say
Vector3 convert = spheres[i].transform.InverseTransformPoint(transform.localPosition);
convert = Vector3.Lerp(meshVertices1[i], meshVertices2[i].position, t);
but upon hitting play it just made the sphere instances all drop to the floor and roll around. Whereas I want the instances to move and stay in the formation of the 2nd Mesh vertices.
One approach is getting a reference to the Rigidbody of each sphere and calling Rigidbody.MovePosition (make sure this is done in FixedUpdate):
for (int i = 0; i < 24; i++)
{
Rigidbody rb = spheres[i].GetComponent<Rigidbody>();
rb.MovePosition(Vector3.Lerp(meshVertices1[i].position, meshVertices2[i].position, t));
}
In the interest of performance, you may want to create a list of each sphere's Rigidbody once, and then loop through that list so you don't have to call GetComponent 24 times every frame.
I have been looking on the code for ARCore Unity for a while and I want to do one simple task that is, to have a toggle button so user can place an object in the scene while knowing where to place it while the tracked planes are visible and once the user places the object, he is given the option of just visually disabling the tracked planes so it looks more realistic. I was able to do this in Android Studio by doing something like this in the main HelloArActivity.java:
if (planeToggle) {
mPlaneRenderer.drawPlanes(mSession.getAllPlanes(), frame.getPose(), projmtx);
}
this was really simple. I made a bool named planeToggle and just placed the mPlaneRenderer.drawPlanes function in an if condition. When the bool is true it displays the planes and when its false, it does not...
However, with Unity I am confused. I did something like this in the HelloARController.cs :
I made a button to togglePlanes.
Set an event listener to it to toggle a boolean variable and did something like this :
for (int i = 0; i < m_newPlanes.Count; i++)
{
// Instantiate a plane visualization prefab and set it to track the new plane. The transform is set to
// the origin with an identity rotation since the mesh for our prefab is updated in Unity World
// coordinates.
GameObject planeObject = Instantiate(m_trackedPlanePrefab, Vector3.zero, Quaternion.identity,
transform);
planeObject.GetComponent<TrackedPlaneVisualizer>().SetTrackedPlane(m_newPlanes[i]);
m_planeColors[0].a = 0;
// Apply a random color and grid rotation.
planeObject.GetComponent<Renderer>().material.SetColor("_GridColor", m_planeColors[0]);
planeObject.GetComponent<Renderer>().material.SetFloat("_UvRotation", Random.Range(0.0f, 360.0f));
if (togglePlanes == false){ // my code
planeObject.SetActive(false); // my code
} //
}
Nothing happens when I press the toggle button.
The other option I had was to make changes in the TrackedPlaneVisualizer.cs where I did something like this :
for (int i = 0; i < planePolygonCount; ++i)
{
Vector3 v = m_meshVertices[i];
// Vector from plane center to current point
Vector3 d = v - planeCenter;
float scale = 1.0f - Mathf.Min((FEATHER_LENGTH / d.magnitude), FEATHER_SCALE);
m_meshVertices.Add(scale * d + planeCenter);
if (togglePlanesbool == true) // my code
{
m_meshColors.Add(new Color(0.0f, 0.0f, 0.0f, 1.0f)); // my code
}
else
{
m_meshColors.Add(new Color(0.0f, 0.0f, 0.0f, 0.0f)); // my code
}
}
This did work. But I am experiencing delays in toggling and sometimes if two different planes have been rendered they start toggling between themselves(if one is enabled, other gets disabled). So I guess this is also not the option to go for....Anyone who can help?
Note that I am a beginner in Unity.
The sample isn't really designed to hide and show the planes, so you have to add a couple things.
First, there is no collection of the GameObjects that represent the ARCore planes. The easiest way to do this is to add a tag to the game objects:
In the Unity editor, find the TrackedPlaneVisualizer prefab and select it. Then in the property inspector, drop down the Tag dropdown and add a tag named plane.
Next, in the Toggle handler method, you need to find all the game objects with the "plane" tag. Then get both the Renderer and TrackedPlaneVisualizer components and enable or disable them based on the toggle. You need to do both components; the Renderer draws the plane, and the TrackedPlaneVisualizer re-enables the Renderer (ideally it would honor the Renderer's state).
public void OnTogglePlanes(bool flag) {
showPlanes = flag;
foreach (GameObject plane in GameObject.FindGameObjectsWithTag ("plane")) {
Renderer r = plane.GetComponent<Renderer> ();
TrackedPlaneVisualizer t = plane.GetComponent<TrackedPlaneVisualizer>();
r.enabled = flag;
t.enabled = flag;
}
}
You can also do a similar thing where the GameObject is instantiated, so new planes honor the toggle.
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.