I have this coroutine to calculate the velocity
IEnumerator CalcVelocity()
{
prevPos = transform.position;
yield return new WaitForEndOfFrame();
print(((prevPos - transform.position) / Time.deltaTime));
print(((prevPos - transform.position) / Time.deltaTime).y);
velocity = Mathf.Abs(((prevPos - transform.position) / Time.deltaTime).y);
}
I'm printing out the velocity in all directions and then the y velocity as that's the only I need for my purpose.
However sometimes the serialized Vector3 and the y component don't match - as below they are wildly different. The y value seems to be completely wrong.
(0.0, 0.0, 0.0) // the Vector3 printout
8.488064E-05 // the y
What's gone wrong here?
What's gone wrong here?
Nothing, it's just how unity show things in your log
If you run this
public void Test()
{
var v = new Vector3(0f, 0.00001f, 0f);
var y = v.y;
Debug.Log($"{v}");
Debug.Log($"{y}");
}
You will see this
If you want to see more than 1 decimal, you can use formatters
public void Test()
{
var v = new Vector3(0f, 0.00001f, 0f);
var y = v.y;
Debug.Log($"{v.ToString("F5")}");
Debug.Log($"{y}");
}
N
Nothing is wrong.
The default implementation of Vector3.ToString uses a human readable rounded display (see source code)
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format)
{
return ToString(format, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format))
format = "F1";
return UnityString.Format("({0}, {1}, {2})", x.ToString(format, formatProvider), y.ToString(format, formatProvider), z.ToString(format, formatProvider));
}
As you can see by default it uses "F1" meaning fixed floating point format rounded to a single decimal.
You can provide any custom format you want like e.g.
print(((prevPos - transform.position) / Time.deltaTime).ToString("G9"));
Where "G" is the general formatter allowing to either print scientific or fixed point presentation depending on which one is more compact.
By chosing G9 it ensures that the float values are printed and then if they would get parsed again would always successfully roundtrip without any data loss.
For more information about available formatting strings see Standard Numeric Format Strings and Custom Format Strings
Related
Ive made a ui touch/click controller by using an UI image with collider. The ui is rendered with a stacked camera.
Im using IPointerDownHandler.OnPointerDown to get the click event.
The controller is supposed to give a value from 0-1 depending on how far up you click it.
Im using Canvas Scaler on the UI to make the controllers resize depending on device. But that messes up my calculations since the click position wont be the same. How is this supposed to be handled? Now the calculation is only correct when i disable Canvas Scaler or run it on a display with the default dimensions.
public void OnPointerDown(PointerEventData pointerEventData)
{
SetAccelerationValue(pointerEventData.position.y);
}
private void SetAccelerationValue(float posY)
{
float percentagePosition;
var positionOnAccelerator = posY - minY;
var acceleratorHeight = maxY - minY;
percentagePosition = positionOnAccelerator / acceleratorHeight;
Debug.Log(percentagePosition);
}
I would use RectTransformUtility.ScreenPointToLocalPointInRectangle to get a position in the local space of the given RectTransform.
Then combine it with Rect.PointToNormalized
Returns the normalized coordinates cooresponding the the point.
The returned Vector2 is in the range 0 to 1 with values more 1 or less than zero clamped.
to get a normalized position within that RectTransform.rect (0,0) being bottom-left corner, (1,1) being the top-right corner
[SerializeField] private RectTransform _rectTransform;
private void Awake ()
{
if(!_rectTransform) _rectTransform = GetComponent<RectTransform>();
}
private bool GetNormalizedPosition(PointerEventData pointerEventData, out Vector2 normalizedPosition)
{
normalizedPosition = default;
// get the pointer position in the local space of the UI element
// NOTE: For click vents use "pointerEventData.pressEventCamera"
// For hover events you would rather use "pointerEventData.enterEventCamera"
if(!RectTransformUtility.ScreenPointToLocalPointInRectangle(_rectTransform, pointerEventData.position, pointerEventData.pressEventCamera, out var localPosition)) return false;
normalizedPosition = Rect.PointToNormalized(_rectTransform.rect, localPosition);
// I think this kind of equals doing something like
//var rect = _rectTransform.rect;
//var normalizedPosition = new Vector2 (
// (localPosition.x - rect.x) / rect.width,
// (localPosition.y - rect.y) / rect.height);
Debug.Log(normalizedPosition);
return true;
}
Since the normalized position returns values like
(0|1)-----(1|1)
| |
| (0|0) |
| |
(0|0)-----(1|0)
but you sounds like what you want to get is
(-1|1)----(1|1)
| |
| 0|0 |
| |
(-1|-1)----(1|-1)
So you can simply shift the returned value using e.g.
// Shift the normalized Rect position from [0,0] (bottom-left), [1,1] (top-right)
// into [-1, -1] (bottom-left), [1,1] (top-right)
private static readonly Vector2 _multiplcator = Vector2.one * 2f;
private static readonly Vector2 _shifter = Vector2.one * 0.5f;
private static Vector2 GetShiftedNormalizedPosition(Vector2 normalizedPosition)
{
return Vector2.Scale((normalizedPosition - _shifter), _multiplcator);
}
So finally you would use e.g.
public void OnPointerDown(PointerEventData pointerEventData)
{
if(!GetNormalizedPosition(pointerEventData, out var normalizedPosition)) return;
var shiftedNormalizedPosition = GetShiftedNormalizedPosition(normalizedPosition);
SetAccelerationValue(shiftedNormalizedPosition.y);
// And probably for your other question also
SetSteeringValue(shiftedNormalizedPosition.x);
}
And of course within SetAccelerationValue you don't calculate anything but just set the value ;)
This uses always the current rect so you don't have to store any min/max values and it also applies to any dynamic re-scaling of the rect.
This would then probably also apply to your other almost duplicate question ;)
I'm newbie in Unity3D scripting and trying to learn how it works internally for my simple projects, what are the best practices. Here I have a simple scene with a cube in it and trying to animate it, going left to certain point in time, then reversing back to negative value and then back in loop (on x axis). Direction is set in class public boolean property. By default it is negative value (which means it should go in positive direction, sorry for confusion). If it is positive it should go negative. However I have noticed that when I change this boolean value in Update method of script, it reverses back to original value? (when set to true, default is false). Then my object gets stack between going true and false and not moving in any direction. However if I set this property as static property in class, it does not resets and works just as intended (loops fine). I do not know why is it resets and here I'm completely confused.
public class CubeAnim : MonoBehaviour
{
public bool directionnegative;
// Update is called once per frame
void Update()
{
float nv = 25.0f * Time.deltaTime;
float posx = transform.position.x;
if (posx > 20.0f)
{
if (!directionnegative)
{
directionnegative = true;
}
}
else if (posx < -20.0f)
{
if (directionnegative)
{
directionnegative = false;
}
}
if(directionnegative)
{
nv = -(nv);
}
transform.Translate(nv, 0, 0);
deltaTime += (Time.deltaTime - deltaTime) * 0.1f;
float fps = 1.0f / deltaTime;
string log = "posx: " + transform.position.x + "\ndir: " + directionnegative + " transx: " + nv + "\nfps: " + Mathf.Ceil(fps).ToString();
Debug.Log(log);
}
}
And if I declare directionnegative as static bool script works fine and cube animation is properly going in one direction, then in another direction, then reverses:
public static bool directionnegative;
I am creating an elevator of which constantly moves from Point A to Point B.
I cannot just use transform.position ( location here * speed here etc) since I have a player that has a Rigidbody, and if using that it would flicker my player out whenever I am on the elevator. I also tried parenting the Player whenever its on the elevator (and de-parents it when it jumps etc), that fixes the flickering however it somewhat bugs the player's jumping mechanism.
Last resort is using a Rigidbody to my elevator and moves it with this code:
private void FixedUpdate()
{
Vector2 l_mypos = new Vector2(transform.position.x, transform.position.y);
Vector2 l_target = new Vector2(_targetPoint.position.x, _targetPoint.position.y);
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
Debug.Log("reached");
}
private void MoveElevator(Vector2 toTarget)
{
Vector2 direction = (toTarget - (Vector2)transform.position).normalized;
_elevatorRB.MovePosition((Vector2)transform.position + direction * _speed * Time.deltaTime);
}
This moves the elevator towards the direction given, however it doesn't reach the "reached" condition. I placed a debug.log there to see both mpos and target to see the differences. It ends with 0, 10, 0 - 0, 10, 0 meaning both my target and the elevator's position is already the same. However it doesn't reach the else condition, and the elevator keeps flickering at Point B.
The == operator for Vector2 uses an estimation of 0.00001 for equality.
However it is very possible that you overshoot the target in
(Vector2)transform.position + direction * _speed * Time.deltaTime
since your final velocity speed * Time.deltaTime is certainly greater than 0.00001. (Except your speed is smaller than 0.0006 which I doubt.)
The value you see in the Debug.Log is the result of a Vector3.ToString which uses human readable rounded values and does not show the actually float values! From the source code
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format)
{
return ToString(format, CultureInfo.InvariantCulture.NumberFormat);
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format)) format = "F1"; // <- !! HERE !!
return UnityString.Format("({0}, {1}, {2})", x.ToString(format, formatProvider), y.ToString(format, formatProvider), z.ToString(format, formatProvider));
}
You should rather use Vector2.MoveTowards which avoids this overshooting.
The API of Vector3.MoveTowards actually explains it better then the one of Vector2.MoveTowards
Calculate a position between the points specified by current and target, moving no farther than the distance specified by maxDistanceDelta.
Use the MoveTowards member to move an object at the current position toward the target position. By updating an object’s position each frame using the position calculated by this function, you can move it towards the target smoothly. Control the speed of movement with the maxDistanceDelta parameter. If the current position is already closer to the target than maxDistanceDelta, the value returned is equal to target; the new position does not overshoot target. To make sure that object speed is independent of frame rate, multiply the maxDistanceDelta value by Time.deltaTime
private void FixedUpdate()
{
// Vector3 and Vector2 have implicit operators allowing to use
// both types exchangeably.
// In order actively to convert them you can simply typecast between them
var l_mypos = (Vector2) transform.position;
var l_target = (Vector2) _targetPoint.position;
if (l_mypos != l_target)
{
MoveElevator(l_target);
Debug.Log(l_mypos + " - " + l_target);
}
else
{
Debug.Log("reached");
}
}
private void MoveElevator(Vector2 toTarget)
{
var pos = Vector2.MoveTowards(transform.position, toTarget, _speed * Time.deltaTime);
_elevatorRB.MovePosition(pos);
}
i wanted to ask you if there is a Resolution Scale Option like in Unreal Engine in Unity too. I have looked around the internet but didn't found anything.
If you are wanting to scale objects that are not on the canvas based on the resolution then offhand I can't think of anything. However, it would be fairly easy to implement something that does this.
Create a Script and attach it to every object that should scale based on the current resolution.
public class ScaleObjectFromRes : MonoBehaviour
{
private Vector2 targetResoultion = new Vector2(1920, 1080); //can be changed here or elsewhere
private bool matchWidth = true; //0=width, 1=height used to maintain aspect ratio
// Start is called before the first frame update
void Start()
{
float difference = CalculateDifference();
ScaleObj(difference);
}
void ScaleObj(float diff)
{
gameObject.transform.localScale += (gameObject.transform.localScale * (diff/100));
}
private float CalculateDifference()
{
Vector2 actualResolution = new Vector2(Screen.width, Screen.height);
Vector2 change = actualResolution-targetResoultion;
Vector2 percentChange = (change / targetResoultion) * 100;
//match width/height
if (matchWidth)
{
return percentChange.x;
}
else
{
return percentChange.y;
}
}
This scales the object based on the percent difference between the target resolution and the actual resolution. We can choose to match the difference based upon the width or height to guarantee a constant ratio for the object. This assumes the object's scale vector's magnitude is 1. Hope this helps!
I made a simple script that goes to one waypoint and then to the next.
My problem is that it seems to be a delay while going from waypoint1 to waypoint2 and i don't know why:
¿Why is that delay happening and how can i remove it?
using UnityEngine;
using System.Collections;
public class Missile : MonoBehaviour
{
public Vector3 finalTarget;
public Transform forwardObject;
public GameObject impactAreaPrefab;
float smoothingDelay = 0.1f;
bool fired = false;
bool powerPhase = true;
Vector3 currentTarget;
private void OnEnable() {
fire(new Vector3(-25.29f, 0.5f, -10.638f));
}
void fire(Vector3 coords) {
currentTarget = forwardObject.position;
finalTarget = coords;
Instantiate(impactAreaPrefab, finalTarget, Quaternion.identity);
fired = true;
}
void Update() {
if (!fired) {
return;
}
if (powerPhase && transform.position == currentTarget) {
powerPhase = false;
currentTarget = finalTarget;
smoothingDelay = 0.05f;
}
transform.position = Vector3.Lerp(transform.position, currentTarget, Time.deltaTime / smoothingDelay);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, currentTarget, 1, 0.0f)), Time.deltaTime / smoothingDelay);
}
}
That's happening, because you're using lerp not exactly properly. If you want to get linear movement you should cache your first argument (position/rotation on beginning) and provide increasing third parameter. This delay is happening because if your bullet is very close to final position and it's still trying to get there, but your current distance |finalPos - transform.position| is so small that your step Time.deltaTime/smoothingDelay is almost not moving it.
Vector3 startPos;
Vector3 finalPos;
float currentT = 0.0f;
void Update()
{
currentT += Time.deltaTime;
transform.position = Vector3.Lerp(startPos, finalPos, currentT);
}
Checking if Vector3 == Vector3 is also not a good idea. Use pattern from above and check if currentT is larger or equal to 1. If it's true then you're on final position. You get also some control over movement duration by dividing currentT.
So First thing read these post to get better understanding of Lerp function-
https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/
http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
You should have a better understanding of lerp now.
In summary lerp does a really simple thing. Say u have two values X and Y. For this example let us give them some value, X = 0, Y = 1, Now you want to get a value some percent between them, like u want to get a value which is 50% from X and Y. You can guess the answer is 0.5. The lerp equation for this would be
Mathf.Lerp(0, 1, 0.5f);
So simply- given two values, x and y, Mathf.Lerp returns a value that is t percent between them.
Now to properly use Lerp you need to cache the position before starting the lerp. Most times I use a coroutine to get this effect works pretty well and then u can use animation curve to change the third parameter to create some crazy good effects. For example on using a animation curve just comment i will write it.
For this problem of yours you have two options-
1) Lerp like a pro using Animation curve to manipulate the speed. Remember u can create animation curves in runtime too.
IENumerator Move(Transform toMove, Vector3 end, float duration){
Vector3 startPos = toMove.position;
float elapsed = 0f;
while(elapsed < duration){
elapsed += Time.deltaTime;
toMove.position = Vector3.Lerp(startPos, end, elapsed / duration);//manipulate the last parameter to move the object linearly
yield return null;//basically wait for next frame
}
toMove.position = end;//after lerp ends
}
Now you can instead of duration use speed and then with it you calculate the time required and change the speed to make it faster
float distance = Vector3.Distance(startPos, end);
toMove.position = Vector3.Lerp(startPos, end, elapsed / (distance/(speed * multiplier)));
2) Use Vector3.MoveTowards - This function moves a point to a end point with a given maximum step, requires three paramters, (currentPosition, end, step), u can multiply step with variable to control the speed, both work really good.
Using this is much easier in most cases
Example-
float step = speed * Time.deltaTime;//to make it framerate independent
transform.position = Vector3.MoveTowards(transform.position, end, step * multiplier);
Hope this helps. I am sorry I was unable to format my answer properly, hopefully will get better at answering. Any edits to improve the answer are welcomed :)
I recommend using iTween for smooth movement.
I modified iTween at some point for me to be able to do anything I want. like this:
public static void Rotate (Transform transform, Vector3 target, float transitionTime, Action onEnd = null, bool ignoreTimescale = false, iTween.EaseType ease = iTween.EaseType.easeInOutQuad, float delay = 0)
{
Vector3 from, to;
from = transform.localEulerAngles;
to = target;
Action <object> onUpdateAction = (rotation =>
{
transform.localEulerAngles = (Vector3) rotation;
});
Action <object> onCompleteAction = (data =>
{
if (onEnd != null)
onEnd ();
});
Hashtable hash = new Hashtable ();
hash.Add ("from", from);
hash.Add ("to", to);
hash.Add ("time", transitionTime);
hash.Add ("delay", delay);
hash.Add ("easetype", iTween.EaseType.easeInOutQuad);
hash.Add ("ignoretimescale", ignoreTimescale);
hash.Add ("onupdate", onUpdateAction);
hash.Add ("oncomplete", onCompleteAction);
iTween.ValueTo (transform.gameObject, hash);
}
That gives me full control in a variety of scenarios.
Here is the code if you want to implement it.
https://drive.google.com/open?id=1nLEEYTp-q4Kfh2n3nWQJcMXmPNtVPLLP