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"
Related
I'm reviewing this piece of code about camera movement:
using UnityEngine;
using System.Collections;
public class CamMove : MonoBehaviour {
Vector2 mouseLook;
Vector2 smoothV;
public float sensitivity=5.0f;
public float smoothing = 2.0f;
GameObject character;
void Start()
{
//moving left and right
character = this.transform.parent.gameObject;
}
void Update()
{
var md = new Vector2 (Input.GetAxisRaw ("Mouse X"), Input.GetAxisRaw ("Mouse Y")); //mouse movement
md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
smoothV.x = Mathf.Lerp (smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp (smoothV.y, md.y, 1f / smoothing);
mouseLook += smoothV;
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);
character.GetComponent<CharacterController>().transform.localRotation = Quaternion.AngleAxis (mouseLook.x, character.transform.up);
}
}
how does he get every new locatiion ? With Math.Lerp interpolation ? Also I can't understand the part md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
Also the part :
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);
Well
var md = new Vector2 (Input.GetAxisRaw ("Mouse X"), Input.GetAxisRaw ("Mouse Y")); //mouse movement
is even commented mouse movement. As usual a better variable name would already explain it. It would be better called mouseDelta. So the code not using a fixed mouse position but rather the traveled dinstance since last frame. (See Input.GetRawAxis)
Then
md = Vector2.Scale (md, new Vector2 (sensitivity * smoothing, sensitivity * smoothing));
Vector2.Scale is scaling up or down this Vector. You could also write it as
md = new Vector2 (md.x * sensitivity * smoothing, md.y * sensitivity * smoothing);
Actually it is quite unnecessary because you could write it way simplier like this:
md *= sensitivity * smoothing;
Then Mathf.Lerp is a linear interpolation between the two given positions using a certain factor between 0 and 1, where 0 would be fully the first parameter, 1 fully the second, otherwise anything in between. E.g. a factor 0.5 would result in the center between both values so this depends on your given value for smoothing
smoothV.x = Mathf.Lerp (smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp (smoothV.y, md.y, 1f / smoothing);
Again this is written quite unnecessarily because it is better written directly using Vector2.Lerp
smoothV = Vector2.Lerp(smoothV, md, 1f/smoothing);
But actually I'm not quite sure this even does what you would expect since this is no absolute value but something you later on add every frame so using a Lerp on it makes not much sense to me anyway... I would probably directly use Lerp for moving the current mouseLoock towards the new target value.
Finally you do
mouseLook += smoothV;
if(mouseLook.y>-40 && mouseLook.y<60)
transform.localRotation = Quaternion.AngleAxis (-mouseLook.y, Vector3.right);
character.GetComponent<CharacterController>().transform.localRotation = Quaternion.AngleAxis (mouseLook.x, character.transform.up);
Which updates two rotations. There is nothing in your code assigning any new position at all ...
Besides that the frame wise usage of GetComponent is extremely inefficient, you should rather store that reference once (e.g. in Start) and reuse it later.
I would like to limit my vertical rotation of the camera so that it can't do a 360 spin. I have tried a lot of tutorials but nothing worked for me so i .
Please also check my code.
[RequireComponent(typeof(PlayerMoto))]
public class PlayerController: MonoBehaviour {
public float speed = 5, sensetivity;
private PlayerMoto motor;
public GameObject hands;
public camera cam;
float lookUpMax = .6 f;
void Start() {
motor = GetComponent < PlayerMoto > ();
cam = GetComponent < camera > ();
}
void Update() {
//cam.transform.localEulerAngles = new Vector3(cam.transform.localEulerAngles.x, 0, 0);
float _xmove = Input.GetAxis("Horizontal");
float _zmove = Input.GetAxis("Vertical");
Vector3 _moveHorizontal = transform.right * _xmove;
Vector3 _movVertical = transform.forward * _zmove;
Vector3 _velocity = (_moveHorizontal + _movVertical) * speed;
motor.Move(_velocity);
float _yRot = Input.GetAxis("Mouse X");
Vector3 _rotation = new Vector3(0 f, _yRot, 0 f) * sensetivity;
motor.Rotate(_rotation);
float _xRot = Input.GetAxis("Mouse Y");
Vector3 _cameraRotation = new Vector3(_xRot, 0 f, 0 f) * sensetivity;
motor.RotateCamera(_cameraRotation);
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.W)) {
for (; speed <= 15; speed++) {
speed = 15;
}
} else {
speed = 10;
}
}
}
Thank you very much for your kind help. I really appreciate every single comment to try and help me in this amazing journey.
Try this out
private void Rotation()
{
x_axis += speed * Input.GetAxis("Mouse X"); // speed = 2f;
y_axis -= speed * Input.GetAxis("Mouse Y");
y_axis = Mathf.Clamp (y_axis, -45, 45); // limits vertical rotation
transform.eulerAngles = new Vector3(y_axis, x_axis, 0.0f);
}
If I understood your code, the part that moves the camera is:
float _xRot = Input.GetAxis("Mouse Y");
Vector3 _cameraRotation = new Vector3(_xRot, 0f, 0f) * sensetivity;
Then, your code calls this method on another class
motor.RotateCamera(_cameraRotation);
which probably (since the previous suggestion did not work) applies the rotation through
GameObject.Rotate(_cameraRotation);
In order to limit your camera’s rotation, we need to directly apply the rotation, and it cannot be clamped if the rotation is applied by adding a rotation to the existing rotation.
So, let’s assume you can skip that call and directly apply the rotation (I don’t know if there will be side effects), and let’s assume your cam variable is your camera.
If all these assumptions are correct, you can try:
float _xRot += Input.GetAxis("Mouse Y") * sensetivity;
xRot = Mathf.Clamp(_xRot, xMin, xMax);
cam.transform.rotation = Quaternion.Euler(_xRot, 0.0f, 0.0f);
Remember to comment out
//motor.RotateCamera(_cameraRotation);
You can declare
public float xMin;
public float xMax;
And experiment with values until you find your optimal ones.
I strongly suspect that the code I suggested will not solve all your problems, or it could add issues, because the actual transformations of both your player and the camera are applied by another script, that is not provided. In that case, you can provide us also that code, but I suggest you try writing your own code, that you can customize to your needs.
As for why clamping a rotation is not as easy as it seems, this post is interesting:
Rotations, Quaternions and Euler Angles
Problem:
If I get the movement to work correctly, then the collision meshes are not detected. If I get the collision meshes detected, then the movement doesn't work correctly.
Brief summary of project:
I have a 3D environment with non-moveable objects (with collider meshes) and a moveable gameobject (rigid body with x2 box colliders) that I am controlling using haptic devices (basically a 3D joystick) through a UDP connection in an C++ app that I have put together and is running while the Unity application runs. The communication between the haptic devices and unity is perfectly fine. I am using the position information passed from the haptic device as my variables for moving my gameobject. Again, The position data arrives to Unity just fine; the method for using the position data with appropriate conditions and functions within Unity is where I am currently stuck.
Things I've tried:
If I use transform.localPosition (hapticDevicePosition); then the movement is great, but it ignores the colliders and passes through everything. I read online and understand that transform.localPosition will basically move my object on top of other objects without regards to physics. I also read that I may be able to introduce a ray that is like 0.000001 in front of my object such that it prevents movement if the ray interacts with any other object. This might be a way to still be able to use transform.localPosition? I'm not sure and I've never used rays so it would be difficult for me to set that script up correctly.
I've tried AddForce. This behaves very oddly. It only gives me 2 force outputs instead of 3...i.e., I can only move in 2 of the 3 axis. I don't understand why its behaving this way. The colliders are detected, however.
I've tried rb.MovePosition (rb.position + posX + posY + posZ) and various combinations of *Time.timeDelay and *speed as well. This also doesn't work correctly. The colliders are detected, but the movement either doesn't work at all, or is not working correctly.
Conclusion:
I've played with my script for the last 4 hours and some (not all) of the things that I attempted are commented out so they are still visible (please see code attached below). I will be reading more online explanations and trying out different code and update here if I work out a solution. If anyone has some pointers or suggestions, in the meantime, I would greatly appreciate it.
Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FalconPegControl_2 : MonoBehaviour {
// Define needed variables
private TestUDPConnection udpListener;
public Vector3 realObjectCurrentPos;
private Vector3 realObjectLastPos;
public Vector3 realObjectCurrentRot;
private Vector3 realObjectLastRot;
public Vector3 realObjectPosChange;
public Vector3 realObjectRotChange;
private Quaternion rotation;
//public float pi = 3.14f;
private Rigidbody rb;
private int control = 0;
public bool collisionOccurred = false;
//public float thrust = 1000;
//public CalibrationManager calibrationManager;
// Use this for initialization
void Start () {
udpListener = GetComponentInParent<TestUDPConnection>();
collisionOccurred = false;
rb = GetComponent<Rigidbody> ();
SharedRefs.falconPegControl = this;
}
public void OffControl ()
{
control = 0;
}
public void CollisionDuplicateFix ()
{
collisionOccurred = true;
}
// Update is called once per frame
void FixedUpdate () {
//WITHOUT UNITY AXIS CONVERSION:
//realObjectCurrentPos[0] = udpListener.xPosReal; //[m]
//realObjectCurrentPos[1] = udpListener.yPosReal; //[m]
//realObjectCurrentPos[2] = udpListener.zPosReal; //[m]
//===============================
//Unity axis conversions:
//CHAI3D --> Unity
//(x, y, z) --> (x, -z, y)
//CHAI3D: realObjectCurrentPos[0], [1], [2] is CHIA3D (x, y, z)
//Also, to compensate for the workspace available to the Falcon Device (~0.04, ~0.06, ~0.06)
//adding a value of x10 allows it to reach the default hemisphere successfully
//updated comment: the sign values that work (-, +, -)
//===============================
//Unity conversion for rotation (using Falcon devices)
//Since one falcon is for translation and the other is for rotation,
//the rotation information is a conversion of translational information
//in other words, max range of (~0.04, ~0.06, ~0.06) has been converted into a max range of (90, 90, 90)
//using basic algebra (i.e., (90/0.04))
//thus giving the user the full range of 180 degrees (from 90 degrees to -90 degrees)
realObjectCurrentPos[0] = udpListener.xPosReal * (-5); //[m]
realObjectCurrentPos[1] = udpListener.zPosReal * (5); //[m]
realObjectCurrentPos[2] = udpListener.yPosReal * (-5); //[m]
realObjectCurrentRot [0] = udpListener.xRot * (90f / 0.04f); //degrees
realObjectCurrentRot [1] = udpListener.yRot * (90f / 0.06f); //degrees
realObjectCurrentRot [2] = udpListener.zRot * (90f / 0.06f); //degrees
if (Input.GetKeyDown ("1")) {
control = 1;
SharedRefs.stopWatch.startTimer ();
}
if (Input.GetKeyDown ("space"))
{
OffControl ();
}
if (control==1)
{
Vector3 posUnity = new Vector3 (realObjectCurrentPos[0], realObjectCurrentPos[1], realObjectCurrentPos[2]);
rb.AddForce (posUnity);
//Vector3 tempVect = new Vector3(realObjectCurrentPos[0], realObjectCurrentPos[1], realObjectCurrentPos[2]);
//Vector3 startPoint = new Vector3 (0f, 0.0225f, 0f);
//tempVect = tempVect * speed * Time.deltaTime;
//transform.localPosition = realObjectCurrentPos; //[m]
//var unityX = Vector3.Scale (posTemp, Vector3.right);
//var unityY = Vector3.Scale (posTemp, Vector3.up);
//var unityZ = Vector3.Scale (posTemp, Vector3.forward);
//Vector3 unityX = new Vector3 (Vector3.Scale (posTemp, Vector3.right), Vector3.Scale (posTemp, Vector3.up), Vector3.Scale (posTemp, Vector3.forward));
//Vector3 unityY = new Vector3 (Vector3.Scale (posTemp, Vector3.up));
//Vector3 unityZ = new Vector3 (Vector3.Scale (posTemp, Vector3.forward));
//rb.MovePosition (rb.position + unityX + unityY + unityZ);
//transform.localPosition = (startPoint + tempVect); //[m]
transform.localRotation = Quaternion.Euler(realObjectCurrentRot); //[m]
realObjectLastPos = realObjectCurrentPos;//[m]
realObjectLastRot = realObjectCurrentRot;//[m]
realObjectPosChange = realObjectCurrentPos - realObjectLastPos; //[m]
realObjectRotChange = realObjectCurrentRot - realObjectLastRot;
}
else if (control==0)
{
Vector3 stop = new Vector3 (0, 0, 0);
rb.constraints = RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezeRotationZ;
rb.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotationX;
rb.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezeRotationX;
rb.velocity = (stop);
}
}
}
Also, updated from #Ali Baba's comments:
I haven't had time to test the other methods yet, but by using AddForce and playing with the drag and a force modifier variable, I was able to get control over all three axes (actually 6DOF because I also have rotational control from a 2nd external device) and I also have much better control over my gameobject than before (specifically due to the drag and force modifier variable adjustments). This may be the best solution, but I originally needed to get my position to change based on the position of the external devices that I am using. I'm adding a basic, slimmed down, adjusted code which uses AddForce and allows for keycontrol adjustments of the drag and my force modifier variable in case other beginners see this thread also. In the meantime, I will try to get the other functions (MovePosition, etc) working and update on the results.
Slim, basic drag/variable testing code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Real_Controller : MonoBehaviour {
// Define needed variables
private TestUDPConnection udpListener;
public Vector3 realObjectCurrentPos;
public Vector3 realObjectCurrentRot;
private Quaternion rotation;
private Rigidbody rb;
private float increaseForce = 23;
// Use this for initialization
void Start () {
udpListener = GetComponentInParent<TestUDPConnection>();
rb = GetComponent<Rigidbody> ();
rb.drag = 1.24f;
}
// Update is called once per frame
void FixedUpdate () {
if (Input.GetKeyDown ("q"))
{
rb.drag -= 0.1f;
Debug.Log ("drag is: " + rb.drag);
}
if (Input.GetKeyDown ("w"))
{
rb.drag += 0.1f;
Debug.Log ("drag is: " + rb.drag);
}
if (Input.GetKeyDown ("a")) {
increaseForce -= 1f;
Debug.Log ("increased force is: " + increaseForce);
}
if (Input.GetKeyDown ("s")) {
increaseForce += 1f;
Debug.Log ("increase force is: " + increaseForce);
}
realObjectCurrentPos[0] = udpListener.xPosReal * (-increaseForce); //[m]
realObjectCurrentPos[1] = udpListener.zPosReal * (increaseForce); //[m]
realObjectCurrentPos[2] = udpListener.yPosReal * (-increaseForce); //[m]
Vector3 forceDirection = realObjectCurrentPos - transform.localPosition;
rb.AddForce (forceDirection * forceDirection.magnitude);
realObjectCurrentRot [0] = udpListener.xRot * (90f / 0.04f); //degrees
realObjectCurrentRot [1] = udpListener.yRot * (90f / 0.06f); //degrees
realObjectCurrentRot [2] = udpListener.zRot * (90f / 0.06f); //degrees
transform.localRotation = Quaternion.Euler(realObjectCurrentRot); //[m]
}
}
Instead of placing the gameObject at the exact position of your controller, you could try applying a force in the direction of the position you want your gameObject to be in:
if (control==1)
{
Vector3 forceDirection = realObjectCurrentPos - transform.localPosition;
rb.AddForce (forceDirection);
transform.localRotation = Quaternion.Euler(realObjectCurrentRot)
}
The force applied here is linear to the distance between the position of the gameObject and the real object, so this basically behaves like a spring. You should try multiplying the force by different factors and test:
rb.AddForce (forceDirection * 0.5f);
Or scale it quadratically:
rb.AddForce (forceDirection * forceDirection.magnitude);
Whatever feels best
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
My camera has to focus on an GameObject that can be scaled over time. How can I compute the camera position so that it is always at the same distance from this GameObject? I've already tried to do something like
camera.position.y += object.scaleFactor / 2;
camera.position.z -= object.scaleFactor / 2;
But the bigger the object becomes, the lesser it works. I'm thinking about using a bounding box, do you think it would work ?
Thanks a lot !
use this:
// compute this when scale is 1.0f
Vector3 originalPosition = camera.position;
Vector3 originalDistance = camera.position - gameObject.position;
// then use:
camera.position = originalPosition + originalDistance * gameObject.scaleFactor;
if this does not work, please describe your situation in more detail and i will edit the answer
for example: if you want that the camera keeps the distance to the object, you will have to use BoundingSphere.radius:
// compute this when scale is 1.0f
Vector3 originalPosition = camera.position;
Vector3 temp = camera.position - gameObject.position;
Vector3 originalDirection = temp.normalized;
float originalDistance = temp.magnitude - boundingSphere.radius;
// use this when object is scaled:
camera.position = originalPosition + originalDirection * (boundingSphere.radius + originalDistance);
public class ObjectRelativeScale : MonoBehaviour
{
public float ObjectScale = 1.0f;
private Vector3 _initialScale;
void Start()
{
_initialScale = transform.localScale;
}
void Update()
{
var cameraMainTransform = Camera.main.transform;
var plane = new Plane(cameraMainTransform.forward, cameraMainTransform.position);
float dist = plane.GetDistanceToPoint(transform.position);
transform.localScale = _initialScale * dist * ObjectScale;
}
}
Basically the way around is better, scale the object depending on the camera position.
Cheers!