How to convert coordinates in Unity? - unity3d

I have a JSON file with coordinates where (0.0) is at top left corner (first picture). In Unity (0.0) is at the center of the screen (second picture). I am looking for a way where I can convert my file's coordinates to Unity coordinates.
So my question is how can I convert the coordinates from the first picture to Unity Coordinates?
For example at position (2.3) I have letter 'A'. How to convert it to match with Unity?
I have already tried Camera.main.ScreenToWorldPoint but I always get the same result for all of the inputs.
This is what I get:

This should work:
int unityX = -gridWidth/2 + jsonX/gridWidth * gridWidth;
int unityY = gridHeight/2 - jsonY/gridHeight * gridHeight;
Edit:
I just realised you would probably need the position of the center of the tile in your grid. In that case the code above needs some modifications:
float unityX = -gridWidth/2 + jsonX/gridWidth * gridWidth + 0.5f;
float unityY = gridHeight/2 - jsonY/gridHeight * gridHeight - 0.5f;
This will place the objects you're working with in the center of their respective tiles.

try this python code, or translate it to your language:
def transformXY(x,y, jsonSizeX,jsonSizeY):
x -= jsonSizeX/2
y -= jsonSizeY/2
return x,y
x,y = getDataFromJson(...)
x,y = transformXY(x,y, jsonMaxX, jsonMaxY)
useInUnity(x,y)

Simplest way I can think of would be to have an empty GameObject where you want 0,0 to be and then calculate the positions relative to that GameObject
public class CoordinateAdjustment : MonoBehaviour
{
public GameObject positionAdjustmentGameObject;
public float cellSize = 1f; // Set to 1f for this example can change to your needs
public Vector2 GetPosition(int row, int column)
{
Vector2 position = new Vector2
(
positionAdjustmentGameObject.transform.position.x + (column * cellSize),
positionAdjustmentGameObject.transform.position.y + (row * cellSize)
);
return position;
}
}
Might need some refining but will work.

Related

In unity, how do you find voxel information at a given worldspace position?

I am trying to have a gameobject in unity react with sound if another object is inside it. I want the gameobject to use the entering objects location to then see what voxel is closest and then play audio based on the voxel intensity/colour. Does anyone have any ideas? I am working with a dataset that is 512x256x512 voxels. I want it to work if the object is resized as well. Any help is much appreciated :).
The dataset I'm working with is a 3d .mhd medical scan of a body. Here is how the texture is added to the renderer on start:
for (int k = 0; k < NumberOfFrames; k++) {
string fname_ = "T" + k.ToString("D2");
Color[] colors = LoadData(Path.Combine (imageDir, fname_+".raw"));
_volumeBuffer.Add (new Texture3D (dim [0], dim [1], dim [2], TextureFormat.RGBAHalf, mipmap));
_volumeBuffer[k].SetPixels(colors);
_volumeBuffer [k].Apply ();
}
GetComponent<Renderer>().material.SetTexture("_Data", _volumeBuffer[0]);
The size of the object is defined by using the mdh header files spacing as well as voxel dimensions:
transform.localScale = new Vector3(mhdheader.spacing[0] * volScale, mhdheader.spacing[1] * volScale * dim[1] / dim[0], mhdheader.spacing[2] * volScale * dim[2] / dim[0]);
I have tried making my own function to get the index from the world by offsetting it to the beginning of the render mesh (not sure if this is right). Then, scaling it by the local scale. Then, multiplying by the amount of voxels in each dimension. However, I am not sure if my logic is right whatsoever... Here is the code I tried:
public Vector3Int GetIndexFromWorld(Vector3 worldPos)
{
Vector3 startOfTex = gameObject.GetComponent<Renderer>().bounds.min;
Vector3 localPos = transform.InverseTransformPoint(worldPos);
Vector3 localScale = gameObject.transform.localScale;
Vector3 OffsetPos = localPos - startOfTex;
Vector3 VoxelPosFloat = new Vector3(OffsetPos[0] / localScale[0], OffsetPos[1] / localScale[1], OffsetPos[2] / localScale[2]);
VoxelPosFloat = Vector3.Scale(VoxelPosFloat, new Vector3(voxelDims[0], voxelDims[1], voxelDims[2]));
Vector3Int voxelPos = Vector3Int.FloorToInt(VoxelPosFloat);
return voxelPos;
}
You can try setting up a large amount of box colliders and the OnTriggerEnter() function running on each. But a much better solution is to sort your array of voxels and then use simple math to clamp the moving objects position vector to ints and do some maths to map the vector to an index in the array. For example the vector (0,0,0) could map to voxels[0]. Then just fetch that voxels properties as you like. For a voxel application this would be a much needed faster calculation than colliders.
I figured it out I think. If anyone sees any flaw in my coding, please let me know :).
public Vector3Int GetIndexFromWorld(Vector3 worldPos)
{
Vector3 deltaBounds = rend.bounds.max - rend.bounds.min;
Vector3 OffsetPos = worldPos - rend.bounds.min;
Vector3 normPos = new Vector3(OffsetPos[0] / deltaBounds[0], OffsetPos[1] / deltaBounds[1], OffsetPos[2] / deltaBounds[2]);
Vector3 voxelPositions = new Vector3(normPos[0] * voxelDims[0], normPos[1] * voxelDims[1], normPos[2] * voxelDims[2]);
Vector3Int voxelPos = Vector3Int.FloorToInt(voxelPositions);
return voxelPos;
}

Ball Mechanics - Is this the best approach?

Good day,
I'd like to program a constantly moving ball (object3) being passed between two stationary objects (object1, object2), with the ability to set the max height Y of the pass trajectory dynamically.
What would you argue is the best way to program the ball physics for this concept?
I've looked at using addForce on a default sphere w/ a rigidbody. It seems like there should be an equation that expresses the trajectory of a pass of object3 from object1's x to object2's x... at a known, given speed, with a known, set mass, and a known gravity environment.
However, currently I have a Vector3.Lerp interpolating the ball between the two objects on each FixedUpdate() with t expressed as:
`(Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;`
It works and all, but with this approach, it seems there's no clear way to add height to the trajectory of the ball path. I've considered adding the height to the Y value in object2 until the ball is half way there, and then setting it back to the original Y position... but it just feels wrong! Thoughts?
Thanks!
Okey so if I understand you correctly currently you are doing
privte void FixedUpdate()
{
var factor = (Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;
object1.MovePosition(Vector3.Lerp(object2.position, object3.position, factor));
}
which moves the ball pingpong between object1 and object2 position but only planar.
Assuming for now the objects will only be moving within the XZ plane and never have different Y position in order to rather get a curve with height you could treat the separatly:
- Interpolate between both positions as before
- Separately calculate the Y position with sinus or any other mathematical curve function - for realistic physics probably rather a parabola actually
Could look somhow like
public class Example : MonoBehaviour
{
public Rigidbody object1;
public Transform object2;
public Transform object3;
// adjust in the Inspector
public float speed = 1;
public float Amplitude = 0;
// Just for debug
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// This always returns a value between 0 and 1
// and linearly pingpongs forth and back
linearFactor = Mathf.PingPong(Time.time * speed, 1);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 (no need for +1 and /2 anymore)
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(linearFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// As before interpolate between the positions
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.MovePosition(new Vector3(position.x, yPosition, position.z));
}
}
You could optionally also try and add some dumping in the Y direction in order to make the vertical movement more realistic (slow down when reaching the peak). I tried a bit using inverted SmoothStep like
// just for debug
[Range(0, 1)] [SerializeField] private float dampedSinusFactor;
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// Use two different factros:
// - a linear one for movement in XZ
// - a smoothed one for movement in Y (in order to slow down when reaching the peak ;) )
linearFactor = Mathf.PingPong(Time.time * speed, 1);
dampedSinusFactor = InvertSmoothStep(linearFactor);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 ()
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(dampedSinusFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.position = new Vector3(position.x, yPosition, position.z);
}
// source: https://stackoverflow.com/a/34576808/7111561
private float InvertSmoothStep(float x)
{
return x + (x - (x * x * (3.0f - 2.0f * x)));
}
However for slow movements this looks a bit strange yet. But you can come up with any other maths curve that results in the expected behavior for x=[0,1] ;)

How to make a 2D arrow on the screen that always point to a 3d object inside the AR Scene

I want to display a 2D arrow on the screen that always moves to point to a 3D object in the AR scene.
The issue is how to measure the angle that should the arrow rotate to point the desired 3D object.
Thanks in advance.
One strategy is to project the position of your object into screen space.
Then calculate the vector between the position of your arrow and that projected position. You can use this vector to calculate an angle of rotation from, for example, a vertical direction, using the Dot Product, followed by an acos.
Finally, you'd need to do a little cross-product calculation to decide whether the above rotation is clockwise or anticlockwise.
Here is some sample code:
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
// Calculate the angle
// We assume the default arrow position at 0° is "up"
float angle = Mathf.Rad2Deg * Mathf.Acos(Vector3.Dot(dir, Vector3.up));
// Use the cross product to determine if the angle is clockwise
// or anticlockwise
Vector3 cross = Vector3.Cross(dir, Vector3.up);
angle = -Mathf.Sign(cross.z) * angle;
// Update the rotation of your arrow
rt.localEulerAngles = new Vector3(rt.localEulerAngles.x, rt.localEulerAngles.y, angle);
}
For the above code, I suppose that:
You are only using one main camera, you may need to change this
Your arrow is on the Canvas, by default pointing upwards (when its rotation is (0, 0, 0))
You are using a Canvas in Render Mode: Screen Space - Overlay. The above code would be different if the Canvas were in World Space.
As a high-level overview:
Find direction from UI/view-plane centre to 3D object
Project direction onto UI/view-plane (using forward as normal vector), and normalize
Point 2D arrow toward projected direction
Thank You all Guys, I got an Answer for two Situations :
First One: When the two objects are in 3D
public GameObject flesh;
public GameObject target;
// Start is called before the first frame update
void Start()
{
flesh.transform.position = Camera.main.ScreenToWorldPoint(new Vector3( Screen.width/2, Screen.height/2,1));
}
// Update is called once per frame
void Update()
{
var dir = Camera.main.WorldToScreenPoint(target.transform.position) -
Camera.main.WorldToScreenPoint(flesh.transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
flesh.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Second: When the Flesh is an Image that Has RectTransform,
, this solution Inspirational from #kevernicus
public GameObject Target;
RectTransform rt;
void Start()
{
rt = GetComponent<RectTransform>();
}
void Update()
{
// Get the position of the object in screen space
Vector3 objScreenPos = Camera.main.WorldToScreenPoint(Target.transform.position);
// Get the directional vector between your arrow and the object
Vector3 dir = (objScreenPos - rt.position).normalized;
float angle = Mathf.Rad2Deg * Mathf.Atan2(dir.y, dir.x);
rt.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
Use Unity's built in Transform.LookAt function

Gameobjects position change with resolution

I have a scene with multiple GameObjects with x-y-z position. I'm in 2D so the z is not used.
For now, I worked with the resolution 1024/768 and when I add a GameObject like :
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Vector3 position = new Vector3 (x, y, z);
star.transform.localPosition = position;
It's ok, the GameObject is in the right position on my scene.
But nox, I'm trying to change the resolution (1920/1080) and all my GameObjects are moved far on the top right, out of my camera.
What's the problem ? How can I fix this ?
For me, when I change the resolution, the 0-0-0 change for my GameObjects....and something weird, If my GameObject has a LineRenderer with positions, they are good on all resolutions...
Edit : This is the function I use for generate my GameObjects, it's a galaxy generator :
for(int i = 0; i < numberOfStars; i++) {
bool checkPosition = false;
while (!checkPosition) {
// Choose a distance from the center of the galaxy.
float distance = Random.Range(5.0f, (float) galaxySize / 2 - 5);
// Choose an angle between 0 and 2 * PI.
float angle = Random.Range(0.0f, 100.0f) * 2 * Mathf.PI;
Vector3 position = new Vector3 (Mathf.Cos (angle) * distance, Mathf.Sin(angle) * distance, 9);
if (! Physics.CheckSphere(position, 1)) {
GameObject star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
star.AddComponent<SolarSystem>();
star.name = i + "_" + PlanetNameGenerator.GenerateName();
star.GetComponent<SolarSystem>().name = i + "_" + PlanetNameGenerator.GenerateName();
star.transform.parent = GameObject.Find ("Targets").transform;
// On change le scale en random
float randomScale = Random.Range (0f, 0.5f);
star.transform.localScale += new Vector3(randomScale,randomScale,randomScale);
star.transform.localPosition = position;
star.tag = "SolarSystem";
checkPosition = true;
}
}
}
This is the scene I see in 1024/768, with my Sphere GameObjects and in pink LineRenderer between them :
This is what I see in 1920/1080, LineRenderer are always in the same position, but GameObjects move away :
The problem was this line :
star.transform.parent = GameObject.Find ("Targets").transform;
I moved the parent out from Canvas, chnage x-y-z positions of "Targets" to 0 et it's ok.

How can I draw a circle in Unity3D?

How to draw circle in Unity 3d?
I want to draw a circle around different objects.
The radiuses of the circles are different and the circles have textures - squares.
I found a big error with this code. The number of points (Size) shouldn't be "(2 * pi / theta_scale) + 1" because this causes the circle to draw 6.28 times. The size should be "1 / theta_scale + 1". So for a theta_scale of 0.01 it needs to draw 100 points, and for a theta_scale of 0.1 it needs to draw 10 points. Otherwise it would draw 62 times and 628 times respectively.
Here is the code I used.
using UnityEngine;
using System.Collections;
public class DrawRadar: MonoBehaviour {
public float ThetaScale = 0.01f;
public float radius = 3f;
private int Size;
private LineRenderer LineDrawer;
private float Theta = 0f;
void Start() {
LineDrawer = GetComponent<LineRenderer>();
}
void Update() {
Theta = 0f;
Size = (int)((1f / ThetaScale) + 1f);
LineDrawer.SetVertexCount(Size);
for (int i = 0; i < Size; i++) {
Theta += (2.0f * Mathf.PI * ThetaScale);
float x = radius * Mathf.Cos(Theta);
float y = radius * Mathf.Sin(Theta);
LineDrawer.SetPosition(i, new Vector3(x, y, 0));
}
}
}
If you modify the number in "Size" that is divided by ThetaScale, you can make a sweeping gauge/pie chart type graphic.
See Unity Answers for a similar question.
Alternatively:
float theta_scale = 0.1; // Circle resolution
LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
lineRenderer.SetColors(c1, c2);
lineRenderer.SetWidth(0.2F, 0.2F);
lineRenderer.SetVertexCount(size);
int i = 0;
for(float theta = 0; theta < 2 * PI; theta += theta_scale) {
x = r*cos(theta);
y = r*sin(theta);
Vector3 pos = new Vector3(x, y, 0);
lineRenderer.SetPosition(i, pos);
i+=1;
}
The LineRenderer requires continuous points. You can modify this code slightly to use cylinder game objects instead of a line renderer. I find the LineRenderer to be a bit hideous.
Lastly, similar to the first link, you could attach a circle texture to a unit plane. Make any part of the texture that isn't part of the circle transparent. Then just scale and align the plane to fit your object. Unfortunately this method isn't great if someone is looking almost parallel to the plane.
Jerdak's solution is good, but the code is messy so I had to tweak a little. Here's the code for a class, where I use i in the loop to avoid a bug.
It also updates the circle's position with its gameObject position.
using UnityEngine;
using System.Collections;
public class CircleDraw : MonoBehaviour {
float theta_scale = 0.01f; //Set lower to add more points
int size; //Total number of points in circle
float radius = 3f;
LineRenderer lineRenderer;
void Awake () {
float sizeValue = (2.0f * Mathf.PI) / theta_scale;
size = (int)sizeValue;
size++;
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
lineRenderer.SetWidth(0.02f, 0.02f); //thickness of line
lineRenderer.SetVertexCount(size);
}
void Update () {
Vector3 pos;
float theta = 0f;
for(int i = 0; i < size; i++){
theta += (2.0f * Mathf.PI * theta_scale);
float x = radius * Mathf.Cos(theta);
float y = radius * Mathf.Sin(theta);
x += gameObject.transform.position.x;
y += gameObject.transform.position.y;
pos = new Vector3(x, y, 0);
lineRenderer.SetPosition(i, pos);
}
}
}
Using Shader Graph we can now draw pixel perfect circle.
Once you created this graph, create a new material based on this shader.
Then create a new gameobject with a sprite renderer and set the material you just created.
You can scale the circle using the "scale" parameter of the material.
The linerenderer method in the top answers is really simple and exactly what I was looking for. I updated it for newer versions of Unity and some small tweaks to make it a bit more beginner/user friendly.
Specifically:
LineRenderer.SetVertexCount() is deprecated in newer versions of Unity, replaced with positionCount
Replaced theta scale with an actual segment count to remove guesswork
Added loop setting - not sure if this was in older versions of Unity, it can be set in the LineRenderer's inspector
Removed unnecessary Update function - the rendered line is a persistent gameobject
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class DrawRing : MonoBehaviour
{
public LineRenderer lineRenderer;
[Range(6,60)] //creates a slider - more than 60 is hard to notice
public int lineCount; //more lines = smoother ring
public float radius;
public float width;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.loop = true;
Draw();
}
void Draw() //Only need to draw when something changes
{
lineRenderer.positionCount = lineCount;
lineRenderer.startWidth = width;
float theta = (2f * Mathf.PI) / lineCount; //find radians per segment
float angle = 0;
for (int i = 0; i < lineCount; i++)
{
float x = radius * Mathf.Cos(angle);
float y = radius * Mathf.Sin(angle);
lineRenderer.SetPosition(i, new Vector3(x, 0, y));
//switch 0 and y for 2D games
angle += theta;
}
}
}
Note this is assumed to be attached to the gameobject you want the ring around. So the Use World Space option in LineRenderer should be unchecked. Also remember that the scale of the gameobject will affect the position of the points and the width of the line.
To put this on the ground (as in a unit selection circle):
Put the script on a separate gameobject
Rotate the gameobject X to 90
Check use world space on the linerenderer
Set the linerenderer Alignment to Transform Z
Add the position of the thing you want to circle to x and y in SetPosition. Possibly along with replacing 0 with 0.1f or a yOffset variable to avoid z-fighting with terrain.
Circle can draw using shader - draw pixel if it on radius from center.
Did the following with a Sprite. Chan is flying in the scene, so she's slightly above the plane. I had her flying so I could get a good screenshot, not because it wouldn't play well with the plane.
I used a low-resolution circle sprite.
X rotation 90
Scale X 15, Y 15, Z 1
Then I set the Sorting Layer, so it will render above the Default Layer. I was testing this out when I came across this post. It doesn't handle shadows well. I'd have to figure out what layer shadows are drawn on to make sure they get rendered onto the sprite.
I have a shader from which I usually start making effects like lens flares, and it makes a circle. Using shader is the best choice because you will get perfectly smooth and round circle.
Also it's easy to experiment with and tune the shader since shader changes don't require recompile and re-entering of play mode.
I recommend ti create extension method to GameObject. Worked good to me.
public static class GameObjectExtension
{
const int numberOfSegments = 360;
public static void DrawCircle(this GameObject go, float radius,
float lineWidth, Color startColor, Color endColor, bool lineRendererExists=true)
{
LineRenderer circle = lineRendererExists ? go.GetComponent<LineRenderer>() : go.AddComponent<LineRenderer>();
circle.useWorldSpace = false;
circle.startWidth = lineWidth;
circle.endWidth = lineWidth;
circle.endColor = endColor;
circle.startColor = startColor;
circle.positionCount = numberOfSegments + 1;
Vector3 [] points = new Vector3[numberOfSegments + 1];
for (int i = 0; i < numberOfSegments + 1; i++)
{
float rad = Mathf.Deg2Rad * i;
points[i] = new Vector3(Mathf.Sin(rad) * radius, 0, Mathf.Cos(rad) * radius);
}
circle.SetPositions(points);
}
}
One More thing to note: If LineRenderer component is not applied last parameter has to be false
create a static class to reuse the code for different game objects. player, enemies... when the class is static, you cannot create the instance of it
public static class CircleGameObject
{
// in static class methods have to be static as well
// "this" refers to the context that we are calling DrawCircle
public static async void DrawCircle(this GameObject container,float radius,float lineWidth)
{
// I provide 360 points because circle is 360 degrees and we will connect them with line
var segments=360;
// LineRenderer is used to draw line
var lineRenderer=container.AddComponent<LineRenderer>();
// now you can use position system relative to the parent game object.
lineRenderer.useWorldSpace=false;
lineRenderer.startWidth=lineWidth;
lineRenderer.endWidth=lineWidth;
lineRenderer.positionCount=segments+1;
// reserve empty array in memory with a size of lineRenderer.positionCount
var points=new Vector3[lineRenderer.positionCount];
// draw all of those points
for(int i=0;i<points.Length;i++)
{
// converting degree to radian because Mathf.Cos and Mathf.Sin expects radian
var radian=Mathf.Deg2Rad*i;
// y direction needs to be 0
// Mathf.Cos(radiant) will give the x position on the circle if the angle size is "radian"
// Mathf.Sin(radiant) will give the y position on the circle if the angle size is "radian"
// after for loop completes we would be getting 360 points
points[i]=new Vector3(Mathf.Cos(radian)*radius,0,Mathf.Sin(radian)*radius);
}
lineRenderer.SetPositions(points);
}
}
then call it in Awake of the context
public class PlayerController : MonoBehaviour
{
private void Awake()
{
GameObject go=new GameObject{
name="Circle"
};
Vector3 circlePosition=Vector3.zero;
go.transform.parent=transform;
// localPosition is relative to the parent
go.transform.localPosition=circlePosition;
go.DrawCircle(2.0f,0.03f);
....
}
}