I’m trying to do something similar to this one. Of course the whole image is just a reference. Can I modify per segments the slider in order to achieve the path from point A to point B with the slider because I also want to benefit from the fill and handle that showing the current position?
UI Toolkit has tools to generate your own meshes/lines via code:
Thanks as always Spiney!
The only thing that I can’t really get to work is how can I set the texture2D instead of a circle. For now I’m using a circle in order to track the position but I also want as an optional attribute to set a texture instead of a circle can you help me with?
here’s the snippet of the code:
[UxmlAttribute] public Texture2D handlerTexture;
[UxmlAttribute] public Vector2 handlerSize = new(20, 20);
if (handlerTexture != null)
{
Rect texRect = new Rect(handlerPos.x - handlerSize.x * 0.5f, handlerPos.y - handlerSize.y * 0.5f, handlerSize.x, handlerSize.y);
//painter.DrawTexture(handlerTexture, texRect);
}
else
{
painter.fillColor = handlerColor;
painter.BeginPath();
painter.Arc(handlerPos, handlerRadius, 0, 360);
painter.Fill();
}
Here’s the result but I can’t really set any texture on it
You would have to place a visual element sprite/image there, or draw a vector image: Unity - Scripting API: UIElements.MeshGenerationContext.DrawVectorImage
Not really sure where I should place it
void OnGenerateVisualContent(MeshGenerationContext ctx)
{
if (points == null || points.Count < 2) return;
var painter = ctx.painter2D;
painter.lineWidth = lineWidth;
painter.strokeColor = backgroundColor;
painter.lineJoin = LineJoin.Round;
painter.lineCap = LineCap.Round;
painter.BeginPath();
painter.MoveTo(points[0]);
for (int i = 1; i < points.Count; i++)
{
painter.LineTo(points[i]);
}
painter.Stroke();
var filledPath = GetFilledPath(points, fillProgress);
if (filledPath.Count >= 2)
{
painter.strokeColor = fillColor;
painter.BeginPath();
painter.MoveTo(filledPath[0]);
for (int i = 1; i < filledPath.Count; i++)
{
painter.LineTo(filledPath[i]);
}
painter.Stroke();
}
Vector2 handlerPos = GetPointAlongPath(points, fillProgress);
if (handlerTexture != null)
{
Rect texRect = new Rect(handlerPos.x - handlerSize.x * 0.5f, handlerPos.y - handlerSize.y * 0.5f, handlerSize.x, handlerSize.y);
//painter.DrawTexture(handlerTexture, texRect);
}
else
{
painter.fillColor = handlerColor;
painter.BeginPath();
painter.Arc(handlerPos, handlerRadius, 0, 360);
painter.Fill();
}
}
Should I set something like this? ctx.visualElement.style.backgroundImage = handlerTexture;
No, you put another visual element at the right spot. One that’s a child of the element you’re drawing the mesh in.
I’m still getting the error
Rect texRect = new Rect(handlerPos.x - handlerSize.x * 0.5f, handlerPos.y - handlerSize.y * 0.5f, handlerSize.x, handlerSize.y);
Vertex[] vertices = new Vertex[4];
ushort[] indices = new ushort[] { 0, 1, 2, 2, 3, 0 };
vertices[0] = new Vertex { position = new Vector3(texRect.xMin, texRect.yMin, Vertex.nearZ), tint = Color.white, uv = new Vector2(0, 0) };
vertices[1] = new Vertex { position = new Vector3(texRect.xMin, texRect.yMax, Vertex.nearZ), tint = Color.white, uv = new Vector2(0, 1) };
vertices[2] = new Vertex { position = new Vector3(texRect.xMax, texRect.yMax, Vertex.nearZ), tint = Color.white, uv = new Vector2(1, 1) };
vertices[3] = new Vertex { position = new Vector3(texRect.xMax, texRect.yMin, Vertex.nearZ), tint = Color.white, uv = new Vector2(1, 0) };
MeshWriteData meshWriteData = ctx.Allocate(vertices.Length, indices.Length);
meshWriteData.SetAllVertices(vertices);
meshWriteData.SetAllIndices(indices);
ctx.DrawMesh(vertices, indices, handlerTexture);
So in order to apply any texture I should create a quad or anything to displaying on it right?
You are literally not doing what I’m saying at all.
I don’t get it… I’m sorry
What part of another visual element do you not understand? Instance a visual element, make a child of the one you’re drawing the lines in. Make the background image the sprite you want; size it as necessary. Then position it where you want it. Can I make it any more clear?
Yes and that’s what I’m explaining how to do. You put a visual element there to act as the sprite/texture. Why is this so hard to understand?
You will have to drive its position. Naturally you should have some data to build the path, so you can use the same data to position this visual element.
Okay I did this
private readonly VisualElement handlerVisual;
[UxmlAttribute] public Texture2D handlerTexture;
public ZigZagLineElement()
{
handlerVisual = new VisualElement
{
style =
{
position = Position.Absolute,
backgroundImage = new StyleBackground(handlerTexture),
unityBackgroundImageTintColor = new StyleColor(Color.white),
backgroundSize = new StyleBackgroundSize(new BackgroundSize(BackgroundSizeType.Contain))
}
};
Add(handlerVisual);
generateVisualContent += OnGenerateVisualContent;
}
if (handlerTexture != null)
{
float scaledWidth = handlerSize.x * handlerScale;
float scaledHeight = handlerSize.y * handlerScale;
handlerVisual.style.width = scaledWidth;
handlerVisual.style.height = scaledHeight;
handlerVisual.style.translate = new Vector3(
handlerPos.x - scaledWidth * 0.5f,
handlerPos.y - scaledHeight * 0.5f,
0f
);
}
else
{
painter.fillColor = handlerColor;
painter.BeginPath();
painter.Arc(handlerPos, handlerRadius, 0, 360);
painter.Fill();
}
but after I have assigned the texture it doesn’t apply to this element. I still can see that the visual element is following the path but it doesn’t displaying any texture on it…
I tried manually apply the texture to this element through uss but it still not displaying. The only thing that I can display is background set to black color
use the image visual element: Unity - Scripting API: Image
Sounds like a bug to me. In the documentation it shows this as a correct way
handlerVisual.style.backgroundImage = new StyleBackground(handlerTexture);
but nothing happens
Nevermind it didn’t worked for me setting it directly though constructor I fixed it forcing it multiple times using schedule execute. After refreshing it more 3 times the image pop up





