Taking average of multiple Vector3's - c#

So I'm working with a depth-camera that works like kinect. (it's not kinect). and with Nuitrack behind it for skeleton tracking. However, The position it returns for the player is shaky. When standing perfectly still it returns numbers that can go up or down by up to like 10.
Example: User is standing as still as he can and the data returns position 100 the first frame, and the next frame it's 102, then it's 97 then its 100 again, then it's 106 etc. It returns these positions in the update and we use it to move a image with it. (so the user controls the image) But as you might expect this image is moving very shaky because of the inconsistent data. According to Nuitrack this is right, and the user itself needs to find a solution for this.
I tried lerping from one position to another, but this makes it feel less interactive, because once i'm on the point where the lerp is actually smooth, it has a huge delay. I also tried only using the new position data if it differs lets say 4 pixels from the previous position nuitrack gave me, this works a bit better but results in jumping of the image, even if I lerp it as well. Using this function:
foreach (User user in frame.Users)
{
if (Vector3.Distance(_lastPos, user.Proj.ToVector3()) >4f)
{
Vector3 final = ((_lastPos + user.Proj.ToVector3()) /2);
userData.Add(new UserData(user.ID, user.Real.ToVector3(), final));
_lastPos = user.Proj.ToVector3();
}
else
{
userData.Add(new UserData(user.ID, user.Real.ToVector3(), _lastPos));
}
And the lerp function:
float _userX = user.ProjPosition.x * (_cameraPos.x *2)- _cameraPos.x;
Vector3 _newPos = new Vector3(_userX, _basketPos.y, _basketPos.z);
_basketPrefab.transform.position = Vector3.Lerp(_basketPrefab.transform.position, _newPos, Time.deltaTime * 30f);
EDIT: Anyone else?

You can try keeping a list of the last n positions then calculate the average of these positions by lerping them together using 0.5f (halfway value) as t. You can then increase the level of "smoothness" by Lerping the previously lerped positions again with each other, making it more smooth with every iteration. Every iteration will however make it feel a bit more sluggish, and a balance needs to be found between smooth and reactive.
(example untested).
List<Quaternion> lastPosition = new List<Quaternion>(); //Keep a list of the last positions
int smoothing = 16; //Max amount of smoothing, higher means more positions will be used for the smoothness
enum SmoothingLevels { None, Mild, Moderate, Severe, Extreme } //Level of smoothness you want to use
SmoothingLevels smoothingLevel;
Vector3 pos;
//remove the oldest entry from the list
if(lastPosition.Count > 0)
{
lastPosition.RemoveAt(0);
}
//Add the newest data to the list
while (lastPosition.Count < smoothing)
{
lastPosition.Add(transform.position);
}
Vector3 vecA = lastPosition[0];
Vector3 vecB = lastPosition[1];
Vector3 vecC = lastPosition[2];
Vector3 vecD = lastPosition[3];
Vector3 vecE = lastPosition[4];
Vector3 vecF = lastPosition[5];
Vector3 vecG = lastPosition[6];
Vector3 vecH = lastPosition[7];
Vector3 vecI = lastPosition[8];
Vector3 vecJ = lastPosition[9];
Vector3 vecK = lastPosition[10];
Vector3 vecL = lastPosition[11];
Vector3 vecM = lastPosition[12];
Vector3 vecN = lastPosition[13];
Vector3 vecO = lastPosition[14];
Vector3 vecP = lastPosition[15];
//Lerp each subsequent position by t 0.5 to get the average position of the two.
//This is the base smoothing, where smoothing is low and responsiveness is high
Vector3 vecAB = Vector3.Lerp(vecA, vecB, 0.5f);
Vector3 vecCD = Vector3.Lerp(vecC, vecD, 0.5f);
Vector3 vecEF = Vector3.Lerp(vecE, vecF, 0.5f);
Vector3 vecGH = Vector3.Lerp(vecG, vecH, 0.5f);
Vector3 vecIJ = Vector3.Lerp(vecI, vecJ, 0.5f);
Vector3 vecKL = Vector3.Lerp(vecK, vecL, 0.5f);
Vector3 vecMN = Vector3.Lerp(vecM, vecN, 0.5f);
Vector3 vecOP = Vector3.Lerp(vecO, vecP, 0.5f);
//moderate smoothing, Lerp the previously lerped position again with each other to increase the smoothness
Vector3 vecABCD = Vector3.Lerp(vecAB, vecCD, 0.5f);
Vector3 vecEFGH = Vector3.Lerp(vecEF, vecGH, 0.5f);
Vector3 vecIJKL = Vector3.Lerp(vecIJ, vecKL, 0.5f);
Vector3 vecMNOP = Vector3.Lerp(vecMN, vecOP, 0.5f);
//Severe smoothing, High smoothness, lower responsiveness
Vector3 vecABCDEFGH = Vector3.Lerp(vecABCD, vecEFGH, 0.5f);
Vector3 vecIJKLMNOP = Vector3.Lerp(vecIJKL, vecMNOP, 0.5f);
//Extreme smoothing, this will take the average of all 16 positions. Very smooth, feels really sluggish
Vector3 vecABCDEFGHIJKLMNOP = Vector3.Lerp(vecABCDEFGH, vecIJKLMNOP, 0.5f);
switch (smoothingLevel)
{
case SmoothingLevels.None:
pos = transform.position;
break;
case SmoothingLevels.Mild:
pos = vecOP;
break;
case SmoothingLevels.Moderate:
pos = vecMNOP;
break;
case SmoothingLevels.Severe:
pos = vecIJKLMNOP;
break;
case SmoothingLevels.Extreme:
pos = vecABCDEFGHIJKLMNOP;
break;
}
//apply pos to your target object

Related

moving a 2d object from point a to b without changing its rotation

I'm trying to move a 2d object from point a to b without changing its rotation in unity
I've tried to use Vector2.Lerp() but its not working
Vector2 pointB = new Vector2(20, 10);
Vector2.Lerp(transform.position, pointB, 3F);
The code should move the object from point a to b in 3F seconds
First, Vector2.Lerp doesn't change the value of the first parameter. You'll want to assign the new value to transform.position if you want to change the transform's position that way.
Secondly, you need to update the transform's position once every frame to keep the transform moving smoothly.
Thirdly, Vector2.Lerp will only produce positions between start and end with a t between 0 and 1. This t should relate to the ratio of how much time has passed since this movement started over how much time will complete the movement.
This is a good use for a coroutine:
private IEnumerator GoToInSeconds(Vector2 pointB, float movementDuration)
{
Vector2 pointA = transform.position;
float timeElapsed = 0f;
while (timeElapsed < movementDuration)
{
yield return null;
timeElapsed += Time.deltaTime;
transform.position = Vector2.Lerp(pointA, pointB, timeElapsed/movementDuration);
}
}
Here's an example of how to use it in Start:
void Start()
{
Vector2 pointB = new Vector2(20, 10);
StartCoroutine(GoToInSeconds(pointB, 3f));
}

Rotate towards the mouse

I want to rotate a cube towards the mouse position (in 2d) in unity (c#). I have this code, but i doesn't work:
Vector3 a = GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b = GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 c = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x);
float aTob = Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position);
float aToc = Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x));
float bToc = Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x));
float winkel = Mathf.Acos(((Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position)) + (Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x))) - (Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x)))) / (2 * Vector3.Distance(GameObject.FindGameObjectWithTag("GunTop").transform.position, GameObject.FindGameObjectWithTag("GunBottom").transform.position) * Vector3.Distance(GameObject.FindGameObjectWithTag("GunBottom").transform.position, new Vector3(Input.mousePosition.x, Input.mousePosition.y, Input.mousePosition.x))));
private void Update()
{
transform.Rotate(0, 0, winkel);
}
GunTop and GunBottom are the highest an lowest points in the middle of the cube, to create a triangle with the mouse.Does someone see the problem?
Edit1:
Okay, good news. Now I have to ways to solve the Problem. First one like memBrain sad(i changed it a bit):
` private void Update()
{
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle - 90);
}`
I had to change it a bit in order to work with unity. The second solution, i could optimize my code, so it works now:
` float winkel;
float echterWinkel;
float aTob;
float aToc;
float bToc;
private void Update()
{
Vector3 a =
GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b =
GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
aTob = Vector3.Distance(a, b);
aToc = Vector3.Distance(a, dir);
bToc = Vector3.Distance(b, dir);
winkel = Mathf.Acos(((aTob * aTob) + (bToc * bToc) - (aToc * aToc)) / (2
* aTob * bToc));
echterWinkel = Mathf.Rad2Deg * winkel;
if (dir.x < 0)
{
transform.rotation = Quaternion.Euler(0, 0, echterWinkel);
}
else
{
transform.rotation = Quaternion.Euler(0, 0, -echterWinkel);
}
}
}`
Okay, so far so good. But I have to questions now. Obviously the first solution is the better one, so I want to know how it works, what does "Mathf.Atan2()"?
And now the seconds question: Why do I have to do this
`Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;`
in order to get the real mouesposition?
Your code seems needlessly complicated to me. Is there a particular reason you need all those vectors? Nevertheless, the following code is an example from the Unity forum (http://answers.unity3d.com/questions/731922/rotate-object-to-face-mouse-2d-rotate-on-z-axis.html) that you can use to rotate an object around the z-axis.
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
Vector3 dir = Input.mousePosition - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
It's really as simple as that.
Edit: Keep in mind that the transform will be the transform of your target GameObject.
On a side note, the non-functioning code in your example is rather inefficient. You have declared vectors that you don't use, and floats you don't use. So you can replace a lot of your code using these stored values instead.
Vector3 a = GameObject.FindGameObjectWithTag("GunTop").transform.position;
Vector3 b = GameObject.FindGameObjectWithTag("GunBottom").transform.position;
Vector3 c = new Vector3 (
Input.mousePosition.x,
Input.mousePosition.y,
Input.mousePosition.x
);
float aTob = Vector3.Distance(a, b);
float aToc = Vector3.Distance(a, c);
float bToc = Vector3.Distance(b, c);
float winkel = Mathf.Acos (
(
(
aTob * aTob
) +
(
bToc * bToc
) -
(
aToc * aToc
)
) /
(
2 * aTob * bToc
)
);
private void Update()
{
transform.Rotate(0, 0, winkel);
}
Note how much simpler the code now looks after properly using your stored values. I also took the liberty of using the parenthesis like curly braces to make long lines of code more readable. You need to keep one thing in mind. Lookups are potentially expensive. Never do a lookup more than once if you don't have to.
I haven't bothered checking to see if the code is correct. I can't even verify that your lookups will return a value. In fact, that is potentially going to be a problem for this code. If either lookup fails, you will have null values stored in either a or b or both! You need some kind of conditional check to verify that a and b had successful lookups, and what to do if they didn't. As it now stands, you are going to have a failure the moment you try to perform an operation on either a or b the first time they are used. The objective is to fail gracefully, not forcefully.
Edit 2: Looking over everything again, I noticed one other problem. Your intention is to rotate around the z-axis, and you are setting the z-coordinate to Input.mousePosition.x. This is wrong. You should set it to a fixed value, like '0'. You don't want z tied to x.
Edit 3: Here is the information provided by the Unity Scripting API for Mathf.Atan2(...).
Yes. those two lines are related to obtaining the mouse position. Let me explain what is happening here.
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
This line converts the world coordinate (transform.position) of your "gun" object to a screen point coordinate and stores it in pos.
Vector3 dir = Input.mousePosition - pos;
This line takes the mousePosition vector and subtracts from it the position vector (pos) from before, and stores it in dir (presumably for "direction"). You could just as easily change your Vector3 c as follows:
Vector3 c = Input.mousePosition;
This is equivalent to what you did without needing to create the new Vector3 and assigning it the vector coordinates explicitly as you did.
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
This line replaces winkel. Consequently, your entire code can be condensed as follows:
GameObject gun;
void Start()
{
/*
initialization code for gun GameObject
*/
}
void Update()
{
Vector3 gunScreenPosition = Camera.main.WorldToScreenPoint(gun.transform.position);
Vector3 mouseDirection = Input.mousePosition - gunScreenPosition;
float angle = Mathf.Atan2(mouseDirection.y, mouseDirection.x) * Mathf.Rad2Deg;
gun.transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
You really don't need all the extra code you were using. Is there a particular reason you needed GunTop and GunBottom? If it's just to calculate the position for the gun, my example is all that you need.
I'd be remiss if I didn't share one more thing. Be careful when using Quaternion.Euler(...). It can suffer from a condition known as "gimbal lock"

Mathf.clamp() function in Unity

I have a Cube(Player) in my game scene. I have written a C# script to constrain the movement(using Mathf.Clamp()) of the Cube so it does not leave the screen.
Below is my FixedUpdate() method from the Script
private void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.velocity = movement * speed;
rb.position = new Vector3(
Mathf.Clamp (rb.position.x, x_min, x_max),
0.5f,
Mathf.Clamp(rb.position.z, z_min, z_max)
);
}
Values of x_min, x_max, z_min, z_max are -3, 3, -2, 8 respectively inputted via the unity inspector.
PROBLEM
The script is working fine but my player(Cube) can move up to -3.1 units in negative X-AXIS (if I keep pressing the left arrow button) which is 0.1 units more in negative X-AXIS(This behavior is true for other axes too). It obviously clamps -3.1 to -3 when I stop pressing the button.
Why is this happening? Why doesn't it(Mathf.Clamp()) restrict the Cube to -3 units in first place?
Why am I getting that extra 0.1 units?
And Why is this value 0.1 units only? Why not more or less?
Your issue could be caused because you are setting velocity and then position, but before the next frame unity adds the velocity to your objects position. Which is why it ends up 0.1 units off.
To fix this, try resetting the velocity of the object to zero if it's about to leave your boundaries.
This is happening because the internal physics update moves the cube after you have put it in its clamped position. Mathf.Clamp() does restrict the cube to the expected position in the first place, as you can verify by putting a Debug.Log directly after you assign the position
You are getting the extra .1 units because you give the object a speed that gets applied after you clamp
It is 0.1 units because of the speed and the setting for Fixed Timestep (in Project Settings > Time). If you would set a higher speed it would be more. If you would lower the Fixed Timestep it would also be a bit more.
You already got the reason why this happens.
In order to fix the problem, you can use this code to prevent the cube to go outside your boundaries:
private void FixedUpdate() {
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
// Get new position in advance
float xPos = rb.position.x + movement.x * speed * Time.fixedDeltaTime;
float zPos = rb.position.z + movement.z * speed * Time.fixedDeltaTime;
// Check if new position goes over boundaries and if true clamp it
if (xPos > x_max || xPos < x_min) {
if (xPos > x_max)
rb.position = new Vector3(x_max, 0, rb.position.z);
else
rb.position = new Vector3(x_min, 0, rb.position.z);
movement.x = 0;
}
if (zPos > z_max || zPos < z_min) {
if (zPos > z_max)
rb.position = new Vector3(rb.position.x, 0, z_max);
else
rb.position = new Vector3(rb.position.x, 0, z_min);
movement.z = 0;
}
rb.velocity = movement * speed;
}

Instantiate Object on Raycast2D hit and rotate Instance

I want to move an instance of a gameObject along the outline of another gameobject. Its a 2D Project in Unity.
My current Code:
Vector3 mousePosition = m_camera.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(new Vector2(mousePosition.x, mousePosition.y), new Vector2(player.transform.position.x, player.transform.position.y));
if (hit.collider != null && hit.collider.gameObject.tag == "Player") {
if (!pointerInstance) {
pointerInstance = Instantiate(ghostPointer, new Vector3(hit.point.x, hit.point.y, -1.1f), Quaternion.identity);
} else if(pointerInstance) {
pointerInstance.gameObject.transform.position = new Vector3(hit.point.x, hit.point.y, -1.1f);
pointerInstance.gameObject.transform.eulerAngles = new Vector3(0f, 0f, hit.normal.x);
}
}
Unfortunately, the gameObject doesn't rotate towards the mouse and the position on the left side of the playerObject is also sometimes off. I tried to use Instantiate() with Quaternion.LookRotation(hit.normal), but no luck either.
Here a rough sketch of what I want to achieve:
Any help is appreciated. Thanks!
it's better to use Mathematical way instead of physical way(Raycasting),because in raycasting you have to throw ray several time for checking hit point and rotate your object,it makes lag in your game.
Attach this script to your instantiated object:
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
public Transform Player;
void Update()
{
//Rotating Around Circle(circular movement depend on mouse position)
Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Player.position);
targetScreenPos.z = 0;//filtering target axis
Vector3 targetToMouseDir = Input.mousePosition - targetScreenPos;
Vector3 targetToMe = transform.position - Player.position;
targetToMe.z = 0;//filtering targetToMe axis
Vector3 newTargetToMe = Vector3.RotateTowards(targetToMe, targetToMouseDir, /* max radians to turn this frame */ 2, 0);
transform.position = Player.position + /*distance from target center to stay at*/newTargetToMe.normalized;
//Look At Mouse position
var objectPos = Camera.main.WorldToScreenPoint(transform.position);
var dir = Input.mousePosition - objectPos;
transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg);
}
}
Useful explanations
Atan2:
atan2(y,x) gives you the angle between the x-axis and the vector (x,y), usually in radians and signed such that for positive y you get an angle between 0 and π, and for negative y the result is between −π and 0.
https://math.stackexchange.com/questions/67026/how-to-use-atan2
Returns the angle in radians whose Tan is y/x.
Return value is the angle between the x-axis and a 2D vector starting at zero and terminating at (x,y).
https://docs.unity3d.com/ScriptReference/Mathf.Atan2.html
Mathf.Rad2Deg:
Radians-to-degrees conversion constant (Read Only).
This is equal to 360 / (PI * 2).

Imposing a limit on translation movement

I have a zoom function on my camera that works by moving it on the Z axis when I pinch my fingers on the screen. However, when the camera moves into any value greater than zero, it has adverse effects on the rest of my camera code (movement, orbiting).
I put in a place a bool that would stop my camera moving once it got to a certain value, but it makes it very jumpy. And if your pinching, the camera will still move past the value until you let go.
So what I'm trying to do now, is use Mathf.Clamp to limit the range it can move, but I'm unsure if I'm using it correctly. This is my method right now:
void CameraZoom()
{
// if fingers moved certain distance apart, zoom
float dot = Vector2.Dot(Input.GetTouch(0).deltaPosition.normalized, Input.GetTouch(1).deltaPosition.normalized);
if (dot < fingerDistance)
{
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// apply zoom to camera
transform.Translate(0, 0, -deltaMagnitudeDiff * perspectiveZoomSpeed);
// clamp movement
transform.position = new Vector3(transform.position.x, transform.position.y, Mathf.Clamp(0, 0, maxDistance));
}
}
}
I only want this to affect the Z axis, but when I zoom, my whole rig gets moved. What is it that I'm doing wrong?
Update
Ive changed my code to the following, but now when I zoom, it just jumps between two points and I can no longer zoom.
New code:
transform.Translate(0, 0, -deltaMagnitudeDiff * perspectiveZoomSpeed);
Vector3 pos = transform.position;
pos.z = Mathf.Clamp(transform.position.z, 0.0f, -200.0f);
transform.position = pos;
Check the documentation for the Mathf.Clamp. You are using the parameters in the wrong order, which breaks the internal implementation of the function. The min value should be the second and the max value should be the third. So changing your line to this should stop the odd behavior:
pos.z = Mathf.Clamp(transform.position.z, -200.0f, 0.0f);

Categories

Resources