Stretching images

I have used some HingeJoint2D objects to create a rope bridge, pictured here:


What I want to do is find a way to automatically (or programmatically) stretch the ropes when the planks move. When the scene is stopped, everything lines up. When you run it, the planks fall due to gravity - it would be really cool if the ropes would go with them.

Any ideas on how to do this??

I think you would need to dynamically deform the mesh (that is, adjust the positions of its vertices). You could do this manually, or you could (with some effort) set up a SkinnedMeshRenderer, and let that do it for you.

(How would you do that? Again, you could do it manually in code, or you could just bust out a 3D modeling program, and make a rigged model of your bridge… never mind that it’s actually 2D; this is one of those cases where Unity’s integration of 2D and 3D comes in really handy!)

In the past I’ve stretched sprites like this using the transform scale. If you set the pivot of the sprite to one end (either in the sprite editor or using parenting and local position offset), you can set the sprite’s scale to the distance between the sprite’s pivot and the target, divided by the sprite’s height. Then it’s a matter of rotating the sprite to keep pointing at the target.

1 Like

Oh yeah, that’s a great point and I feel dumb for not pointing it out. It won’t suffice when you need to deform your sprites in more complex ways (like making bends in a rope), but for the simple case of making the vertical ropes reach the bridge, it should work great!

1 Like

There isn’t a sprite editor for this - it’s a single image. Also, I don’t know what you mean by parenting & local position offset. Can you give me an example?

There is so a sprite editor for it. Even if the sprite mode is “single” rather than “multiple,” you still have a sprite editor with which you can set the pivot point.

And to do what @LiterallyJeff is suggesting, you will need to have a separate sprite for each vertical rope. Then you can scale them in Y to make them stretch down to meet the bridge.

1 Like

No, there is no sprite editor on the object. There is a transform and a sprite renderer for each instance of the rope Image. None of them have a button for sprite editor. Aah. I have to edit the object in the Asset folder, not in the Hierarchy list; the sprite editor was there.

Ok, so I set the pivot in the top end. What’s next??

Next, you experiment with the scale in the inspector until you convince yourself that scaling in Y lets you stretch the ropes down to the bridge. And once you’ve got a grip on what you want to do, by doing it manually, you write some code to do it automatically.

If you’re new to both Unity3D and coding, this may be a hard task. In that case you might want to replace your rope bridge with a steel bridge or something else that avoids the problem, and get on with your game. Perhaps you can come back to it when you’ve got more experience under your hat.

1 Like

Here’s something to help you along. In my other reply I described the calculation as “the distance between the sprite’s pivot and the target, divided by the sprite’s height.”

Here is that in code to calculate the Y scale. The things you need are the sprite height in world units, the sprite’s position, and the end point position. Sprite height can be found in the SpriteRenderer bounds property.

newScale.y = Mathf.Abs(endPosition.y - spritePosition.y) / spriteHeight;

Ok… but where do I put this? Do I add a script to the image? I’m just not sure where to go with this.

There are of course many ways to handle this, but I recommend making a script that you can put on each rope that needs stretching. That script will need to know about the endpoint that the rope will stretch to, so give it a “public Transform endPoint” variable which will appear in the inspector for that component. Then for each rope, in the inspector for that rope, assign it the corresponding bridge plank for the Transform field.

Then in the script, create a FixedUpdate function (fixed update is called every time physics is updated) where you can do “transform.localScale = Mathf.Abs(endPoint.position.y - transform.position.y) / spriteHeight”. Provided you first get the spriteHeight from the SpriteRenderer component, perhaps in a Start function, and saved in a float variable.

Ok, I’ve got this:

public Transform endPoint; float spriteHeight; // Use this for initialization void Start () { SpriteRenderer sr = gameObject.GetComponent(); spriteHeight = sr.transform.localScale.y / sr.GetComponent().sprite.bounds.size.y; } // Update is called once per frame void Update () { } void FixedUpdate() { transform.localScale = Mathf.Abs (endPoint.position.y - transform.position.y) / spriteHeight; }

But I’m getting an error - Assets/Scripts/stretchRope1.cs(20,27): error CS0029: Cannot implicitly convert type float' to UnityEngine.Vector3’ - most likely from the spriteHeight. Any ideas?

Make sure and use the proper Code Tags.

That error is coming from the line in the fixed update, because you’re trying to assign a float to “localScale” which is a vector3.

Otherwise looking good though, try this:

public Transform endPoint;
private float spriteHeight;

private void Start()
{
    SpriteRenderer sr = GetComponent<SpriteRenderer>();
    spriteHeight = sr.bounds.size.y;
}

private void FixedUpdate()
{
    // first copy the current scale
    Vector3 newScale = transform.localScale;

    // change only the Y value
    newScale.y = Mathf.Abs(endPoint.position.y - transform.position.y) / spriteHeight;

    // apply the new scale
    transform.localScale = newScale;
}

Thank you again… that was VERY helpful. Ok, it’s stretching. But…

It’s taking the bottom end WAY beyond the plank that the endpoints are designated as. Is the scale perhaps off?

Haha, that’s interesting.

I just tested the code on my end, and it definitely works as expected.

Since the ropes appear to make an arch, i think your endpoints are fine. Are you certain you’re getting the correct SpriteRenderer in the start function? I think your Sprite Height may not be coming in correctly.

Try giving it the sprite height manually. Assign the sprite height to (spriteHeightInPixels / pixelsPerUnit) to get the worldspace height.

Using the code I posted above, I get this result stretching the black line to the center of the buddha. The line started out 80 pixels tall, and with a PixelsPerUnit of 100 it had a world space height of .8

That got it!

When I specified the height as 7f, the ropes all worked properly. I’m not sure why sr.bounds.size.y didn’t work (perhaps the scaling of the image onscreen??). Anyway, it works, I’m happy with it. Thanks!

1 Like

No problem. I’m glad it’s working. I don’t know why the bounds size was wrong. If the sprite renderer was disabled at the start, it would return zero, but I’m not sure that would cause that behavior.

In any case, problem solved more or less. Best of luck moving forward.