Unity make a curved line with existing points - c#

I found the following class, which takes in a Vector3 array of points and returns a Vector3 array of new points to make a curve. You can see the result in the following screenshot, where the cubes are the original points, and the blue line is the result of the class.
What I would like to achieve are curved lines, but have the lines connected to the cubes just like the first and last cube for a result like this (blue points = original points; orange points = new points):
public class Curver : MonoBehaviour{
//arrayToCurve is original Vector3 array, smoothness is the number of interpolations.
public static Vector3[] MakeSmoothCurve(Vector3[] arrayToCurve, float smoothness){
List<Vector3> points;
List<Vector3> curvedPoints;
int pointsLength = 0;
int curvedLength = 0;
if(smoothness < 1.0f) smoothness = 1.0f;
pointsLength = arrayToCurve.Length;
curvedLength = (pointsLength*Mathf.RoundToInt(smoothness))-1;
curvedPoints = new List<Vector3>(curvedLength);
float t = 0.0f;
for(int pointInTimeOnCurve = 0;pointInTimeOnCurve < curvedLength+1;pointInTimeOnCurve++){
t = Mathf.InverseLerp(0,curvedLength,pointInTimeOnCurve);
points = new List<Vector3>(arrayToCurve);
for(int j = pointsLength-1; j > 0; j--){
for (int i = 0; i < j; i++){
points[i] = (1-t)*points[i] + t*points[i+1];
}
}
curvedPoints.Add(points[0]);
}
return(curvedPoints.ToArray());
}
}
Here is how I am using the class:
void OnDrawGizmos(){
Vector3[] points = Curver.MakeSmoothCurve(toVector3Array(wayPoints), 10f);
bool ptset = false;
Vector3 lastpt = Vector3.zero;
for(int j = 0; j < points.Length; j++){
Vector3 wayPoint = points[j];
if(ptset){
Gizmos.color = new Color(0, 0, 1, 0.5f);
Gizmos.DrawLine(lastpt, wayPoint);
}
lastpt = wayPoint;
ptset = true;
}
if(isCircular){
Gizmos.DrawLine(lastpt, wayPoints[0].position);
}
}

Related

Normal issues in Unity

We are trying, with a friend, to modelise a tree on unity. To do so, we want to modelise a tree trunk. We are doing it with the following code :
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Frustum : MonoBehaviour
{
/*====== PUBLIC ======*/
public float height = 1;
public float top_radius = 0.9F;
public float base_radius = 1F;
public int circle_subdivisions = 30;
/*====== PRIVATE ======*/
private Mesh mesh;
private Vector3[] vao;
private int[] ibo;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
mesh.name = "Cone";
this.GetComponent<MeshFilter>().mesh = mesh;
draw();
}
// OnValidate is called at each public input change
private void OnValidate() {
draw();
}
private void draw() {
ComputeFrustumVao();
ComputeFrustumIbo();
mesh.Clear();
mesh.vertices = vao;
mesh.triangles = ibo;
mesh.RecalculateBounds();
mesh.RecalculateTangents();
mesh.RecalculateNormals();
// mesh.Optimize();
}
private void ComputeFrustumVao()
{
// COMPUTE BASE VERTICES
List<Vector3> vertices = ComputeCircleVertices(transform.position, base_radius, circle_subdivisions);
// ADD TOP VERTICES
Vector3 top_origin = transform.position + new Vector3(0, height, 0);
vertices.AddRange(ComputeCircleVertices(top_origin, top_radius, circle_subdivisions));
vao = vertices.ToArray();
}
private List<Vector3> ComputeCircleVertices(Vector3 origin, float radius, int nb_subdivisions) {
List<Vector3> vao = new List<Vector3>();
float step_angle = (2*Mathf.PI)/nb_subdivisions;
for(int i = 0; i < nb_subdivisions; ++i){
float current_angle = step_angle * i;
Vector3 translate_vector = new Vector3(
Mathf.Cos(current_angle) * radius, // x coordinate
0, // y coordinate
Mathf.Sin(current_angle) * radius // z coordinate
);
Vector3 new_vertex = origin + translate_vector;
vao.Add(new_vertex);
}
return vao;
}
private void ComputeFrustumIbo() {
List<int> indices = new List<int>();
int nb_vertices = vao.Length;
int nb_vertices_per_circle = nb_vertices/2;
int nb_triangles = nb_vertices_per_circle - 2;
// DRAW BASE
for(int i = 0; i < nb_triangles; ++i){
indices.Add(0);
indices.Add(i + 1);
indices.Add(i + 2);
}
// DRAW TOP
for(int i = nb_vertices_per_circle; i < nb_vertices_per_circle + nb_triangles; ++i){
indices.Add(nb_vertices_per_circle);
indices.Add(i + 2);
indices.Add(i + 1);
}
// DRAW SIDES
for(int i = 0; i < nb_vertices_per_circle; ++i){
int next_index = (i + 1) % nb_vertices_per_circle;
indices.Add(i); // BOTTOM LEFT
indices.Add( nb_vertices_per_circle + next_index); // TOP RIGHT
indices.Add(next_index); // BOTTOM RIGHT
indices.Add(i); // BOTTOM LEFT
indices.Add(i + nb_vertices_per_circle); // TOP LEFT
indices.Add(nb_vertices_per_circle + next_index); // TOP RIGHT
}
ibo = indices.ToArray();
}
void OnDrawGizmosSelected() //Draw the markers, if the object is selected
{
Gizmos.color = Color.red;
for (int i=0; i< mesh.vertices.Length; i++){
Gizmos.color= new Color(((float)i)/mesh.vertices.Length,0, 0);
Vector3 worldPos = transform.TransformPoint(mesh.vertices[i]);
Gizmos.DrawCube(worldPos, Vector3.one * 0.1f);
}
}
}
`
With this code we can create a 3D shape like a cube or a cylinder but when we create our top and our base ( in DRAW TOP and DRAW BASE) it makes the normal looks weird and I dont understand why. Can you help me to understand it please ?
our Normal problem
We know that ussually people create their models in 3D software like blender but we really need to do it in unity for our project.
I've seen that if my normals are pointing to the top the face are too illuminated but when they are facing down it's too shady. To create our base and top shape, we have followed this tutorial

I am not able to successfully move my background with the script below

The script below is intended to move the background with the character. However, the background does not move at all when using this code and I suspect the problem may be somewhere in the LateUpdate function as once that is changed to update, the background continuously moves on its own, but not with the player.
public class ParallaxController : MonoBehaviour
{
Transform cam; // Main Camera
Vector3 camStartPos;
Vector2 distance;
GameObject[] backgrounds;
Material[] mat;
float[] backSpeed;
float farthestBack;
[Range(0f,0.05f)]
public float parallaxSpeed;
void Start()
{
cam = Camera.main.transform;
camStartPos = cam.position;
int backCount = transform.childCount;
mat = new Material[backCount];
backSpeed = new float[backCount];
backgrounds = new GameObject[backCount];
for (int i = 0; i < backCount; i++)
{
backgrounds[i] = transform.GetChild(i).gameObject;
mat[i] = backgrounds[i].GetComponent<Renderer>().material;
}
BackSpeedCalculate(backCount);
}
void BackSpeedCalculate(int backCount)
{
for (int i = 0; i < backCount; i++)
{
if ((backgrounds[i].transform.position.z - cam.position.z) > farthestBack)
{
farthestBack = backgrounds[i].transform.position.z - cam.position.z;
}
}
for (int i = 0; i < backCount; i++)
{
backSpeed[i] = 1 - (backgrounds[i].transform.position.z - cam.position.z) / farthestBack;
}
}
void LateUpdate()
{
distance = cam.position - camStartPos;
transform.position = new Vector3(cam.position.x, transform.position.y, 0);
for (int i = 0; i < backgrounds.Length; i++)
{
float speedX = backSpeed[i] * parallaxSpeed;
float speedY = speedX / 2; // if you close Y movement , set to 0
mat[i].SetTextureOffset("_MainTex", new Vector2(distance.x*speedX, distance.y*speedY));
Debug.Log("for loop works!");
}
}
}

How can I set random positions on radius?

In this script I'm creating a circle with specific radius size and get the radius size :
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(LineRenderer))]
public class DrawRadiusAroundTurret : MonoBehaviour
{
[Range(0, 50)]
public int segments = 50;
[Range(0, 5)]
public float xradius = 5;
[Range(0, 5)]
public float yradius = 5;
[Range(0.1f, 5f)]
public float width = 0.1f;
LineRenderer line;
void Start()
{
line = gameObject.GetComponent<LineRenderer>();
line.enabled = true;
line.positionCount = segments + 1;
line.widthMultiplier = width;
line.useWorldSpace = false;
CreatePoints();
}
private void Update()
{
CreatePoints();
}
public Vector3[] CreatePoints()
{
line.widthMultiplier = width;
float x;
float y;
float z;
float angle = 20f;
for (int i = 0; i < (segments + 1); i++)
{
x = Mathf.Sin(Mathf.Deg2Rad * angle) * xradius;
y = Mathf.Cos(Mathf.Deg2Rad * angle) * yradius;
line.SetPosition(i, new Vector3(x, 0f, y));
angle += (380f / segments);
}
var positions = new Vector3[line.positionCount];
return positions;
}
}
And in this script I have this method and I want the last point/s to be set randomly on the radius edge positions :
private void GeneratePointsInTracks()
{
var startPoints = GameObject.FindGameObjectsWithTag("Start Point");
var curvedLines = GameObject.FindGameObjectsWithTag("Curved Line");
for (int i = 0; i < startPoints.Length; i++)
{
for (int x = 0; x < numberOfPointsInTrack; x++)
{
GameObject go = Instantiate(tracksPrefab, curvedLines[i].transform);
go.name = "Point In Track";
go.transform.position = turrent.position + new Vector3(Random.Range(-100f, 100f), Random.Range(-100f, 100f), Random.Range(-100f, 100f));
if(x == numberOfPointsInTrack - 1)
{
go.name = "Last Point In Track";
for(int y = 0; y < drawRadius.CreatePoints().Length; y++)
{
go.transform.position = new Vector3(Random.Range(0,1)[y].x,
drawRadius.CreatePoints()[y].y,
drawRadius.CreatePoints()[y].z);
}
}
}
}
}
I tried this :
go.transform.position = new Vector3(Random.Range(0,1)[y].x,
drawRadius.CreatePoints()[y].y,
drawRadius.CreatePoints()[y].z);
but the random on the x give error :
Cannot apply indexing with [] to an expression of type 'int'
The first script create a circle like this :
And this is an example I drawed in paint just to show what I mean that I said I want the endPoints in the second script to be position randomly on the circle edges :
So each "Last Point In Track" object should be position randomly on the circle edge like in the second screenshot.
If you know the center and radius it is fairly easy to get random points on the circle:
go.name = "Last Point In Track";
Vector2 p = Random.insideUnitCircle.normalized * radius;
go.transform.position = center + new Vector3(p.x, 0, p.y);
To get rid of the edge case where Random.insideUnitCircle is to small to be normalized you should use:
Vector2 RandomOnUnitCircle(){
Vector2 result = Vector2.zero;
do{
result = Random.insideUnitCircle.normalized;
}while(result == Vector2.zero);
return result;
}
I think what you want is this:
Vector3[] points = drawRadius.CreatePoints(); //get all edge points
Vector3 randomPoint = points[Random.Range(0, points.Length)]; //pick a random one
go.transform.position = randomPoint; //set go.transform.position to position of random point
Sorry if I am misunderstanding your intentions, but I hope this works for you!

Making a hollow voxel cone

I'm trying to create a voxel-style cone shape in C#. I've got the cone building using cubes, but can't work out how to only build the outer layer (make it hollow) rather than build a solid cone as shown in the pic.
The code so far (Edited from someone else's script)
// Create a cone made of cubes. Can be called at runtime
public void MakeVoxelCone()
{
for (int currentLength = 0; currentLength < maxLength; currentLength++)
MakeNewLayer(currentLength);
}
// Make a new layer of cubes on the cone
private void MakeNewLayer(int currentLength)
{
center = new Vector3(0, currentLength, 0);
for (int x = -currentLength; x < currentLength; x++)
{
for (int z = -currentLength; z < currentLength; z++)
{
// Set position to spawn cube
Vector3 pos = new Vector3(x, currentLength, z);
// The distance to the center of the cone at currentLength point
float distanceToMiddle = Vector3.Distance(pos, center);
// Create another layer of the hollow cone
if (distanceToMiddle < currentLength)
{
// Add all cubes to a List array
Cubes.Add(MakeCube(pos));
}
}
}
}
// Create the cube and set its properties
private GameObject MakeCube(Vector3 position)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.GetComponent<Renderer>().material.color = Color.blue;
if (!AddCollider)
Destroy(cube.GetComponent<BoxCollider>());
cube.transform.position = position;
cube.transform.parent = transform;
cube.name = "cube [" + position.y + "-" + position.x + "," + position.z + "]";
return cube;
}
I think its probably simple, but cant figure it out.
Maybe something about the if (distanceToMiddle < currentLength) part, but swapping < for == breaks the whole shape.
#Jax297 >= (currentLength-1) Closer, but not correct just yet. Now its a pyramid with a cone cut out.
assuming your currentlength is the outer diameter you have to introduce a thickness vareable and compare to currentleght - thickness, so there is a inner diameter to be kept free
(currentLength - thickness) < distanceToMiddle && distanceToMiddle < currentLength
If you want to create a "empty" cone. Try to describe that shape in English first.
Create a circle at height going from 0...h with increasing radius for each step from 0..r
So now you need to find equation of a circle (should take you back to HS trig): sin(theta)^2 + cos(theta)^2 = radius:
Now you want to create this circle while increasing the height.
This will create only the empty cone you wish.
Here's an quick implementation (adjust as needed):
public List<GameObject> instantiatedObjects = new List<GameObject>();
public GameObject rootParent;
public int numOfRows = 10;
public int incrementPerRow = 4;
public float radiusStep = 0.5f;
public float height = 5;
[Button]
public void CreateVoxelCone()
{
// first one. root object.
rootParent = GameObject.CreatePrimitive(PrimitiveType.Cube);
rootParent.name = "Pivot";
rootParent.transform.position = Vector3.zero;
var itemsForThisRow = incrementPerRow;
var heightStep = height / numOfRows;
// for each row...
for (int i = 0; i < numOfRows; i++)
{
// create items in a circle
var radianStep = Mathf.PI * 2 / itemsForThisRow;
for (float j = 0; j < Mathf.PI*2; j=j+radianStep)
{
var newPosition = new Vector3(Mathf.Cos(j) * radiusStep * i, heightStep * i, Mathf.Sin(j) * radiusStep*i);
var point = GameObject.CreatePrimitive(PrimitiveType.Cube);
point.name = ($"Row: {i}, {j}");
point.transform.SetParent(rootParent.transform);
point.transform.localPosition = newPosition;
instantiatedObjects.Add(point);
}
itemsForThisRow += incrementPerRow;
}
}
[Button]
public void CleanUp()
{
DestroyImmediate(rootParent);
}

Can I have some help optimizing this script

I wrote a infinite terrain script which works! Saddly everytime the player moves a chunk it lags for a moment. I know my code isn't great but I'm here to learn why :D
I'm unsure of what else to do. I've looked online and found no simple or understandable solution to me because I just don't know enough so I tried to write it on my own and it works but barley.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GEN_InfiniteTerrain : MonoBehaviour
{
public GameObject targetObject;
public GameObject chunkObject;
public int chunkSize;
public float unitSize;
public int renderDistance;
Dictionary<Vector2, GameObject> gridOfChunks = new Dictionary<Vector2, GameObject>();
List<Vector2> expectedChunkGridPositions = new List<Vector2>();
public float noiseScale;
// Infinite terrain values
float absoluteChunkSize;
private void Start()
{
// Calculate absolute chunk size
GetAbsoluteChunkSize();
// Generate base world
GenerateBase();
}
Vector2 lastTargetGridPosition = Vector2.zero;
private void LateUpdate()
{
// Get the targets position in world space
Vector3 targetAbsolutePosition = targetObject.transform.position;
// Convert the targets world position to grid position (/ 10 * 10 is just rounding to 10)
Vector2 targetGridPosition = new Vector2();
targetGridPosition.x = Mathf.RoundToInt(targetAbsolutePosition.x / 10) * 10 / absoluteChunkSize;
targetGridPosition.y = Mathf.RoundToInt(targetAbsolutePosition.z / 10) * 10 / absoluteChunkSize;
if (targetGridPosition - lastTargetGridPosition != Vector2.zero)
{
GenerateExpectedChunkAreas(targetGridPosition);
UpdateChunkPositions(targetGridPosition);
}
lastTargetGridPosition = targetGridPosition;
}
void GenerateBase()
{
for (int x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z);
Vector3 worldPosition = new Vector3(x * (unitSize * chunkSize), 0, z * (unitSize * chunkSize));
GameObject chunk = Instantiate(chunkObject, worldPosition, Quaternion.identity);
chunk.GetComponent<GEN_Chunk>().gridPosition = gridPosition;
gridOfChunks.Add(gridPosition, chunk);
}
}
GenerateExpectedChunkAreas(Vector2.zero);
}
void GenerateExpectedChunkAreas(Vector2 targetGridPosition)
{
expectedChunkGridPositions.Clear();
for (int x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z) + targetGridPosition;
expectedChunkGridPositions.Add(gridPosition);
}
}
}
void UpdateChunkPositions(Vector2 targetGridPosition)
{
List<Vector2> positionsWithoutChunks = new List<Vector2>();
List<Vector2> positionsWithOldChunks = new List<Vector2>();
for (int chunkCount = 0, x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z) + targetGridPosition;
if(!gridOfChunks.ContainsKey(gridPosition))
{
positionsWithoutChunks.Add(gridPosition);
}
chunkCount++;
}
}
foreach (GameObject chunk in gridOfChunks.Values)
{
if(!expectedChunkGridPositions.Contains(chunk.GetComponent<GEN_Chunk>().gridPosition))
{
positionsWithOldChunks.Add(chunk.GetComponent<GEN_Chunk>().gridPosition);
}
}
for (int i = 0; i < positionsWithOldChunks.Count; i++)
{
Vector3 worldPosition = new Vector3(positionsWithoutChunks[i].x * absoluteChunkSize, 0, positionsWithoutChunks[i].y * absoluteChunkSize);
gridOfChunks[positionsWithOldChunks[i]].transform.position = worldPosition;
// Recalculating noise for chunk based on its new position does lag more but even WITHOUT this it still stutters when player moves around. ( plan to learn threading just to calculate noise on seperate threads )
// gridOfChunks[positionsWithOldChunks[i]].GetComponent<GEN_Chunk>().ApplyNoise();
}
}
void GetAbsoluteChunkSize()
{
absoluteChunkSize = unitSize * chunkSize;
}
}
I need some smooth working infinite terrain (in quotes 'infinite')
And I'd like to learn too!

Categories