- Home /
Making a repeating chain of Sprites
I want a make a dynamic chain of sprites, where I take one sprite and unity repeats it between two points (represented by the vector3 positions of two empties). Here's a gif I created in Blender which represents the behaviour I want:
Have you tried figuring it out for yourself? It's much better to at least have a go at making it, since you might get it right, and you'll learn loads. If you have already tried, we might be able to help you out if you share your code.
I've tried a few things but I always get stuck at the positioning of the chain links.
What I got so far is to get the distance between the two points and use that to deter$$anonymous$$e the number of chain links. Then, I would use Vector3.lerp , but I don't know what value to give the lerp function
Also, I know I should be doing object pooling to decrease the use of the garbage collector, but I also don't know how to deter$$anonymous$$e which links should be deleted and which ones to re-position.
Here's the current script (it's a terrible mess):
if (UpdateChain) {
RoundedDistance = $$anonymous$$athf.RoundToInt (Vector3.Distance (transform.position, ConnectTo.position) * 100);
for (int i = 0; i < ChainInstances.Count; i ++) {
DestroyImmediate (ChainInstances [i]);
}
ChainInstances = new List<GameObject> ();
for (int i = 0; i < RoundedDistance/100; i++) {
ChainInstances.Add (Instantiate (ChainPrefab) as GameObject);
ChainInstances [i].transform.position = Vector3.Lerp (
transform.position,
ConnectTo.position,
(float)i/((float) RoundedDistance)
);
}
}
Answer by ElijahShadbolt · Oct 16, 2016 at 06:03 AM
The 'LineRenderer' component can be used to make a repeating texture like the one you described, and it always faces the camera. Note that its Material must be a Particle Shader, which means the texture may have transparency where it should be opaque.
Add a LineRenderer to the first GameObject
set its UseWorldSpace to true
Create a new Material
Set its Shader to Particles/Additive (LineRenderers only work with Particle shaders)
Set your texture (i.e. chain link sprite) as its Particle Texture
Set this Material as the LineRenderer's first Material
Add the new SpriteLine script below to the GameObject with the LineRenderer
Set the second GameObject as its ConnectTo
And you're good to go!
If you want the line to overlap the ConnectTo point instead of stopping before it, change Mathf.FloorToInt
to Mathf.CeilToInt
.
'Sprite Scale' is how long each sprite should be. If you want to change the width of the sprite, change both the 'Start Width' and 'End Width' of the LineRenderer's 'Parameters' block.
SpriteLine.cs
using UnityEngine;
public class SpriteLine : MonoBehaviour
{
public Transform ConnectTo;
public bool UpdateChain = true;
public float spriteScale = 1f;
void Update()
{
LineRenderer renderer = GetComponent<LineRenderer>();
if (renderer != null)
{
if (ConnectTo != null)
{
if (UpdateChain)
{
int spriteCount = Mathf.FloorToInt(Vector3.Distance(ConnectTo.position, transform.position) / spriteScale);
Vector3[] positions = new Vector3[] {
transform.position,
(ConnectTo.position - transform.position).normalized * spriteScale * spriteCount
};
renderer.SetVertexCount(positions.Length);
renderer.SetPositions(positions);
if (renderer.material != null)
renderer.material.mainTextureScale = new Vector2(spriteScale * spriteCount, 1);
else
Debug.LogError(name + "'s Line Renderer has no material!");
}
}
else
{
renderer.SetVertexCount(0);
}
}
else
{
Debug.Log(name + " has no LineRenderer component!");
}
}
}
If you do not want to use a LineRenderer, you could take this approach, where you make a $$anonymous$$esh from scratch. With this you can use any type of $$anonymous$$aterial shader.
Add the below script to an empty GameObject
Create a different GameObject with $$anonymous$$eshFilter and $$anonymous$$eshRenderer components
$$anonymous$$ake sure the $$anonymous$$eshFilter's $$anonymous$$esh is set to 'none'
Set this new GameObject as the SpriteLine3's 'Sprite Renderer' object.
SpriteLine3.cs
using UnityEngine;
public class SpriteLine3 : $$anonymous$$onoBehaviour
{
public Transform ConnectTo;
public bool UpdateChain = true;
public Vector2 spriteScale = new Vector2(1f, 1f);
public GameObject spriteRenderer; // must have $$anonymous$$eshFilter and $$anonymous$$eshRenderer components
void Update()
{
$$anonymous$$eshFilter filter = spriteRenderer.GetComponent<$$anonymous$$eshFilter>();
Renderer renderer = spriteRenderer.GetComponent<Renderer>();
if (filter != null && renderer != null)
{
if (ConnectTo != null)
{
if (UpdateChain)
{
int spriteCount = $$anonymous$$athf.FloorToInt(Vector3.Distance(ConnectTo.position, transform.position) / spriteScale.y); // or CeilToInt or RoundToInt
if (spriteCount > 0)
{
renderer.enabled = true;
// move the renderer to this point
spriteRenderer.transform.position = transform.position;
Vector3 toTarget = ConnectTo.position - transform.position,
toCameraFrom$$anonymous$$id = Camera.main.transform.position - (toTarget / 2);
// rotate to have the mesh face the camera
// if any of the vectors are Vector3.zero, just set rotation to default
spriteRenderer.transform.rotation = (toTarget.sqr$$anonymous$$agnitude > 0.001f && toCameraFrom$$anonymous$$id.sqr$$anonymous$$agnitude > 0.001f) ?
Quaternion.LookRotation(toTarget, toCameraFrom$$anonymous$$id) : Quaternion.identity;
float length = spriteScale.y * spriteCount; // distance to point where sprites stop
float width = spriteScale.x / 2;
// create the mesh
Vector3[] vertices = new Vector3[] {
new Vector3(-width, 0, 0),
new Vector3(-width, 0, length),
new Vector3(width, 0, length),
new Vector3(width, 0, length),
new Vector3(width, 0, 0),
new Vector3(-width, 0, 0),
};
int[] triangles = new int[]
{
0, 1, 2,
3, 4, 5,
};
Vector2[] uvs = new Vector2[]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(1, 1),
new Vector2(1, 1),
new Vector2(0, 1),
new Vector2(0, 0),
};
if (filter.mesh == null)
filter.mesh = new $$anonymous$$esh();
$$anonymous$$esh mesh = filter.mesh;
mesh.name = "Sprite Line $$anonymous$$esh";
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
// scale the texture
if (renderer.material != null)
renderer.material.mainTextureScale = new Vector2(length, 1);
else
Debug.LogError(name + "'s $$anonymous$$esh Renderer has no material!");
}
else
{
renderer.enabled = false;
}
}
}
else
{
renderer.enabled = false;
}
}
else
{
if (filter == null)
Debug.Log(name + " has no $$anonymous$$esh Filter component!");
if (renderer == null)
Debug.Log(name + " has no $$anonymous$$esh Renderer component!");
}
}
}
Wow! This is very useful! I only have one problem right now. It only shows one instance of the sprite, and makes the rest transparent. However, is seems like the actual mesh generation is on point, just not the UV mapping.
Particles>Additive or Unlit>Transparent
Standard Shader>Transparent:
(Also the sprite rotation is off but I'll fix that later )
Go to the sprite texture in your assets, and make sure its 'Wrap $$anonymous$$ode' is set to Repeat ins$$anonymous$$d of Clamp.
You can rotate the sprite by using different UVs.
Beneath Vector2 spriteScale = new Vector2(1f, 1f);
add these lines. This lets you choose the sprite direction in the inspector (you would want Bottom To Top)
public enum TextureDirection { leftToRight, bottomToTop }
public TextureDirection spriteDirection;
Replace the code that creates the uvs
array with these lines. This checks if it is left to right, and if not it does bottom to top.
Vector2[] uvs = spriteDirection == TextureDirection.leftToRight ?
new Vector2[]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(1, 1),
new Vector2(1, 1),
new Vector2(0, 1),
new Vector2(0, 0),
} :
new Vector2[] // bottomToTop
{
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(1, 0),
new Vector2(1, 1),
new Vector2(0, 1),
};
And where you scale the texture by setting renderer.material.mainTextureScale
, use this ins$$anonymous$$d.
renderer.material.mainTextureScale = spriteDirection == TextureDirection.leftToRight ? new Vector2(spriteCount, 1) : new Vector2(1, spriteCount);
Excellent! The looping works. There's only one more thing, if you don't $$anonymous$$d. The "Sprite Scale" variable doesn't work properly. Only the X scale works, but the Y only offsets it sorta.
Oh yeah, when setting the material's mainTextureScale to a new Vector2, change length
to spriteCount
.
(I also edited the line in the previous comment)
Your answer
![](https://koobas.hobune.stream/wayback/20220612094554im_/https://answers.unity.com/themes/thub/images/avi.jpg)