How to make extendable Mario warp pipe in Unity?

I want to make a warp pipe from the Mario games in Unity. The key feature of this object will be that I can set the length (height) of its "neck" to any length I require, as long as it is greater than 0.5m (the fixed depth of the interior of its "cap"). That is, I want this object to be "extrudable" and the neck material to be tilable under this operation.

I have already modeled the pipe in Blender and baked textures for the material (but I won't be upset if I have to redo the material, it just needs to be a green anistropic type, very basic). It consists of 2 pieces, the neck and the cap. The cap is a child of the neck. Only the neck grows/shrinks.

I have never done this before and am not sure how to begin. How can I create this feature? I would like the pipe to grow/shrink in the editor when I change the public variable "height".

EDIT: I just put the neck and cap back together as one object and used a bone to control the neck length. I exported the FBX and am seeing how the material looks on it at different lengths now

EDIT 2: So I tried to make a script that alters the transforms of the prefab's children in the inspector but it is not working. It doesn't work in the inspector and it doesn't work while running the game if I execute the function in the start method. The prefab contains two objects, PipeNeck and PipeCap. The script is attached to the parent inside the prefab. Here is the script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExtrudePipe : MonoBehaviour
{
public float pipeHeight = 0.5f;
private GameObject neck = GameObject.Find("PipeNeck").gameObject;
private GameObject cap = GameObject.Find("PipeCap").gameObject;

void Start()
{
Extrusion();
}

void Extrusion()
{
neck.transform.localScale = new Vector3(1, 2 * pipeHeight, 1);
cap.transform.localPosition = new Vector3(0, pipeHeight - 0.5f, 0);
}
}


EDIT 3: So I realized there were 8 new errors hidden under a hundred known warnings for something else that I didn't clear on my last test. These errors say that I can't use transform.* in monobehavior, I had to put them in a function instead. So I put it in Awake() and now the pipe is stretched when the game is played, but I would still rather get instant feedback from the editor. Here is the updated code. Does anyone know what will get me instant feedback by typing the new height into the prefab's inspector field, "pipeHeight"?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExtrudePipe : MonoBehaviour
{
public float pipeHeight = 0.5f;

void Awake()
{
Extrusion();
}

void Extrusion()
{
GameObject neck = GameObject.Find("PipeNeck").gameObject;
GameObject cap = GameObject.Find("PipeCap").gameObject;
neck.transform.localScale = new Vector3(1, 2 * pipeHeight, 1);
cap.transform.localPosition = new Vector3(0, pipeHeight - 0.5f, 0);
}
}


pipeHeight changed from "0.5" to "2":

EDIT 4: Last script was garbage, this one works when the game runs. Funny how simple things mess you up:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExtrudePipe : MonoBehaviour
{
public float pipeHeight = 1.0f;

void Awake()
{
Extrusion();
}

void Extrusion()
{
if (pipeHeight == 1.0f)
{
return;
}

GameObject neck = GameObject.Find("PipeNeck").gameObject;
GameObject cap = GameObject.Find("PipeCap").gameObject;
float scaleFactor = (pipeHeight -0.5f)/ (0.5f * neck.transform.localScale.y);
neck.transform.localScale = new Vector3(1, scaleFactor, 1);
cap.transform.localPosition = new Vector3(0, pipeHeight - 1, 0);
}
}


EDIT 5: WORKING SOLUTION, IN EDITOR AND IN GAME:

Slap this on the default parent object within the prefab (or I guess on the prefab in the scene) and it will work instantly in the editor as well as in the game. Special thanks to DMGregory, who has been tremendous help here since I started months ago!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExtrudePipe : MonoBehaviour
{
public float pipeHeight = 1.0f;

void OnValidate()
{
Extrusion();
}

void OnAwake()
{
Extrusion();
}

void Extrusion()
{
GameObject neck = GameObject.Find("PipeNeck").gameObject;
GameObject cap = GameObject.Find("PipeCap").gameObject;
neck.transform.localScale = Vector3.one;
cap.transform.position = Vector3.zero;

if (pipeHeight <= 1)
{
return;
}

float scaleFactor = (pipeHeight -0.5f)/ (0.5f * neck.transform.localScale.y);
neck.transform.localScale = new Vector3(1, scaleFactor, 1);
cap.transform.localPosition = new Vector3(0, pipeHeight - 1, 0);
}
}

• "it doesn't work" is never enough information to diagnose a problem. "doesn't work" spans everything from no effect to a slightly wrong effect to your computer catching fire and imploding. Tell is more about the specific symptoms you observe, and how the behaviour differs from what you want. – DMGregory Jul 13 '19 at 11:12
• Hang on, man, I have an update. It says I can't use transform in monobehavior. I got it to work in Awake() (while playing) but I would still it rather see instant feedback in the editor – hatinacat2000 Jul 13 '19 at 11:16
• You absolutely can use transform in a MonoBehaviour's methods, so it's probably just warning you about a syntax error or typo. Looks like your field initializers here are incorrect, for example - those should be initialized in a method, not on the loading thread. For editor use, presumably you considered the [ExecuteInEditMode] attribute? – DMGregory Jul 13 '19 at 11:18
• That's what I meant to say, sorry for not being clear, I was rushing. The hundred-x warnings I get are for an unrelated bug with the player controller (zero vector) that I have to iron out later. And no! I didn't know about that attribute, let me Google that, thanks! – hatinacat2000 Jul 13 '19 at 11:23

To edit your height in the editor, just call your extrustion method from OnValidate. This is called in the editor when you change a parameter:

void OnValidate() {
Extrusion();
}


Note that this is not called in the built game, so you'll want to either save your scene with the pipe in the desired height, or call the extrusion method at runtime in Start or similar if you need to adjust the height of a dynamically-spawned pipe.

• BOOM! That did it! Instant grat--oh, lol, I'll make sure I append it in Awake() too. – hatinacat2000 Jul 13 '19 at 12:13

Why so complicated? If you just want to linearly stretch the whole object, you could just change the y-scale of its Transform component, either directly in the Inspector or via script with transform.localScale.y.

If you want to stretch the neck of the pipe but not the cap, make them both children of the same object instead of children of each other. Change the transform.localScale of the neck and the transform.localPosition of the cap so it stays on top.

If you want to do that via script in the editor, you will need to write a custom inspector script which changes these transforms when the user changes the height of the pipe.

• Please see updated question – hatinacat2000 Jul 13 '19 at 11:06
• there are two reasons an inspector parameter is preferable: 1) I might have to set a lot of warp pipe assets in the future and it's nice not to set the transforms for child objects manually each time. 2) the localScale.y value is not directly correlated to the desired height (in the mathematical sense--I can't multiply my scale factor by a constant and produce the desired height because the height of the entire pipe is offset 0.5 from the height of the neck).That is, calculating the proper scaling for the neck would be a brain fart for me each time if I don't just program it in advance. – hatinacat2000 Jul 14 '19 at 3:51