Загрузка данных
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class ConveyorGenerator : MonoBehaviour
{
// ============================ НАСТРОЙКИ (1-в-1 как в Blender) ============================
[Header("Плита")]
public float bedLength = 4.5f;
public float bedWidth = 1.5f;
public float bedThick = 0.10f;
[Header("Лента")]
public float beltThick = 0.012f;
public float clearance = 0.001f;
public float minRadius = 0.01f;
[Header("Зубцы")]
public float ribHeight = 0.007f;
public float ribThickRel = 0.16f;
public float ribPitchRel = 0.24f;
public float ribMarginRel = 0.015f;
public float ribAngleDeg = 45f;
public int direction = 1;
[Header("Сетка")]
public int plateSegments = 240;
public int ribSegmentsY = 12;
public int ribSegmentsX = 3;
[Header("Движение")]
[Tooltip("Шагов зубца в секунду. 1 = один шаг за секунду")]
public float stepsPerSecond = 1.0f;
// ============================ ВНУТРЕННЕЕ ============================
float R, TH, L, pathLen, pitch;
int ribCount;
Mesh ribsMesh;
void Start()
{
BuildAll();
}
void Update()
{
float offset = Mathf.Repeat(Time.time * stepsPerSecond * pitch, pitch);
RebuildRibs(offset);
}
// ============================ СБОРКА ============================
void BuildAll()
{
R = Mathf.Max(bedThick / 2f + clearance, minRadius);
TH = Mathf.Max(beltThick, 0.45f * R);
L = bedLength - 2f * R;
if (L < 0.1f) L = 0.1f;
pathLen = 2f * L + 2f * Mathf.PI * R;
float wantPitch = ribPitchRel * bedWidth;
ribCount = Mathf.Max(1, Mathf.RoundToInt(pathLen / wantPitch));
pitch = pathLen / ribCount;
// ---- материалы создаются в коде ----
Material beltMat = CreateMaterial("Belt_Mat", new Color(0.045f, 0.045f, 0.05f));
Material ribMat = CreateMaterial("Rib_Mat", new Color(0.16f, 0.16f, 0.18f));
Material bedMat = CreateMaterial("Bed_Mat", new Color(0.10f, 0.10f, 0.10f));
// ---- плита под лентой ----
var bed = GameObject.CreatePrimitive(PrimitiveType.Cube);
bed.name = "Bed";
bed.transform.SetParent(transform, false);
bed.transform.localScale = new Vector3(bedLength, bedThick, bedWidth);
bed.transform.localPosition = new Vector3(0f, 0f, 0f);
bed.GetComponent<MeshRenderer>().material = bedMat;
// ---- лента ----
var plateMesh = BuildPlateMesh();
var mf = GetComponent<MeshFilter>();
mf.mesh = plateMesh;
var mr = GetComponent<MeshRenderer>();
mr.material = beltMat;
// ---- зубцы ----
var ribsObj = new GameObject("Ribs");
ribsObj.transform.SetParent(transform, false);
ribsObj.AddComponent<MeshFilter>();
var rr = ribsObj.AddComponent<MeshRenderer>();
rr.material = ribMat;
ribsMesh = new Mesh();
ribsMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
ribsMesh.MarkDynamic();
ribsObj.GetComponent<MeshFilter>().mesh = ribsMesh;
// первый прогон зубцов
RebuildRibs(0f);
}
Material CreateMaterial(string name, Color color)
{
var shader = Shader.Find("Standard");
if (shader == null) shader = Shader.Find("Diffuse");
var mat = new Material(shader);
mat.name = name;
mat.color = color;
if (mat.HasProperty("_Glossiness")) mat.SetFloat("_Glossiness", 0.15f);
if (mat.HasProperty("_Metallic")) mat.SetFloat("_Metallic", 0f);
return mat;
}
// ============================ ЛЕНТА ============================
Mesh BuildPlateMesh()
{
int n = Mathf.Max(64, plateSegments);
int ringVerts = 4;
var verts = new Vector3[(n + 1) * ringVerts];
var tris = new int[n * ringVerts * 6];
float hw = bedWidth / 2f;
Vector2[] profile = new Vector2[]
{
new Vector2(-hw, 0f),
new Vector2( hw, 0f),
new Vector2( hw, TH),
new Vector2(-hw, TH),
};
for (int i = 0; i <= n; i++)
{
float t = (float)i / n * pathLen;
Vector3 p = PointOnPath(WrapT(t));
Vector3 dir = PathTangent(WrapT(t));
Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;
for (int k = 0; k < ringVerts; k++)
verts[i * ringVerts + k] = p + normal * profile[k].x + Vector3.up * profile[k].y;
}
int ti = 0;
for (int i = 0; i < n; i++)
{
int a = i * ringVerts;
int b = (i + 1) * ringVerts;
for (int k = 0; k < ringVerts; k++)
{
int k2 = (k + 1) % ringVerts;
tris[ti++] = a + k;
tris[ti++] = a + k2;
tris[ti++] = b + k2;
tris[ti++] = a + k;
tris[ti++] = b + k2;
tris[ti++] = b + k;
}
}
var m = new Mesh();
m.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
m.vertices = verts;
m.triangles = tris;
m.RecalculateNormals();
m.RecalculateBounds();
return m;
}
// ============================ ЗУБЦЫ ============================
void RebuildRibs(float offset)
{
float hw = bedWidth / 2f - ribMarginRel * bedWidth;
float a = hw * Mathf.Tan(ribAngleDeg * Mathf.Deg2Rad);
float t = ribThickRel * bedWidth;
float span = a + t;
float xOff = (span >= pitch) ? 0f : (pitch - span) / 2f;
float embed = 0.3f * TH;
float z0 = TH - embed;
float z1 = TH + ribHeight;
int ny = Mathf.Max(4, ribSegmentsY);
int nx = Mathf.Max(2, ribSegmentsX);
int rowVerts = (nx + 1) * 2;
var verts = new List<Vector3>(ribCount * (ny + 1) * rowVerts);
var tris = new List<int>(ribCount * ny * rowVerts * 6);
for (int r = 0; r < ribCount; r++)
{
float baseT = WrapT(r * pitch + offset);
BuildOneRib(baseT, hw, a, t, span, xOff, z0, z1, ny, nx, rowVerts, verts, tris);
}
ribsMesh.Clear();
ribsMesh.SetVertices(verts);
ribsMesh.SetTriangles(tris, 0);
ribsMesh.RecalculateNormals();
ribsMesh.RecalculateBounds();
}
void BuildOneRib(float baseT,
float hw, float a, float t, float span, float xOff,
float z0, float z1,
int ny, int nx, int rowVerts,
List<Vector3> verts, List<int> tris)
{
int startIdx = verts.Count;
for (int j = 0; j <= ny; j++)
{
float yLocal = -hw + 2f * hw * j / ny;
float d = a * Mathf.Abs(yLocal) / hw;
float xl = span - d;
float xt = xl - t;
if (direction < 0) { xl = span - xl; xt = span - xt; }
xl += xOff;
xt += xOff;
for (int i = 0; i <= nx; i++)
{
float xLocal = Mathf.Lerp(xt, xl, (float)i / nx);
float tAlong = WrapT(baseT + xLocal);
Vector3 p = PointOnPath(tAlong);
Vector3 dir = PathTangent(tAlong);
Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;
verts.Add(p + normal * yLocal + Vector3.up * z0);
}
for (int i = nx; i >= 0; i--)
{
float xLocal = Mathf.Lerp(xt, xl, (float)i / nx);
float tAlong = WrapT(baseT + xLocal);
Vector3 p = PointOnPath(tAlong);
Vector3 dir = PathTangent(tAlong);
Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized;
verts.Add(p + normal * yLocal + Vector3.up * z1);
}
}
int loop = rowVerts;
for (int j = 0; j < ny; j++)
{
int r0 = startIdx + j * loop;
int r1 = startIdx + (j + 1) * loop;
for (int k = 0; k < loop; k++)
{
int k2 = (k + 1) % loop;
tris.Add(r0 + k);
tris.Add(r0 + k2);
tris.Add(r1 + k2);
tris.Add(r0 + k);
tris.Add(r1 + k2);
tris.Add(r1 + k);
}
}
int first = startIdx;
int last = startIdx + ny * loop;
for (int k = 1; k < loop - 1; k++)
{
tris.Add(first);
tris.Add(first + k);
tris.Add(first + k + 1);
}
for (int k = 1; k < loop - 1; k++)
{
tris.Add(last);
tris.Add(last + k + 1);
tris.Add(last + k);
}
}
// ============================ ПУТЬ ============================
float WrapT(float t)
{
t %= pathLen;
if (t < 0f) t += pathLen;
return t;
}
Vector3 PointOnPath(float t)
{
float straight = L;
float halfCirc = Mathf.PI * R;
if (t < straight)
return new Vector3(-L / 2f + t, R, 0f);
t -= straight;
if (t < halfCirc)
{
float ang = t / R;
float x = L / 2f + R * Mathf.Sin(ang);
float y = R * Mathf.Cos(ang);
return new Vector3(x, y, 0f);
}
t -= halfCirc;
if (t < straight)
return new Vector3(L / 2f - t, -R, 0f);
t -= straight;
{
float ang = t / R;
float x = -L / 2f - R * Mathf.Sin(ang);
float y = -R * Mathf.Cos(ang);
return new Vector3(x, y, 0f);
}
}
Vector3 PathTangent(float t)
{
float dt = 0.001f;
Vector3 p0 = PointOnPath(WrapT(t - dt));
Vector3 p1 = PointOnPath(WrapT(t + dt));
return (p1 - p0).normalized;
}
}