Unity3D/NGUI UI elements drawn at wrong location - c#

I have a curious problem with rendering the actual content in NGUI widgets when the underlying panel is moved. For some reason, the content itself doesn't update to correct position even if the bounds are. First image is how it should be and second is how it is sometimes after moving the underlying panel from right to left. You can see from the widget rectangles that they are in correct place but the contents (text or sprite) is misplaced.
I have tried updating, refreshing anchors etc. but none seem to be working. Any ideas? It seems to be a rendering problem. We are using Unity 4.6 and NGUI 3.7.4 currently.

I suppose NGUI don't work well with (parent) gameobject being inactive.
Especially if you try to UIScrollView.ResetPosition, scaling or re-positioning.
so I have an extension that look like this
public static void ResetPositionHack(this UIScrollView scrollView, float animLength = 1f ) {
scrollView.ResetPosition();
scrollView.currentMomentum = Vector3.zero;
KeepMakingDirty.Begin( scrollView, animLength );
}
The method KeepMakingDirty.Begin will attach a MonoBehavior to the object (also make sure no double attachment).
SetDirty() will be called in it's Update() for a specified period of time.
Then the script self-destructs.

This is a NGUI's bug. It is related to UIPanel.UpdateTransformMatrix(), and has been fixed at NGUI3.9.3 or NGUI 3.9.4.
The code blow works fine in my project:
void UpdateTransformMatrix ()
{
int fc = Time.frameCount;
if (cachedTransform.hasChanged)
{
mTrans.hasChanged = false;
mMatrixFrame = -1;
}
if (mMatrixFrame != fc)
{
mMatrixFrame = fc;
worldToLocal = mTrans.worldToLocalMatrix;
Vector2 size = GetViewSize() * 0.5f;
float x = mClipOffset.x + mClipRange.x;
float y = mClipOffset.y + mClipRange.y;
mMin.x = x - size.x;
mMin.y = y - size.y;
mMax.x = x + size.x;
mMax.y = y + size.y;
}
}

Related

Saving a float value in a certain moment in unity

Hello I did the Brackeys Tutorial and I am trying to make my own game off of it and the first thing i wanted to do was a restart screen and for that i need the score but if the cube falls off it still goes forward so i need the z value of the cube when it falls of the edge
What do you mean?
If you just want to save the float value of a transform, use something like
float z = cube.transform.position.z;
When setting the transform, you need to use a whole Vector3; you can't just change one value.
cube.transform.position = Vector3(cube.transform.position.x, cube.transform.position.y, z);
Ok. According to your comment,
bool checkForY = true;
GameObject cube;
float cubeZ;
void Update()
{
if (checkForY)
{
if (cube.transform.position.y < 0)
{
cubeZ = cube.transform.position.z;
checkForY = false;
}
}
}
This should work. You might want to rename "cubeZ" to whatever you're using it for, though.

UNITY: How to get the coordinates of the SafeArea rect?

I am working on a project in Unity for Android Portrait mode. I will constantly be modifying the textures of two RawImages, the catch is that the images must be the same width and height(1080x1080, 800x800) 1080x1079 is wrong. Because a lot of phones nowadays are notched, I'm forced to maintain all the UI Elements inside the SafeArea rect. BUT, I don't know how to get the y coordinate of the SafeArea rect relative to the Display. Here's a photo:
So, I only need to get the y coordinate of the SafeArea rect, by my guesses, for the Phone in the example, the y coordinate should be around 100 pixels height. Thank you in advance.
If you are using UI Scale Mode: Scale with screen size Like in this picture, here is my solution for adaptive RectTransform with your safe area (only Vertical):
using UnityEngine;
using UnityEngine.UI;
public class MySafeArea : MonoBehaviour
{
private CanvasScaler canvasScaler;
private float bottomUnits, topUnits;
void Start()
{
canvasScaler = FindObjectOfType<CanvasScaler>();
ApplyVerticalSafeArea();
}
public void ApplyVerticalSafeArea()
{
var bottomPixels = Screen.safeArea.y;
var topPixel = Screen.currentResolution.height - (Screen.safeArea.y + Screen.safeArea.height);
var bottomRatio = bottomPixels / Screen.currentResolution.height;
var topRatio = topPixel / Screen.currentResolution.height;
var referenceResolution = canvasScaler.referenceResolution;
bottomUnits = referenceResolution.y * bottomRatio;
topUnits = referenceResolution.y * topRatio;
var rectTransform = GetComponent<RectTransform>();
rectTransform.offsetMin = new Vector2(rectTransform.offsetMin.x, bottomUnits);
rectTransform.offsetMax = new Vector2(rectTransform.offsetMax.x, -topUnits);
}
}
Just attach this script to your RectTransform with stretched anchors and enjoy the result

Is there a way to stabilize the 3D object on the Augmented Image

I'm setting up a scene and need my object to appear on one corner of the augmented image.
I've created a script to find the augmented image and set it as an anchor. domPrefab is instantiated on the anchors' position
if (image.TrackingState == TrackingState.Tracking && visualizer == null)
{
//First: detect the augmented image
Anchor imageAnchor = image.CreateAnchor(image.CenterPose);
visualizer = Instantiate(AugmentedImageVisualizerPrefab, imageAnchor.transform, imageAnchor);
visualizer.Image = image;
m_Visualizers.Add(image.DatabaseIndex, visualizer);
//Second: Instantiate the prefabs
GameObject prefab = domPrefab;
var domObject = Instantiate(prefab, anchor.transform.position, anchor.transform.rotation);
domObject.transform.Rotate(0, k_ModelRotation, 0, Space.Self);
domObject.transform.SetParent(anchor.transform, true);
}
I expect the domPrefab to be instantiated in the center of the augmented image, and when I will learn how to place it in the corner, then have the domPrefab appear in the corner of the image.
The actual result is: the domPrefab appear in the middle of the image but it's not stable. When camera looks around it moves a bit and its' scale changes.
Because you are creating the anchor in the center of the image with this line
Anchor imageAnchor = image.CreateAnchor(image.CenterPose);
Then instantiate your visualizer at imageAnchor position and then try to rotate move or something but instead you can do this in your AugmentedImageVisualizer. To do this
Create an empty object and attach AugmentedImageVisualizer script to it.
Create a public GameObject myModel in AugmentedImageVisualizer
Put your model as a child to this object and drag it to myModel in inspector.
Create a prefab with this object
Modify your AugmentedImageVisualizer like this:
public AugmentedImage Image;
public GameObject myModel;
public void Update()
{
float halfWidth = Image.ExtentX / 2;
float halfHeight = Image.ExtentZ / 2;
// for upper left
myModel.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.back);
//for upper right
myModel.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.back);
//for lower left
myModel.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.forward);
//for lower right
myModel.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.forward);
}
Use this as AugmentedImageVisualizer in your AugmentedImageExampleController
If you want it to be stable and not change position over time, do the operation in fifth step in Start instead of Update or in another function and make sure it runs only once.
I hope this helps Good Luck!

Place object without overlap others

Im having a problem in one of my projects in unity. I basicly have a system where you click an object and then you click somewhere inside an area and the object goes to that position. the problem is that those objects can't overlap. And they can't use physics. to detect colision with each other. I have been working on this for a long time I got something but still not totally working and I hoped someone could help me.
What im basicly doing is getting all objects near the click and then if there are some just calculate the directions between the click and those objects and then add them to the position that seems to work sometimes they don't overlap other times they do and they go to far away and I need them to be near the click.
code:
public Vector3 GetPossiblePosition(List<GameObject> nearbyDiscs, Vector3 position)
{
float initialY = transform.position.y;
Vector3 idealPosition = position;
List<Vector3> directions = new List<Vector3>();
if(nearbyDiscs.Count > 0)
{
foreach (GameObject disc in nearbyDiscs)
{
Vector3 newDirection = position - disc.transform.position;
directions.Add(newDirection);
}
for (int i = 0; i < directions.Count; i++)
{
idealPosition += directions[i] / directions.Count;
List<GameObject> discs = CheckForNearbyDiscs(idealPosition);
if (discs.Count < 1)
break;
}
}
idealPosition.y = initialY;
return idealPosition;
}
behaviour:
You can easily do this using Physics2D.OverlapCircleAll
public static Collider2D[] OverlapCircleAll(Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);
this method returns an array of colliders, you can simply check overlapping if the length of the returning array is greater than one and can handle accordingly.

Unity Gizmos for arraylist

How can i apply Gizmos to parallel arrays of positions ?
The current code i have now is that, the Gizmos does its job.
But since its a loop, it flashes which means going through the loop and drawing an array using the method
Gizmos.DrawRay();
But i don't want it to go through the loop and draw a ray and then removing it and repeating the process with a new position.
I want it to draw a ray for every element in the array and have the ray fixed where it doesn't get rewritten.
So here is my current code.
Edit :
void Platform_Position_Scale_Generator(int i) {
posX[i] = Random.Range(minPosRange, maxPosRange + 1);
posY[i] = Random.Range(minPosRange, maxPosRange + 1);
posZ[i] = 0;
scaleX[i] = Random.Range(minScaleRange, maxScaleRange + 1);
scaleY[i] = 1;
scaleZ[i] = 1;
}
void Platform_Generator(int i) {
platformPrefabPosition[i].x = posX[i];
platformPrefabPosition[i].y = posY[i];
platformPrefabPosition[i].z = posZ[i];
Instantiate(platformPrefab, platformPrefabPosition[i], Quaternion.identity);
platformPrefab.transform.localScale = new Vector3(scaleX[i], 1, 1);
}
void OnDrawGizmos() {
if(numOfGeneratedPlatforms < numOfPlatformsToGenerate) {
Platform_Position_Scale_Generator(numOfGeneratedPlatforms);
Platform_Generator(numOfGeneratedPlatforms);
Gizmos.color = Color.blue;
Gizmos.DrawRay(new Vector3((posX[numOfGeneratedPlatforms]), (posY[numOfGeneratedPlatforms]), (posZ[numOfGeneratedPlatforms])), transform.TransformDirection(Vector3.up));
numOfGeneratedPlatforms++;
}
}
Unity as well as the SceneView need to redraw the whole area everytime something changes. There is no way to draw something that will stay. If you want it to stay you have to redraw it every time the SceneView is redrawn. Unlike at runtime, at edittime Unity only redraws a window when some event happened.
So if you want to have a line for each position you have to draw a line for each position in OnDrawGizmos, everytime it's called. The SceneView works the same as the GameView. However it uses an invisible camera to render the scene from the editors perspective. Each time it is redrawn the SceneView will be cleared and completely redrawn. During that process Unity will call your OnDrawGizmos method where you have to draw everything you want to be visible in the scene.
edit
To draw all lines for all elelments you just have to use a loop, like this:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
for(int i = 0; i < posX.Length; i++)
{
Gizmos.DrawRay(new Vector3(posX[i], posY[i], posZ[i]), transform.up);
}
}
This will draw a one unit long line from each point in your list upwards. As i said if you work with positions it's usually way easier to work with a single array and use Vector3 has type:
public Vector3[] positions;
Or if you have to add / remove items quite often, use a generic List:
public List<Vector3> positions
ps: To me it looks a bit strage that you want to draw a line upwards. If you wanted to draw a line from point to point, you have to do:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
// important to reduce the upper limit by one since we add 1 inside the loop
for(int i = 0; i < posX.Length-1; i++)
{
Gizmos.DrawLine(
new Vector3(posX[i], posY[i], posZ[i]),
new Vector3(posX[i+1], posY[i+1], posZ[i+1])
);
}
}

Categories

Resources