Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411433 Posts in 69363 Topics- by 58418 Members - Latest Member: Pix_RolleR

April 20, 2024, 07:56:51 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Unity C# - Transform Translate Overshoot on Start/Awake
Pages: [1]
Print
Author Topic: Unity C# - Transform Translate Overshoot on Start/Awake  (Read 1204 times)
Sundrop
Level 1
*

Grandma cooks best!


View Profile WWW
« on: December 11, 2017, 10:43:01 AM »

Been messing around with some UI stuff and thought I'd try that Final Fantasy bouncing cursor thing we love so much.



If you look closely, the cursor overshoots at the beginning, and that happens every time I start the game, and I can't figure out why.  Huh?

Code:

private Transform _transform;
private Vector2 startPos;
private Vector2 offsetDistance = new Vector3 (2, 0);
private bool slidingRight = true;
private float translateSpeed = 20;
private int direction = 1;

void Awake () {
_transform = GetComponent<Transform> ();
}

void Update () {
if (_transform.localPosition.x >= offsetDistance.x && slidingRight == true) {
ChangeDirection ();
} else if (_transform.localPosition.x <= startPos.x && slidingRight == false) {
ChangeDirection ();
}
Slide ();
}

void Slide () {
if (slidingRight == true) {
_transform.Translate (new Vector2 (1, 0) * direction * translateSpeed * Time.deltaTime);
} else {
_transform.Translate (new Vector2 (1, 0) * -direction * translateSpeed / 4 * Time.deltaTime);
}
}

void ChangeDirection () {
slidingRight = !slidingRight;
}

« Last Edit: December 11, 2017, 10:50:41 AM by Sundrop » Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #1 on: December 11, 2017, 11:01:25 AM »

I see a multiplication by Time.deltaTime in Slide(). You're probably getting a large delta on the first update, which moves the object beyond its intended range. Several possible solutions:

- It's been a while since I used Unity, but I seem to recall that FixedUpdate() exists. Using that instead of Update() should give you fixed timestep movement, which will categorically avoid the problem of large deltas causing this kind of issue. However, if this is simply a graphical effect and not something that affects game logic, this might not be the most fitting solution.

- Inside Slide(), you could clamp the transformed position such that it never goes outside the range of [startPos.x, offsetDistance.x].

- Instead of accumulating translations in _transform to move your object, you could model the movement as an oscillating function, and just sample the function each frame to get the object's position. This would also have the benefit of removing the possibility of your object's position drifting over time from accumulated floating point rounding.
Logged

LittleTwig
Level 0
**


View Profile
« Reply #2 on: December 11, 2017, 11:32:01 AM »

I don't think it has to to with the deltaTime spike(otherwise it wouldn't be such a smooth movement).

However, your startPos isn't set. Could it be that your cursor goes to 0/0 and slides around there?
« Last Edit: December 11, 2017, 11:37:52 AM by LittleTwig » Logged
Sundrop
Level 1
*

Grandma cooks best!


View Profile WWW
« Reply #3 on: December 11, 2017, 12:52:42 PM »

- Inside Slide(), you could clamp the transformed position such that it never goes outside the range of [startPos.x, offsetDistance.x].
- Instead of accumulating translations in _transform to move your object, you could model the movement as an oscillating function, and just sample the function each frame to get the object's position. This would also have the benefit of removing the possibility of your object's position drifting over time from accumulated floating point rounding.
- Wow, I didn't even think of that! I'll look into Mathf.Clamp and see if that works!
- By "sample the function", do you mean to increment the localPosition.x value 'manually'?

However, your startPos isn't set. Could it be that your cursor goes to 0/0 and slides around there?
I tried setting startPos to the transform's local position since that where I want the initial position to be, but that didn't seem to change anything. Droop


Btw, thanks so much for helping me out with this!  Coffee
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #4 on: December 11, 2017, 04:29:35 PM »

- By "sample the function", do you mean to increment the localPosition.x value 'manually'?

Sort of. Instead of adding to the value to get an accumulated value, you'd derive an entirely new x value every frame. Something like this (pseudocode, probably not valid C#):

Code:
void init() {
  startTime = GetTime();
}

float sample(float timeValue) {
  return 1.0 - fabs(sin(timeValue)) * offsetDistance.x; // Or some other curve function to fit the desired visual effect
}

void update() {
  position.x = startPos.x + sample(GetTime() - startTime);
}

If you have to work with _transform (which I presume is a matrix object) instead of being able to set a position variable directly, I guess the thing to do would be to reset the transformation every update, then translate it by the amount given by sample().
Logged

Sundrop
Level 1
*

Grandma cooks best!


View Profile WWW
« Reply #5 on: December 11, 2017, 10:05:00 PM »

Thank you so much for this! I don't understand the "sample" function for now (fabs/sin?) but will do some research and post back here!

Thank you again, ThemsAllTook!  Gomez
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #6 on: December 12, 2017, 08:15:31 AM »

I suppose for Unity C#, those would be Mathf.Abs() and Mathf.Sin(). Here's an alternate implementation that should approximate the animation you have now, interpolating linearly rightward for 0.1 seconds and linearly leftward for 0.4 seconds:

Code:
const float duration = 0.5;
const float moveDistance = 2.0;

float sample(float timeValue) {
  timeValue = timeValue % duration / duration;
  if (timeValue < 0.2) {
    return timeValue / 0.2 * moveDistance;
  }
  return (1.0 - (timeValue - 0.2) / 0.8) * moveDistance;
}
Logged

Sundrop
Level 1
*

Grandma cooks best!


View Profile WWW
« Reply #7 on: December 15, 2017, 02:03:53 AM »

I'd just discovered the Mathf.Sin function and was just about to ask how I would increase the speed of the positive direction when I tried your code  Screamy

Could you kindly explain what you've written? I would hate to implement code that I don't understand.  Embarrassed
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #8 on: December 15, 2017, 10:38:14 AM »

Did it work? I didn't actually test before posting it. Hopefully it's the effect you were after.

The basic idea of a curve function is to map a linear input (usually 0.0 to 1.0) to a nonlinear output (usually also in the same range, though it might go outside for extrapolation effects). The sample() function that I've written is a slight modification of that concept, taking a monotonically increasing time input (presumably in seconds with a fractional component), and mapping it to consistently oscillating output.

The first line uses floating point modulus to trim the input time value so that it's within the range of [0.0, duration]. (If negative timeValue inputs were possible, some extra handling might be necessary to make sure the result is positive.) Then I divide by duration to normalize to the range of [0.0, 1.0]. Every half a second (or whatever value duration is set to), this variable will ramp up from 0.0 to 1.0, and then start over from 0.0.

Since the desired effect is to interpolate out in one direction quickly and then back in the other direction slowly, I split the function's behavior into two separate interpolations depending on timeValue. For the first 1/5th of the cycle (the timeValue < 0.2 part), that initial 1/5th of timeValue is scaled up to the [0.0, 1.0] range by dividing it by 0.2 (aside: I probably should've made that a third constant), and then multiplied by moveDistance to scale it to the desired amount of translation. Technically, it might be cleaner to multiply the output of sample() by moveDistance at the call site rather than having sample() do the multiplication itself.

For timeValues >= 0.2, I do basically the same thing in reverse. (timeValue - 0.2) / 0.8 scales the trailing 4/5ths of timeValue to the [0.0, 1.0] range, which is then subtracted from 1.0 so that it counts backward toward 0. Multiply that by moveDistance the same way as the initial 1/5th, and the job is done.

I hope that all made sense!
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic