UV mapping a procedural cylinder in Unity - unity3d

I have a method that creates a cylinder based on variables that contain the height, radius and number of sides.
The mesh generates fine with any number of sides, however I am really struggling with understanding how this should be UV mapped.
Each side of the cylinder is a quad made up of two triangles.
The triangles share vertices.
I think the placement of the uv code is correct, however I have no idea what values would be fitting?
Right now the texture is stretched/crooked on all sides of the mesh.
Please help me understand this.
private void _CreateSegmentSides(float height)
{
if(m_Sides > 2) {
float angleStep = 360.0f / (float) m_Sides;
BranchSegment seg = new BranchSegment(m_NextID++);
Quaternion rotation = Quaternion.Euler(0.0f, angleStep, 0.0f);
int index_tr = 0, index_tl = 3, index_br = 2, index_bl = 1;
float u0 = (float)1 / (float) m_Sides;
int max = m_Sides - 1;
// Make first triangles.
seg.vertexes.Add(rotation * (new Vector3(m_Radius, height, 0f)));
seg.vertexes.Add(rotation * (new Vector3(m_Radius, 0f, 0f)));
seg.vertexes.Add(rotation * seg.vertexes[seg.vertexes.Count - 1]);
seg.vertexes.Add(rotation * seg.vertexes[seg.vertexes.Count - 3]);
// Add triangle indices.
seg.triangles.Add(index_tr); // 0
seg.triangles.Add(index_bl); // 1
seg.triangles.Add(index_br); // 2
seg.triangles.Add(index_tr); // 0
seg.triangles.Add(index_br); // 2
seg.triangles.Add(index_tl); // 3
seg.uv.Add(new Vector2(0, 0));
seg.uv.Add(new Vector2(0, u0));
seg.uv.Add(new Vector2(u0, u0));
seg.uv.Add(new Vector2(u0, 0));
for (int i = 0; i < max; i++)
{
seg.vertexes.Add(rotation * seg.vertexes[seg.vertexes.Count - 2]); // new vertex
seg.triangles.Add(seg.vertexes.Count - 1); // new vertex
seg.triangles.Add(seg.vertexes.Count - 2); // shared
seg.triangles.Add(seg.vertexes.Count - 3); // shared
seg.vertexes.Add(rotation * seg.vertexes[seg.vertexes.Count - 2]); // new vertex
seg.triangles.Add(seg.vertexes.Count - 3); // shared
seg.triangles.Add(seg.vertexes.Count - 2); // shared
seg.triangles.Add(seg.vertexes.Count - 1); // new vertex
// How should I set up the variables for this part??
// I know they are not supposed to be zero.
if (i % 2 == 0) {
seg.uv.Add(new Vector2(0, 0));
seg.uv.Add(new Vector2(0, u0));
} else {
seg.uv.Add(new Vector2(u0, u0));
seg.uv.Add(new Vector2(u0, 0));
}
}
m_Segments.Add(seg);
}
else
{
Debug.LogWarning("Too few sides in the segment.");
}
}
Edit: Added pictures
This is what the cylinder looks like (onesided triangles):
This is what the same shader should look like (on a flat plane):
Edit 2: Wireframe pics

So your wireframe is okey(you linked only wireframe but i asked for shaded wireframe: this is a shaded wireframe buts its okey).
The reason your texture looks like this, is because its trying to strecth your image alongside any height, so it might look good on an 1m height cylinder, but would look stretched on an 1000m height one, so you actually need to dynamically strecth this uv map.
Example for 1m height cylinder, texture is okey cos it is for 1x1 dimension:
Example for 2m height cylinder texture stretched because double the length 2x1 dimension:
So what you can do is if you generate always the same height cylinder you can just adjust it inside unity, at the texture properties its called tiling, just increase the x or y dimension of your texture and dont forget to make the texture repeat itself.
Also your cylinder cap should look like this(it is not a must have thing but yeah):

Related

how to copy a part of a raw image

I want to copy selected part of a raw image to another image
I get start and end position as percentage and by that I can calculate the start and end position in width
how can I copy that selected part to another raw image?
Assuming it's a Texture2D, you can do the following:
Calculate A texture start/end X (dX)
Create a new Texture2D (B), sized as dX and full Y
Call A.GetPixels()
Iterate on array copying pixels to new texture
Apply on new texture
Pseudo code:
var aPixels = aTexture.GetPixels();
var bWidth = endX - startX;
var bTexture = new Texture2D(bWidth, endY);
var bPixels = bTexture.GetPixels();
for (int x = startX; x < endX; x++)
{
for (int y = 0; y < endY; y++)
{
var aIndex = x + y * A.width;
var bIndex = (x - startX) + y * bWidth;
bPixels[bIndex] = aPixels[aIndex];
}
}
bTexture.Apply();
Note that my code quite possibly won't work; as I'm typing this on a mobile phone.
Usually, Image Processing is an expensive process for CPUs, so I don't recommend it in Unity,
But anyway, For your image and in this special case, I think you can crop your image by changing the Size and Offset of texture in material.
Update:
This is an example of what I mentioned:
You can calculate Tile and Offset based on the dragged mouse position on Texture. (Check Here)
I found this.
you can set start coordinates and width and height to GetPixels();
void Start () {
public Texture2D mTexture;
Color[] c = mTexture.GetPixels (startX, startY, width, height);
Texture2D m2Texture = new Texture2D (width, height);
m2Texture.SetPixels (c);
m2Texture.Apply ();
gameObject.GetComponent<MeshRenderer> ().material.mainTexture = m2Texture;
}
```

Unity - Tinting mesh UVs

I followed the lovely tutorial by Sebastian Lague here Link to tutorial. I applied it to my own scenario where I want to generate landmass, and ended up with a cool result:
As you can see in the image there is a grid, this is simply a texture that is repeated (tiled) x amount of times and applied to the generated mesh. The code for that looks like this:
Vector2[] uvs = new Vector2[vertices.Count];
for (int i = 0; i < vertices.Count; i++)
{
float percentX = Mathf.InverseLerp(-map.GetLength(0) / 2 * squareSize, map.GetLength(0) / 2 * squareSize, vertices[i].x) * tileAmount;
float percentY = Mathf.InverseLerp(-map.GetLength(0) / 2 * squareSize, map.GetLength(0) / 2 * squareSize, vertices[i].z) * tileAmount;
uvs[i] = new Vector2(percentX, percentY);
}
mesh.uv = uvs;
I am wondering, if there is any way to tint each tile a different shade during this process, either in this script or using a shader.
Vertex colors
They will be automatically interpolated for smooth gradients. If you don't want that, you'll have to build the mesh so that each square has separate vertices, not shared with the neighboring squares.

How to manipulate a shaped area of terrain in runtime - Unity 3D

My game has a drawing tool - a looping line renderer that is used as a marker to manipulate an area of the terrain in the shape of the line. This all happens in runtime as soon as the player stops drawing the line.
So far I have managed to raise terrain verteces that match the coordinates of the line renderer's points, but I have difficulties with raising the points that fall inside the marker's shape. Here is an image describing what I currently have:
I tried using the "Polygon Fill Algorithm" (http://alienryderflex.com/polygon_fill/), but raising the terrain vertices one line at a time is too resourceful (even when the algorithm is narrowed to a rectangle that surrounds only the marked area). Also my marker's outline points have gaps between them, meaning I need to add a radius to the line that raises the terrain, but that might leave the result sloppy.
Maybe I should discard the drawing mechanism and use a mesh with a mesh collider as the marker?
Any ideas are appreciated on how to get the terrain manipulated in the exact shape as the marker.
Current code:
I used this script to create the line - the first and the last line points have the same coordinates.
The code used to manipulate the terrain manipulation is currently triggered when clicking a GUI button:
using System;
using System.Collections;
using UnityEngine;
public class changeTerrainHeight_lineMarker : MonoBehaviour
{
public Terrain TerrainMain;
public LineRenderer line;
void OnGUI()
{
//Get the terrain heightmap width and height.
int xRes = TerrainMain.terrainData.heightmapWidth;
int yRes = TerrainMain.terrainData.heightmapHeight;
//GetHeights - gets the heightmap points of the tarrain. Store them in array
float[,] heights = TerrainMain.terrainData.GetHeights(0, 0, xRes, yRes);
if (GUI.Button(new Rect(30, 30, 200, 30), "Line points"))
{
/* Set the positions to array "positions" */
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
/* use this height to the affected terrain verteces */
float height = 0.05f;
for (int i = 0; i < line.positionCount; i++)
{
/* Assign height data */
heights[Mathf.RoundToInt(positions[i].z), Mathf.RoundToInt(positions[i].x)] = height;
}
//SetHeights to change the terrain height.
TerrainMain.terrainData.SetHeights(0, 0, heights);
}
}
}
Got to the solution thanks to Siim's personal help, and thanks to the article: How can I determine whether a 2D Point is within a Polygon?.
The end result is visualized here:
First the code, then the explanation:
using System;
using System.Collections;
using UnityEngine;
public class changeTerrainHeight_lineMarker : MonoBehaviour
{
public Terrain TerrainMain;
public LineRenderer line;
void OnGUI()
{
//Get the terrain heightmap width and height.
int xRes = TerrainMain.terrainData.heightmapWidth;
int yRes = TerrainMain.terrainData.heightmapHeight;
//GetHeights - gets the heightmap points of the tarrain. Store them in array
float[,] heights = TerrainMain.terrainData.GetHeights(0, 0, xRes, yRes);
//Trigger line area raiser
if (GUI.Button(new Rect(30, 30, 200, 30), "Line fill"))
{
/* Set the positions to array "positions" */
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
float height = 0.10f; // define the height of the affected verteces of the terrain
/* Find the reactangle the shape is in! The sides of the rectangle are based on the most-top, -right, -bottom and -left vertex. */
float ftop = float.NegativeInfinity;
float fright = float.NegativeInfinity;
float fbottom = Mathf.Infinity;
float fleft = Mathf.Infinity;
for (int i = 0; i < line.positionCount; i++)
{
//find the outmost points
if (ftop < positions[i].z)
{
ftop = positions[i].z;
}
if (fright < positions[i].x)
{
fright = positions[i].x;
}
if (fbottom > positions[i].z)
{
fbottom = positions[i].z;
}
if (fleft > positions[i].x)
{
fleft = positions[i].x;
}
}
int top = Mathf.RoundToInt(ftop);
int right = Mathf.RoundToInt(fright);
int bottom = Mathf.RoundToInt(fbottom);
int left = Mathf.RoundToInt(fleft);
int terrainXmax = right - left; // the rightmost edge of the terrain
int terrainZmax = top - bottom; // the topmost edge of the terrain
float[,] shapeHeights = TerrainMain.terrainData.GetHeights(left, bottom, terrainXmax, terrainZmax);
Vector2 point; //Create a point Vector2 point to match the shape
/* Loop through all points in the rectangle surrounding the shape */
for (int i = 0; i < terrainZmax; i++)
{
point.y = i + bottom; //Add off set to the element so it matches the position of the line
for (int j = 0; j < terrainXmax; j++)
{
point.x = j + left; //Add off set to the element so it matches the position of the line
if (InsidePolygon(point, bottom))
{
shapeHeights[i, j] = height; // set the height value to the terrain vertex
}
}
}
//SetHeights to change the terrain height.
TerrainMain.terrainData.SetHeightsDelayLOD(left, bottom, shapeHeights);
TerrainMain.ApplyDelayedHeightmapModification();
}
}
//Checks if the given vertex is inside the the shape.
bool InsidePolygon(Vector2 p, int terrainZmax)
{
// Assign the points that define the outline of the shape
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
int count = 0;
Vector2 p1, p2;
int n = positions.Length;
// Find the lines that define the shape
for (int i = 0; i < n; i++)
{
p1.y = positions[i].z;// - p.y;
p1.x = positions[i].x;// - p.x;
if (i != n - 1)
{
p2.y = positions[(i + 1)].z;// - p.y;
p2.x = positions[(i + 1)].x;// - p.x;
}
else
{
p2.y = positions[0].z;// - p.y;
p2.x = positions[0].x;// - p.x;
}
// check if the given point p intersects with the lines that form the outline of the shape.
if (LinesIntersect(p1, p2, p, terrainZmax))
{
count++;
}
}
// the point is inside the shape when the number of line intersections is an odd number
if (count % 2 == 1)
{
return true;
}
else
{
return false;
}
}
// Function that checks if two lines intersect with each other
bool LinesIntersect(Vector2 A, Vector2 B, Vector2 C, int terrainZmax)
{
Vector2 D = new Vector2(C.x, terrainZmax);
Vector2 CmP = new Vector2(C.x - A.x, C.y - A.y);
Vector2 r = new Vector2(B.x - A.x, B.y - A.y);
Vector2 s = new Vector2(D.x - C.x, D.y - C.y);
float CmPxr = CmP.x * r.y - CmP.y * r.x;
float CmPxs = CmP.x * s.y - CmP.y * s.x;
float rxs = r.x * s.y - r.y * s.x;
if (CmPxr == 0f)
{
// Lines are collinear, and so intersect if they have any overlap
return ((C.x - A.x < 0f) != (C.x - B.x < 0f))
|| ((C.y - A.y < 0f) != (C.y - B.y < 0f));
}
if (rxs == 0f)
return false; // Lines are parallel.
float rxsr = 1f / rxs;
float t = CmPxs * rxsr;
float u = CmPxr * rxsr;
return (t >= 0f) && (t <= 1f) && (u >= 0f) && (u <= 1f);
}
}
The used method is filling the shape one line at a time - "The Ray Casting method". It turns out that this method starts taking more resources only if the given shape as a lot of sides. (A side of the shape is a line that connects two points in the outline of the shape.)
When I posted this question, my Line Renderer had 134 points defining the line. This also means the shape has the same number of sides that needs to pass the ray cast check.
When I narrowed down the number of points to 42, the method got fast enough, and also the shape did not lose almost any detail.
Furthermore I am planning on using some methods to make the contours smoother, so the shape can be defined with even less points.
In short, you need these steps to get to the result:
Create the outline of the shape;
Find the 4 points that mark the bounding box around the shape;
Start ray casting the box;
Check the number of how many times the ray intersects with the sides of the shape. The points with the odd number are located inside the shape:
Assign your attributes to all of the points that were found in the shape.

Vertex position relative to normal

In a surface shader, given the world's up axis (and the others too), a world space position and a normal in world space, how can we rotate the worldspace position into the space of the normal?
That is, given a up vector and a non-orthogonal target-up vector, how can we transform the position by rotating its up vector?
I need this so I can get the vertex position only affected by the object's rotation matrix, which I don't have access to.
Here's a graphical visualization of what I want to do:
Up is the world up vector
Target is the world space normal
Pos is arbitrary
The diagram is bidimensional, but I need to solve this for a 3D space.
Looks like you're trying to rotate pos by the same rotation that would transform up to new_up.
Using the rotation matrix found here, we can rotate pos using the following code. This will work either in the surface function or a supplementary vertex function, depending on your application:
// Our 3 vectors
float3 pos;
float3 new_up;
float3 up = float3(0,1,0);
// Build the rotation matrix using notation from the link above
float3 v = cross(up, new_up);
float s = length(v); // Sine of the angle
float c = dot(up, new_up); // Cosine of the angle
float3x3 VX = float3x3(
0, -1 * v.z, v.y,
v.z, 0, -1 * v.x,
-1 * v.y, v.x, 0
); // This is the skew-symmetric cross-product matrix of v
float3x3 I = float3x3(
1, 0, 0,
0, 1, 0,
0, 0, 1
); // The identity matrix
float3x3 R = I + VX + mul(VX, VX) * (1 - c)/pow(s,2) // The rotation matrix! YAY!
// Finally we rotate
float3 new_pos = mul(R, pos);
This is assuming that new_up is normalized.
If the "target up normal" is a constant, the calculation of R could (and should) only happen once per frame. I'd recommend doing it on the CPU side and passing it into the shader as a variable. Calculating it for every vertex/fragment is costly, consider what it is you actually need.
If your pos is a vector-4, just do the above with the first three elements, the fourth element can remain unchanged (it doesn't really mean anything in this context anyway).
I'm away from a machine where I can run shader code, so if I made any syntactical mistakes in the above, please forgive me.
Not tested, but should be able to input a starting point and an axis. Then all you do is change procession which is a normalized (0-1) float along the circumference and your point will update accordingly.
using UnityEngine;
using System.Collections;
public class Follower : MonoBehaviour {
Vector3 point;
Vector3 origin = Vector3.zero;
Vector3 axis = Vector3.forward;
float distance;
Vector3 direction;
float procession = 0f; // < normalized
void Update() {
Vector3 offset = point - origin;
distance = offset.magnitude;
direction = offset.normalized;
float circumference = 2 * Mathf.PI * distance;
angle = (procession % 1f) * circumference;
direction *= Quaternion.AngleAxis(Mathf.Rad2Deg * angle, axis);
Ray ray = new Ray(origin, direction);
point = ray.GetPoint(distance);
}
}

How can I draw a circle in Unity3D?

How to draw circle in Unity 3d?
I want to draw a circle around different objects.
The radiuses of the circles are different and the circles have textures - squares.
I found a big error with this code. The number of points (Size) shouldn't be "(2 * pi / theta_scale) + 1" because this causes the circle to draw 6.28 times. The size should be "1 / theta_scale + 1". So for a theta_scale of 0.01 it needs to draw 100 points, and for a theta_scale of 0.1 it needs to draw 10 points. Otherwise it would draw 62 times and 628 times respectively.
Here is the code I used.
using UnityEngine;
using System.Collections;
public class DrawRadar: MonoBehaviour {
public float ThetaScale = 0.01f;
public float radius = 3f;
private int Size;
private LineRenderer LineDrawer;
private float Theta = 0f;
void Start() {
LineDrawer = GetComponent<LineRenderer>();
}
void Update() {
Theta = 0f;
Size = (int)((1f / ThetaScale) + 1f);
LineDrawer.SetVertexCount(Size);
for (int i = 0; i < Size; i++) {
Theta += (2.0f * Mathf.PI * ThetaScale);
float x = radius * Mathf.Cos(Theta);
float y = radius * Mathf.Sin(Theta);
LineDrawer.SetPosition(i, new Vector3(x, y, 0));
}
}
}
If you modify the number in "Size" that is divided by ThetaScale, you can make a sweeping gauge/pie chart type graphic.
See Unity Answers for a similar question.
Alternatively:
float theta_scale = 0.1; // Circle resolution
LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
lineRenderer.SetColors(c1, c2);
lineRenderer.SetWidth(0.2F, 0.2F);
lineRenderer.SetVertexCount(size);
int i = 0;
for(float theta = 0; theta < 2 * PI; theta += theta_scale) {
x = r*cos(theta);
y = r*sin(theta);
Vector3 pos = new Vector3(x, y, 0);
lineRenderer.SetPosition(i, pos);
i+=1;
}
The LineRenderer requires continuous points. You can modify this code slightly to use cylinder game objects instead of a line renderer. I find the LineRenderer to be a bit hideous.
Lastly, similar to the first link, you could attach a circle texture to a unit plane. Make any part of the texture that isn't part of the circle transparent. Then just scale and align the plane to fit your object. Unfortunately this method isn't great if someone is looking almost parallel to the plane.
Jerdak's solution is good, but the code is messy so I had to tweak a little. Here's the code for a class, where I use i in the loop to avoid a bug.
It also updates the circle's position with its gameObject position.
using UnityEngine;
using System.Collections;
public class CircleDraw : MonoBehaviour {
float theta_scale = 0.01f; //Set lower to add more points
int size; //Total number of points in circle
float radius = 3f;
LineRenderer lineRenderer;
void Awake () {
float sizeValue = (2.0f * Mathf.PI) / theta_scale;
size = (int)sizeValue;
size++;
lineRenderer = gameObject.AddComponent<LineRenderer>();
lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
lineRenderer.SetWidth(0.02f, 0.02f); //thickness of line
lineRenderer.SetVertexCount(size);
}
void Update () {
Vector3 pos;
float theta = 0f;
for(int i = 0; i < size; i++){
theta += (2.0f * Mathf.PI * theta_scale);
float x = radius * Mathf.Cos(theta);
float y = radius * Mathf.Sin(theta);
x += gameObject.transform.position.x;
y += gameObject.transform.position.y;
pos = new Vector3(x, y, 0);
lineRenderer.SetPosition(i, pos);
}
}
}
Using Shader Graph we can now draw pixel perfect circle.
Once you created this graph, create a new material based on this shader.
Then create a new gameobject with a sprite renderer and set the material you just created.
You can scale the circle using the "scale" parameter of the material.
The linerenderer method in the top answers is really simple and exactly what I was looking for. I updated it for newer versions of Unity and some small tweaks to make it a bit more beginner/user friendly.
Specifically:
LineRenderer.SetVertexCount() is deprecated in newer versions of Unity, replaced with positionCount
Replaced theta scale with an actual segment count to remove guesswork
Added loop setting - not sure if this was in older versions of Unity, it can be set in the LineRenderer's inspector
Removed unnecessary Update function - the rendered line is a persistent gameobject
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class DrawRing : MonoBehaviour
{
public LineRenderer lineRenderer;
[Range(6,60)] //creates a slider - more than 60 is hard to notice
public int lineCount; //more lines = smoother ring
public float radius;
public float width;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.loop = true;
Draw();
}
void Draw() //Only need to draw when something changes
{
lineRenderer.positionCount = lineCount;
lineRenderer.startWidth = width;
float theta = (2f * Mathf.PI) / lineCount; //find radians per segment
float angle = 0;
for (int i = 0; i < lineCount; i++)
{
float x = radius * Mathf.Cos(angle);
float y = radius * Mathf.Sin(angle);
lineRenderer.SetPosition(i, new Vector3(x, 0, y));
//switch 0 and y for 2D games
angle += theta;
}
}
}
Note this is assumed to be attached to the gameobject you want the ring around. So the Use World Space option in LineRenderer should be unchecked. Also remember that the scale of the gameobject will affect the position of the points and the width of the line.
To put this on the ground (as in a unit selection circle):
Put the script on a separate gameobject
Rotate the gameobject X to 90
Check use world space on the linerenderer
Set the linerenderer Alignment to Transform Z
Add the position of the thing you want to circle to x and y in SetPosition. Possibly along with replacing 0 with 0.1f or a yOffset variable to avoid z-fighting with terrain.
Circle can draw using shader - draw pixel if it on radius from center.
Did the following with a Sprite. Chan is flying in the scene, so she's slightly above the plane. I had her flying so I could get a good screenshot, not because it wouldn't play well with the plane.
I used a low-resolution circle sprite.
X rotation 90
Scale X 15, Y 15, Z 1
Then I set the Sorting Layer, so it will render above the Default Layer. I was testing this out when I came across this post. It doesn't handle shadows well. I'd have to figure out what layer shadows are drawn on to make sure they get rendered onto the sprite.
I have a shader from which I usually start making effects like lens flares, and it makes a circle. Using shader is the best choice because you will get perfectly smooth and round circle.
Also it's easy to experiment with and tune the shader since shader changes don't require recompile and re-entering of play mode.
I recommend ti create extension method to GameObject. Worked good to me.
public static class GameObjectExtension
{
const int numberOfSegments = 360;
public static void DrawCircle(this GameObject go, float radius,
float lineWidth, Color startColor, Color endColor, bool lineRendererExists=true)
{
LineRenderer circle = lineRendererExists ? go.GetComponent<LineRenderer>() : go.AddComponent<LineRenderer>();
circle.useWorldSpace = false;
circle.startWidth = lineWidth;
circle.endWidth = lineWidth;
circle.endColor = endColor;
circle.startColor = startColor;
circle.positionCount = numberOfSegments + 1;
Vector3 [] points = new Vector3[numberOfSegments + 1];
for (int i = 0; i < numberOfSegments + 1; i++)
{
float rad = Mathf.Deg2Rad * i;
points[i] = new Vector3(Mathf.Sin(rad) * radius, 0, Mathf.Cos(rad) * radius);
}
circle.SetPositions(points);
}
}
One More thing to note: If LineRenderer component is not applied last parameter has to be false
create a static class to reuse the code for different game objects. player, enemies... when the class is static, you cannot create the instance of it
public static class CircleGameObject
{
// in static class methods have to be static as well
// "this" refers to the context that we are calling DrawCircle
public static async void DrawCircle(this GameObject container,float radius,float lineWidth)
{
// I provide 360 points because circle is 360 degrees and we will connect them with line
var segments=360;
// LineRenderer is used to draw line
var lineRenderer=container.AddComponent<LineRenderer>();
// now you can use position system relative to the parent game object.
lineRenderer.useWorldSpace=false;
lineRenderer.startWidth=lineWidth;
lineRenderer.endWidth=lineWidth;
lineRenderer.positionCount=segments+1;
// reserve empty array in memory with a size of lineRenderer.positionCount
var points=new Vector3[lineRenderer.positionCount];
// draw all of those points
for(int i=0;i<points.Length;i++)
{
// converting degree to radian because Mathf.Cos and Mathf.Sin expects radian
var radian=Mathf.Deg2Rad*i;
// y direction needs to be 0
// Mathf.Cos(radiant) will give the x position on the circle if the angle size is "radian"
// Mathf.Sin(radiant) will give the y position on the circle if the angle size is "radian"
// after for loop completes we would be getting 360 points
points[i]=new Vector3(Mathf.Cos(radian)*radius,0,Mathf.Sin(radian)*radius);
}
lineRenderer.SetPositions(points);
}
}
then call it in Awake of the context
public class PlayerController : MonoBehaviour
{
private void Awake()
{
GameObject go=new GameObject{
name="Circle"
};
Vector3 circlePosition=Vector3.zero;
go.transform.parent=transform;
// localPosition is relative to the parent
go.transform.localPosition=circlePosition;
go.DrawCircle(2.0f,0.03f);
....
}
}