I am Trying to make a GUI for HTC VIVE but having trouble in opening it on certain controller angle.
I have done some work and achieved a bit sketchy one because my object is a child which make it hard for me to track its rotation or position, as i wanted it to open only when controller is at certain angle (as a guy looking at his watch)
Here is some visual Example:
This is my controller rotation without GUI:
As i rotate the controller the GUI should show something like this:
Here is some code I have managed
void RayCastFromHead() // is just a name for Method i am raycasting from a dummy which contains left Grip button
{
if (Physics.Raycast(dummy.position, dummy.up, out hitInfo, 30))
{
transform.rotation.ToAngleAxis(out tempAngle, out tempAxis);
if (hitInfo.collider.name.Contains("Camera (eye)"))
{
if (dummy.gameObject.GetComponent<MeshRenderer>().enabled)
{
if ((transform.localEulerAngles.z > 270.0f && transform.localEulerAngles.z < 315.0f)&&
(transform.position.y > 0.9f && transform.position.y < 2f))
{
staticRotaion = transform.localRotation;
canvasOnHead.GetComponent<TweenScale>().PlayForward();
}
}
}
}
}
I do not know that it is a right method to do this kind of task? In Simple manner i want to show GUI on certain controller rotation.
This is My hierarchy what i am talking about
This is the same i wanna do with my GUI it should open when my hand angle is something like this image
There is a simple solution to handle UI rotation.
I suppose you have a canvas for your GUI. This canvas can be child of any object. If you add the canvas (root of this menu) as a child of the left hand it should move and rotate with the left hand.
Note that the render mode of the canvas must be World Space.
This is the parent (left hand):
set the values of canvas's rect transform correctly (most important part is pos.z, I changed the scale of the canvas instead of changing the z. I could change width and height of canvas but it would have adverse effects)
it will behave as you described when rotating the parent object (left hand):
Edit
Add this script to your camera
public class LookAtWatchController : MonoBehaviour
{
public GameObject menuGUI;
public GameObject hand;
void Update(){
if(transform.eulerAngles.x > 10)
{
menuGUI.transform.eulerAngles = new Vector3(transform.eulerAngles.x, 0, 0);
menuGUI.SetActive(true);
menuGUI.transform.position = hand.transform.position;
}
else{
menuGUI.SetActive(false);
}
}
}
assign the gui menu to menuGUI
assign the left hand to hand
you can also include the rotation elements of the hand in the menuGUI rotation:
menuGUI.transform.eulerAngles = new Vector3(transform.eulerAngles.x, hand.transform.eulerAngles.y, hand.transform.eulerAngles.z);
I haven't tested this yet but menuGUI.transform.eulerAngles = new Vector3(transform.eulerAngles.x, 0, 0); should also work fine.
As you see below the rotation.eulerAngles.x of camera and canvas are the same when canvas is seen right in front of camera
Related
I'm making my first game where obstacles (which are prefabs) are placed by a script into a scene. They are all different sizes in a 2D environment. I am placing them using this code below
Instantiate(normal1, new Vector3(x, y , 0), Quaternion.identity);
This works perfectly, except that I need all of the obstacles to be positioned from the top left. So if I would have 0, 0 for my x and y, only the obstacle's corner would be covering the 0, 0 position, not the entire thing. From the little I've worked with GUI elements, you can align to however you like. Is this the same with prefab, and if so, how? Or can you somehow move the origin of the object?
I assume you are talking about non-UI elements.
The easiest thing would be to give your objects a parent GameObject and arrange them in a way so the parent GameObject already has the pivot where you want it (the top-left corner). You do this by first positioning the (future) parent object correctly and then simply drag the child object into it in the hierachy (while it keeps its current position).
Then in your script you have to use Camera.ScreenToWorldPoint in order to find the top-left screen corner position in the 3D world like
// an optional offset from the top-left corner in pixels
Vector2 PixelOffsetFromTopLeft;
// Top Left pixel is at
// x = 0
// y = Screen height
// and than add the desired offset
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// as z we want a given distance to camera (e.g. 2 Units in front)
spawnedObject = Instantiate(Prefab, camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, 2f)), Quaternion.identity);
Full script I used as example
public class Spawner : MonoBehaviour
{
public GameObject Prefab;
public Vector2 PixelOffsetFromTopLeft = Vector2.zero;
private GameObject spawnedObject;
private Camera camera;
private void Start()
{
camera = Camera.main;
}
private void Update()
{
if (Input.anyKeyDown)
{
// you don't need this I just didn't want to mess up the scene
// so I just destroy the last spawned object before placing a new one
if (spawnedObject)
{
Destroy(spawnedObject);
}
// Top Left pixel is at
// x = 0
// y = Screen height
// and than add the desired offset
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// as z we want a given distance to camera (e.g. 2 Units in front)
spawnedObject = Instantiate(Prefab, camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, 2f)), Quaternion.identity);
}
}
}
This will now always spawn the object anchored to the top left. It will not keep it this way if the camera moves or the window is resized.
If your question was about keeping that position also when the window is resized you can use the same code e.g. in an Update method (later you due to efficiency you should only call it when really needed / window actually was resized) like
public class StickToTopLeft : MonoBehaviour
{
private Camera camera;
public Vector2 PixelOffsetFromTopLeft = Vector2.zero;
private void Start()
{
camera = Camera.main;
}
// Update is called once per frame
private void Update()
{
var spawnpos = new Vector2(0 + PixelOffsetFromTopLeft.x, Screen.height - PixelOffsetFromTopLeft.y);
// This time simply stay at the current distance to camera
transform.position = camera.ScreenToWorldPoint(new Vector3(spawnpos.x, spawnpos.y, Vector3.Distance(camera.transform.position, transform.position)));
}
}
This now keeps the object always anchored to the top-left corner also after resizing the window and allows to adjust its offset afterwards.
I have an enemy that has children in it; the enemy also has a death animation. Within the death animation (using the animator), I have scaled the enemy to an appropriate size. However, the children within the enemy is also being scaled down even though I have a animation on the child where I have sized it, I also added anchor positions on this child. Is there a way I can scale down the enemy but also keep the size of the child, p.s. the child is a UI text object. Thank you!
The easiest way to solve your problem that I see is to introduce another GameObject higher in the hierarchy.
This GameObject would be the parent of your enemy object and your Text object which currently is a child of the enemy.
This way you can scale the enemy independently of the Text.
Maybe (hopefully) there are better solutions but you could use a component on the child objects that always keeps the original scale inverting relative changes in the parents scale
public class FreezeScale : MonoBehaviour
{
private Vector3 originalScale;
private Vector3 parentOriginalScale;
private void Awake()
{
// afaik RectTransform inherits from Transform
// so this should also work for UI objects.
originalScale = transform.localScale;
parentOriginalScale = transform.parent.localScale;
}
private void LateUpdate()
{
var currentParentScale = Transform.parent.localScale;
// Get the relative difference to the original scale
var diffX = currentParentScale.x / parentOriginalScale.x;
var diffY = currentParentScale.y / parentOriginalScale.y;
var diffZ = currentParentScale.z / parentOriginalScale.z;
// This inverts the scale differences
var diffVector = new Vector3 (1/diffX, 1/diffY, 1/diffZ);
// Apply the inverted differences to the original scale
transform.localScale = originalScale * diffVector;
}
}
Not tested since hacked in on my mobile phone but I hope you get the idea ;)
set the child's scale to world space not local space.
local space is the default, but it will go off of the scale of the parent so when the enemy shrinks so will the text.
alternatively you could set both objects to be children of an empty object, then just scale your enemy down and the text should stay the same size since its using the scale of the empty parent, which isn't changing size either.
see here:
public static Vector3 GetWorldScale(Transform transform)
{
Vector3 worldScale = transform.localScale;
Transform parent = transform.parent;
while (parent != null)
{
worldScale = Vector3.Scale(worldScale,parent.localScale);
parent = parent.parent;
}
return worldScale;
}
just a work around though, your meant to use this:
yourtransform.LocalScale=Transform.localToWorldMatrix
but it gives me issues... the above method works well though.
transform.scale=GetWorldScale(transform);
edit: lets be clear, the easiest thing to do would be to unpraent the objext before shrinking the parent. this will separate the scales.
I need some help with my college project. I have a cylinder and need it to act as a coil. For example, if I touched the cylinder's surface it's height will decrease (scaled in the y direction) as if pressing on a coil then when I remove my hand it returns back to its original size.
This is what I reached till now but I still have some problems that I can't solve.
public class Deformation : MonoBehaviour
{
Vector3 tempPos;
private void InteractionManager_SourceUpdated(InteractionSourceUpdatedEventArgs hand)
{
if (hand.state.source.kind == InteractionSourceKind.Hand)
{
Vector3 handPosition;
hand.state.sourcePose.TryGetPosition(out handPosition);
float negXRange = transform.position.x - transform.localScale.x;
float posXRange = transform.position.x + transform.localScale.x;
float negYRange = transform.position.y - (transform.localScale.y / 2);
float posYRange = transform.position.y + (transform.localScale.y / 2);
float negZRange = transform.position.z - transform.localScale.z;
float posZRange = transform.position.z + transform.localScale.z;
float handX = handPosition.x;
float handY = handPosition.y;
float handZ = handPosition.z;
if ((negXRange <= handX) && (handX <= posXRange) && (negYRange <= handY) && (handY <= posYRange) && (negZRange <= handZ) && (handZ <= posZRange))
{
tempPos.y = handPosition.y;
transform.localScale = tempPos;
}
else
{
tempPos.y = 0.3f;
transform.localScale = tempPos;
}
}
}
// Use this for initialization
void Start()
{
tempPos = transform.localScale;
InteractionManager.InteractionSourceUpdated += InteractionManager_SourceUpdated;
}
I attached two scripts to my object (cylinder) the TapToPlace script from the HoloToolKit and the deformation script stated above. The problem is when I deploy to my HoloLens to test, when I place the cylinder first to the needed place then try to deform it after that, it is placed but not deformed. If I tried it the other way around both work. Any ideas why does the deformation script does not work after the TapToPlace one?
The cylinder when viewed by my HoloLens is somehow transparent. I mean that I can see my hand through it. I need it to be more solid.
I wonder if there is something like a delay that I can use because when I use the deformation script stated above the cylinder is scaled to my hand position then scaled back to its default size very fast and appears as if blinking.
At first I place the cylinder on a setup (something as a table for example) then I begin to deform it. When I commented the else part in the deformation script stated above, it was scaled and left stable without returning to the original size. It is scaled symmetrically so its height is decreased from up and down resulting in the base of the cylinder becomes away from the table. I need the base of the cylinder to be always stable and touching the table under it.
Note: I am using Unity 2017.3.1f1 (64-bit) - HoloToolkit-Unity-2017.2.1.3
Thank you in advance.
1) Did you see the MRTK 2017.2.1.4 release? It has some useful features such as two handed resizing/scaling of objects. The BoundingBox code in the new MRTK release does moving and resizing in one component, it might be a better base to start from than the TapToPlace, or at least show how the two types of transform can work together.
2) What colour is your object? Hololens will render black as transparent, so try making the object bright white for testing. Also, just double check the brightness is turned up to full (the LHS buttons on the hololens). Finally, check your shader is the MRTK Standard shader. (again, the 2017.2.1.4 release has new shader code you might want to try.) . In a room without direct sunlight it should pretty much cover up your hand.
4) I'm not sure I follow completely, but the pivot point could be important here. If it is centred in the middle of the coil (as I'd imagine it is) then when you deform the coil down it will still stay centered at that central pivot point.
If you instead set the pivot point to the bottom of the coil, touching the table, you can scale and that point stays on the table and the top does all the moving.
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'm working on an application where several GUI Label display names of planes.
And here's the result :
The problem is, if I rotate my camera by 180 °, those label are here, like there is a point symmetry :
So my label appear twice, once on the plane, which is good, and a second time, behind the camera.
I check if my script was not added twice, but there is no problem, more strange, if I look from an above view, the problem just disappear :
I have no idea where this can come from, here's my code, who is attached to every plane :
void OnGUI()
{
if (showInfos)
{
Rect r = new Rect((Camera.main.WorldToScreenPoint(gameObject.transform.position)).x+25, Camera.main.pixelHeight - (Camera.main.WorldToScreenPoint(gameObject.transform.position)).y+25, 75f, 75f);
GUI.Label(r, gameObject.transform.root.name);
}
}
You are drawing the labels whether or not they are in the view frustum or not.
From Camera.WorldToScreenPoint (emphasis mine):
Screenspace is defined in pixels. The bottom-left of the screen is (0,0); the right-top is (pixelWidth,pixelHeight). The z position is in world units from the camera.
You need to check if the Z value of the screen point is negative or positive (I don't know which one is in front of cam and which one is behind it, I don't use Unity), and according to that decide if it needs to be rendered or not.
void OnGUI()
{
if (showInfos)
{
var pt = Camera.main.WorldToScreenPoint(gameObject.transform.position);
if (pt.z > 0) //or < 0, no idea.
{
Rect r = new Rect(pt.x + 25, Camera.main.pixelHeight - pt.y + 25, 75f, 75f);
GUI.Label(r, gameObject.transform.root.name);
}
}
}