How would I expand and retract an object on tap?

So when I tap I want an object to expand a little and then retract. I have this working like this.

    public float size;
    float velocity;

    void Update()
    {

        size = Mathf.SmoothDampAngle( size, (Input.GetMouseButton(0)) ? .65f : .6f, ref velocity, .05f);

        transform.localScale = new Vector3(size, size, 1);
    }

This works like this. When I press down the object starts expanding but right when I let go of the tap the object stops expanding and retracts. I don’t want this to happen. What I want is when I do a quick tap the obejct expands and retracts. You’d think i would just add in “Input.GetMouseButtonDown(0)” but if I do that then the object expands and retracts really really really fast. I don’t know how to slow it down. I’m not sure i’m doing this right in the first place.

I’m not a coder but I believe I’ve seen mention of this type of mishap before. I believe it might be due to the incorrect input (function/action?). There are several GetMouseButton - inputs. I believe you may want Input.GetMouseButtonDown Maybe -

I just whipped this up quickly without testing, but this is another approach that might help you:

public bool startsBig = false;
public float smallSize = .6f;
public float bigSize = .65f;
public float speed = 1f;

private Vector3 targetScale;
private bool isBig;

private void Start() {
    float size = startsBig ? bigSize : smallSize;
    transform.localScale = new Vector3(size, size, 1);
    targetScale = transform.localScale;
    isBig = startsBig;
}

private void Update() {
    // on click, toggle the target scale
    if(Input.GetMouseButtonDown(0)) {
        toggleTargetSize();
    }

    if(transform.localScale != targetScale) {
        // lerp the scale by a speed variable over time
        transform.localScale = Vector3.Lerp(transform.localScale, targetScale, speed * Time.deltaTime);
    }
}

private void toggleTargetSize() {
    // toggle the "isBig" bool
    isBig = !isBig;
    // based on the "isBig" bool, choose the new target scale
    float size = isBig ? bigSize : smallSize;
    // set the target scale
    targetScale = new Vector3(size, size, 1);
}
1 Like

Thanks for the pickup. I felt I was wandering into the forest a little too far - and it felt kinda scary. :wink:

This is kinda what i need but you made it so when i click it expands but when i click again it retracts. What id like is when i click once it expands and retracts.

Well, I recommend you check out the Animator system. It would be great for something like this and wouldn’t require anywhere near as much programming.

I hope this script doesn’t fly over your head (no offense intended), but I reworked it to use a Coroutine to do the sequenced animations. Let me know if you have any questions about what is happening.

Also once again I did not test this, but it compiles so it must work right? haha…

public float smallSize = .6f;
public float bigSize = .65f;
public float speed = 1f;

private void Update() {
    // on click, start the animation coroutine
    if(Input.GetMouseButtonDown(0)) {
        // stops any currently running coroutines before starting a new one
        StopAllCoroutines();
        // start a coroutine which will grow, then shrink the scale
        StartCoroutine(growAndShrink());
    }
}

private IEnumerator growAndShrink() {
    // create Vector3's from the size floats
    Vector3 smallScale = smallSize * Vector3.one;
    Vector3 bigScale = bigSize * Vector3.one;

    // animate from small to large, and dont move on until its done
    yield return animateScale(smallScale, bigScale);
    // animate large to small, and dont move on until it's done
    yield return animateScale(bigScale, smallScale);
}

private IEnumerator animateScale(Vector3 startScale, Vector3 endScale) {
    bool done = false;
    while(!done) {
        // lerp the scale from start to end over time
        transform.localScale = Vector3.Lerp(startScale, endScale, speed * Time.deltaTime);

        // get the distance between the current scale and the end scale
        float distance = Vector3.Distance(transform.localScale, endScale);

        // if the current scale is within 0.01 of the end scale... close enough!
        if(distance <= 0.01) {
            // now were done
            done = true;
        }

        // wait a frame then continue from here
        yield return null;
    }
}

Code doesn’t seem to work. Hmm. Here. The funny thing is; is that I have 4 lines of code which is working almost perfect. So really i don’t think adding another 40 lines will make it work any better. No offence. Just talking from experience as a programmer coming from a different platform and language. Can you possibly explain how this code works by any chance?

    public float size;
    float velocity;

    void Update()
    {

        size = Mathf.SmoothDampAngle( size, (Input.GetMouseButton(0)) ? .65f : .6f, ref velocity, .05f);

        transform.localScale = new Vector3(size, size, 1);
    }

What I know is that if i change the “.65f” and “.6f” then the object changes sizes and such. If i change “.05f” then it goes faster and slower. The docs don’t explain how the Math.SmoothDampAngle works so I need a little bit of help with that. Thanks for the help so far!

What do ya know - this circled back around into my neck of the woods.
Two animation states - one idle (motionless/closed) mouse click to trigger animation, animation plays open/close - returns back to idle/closed state.
Forget all that code stuff - well except for the input.

Well like I said. The code works. I just need someones expert eyes to take a look and maybe explain it.

Well as a programmer you should know that # of lines doesn’t determine what is good code. A longer script with better readability is better than a single line that is difficult to determine what is happening, even if it works perfectly.

SmoothDampAngle smoothly interpolates from the first float parameter to the second float parameter incrementally based on the speed parameter. So every frame your code is interpolating from the current value of “size” towards .6, and then assigning it back to “size”.

It will only interpolate towards .65 when you hold down the mouse button, so that is not doing what you said you wanted.

I tested my code and found the problem, and then reworked it to use Vector3.SmoothDamp.

My code does indeed represent two states. It remains idle until you click, and it does the grow/shrink and then goes back to idle. I didn’t include any explicit named-states for it, but that is what it does.

There may be less verbose ways to accomplish this, but the way I did it is sufficiently reusable and clean.

Alternatively you can download DOTween asset (its free), and use that for all your animation needs.

This does work:

public float smallSize = .6f;
public float bigSize = .65f;
public float speed = 1f;

private void Update() {
    // on click, start the animation coroutine
    if(Input.GetMouseButtonDown(0)) {
        // stops any currently running coroutines before starting a new one
        StopAllCoroutines();
        // start a coroutine which will grow, then shrink the scale
        StartCoroutine(growAndShrink());
    }
}

private IEnumerator growAndShrink() {
    // animate from small to large, and dont move on until its done
    yield return animateScale(bigSize * Vector3.one);
    // animate large to small, and dont move on until it's done
    yield return animateScale(smallSize * Vector3.one);
}

private IEnumerator animateScale(Vector3 endSize) {
    bool done = false;
    Vector3 vel = Vector3.zero;
    while(!done) {
        // lerp the scale from current to end over time
        transform.localScale = Vector3.SmoothDamp(transform.localScale, endSize, ref vel, speed);

        // get the distance between the current scale and the end scale
        float distance = Vector3.Distance(transform.localScale, endSize);

        // if the current scale is within 0.1 of the end scale... close enough!
        if(distance <= 0.1) {
            // now were done
            done = true;
        }

        // wait a frame then continue from here
        yield return null;
    }
}