I’ve created a game with multiple points. Currently, I’m moving a primitive between these points (based on player input, which is arbitrary).
Right now I’m using Vector3.Lerp to move the primitive’s transform from point to point. What I’d like to do is stretch the primitive to the end point, and then return the primitive to its original shape at the end (like a rubber band motion).
Graphically, I want this (I hope this makes sense):
o x
ooooo x
ooooooooooo
x ooooo
x o
I know that there are ways to perform stuff like this by parenting the object to an empty transform to change the pivot point, but because I want a rubber band motion, I feel like that would be too complex.
Is there a way to accomplish this without using bones? I’m okay with going the bone route if necessary (as they should stretch the mesh accordingly without much setup, I believe) but if there’s a better way…
Edit:
Note that I want my primitive to stretch completely from point to point;, and I’m using coroutines for movement right now.
Bones would be soo much easier but this will work for a stretching cube I think.
IEnumerator Stretch(GameObject cube, float stretchTime, float snapTime, Vector3 goal)
{
Vector3 start = cube.transform.position;
Vector3 halfGoal = Vector3.Lerp(start, goal, 0.5f);
float distance = Vector3.Distance(start, goal);
//change x to the axis you need
float startX = cube.transform.localScale.x;
float wantedX = distance;
//change it in the localScale assign as well
float t = 0;
while(t < stretchTime)
{
t += Time.deltaTime;
cube.transform.position = Vector3.Lerp(start, halfGoal, t/stretchTime);
//in order to get it to 'stick' to the goal, we cant use a lerp for this part
//float x = Mathf.Lerp(startX, wantedX, t/stretchTime);
float distanceToStart = Vector3.Distance(cube.transform.position, start);
float x = distanceToStart * 2;
if(x < startX)
X = startX;
cube.transform.localScale = new Vector3(x, cube.transform.localScale.y, cube.transform.localScale.z);
yield return null;
}
t = 0;
while(t < snapTime)
{
t += Time.deltaTime;
cube.transform.position = Vector3.Lerp(halfGoal, goal, t/snapTime);
//in order to get it to 'stick' to the goal, we cant use a lerp for this part
//float x = Mathf.Lerp(wantedX, startX, t/snapTime);
float distanceToEnd = Vector3.Distance(cube.transform.position, goal);
float x = distanceToEnd * 2;
cube.transform.localScale = new Vector3(x, cube.transform.localScale.y, cube.transform.localScale.z);
yield return null;
}
}
See the Mathf documentation if that’s confusing. Basically, I see how far we are through 0-50%, then through 50-100%.
That’ll tell you how far across the left and right edges of your primitive should be. You can lerp between the start and end positions to convert those into 3D points:
Here is my take on the question. Whenever I see this kind of mechanic (spring, rubber band, etc.), I think curve. As a programmer, I think code, not Animation. So this code does two two things beyond what you asked for. First, it solves the problem for an arbitrary direction. Second it computes time through a sine curve, so the object has more of a snapping feel. I hesitated to post it with @KMKxJOEY1’s excellent answer that directly addresses your question, but maybe some future person will benefit from my additions. Start a new scene, put the code on a cube, and hit the spacebar. Hit the spacebar again when the cycle completes.
#pragma strict
public var moveTime : float = 1.0;
public var objectWidth : float = 1.0;
private var startMarker : Transform;
private var endMarker : Transform;
private var stretching = false;
function Start () {
startMarker = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
endMarker = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
startMarker.position = transform.position;
endMarker.position = transform.position;
startMarker.localScale = Vector3(.2, .2, .2);
endMarker.localScale = Vector3(.2, .2, .2);
}
function Update () {
if (!stretching && Input.GetKeyDown(KeyCode.Space)) {
endMarker.transform.position = Quaternion.AngleAxis(Random.Range(0,360), Vector3.forward) * Vector3.up * 5.5;
DoTheBand(moveTime);
}
}
function DoTheBand(time : float) {
stretching = true;
var dir : Vector3 = endMarker.position - startMarker.position;
var maxWidth : float = dir.magnitude + objectWidth;
var timer = 0.0;
transform.rotation = Quaternion.FromToRotation(transform.right, dir) * transform.rotation;
while (timer <= time) {
timer += Time.deltaTime;
var t = timer / time;
var _t = Mathf.Sin(t * Mathf.PI);
var __t = (t < 0.5) ? _t * 0.5 : 1.0 - 0.5 * _t;
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, __t);
transform.localScale.x = Mathf.Lerp(objectWidth, maxWidth, _t);
Debug.Log(_t);
yield;
}
transform.position = endMarker.position;
startMarker.position = transform.position;
transform.localScale.x = objectWidth;
stretching = false;
}