robotArm
This commit is contained in:
@@ -18,11 +18,10 @@ public class ConveyorComponent : ComponentBase
|
||||
SplineContainer spline;
|
||||
float duration = 3f;
|
||||
|
||||
// === 추가: 슬롯 기반 제어 ===
|
||||
public conveyorType type = conveyorType.overall;
|
||||
public int segmentCount = 10; // 칸 개수
|
||||
public float borderEpsilon = 1e-4f; // 경계 떨림 방지
|
||||
bool[] occupancy; // 프레임 점유 캐시
|
||||
public int segmentCount = 10;
|
||||
public float borderEpsilon = 1e-4f;
|
||||
bool[] occupancy;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
@@ -121,7 +120,6 @@ public class ConveyorComponent : ComponentBase
|
||||
|
||||
if (type == conveyorType.overall)
|
||||
{
|
||||
// 기존: 전 화물 동일 진행
|
||||
foreach (var target in ConveyorQueue)
|
||||
{
|
||||
target.Progress += progress;
|
||||
@@ -130,7 +128,6 @@ public class ConveyorComponent : ComponentBase
|
||||
}
|
||||
else
|
||||
{
|
||||
// 슬롯 기반: 끝 칸이 막혀 있어도 압축 전진
|
||||
PartialAdvance(progress);
|
||||
}
|
||||
}
|
||||
@@ -146,7 +143,6 @@ public class ConveyorComponent : ComponentBase
|
||||
target.transform.position = localPos;
|
||||
target.transform.position += new Vector3(0, 1f, -0.275f);
|
||||
|
||||
// 끝(1.0) 도달 → WaitingQueue로 이동(배출 대기)
|
||||
if (target.Progress >= 1f)
|
||||
{
|
||||
ConveyorQueue.RemoveAt(i);
|
||||
@@ -155,10 +151,8 @@ public class ConveyorComponent : ComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
// === 한 칸씩 채우는 압축 로직 ===
|
||||
void PartialAdvance(float delta)
|
||||
{
|
||||
// 끝에 가까운 순서로 처리 → 뒤따르는 한계 계산이 정확
|
||||
ConveyorQueue.Sort((a, b) => b.Progress.CompareTo(a.Progress));
|
||||
|
||||
int cellCount = Mathf.Max(1, segmentCount);
|
||||
@@ -168,7 +162,6 @@ public class ConveyorComponent : ComponentBase
|
||||
occupancy = new bool[cellCount];
|
||||
System.Array.Clear(occupancy, 0, occupancy.Length);
|
||||
|
||||
// ★ 중요: 출구가 막혀 있으면 마지막 칸을 항상 점유된 것으로 간주
|
||||
if (WaitingQueue.Count > 0)
|
||||
occupancy[cellCount - 1] = true;
|
||||
|
||||
@@ -180,23 +173,19 @@ public class ConveyorComponent : ComponentBase
|
||||
int wantCell = CellIndex(desired, cellCount);
|
||||
|
||||
int allowedCell = currCell;
|
||||
// 앞칸이 비어 있으면 한 칸 더, 또 비어 있으면 또 한 칸… 식으로 최대 전진
|
||||
for (int c = currCell + 1; c <= wantCell && c < cellCount; c++)
|
||||
{
|
||||
if (occupancy[c]) break;
|
||||
allowedCell = c;
|
||||
}
|
||||
|
||||
// 허용 칸의 끝 경계 직전까지
|
||||
float maxProg = Mathf.Min(desired, CellEnd(allowedCell, cellCount) - borderEpsilon);
|
||||
|
||||
// 마지막 칸이면 1.0까지 허용(도달 시 MoveOnConveyor에서 대기열로 이동)
|
||||
if (allowedCell == cellCount - 1)
|
||||
maxProg = Mathf.Min(desired, 1f);
|
||||
|
||||
t.Progress = Mathf.Clamp01(maxProg);
|
||||
|
||||
// 점유 표시
|
||||
int newCell = CellIndex(t.Progress, cellCount);
|
||||
occupancy[newCell] = true;
|
||||
}
|
||||
|
||||
@@ -3,278 +3,286 @@ using Simulator.Data;
|
||||
using Simulator.Data.Transport;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using UVC.Core;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Data.Mqtt;
|
||||
public class ConveyorPathMeta : MonoBehaviour
|
||||
using Simulator.Config;
|
||||
|
||||
namespace Simulator.Data
|
||||
{
|
||||
public string Name;
|
||||
public string From;
|
||||
public string To;
|
||||
public float Speed;
|
||||
public float Length;
|
||||
public int Capacity;
|
||||
}
|
||||
|
||||
public class ConveyorManager : SingletonScene<ConveyorManager>
|
||||
{
|
||||
[Header("Root & Visual")]
|
||||
[SerializeField] private Transform rootParent; // 생성될 경로의 부모(없으면 빌더 자신)
|
||||
[SerializeField] private float tangentScale = 0.25f; // 곡률(탄젠트 길이 비율)
|
||||
[SerializeField] private ConveyorComponent splineprefab;
|
||||
[SerializeField] private GameObject junction;
|
||||
|
||||
[Header("Corner Modules")] // ▼ 추가: 코너 프리팹 & 각도 허용 오차
|
||||
[SerializeField] private GameObject corner45Prefab;
|
||||
[SerializeField] private GameObject corner90Prefab;
|
||||
[SerializeField, Range(1f, 30f)] private float angleTolerance = 10f;
|
||||
|
||||
public Dictionary<string, ConveyorNode> Nodes = new Dictionary<string, ConveyorNode>();
|
||||
public Dictionary<(string, string), ConveyorComponent> Paths = new Dictionary<(string, string), ConveyorComponent>();
|
||||
|
||||
// ▼ 추가: station 연결성 계산용 테이블
|
||||
// stationName -> (경로 키, station에서 맞닿는 cap index(0=from,1=to), 반대편 노드명)
|
||||
private readonly Dictionary<string, List<(ValueTuple<string, string> pathKey, int capIndex, string otherNode)>> _stationAdj
|
||||
= new Dictionary<string, List<(ValueTuple<string, string>, int, string)>>();
|
||||
|
||||
public DataMapper conveyorStartDataMapper;
|
||||
public DataMapper conveyorCompleteDataMapper;
|
||||
|
||||
protected override void Init()
|
||||
public class ConveyorPathMeta
|
||||
{
|
||||
var conveyorStartDataMask = new DataMask();
|
||||
conveyorStartDataMask.ObjectName = "conveyor";
|
||||
conveyorStartDataMask.ObjectIdKey = "component_id";
|
||||
conveyorStartDataMask["component_id"] = "";
|
||||
conveyorStartDataMask["event_name"] = "";
|
||||
conveyorStartDataMask["timestamp"] = new DateTime();
|
||||
conveyorStartDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
["travel_time"] = 0.0f
|
||||
};
|
||||
conveyorStartDataMapper = new DataMapper(conveyorStartDataMask);
|
||||
|
||||
var conveyorCompleteDataMask = new DataMask();
|
||||
conveyorCompleteDataMask.ObjectName = "conveyor";
|
||||
conveyorCompleteDataMask.ObjectIdKey = "component_id";
|
||||
conveyorCompleteDataMask["component_id"] = "";
|
||||
conveyorCompleteDataMask["event_name"] = "";
|
||||
conveyorCompleteDataMask["timestamp"] = new DateTime();
|
||||
conveyorCompleteDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
};
|
||||
conveyorCompleteDataMapper = new DataMapper(conveyorCompleteDataMask);
|
||||
public string Name;
|
||||
public string From;
|
||||
public string To;
|
||||
public float Speed;
|
||||
public float Length;
|
||||
public int Capacity;
|
||||
}
|
||||
|
||||
public void Build(List<ConveyorDataClass> datas)
|
||||
public class ConveyorManager : SingletonScene<ConveyorManager>
|
||||
{
|
||||
if (datas == null || datas.Count <= 0) return;
|
||||
[Header("Root & Visual")]
|
||||
[SerializeField] private Transform rootParent; // 생성될 경로의 부모(없으면 빌더 자신)
|
||||
[SerializeField] private float tangentScale = 0.25f; // 곡률(탄젠트 길이 비율)
|
||||
[SerializeField] private ConveyorComponent splineprefab;
|
||||
[SerializeField] private GameObject junction;
|
||||
|
||||
var data = datas[0];
|
||||
if (rootParent == null) rootParent = transform;
|
||||
[Header("Corner Modules")] // ▼ 추가: 코너 프리팹 & 각도 허용 오차
|
||||
[SerializeField] private GameObject corner45Prefab;
|
||||
[SerializeField] private GameObject corner90Prefab;
|
||||
[SerializeField, Range(1f, 30f)] private float angleTolerance = 10f;
|
||||
|
||||
Nodes.Clear();
|
||||
Paths.Clear();
|
||||
_stationAdj.Clear(); // ▲ 추가: 초기화
|
||||
public Dictionary<string, ConveyorNode> Nodes = new Dictionary<string, ConveyorNode>();
|
||||
public Dictionary<(string, string), ConveyorComponent> Paths = new Dictionary<(string, string), ConveyorComponent>();
|
||||
|
||||
if (data.nodes != null)
|
||||
// ▼ 추가: station 연결성 계산용 테이블
|
||||
// stationName -> (경로 키, station에서 맞닿는 cap index(0=from,1=to), 반대편 노드명)
|
||||
private readonly Dictionary<string, List<(ValueTuple<string, string> pathKey, int capIndex, string otherNode)>> _stationAdj
|
||||
= new Dictionary<string, List<(ValueTuple<string, string>, int, string)>>();
|
||||
|
||||
public DataMapper conveyorStartDataMapper;
|
||||
public DataMapper conveyorCompleteDataMapper;
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
foreach (var n in data.nodes)
|
||||
Nodes[n.name] = n;
|
||||
}
|
||||
|
||||
foreach (var path in data.paths)
|
||||
CreatePathGOWithSpline(path, Nodes[path.from_node], Nodes[path.to_node]);
|
||||
|
||||
SpawnJunction();
|
||||
|
||||
// ▲ 추가: station의 연결성(직선/코너) 기반 cap 분리 & 코너 배치
|
||||
ProcessStations();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void SubscribeConveyor(ConveyorDataClass data)
|
||||
{
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
var mqttConfigConveyorStart = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
mqttConfigConveyorStart.SetDataMapper(conveyorStartDataMapper);
|
||||
mqttConfigConveyorStart.SetHandler(OnSetData);
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfigConveyorStart);
|
||||
}
|
||||
|
||||
private void CreatePathGOWithSpline(ConveyorPath path, ConveyorNode fromNode, ConveyorNode toNode)
|
||||
{
|
||||
TryGetNodePos(fromNode.name, out Vector3 fromPos);
|
||||
TryGetNodePos(toNode.name, out Vector3 toPos);
|
||||
|
||||
var go = Instantiate(splineprefab);
|
||||
Paths.Add((fromNode.name, toNode.name), go);
|
||||
go.transform.SetParent(rootParent, worldPositionStays: false);
|
||||
|
||||
var container = go.GetComponent<SplineContainer>();
|
||||
var spline = new Spline();
|
||||
|
||||
Vector3 dir = (toPos - fromPos);
|
||||
float dist = dir.magnitude;
|
||||
Vector3 dirNorm = dist > 1e-5f ? dir / dist : Vector3.forward;
|
||||
|
||||
Vector3 tan = dirNorm * dist * Mathf.Clamp01(tangentScale);
|
||||
|
||||
var k0 = new BezierKnot(fromPos)
|
||||
{
|
||||
TangentOut = tan,
|
||||
TangentIn = Vector3.zero
|
||||
};
|
||||
|
||||
var k1 = new BezierKnot(toPos)
|
||||
{
|
||||
TangentIn = -tan,
|
||||
TangentOut = Vector3.zero
|
||||
};
|
||||
|
||||
spline.Add(k0, TangentMode.Broken);
|
||||
spline.Add(k1, TangentMode.Broken);
|
||||
|
||||
container.Spline = spline;
|
||||
var mesher = go.GetComponent<SplineMesher>();
|
||||
|
||||
// ▼ 변경: junction만 즉시 cap 분리, station은 후처리에서 판단
|
||||
if (string.Equals(fromNode.node_type, "junction"))
|
||||
mesher.DetachCap(0);
|
||||
if (string.Equals(toNode.node_type, "junction"))
|
||||
mesher.DetachCap(1);
|
||||
|
||||
mesher.UpdateCaps();
|
||||
mesher.Rebuild();
|
||||
|
||||
// ▼ 추가: station 인접 정보 기록(후처리에서 cap/코너 결정)
|
||||
if (string.Equals(fromNode.node_type, "station"))
|
||||
AddStationAdj(fromNode.name, (fromNode.name, toNode.name), capIndex: 0, otherNode: toNode.name);
|
||||
|
||||
if (string.Equals(toNode.node_type, "station"))
|
||||
AddStationAdj(toNode.name, (fromNode.name, toNode.name), capIndex: 1, otherNode: fromNode.name);
|
||||
}
|
||||
|
||||
// ▼ 추가: station 인접 정보 수집
|
||||
private void AddStationAdj(string stationName, (string, string) pathKey, int capIndex, string otherNode)
|
||||
{
|
||||
if (!_stationAdj.TryGetValue(stationName, out var list))
|
||||
{
|
||||
list = new List<(ValueTuple<string, string>, int, string)>();
|
||||
_stationAdj[stationName] = list;
|
||||
}
|
||||
list.Add((pathKey, capIndex, otherNode));
|
||||
}
|
||||
|
||||
// ▼ 추가: station의 연결성이 직선인지 코너인지 판정하고 cap/코너를 적용
|
||||
private void ProcessStations()
|
||||
{
|
||||
foreach (var kv in _stationAdj)
|
||||
{
|
||||
string station = kv.Key;
|
||||
var adj = kv.Value;
|
||||
|
||||
// 연결 경로가 2개일 때만 직선/코너 판정(기타 케이스는 유지)
|
||||
if (adj.Count != 2) continue;
|
||||
|
||||
if (!TryGetNodePos(station, out var stationPos)) continue;
|
||||
|
||||
var (pathA, capA, otherA) = adj[0];
|
||||
var (pathB, capB, otherB) = adj[1];
|
||||
|
||||
if (!TryGetNodePos(otherA, out var otherAPos)) continue;
|
||||
if (!TryGetNodePos(otherB, out var otherBPos)) continue;
|
||||
|
||||
Vector3 vA = (otherAPos - stationPos); vA.y = 0f;
|
||||
Vector3 vB = (otherBPos - stationPos); vB.y = 0f;
|
||||
|
||||
if (vA.sqrMagnitude < 1e-6f || vB.sqrMagnitude < 1e-6f) continue;
|
||||
|
||||
float angle = Vector3.SignedAngle(vA.normalized, vB.normalized, Vector3.up); // 0~180
|
||||
float straightThreshold = 180f - angleTolerance; // 예: tol=10 → 170°
|
||||
|
||||
Debug.Log(angle);
|
||||
bool isStraight = Mathf.Abs(angle) >= straightThreshold; // 거의 반대 방향이면 직선
|
||||
if (isStraight)
|
||||
var conveyorStartDataMask = new DataMask();
|
||||
conveyorStartDataMask.ObjectName = "conveyor";
|
||||
conveyorStartDataMask.ObjectIdKey = "component_id";
|
||||
conveyorStartDataMask["component_id"] = "";
|
||||
conveyorStartDataMask["event_name"] = "";
|
||||
conveyorStartDataMask["timestamp"] = new DateTime();
|
||||
conveyorStartDataMask["data"] = new DataMask()
|
||||
{
|
||||
// 직선: cap 유지
|
||||
continue;
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
["travel_time"] = 0.0f
|
||||
};
|
||||
conveyorStartDataMapper = new DataMapper(conveyorStartDataMask);
|
||||
|
||||
var conveyorCompleteDataMask = new DataMask();
|
||||
conveyorCompleteDataMask.ObjectName = "conveyor";
|
||||
conveyorCompleteDataMask.ObjectIdKey = "component_id";
|
||||
conveyorCompleteDataMask["component_id"] = "";
|
||||
conveyorCompleteDataMask["event_name"] = "";
|
||||
conveyorCompleteDataMask["timestamp"] = new DateTime();
|
||||
conveyorCompleteDataMask["data"] = new DataMask()
|
||||
{
|
||||
["entity_id_original"] = "",
|
||||
["start_node"] = "",
|
||||
["end_node"] = "",
|
||||
};
|
||||
conveyorCompleteDataMapper = new DataMapper(conveyorCompleteDataMask);
|
||||
}
|
||||
|
||||
public void Build(List<ConveyorDataClass> datas)
|
||||
{
|
||||
if (datas == null || datas.Count <= 0) return;
|
||||
|
||||
var data = datas[0];
|
||||
if (rootParent == null) rootParent = transform;
|
||||
|
||||
Nodes.Clear();
|
||||
Paths.Clear();
|
||||
_stationAdj.Clear(); // ▲ 추가: 초기화
|
||||
|
||||
if (data.nodes != null)
|
||||
{
|
||||
foreach (var n in data.nodes)
|
||||
Nodes[n.name] = n;
|
||||
}
|
||||
|
||||
// 코너: station에 맞닿는 두 cap 분리
|
||||
if (Paths.TryGetValue(pathA, out var convA))
|
||||
DetachAndRebuild(convA, capA);
|
||||
if (Paths.TryGetValue(pathB, out var convB))
|
||||
DetachAndRebuild(convB, capB);
|
||||
foreach (var path in data.paths)
|
||||
CreatePathGOWithSpline(path, Nodes[path.from_node], Nodes[path.to_node]);
|
||||
|
||||
SpawnJunction();
|
||||
|
||||
// ▲ 추가: station의 연결성(직선/코너) 기반 cap 분리 & 코너 배치
|
||||
ProcessStations();
|
||||
|
||||
|
||||
var corner = Instantiate(corner90Prefab);
|
||||
corner.transform.position = stationPos;
|
||||
corner.transform.rotation = Quaternion.Euler(new Vector3(-90, -angle+90f, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// ▼ 추가: cap 분리 & 리빌드 헬퍼
|
||||
private void DetachAndRebuild(ConveyorComponent comp, int capIndex)
|
||||
{
|
||||
var mesher = comp.GetComponent<SplineMesher>();
|
||||
if (mesher == null) return;
|
||||
mesher.DetachCap(capIndex);
|
||||
mesher.UpdateCaps();
|
||||
mesher.Rebuild();
|
||||
}
|
||||
|
||||
private void SpawnJunction()
|
||||
{
|
||||
foreach (var node in Nodes)
|
||||
public void SubscribeConveyor(ConveyorDataClass data)
|
||||
{
|
||||
if (string.Equals(node.Value.node_type, "junction"))
|
||||
DataRepository.Instance.MqttReceiver.AddTopic($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
var mqttConfigConveyorStart = new MqttSubscriptionConfig($"simulation/{SimulationConfig.SimulationCode}/components/+/{data.name}/+/node_started");
|
||||
mqttConfigConveyorStart.SetDataMapper(conveyorStartDataMapper);
|
||||
mqttConfigConveyorStart.SetHandler(OnSetData);
|
||||
DataRepository.Instance.MqttReceiver.Add(mqttConfigConveyorStart);
|
||||
}
|
||||
|
||||
private void CreatePathGOWithSpline(ConveyorPath path, ConveyorNode fromNode, ConveyorNode toNode)
|
||||
{
|
||||
TryGetNodePos(fromNode.name, out Vector3 fromPos);
|
||||
TryGetNodePos(toNode.name, out Vector3 toPos);
|
||||
|
||||
var go = Instantiate(splineprefab);
|
||||
Paths.Add((fromNode.name, toNode.name), go);
|
||||
go.transform.SetParent(rootParent, worldPositionStays: false);
|
||||
|
||||
var container = go.GetComponent<SplineContainer>();
|
||||
var spline = new Spline();
|
||||
|
||||
Vector3 dir = (toPos - fromPos);
|
||||
float dist = dir.magnitude;
|
||||
Vector3 dirNorm = dist > 1e-5f ? dir / dist : Vector3.forward;
|
||||
|
||||
Vector3 tan = dirNorm * dist * Mathf.Clamp01(tangentScale);
|
||||
|
||||
var k0 = new BezierKnot(fromPos)
|
||||
{
|
||||
var module = Instantiate(junction);
|
||||
module.transform.position = ToVector3(node.Value.physical.position);
|
||||
TangentOut = tan,
|
||||
TangentIn = Vector3.zero
|
||||
};
|
||||
|
||||
var k1 = new BezierKnot(toPos)
|
||||
{
|
||||
TangentIn = -tan,
|
||||
TangentOut = Vector3.zero
|
||||
};
|
||||
|
||||
spline.Add(k0, TangentMode.Broken);
|
||||
spline.Add(k1, TangentMode.Broken);
|
||||
|
||||
container.Spline = spline;
|
||||
var mesher = go.GetComponent<SplineMesher>();
|
||||
|
||||
// ▼ 변경: junction만 즉시 cap 분리, station은 후처리에서 판단
|
||||
if (string.Equals(fromNode.node_type, "junction"))
|
||||
mesher.DetachCap(0);
|
||||
if (string.Equals(toNode.node_type, "junction"))
|
||||
mesher.DetachCap(1);
|
||||
|
||||
if (string.Equals(fromNode.node_type, "start"))
|
||||
mesher.startCap.offset = new Vector3(0, -0.772f, 0.25f);
|
||||
if (string.Equals(toNode.node_type, "endPoint"))
|
||||
mesher.endCap.offset = new Vector3(0, -0.772f, 0.25f);
|
||||
|
||||
mesher.UpdateCaps();
|
||||
mesher.Rebuild();
|
||||
|
||||
// ▼ 추가: station 인접 정보 기록(후처리에서 cap/코너 결정)
|
||||
if (string.Equals(fromNode.node_type, "station"))
|
||||
AddStationAdj(fromNode.name, (fromNode.name, toNode.name), capIndex: 0, otherNode: toNode.name);
|
||||
|
||||
if (string.Equals(toNode.node_type, "station"))
|
||||
AddStationAdj(toNode.name, (fromNode.name, toNode.name), capIndex: 1, otherNode: fromNode.name);
|
||||
}
|
||||
|
||||
// ▼ 추가: station 인접 정보 수집
|
||||
private void AddStationAdj(string stationName, (string, string) pathKey, int capIndex, string otherNode)
|
||||
{
|
||||
if (!_stationAdj.TryGetValue(stationName, out var list))
|
||||
{
|
||||
list = new List<(ValueTuple<string, string>, int, string)>();
|
||||
_stationAdj[stationName] = list;
|
||||
}
|
||||
list.Add((pathKey, capIndex, otherNode));
|
||||
}
|
||||
|
||||
// ▼ 추가: station의 연결성이 직선인지 코너인지 판정하고 cap/코너를 적용
|
||||
private void ProcessStations()
|
||||
{
|
||||
foreach (var kv in _stationAdj)
|
||||
{
|
||||
string station = kv.Key;
|
||||
var adj = kv.Value;
|
||||
|
||||
// 연결 경로가 2개일 때만 직선/코너 판정(기타 케이스는 유지)
|
||||
if (adj.Count != 2) continue;
|
||||
|
||||
if (!TryGetNodePos(station, out var stationPos)) continue;
|
||||
|
||||
var (pathA, capA, otherA) = adj[0];
|
||||
var (pathB, capB, otherB) = adj[1];
|
||||
|
||||
if (!TryGetNodePos(otherA, out var otherAPos)) continue;
|
||||
if (!TryGetNodePos(otherB, out var otherBPos)) continue;
|
||||
|
||||
Vector3 vA = (otherAPos - stationPos); vA.y = 0f;
|
||||
Vector3 vB = (otherBPos - stationPos); vB.y = 0f;
|
||||
|
||||
if (vA.sqrMagnitude < 1e-6f || vB.sqrMagnitude < 1e-6f) continue;
|
||||
|
||||
float angle = Vector3.SignedAngle(vA.normalized, vB.normalized, Vector3.up); // 0~180
|
||||
float straightThreshold = 180f - angleTolerance; // 예: tol=10 → 170°
|
||||
|
||||
Debug.Log(angle);
|
||||
bool isStraight = Mathf.Abs(angle) >= straightThreshold; // 거의 반대 방향이면 직선
|
||||
if (isStraight)
|
||||
{
|
||||
// 직선: cap 유지
|
||||
continue;
|
||||
}
|
||||
|
||||
// 코너: station에 맞닿는 두 cap 분리
|
||||
if (Paths.TryGetValue(pathA, out var convA))
|
||||
DetachAndRebuild(convA, capA);
|
||||
if (Paths.TryGetValue(pathB, out var convB))
|
||||
DetachAndRebuild(convB, capB);
|
||||
|
||||
var corner = Instantiate(corner90Prefab);
|
||||
corner.transform.position = stationPos;
|
||||
corner.transform.rotation = Quaternion.Euler(new Vector3(-90, -angle + 90f, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetData(IDataObject idata)
|
||||
{
|
||||
if (idata == null) return;
|
||||
|
||||
DataObject? obj = idata as DataObject;
|
||||
if (obj == null) return;
|
||||
|
||||
var data = obj.GetDataObject("data");
|
||||
var fromNodeName = data.GetString("start_node");
|
||||
var toNodeName = data.GetString("end_node");
|
||||
ConveyorComponent? conveyor = Paths[(fromNodeName, toNodeName)];
|
||||
conveyor.SetModelData(obj);
|
||||
}
|
||||
|
||||
private bool TryGetNodePos(string nodeName, out Vector3 pos)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(nodeName) && Nodes.TryGetValue(nodeName, out ConveyorNode node))
|
||||
// ▼ 추가: cap 분리 & 리빌드 헬퍼
|
||||
private void DetachAndRebuild(ConveyorComponent comp, int capIndex)
|
||||
{
|
||||
pos = ToVector3(node.physical.position);
|
||||
return true;
|
||||
var mesher = comp.GetComponent<SplineMesher>();
|
||||
if (mesher == null) return;
|
||||
mesher.DetachCap(capIndex);
|
||||
mesher.UpdateCaps();
|
||||
mesher.Rebuild();
|
||||
}
|
||||
|
||||
pos = default;
|
||||
return false;
|
||||
}
|
||||
private void SpawnJunction()
|
||||
{
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (string.Equals(node.Value.node_type, "junction"))
|
||||
{
|
||||
var module = Instantiate(junction);
|
||||
module.transform.position = ToVector3(node.Value.physical.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 ToVector3(Position p)
|
||||
{
|
||||
return new Vector3(p.x, p.z, -p.y);
|
||||
private void OnSetData(IDataObject idata)
|
||||
{
|
||||
if (idata == null) return;
|
||||
|
||||
DataObject? obj = idata as DataObject;
|
||||
if (obj == null) return;
|
||||
|
||||
var data = obj.GetDataObject("data");
|
||||
var fromNodeName = data.GetString("start_node");
|
||||
var toNodeName = data.GetString("end_node");
|
||||
ConveyorComponent? conveyor = Paths[(fromNodeName, toNodeName)];
|
||||
conveyor.SetModelData(obj);
|
||||
}
|
||||
|
||||
private bool TryGetNodePos(string nodeName, out Vector3 pos)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(nodeName) && Nodes.TryGetValue(nodeName, out ConveyorNode node))
|
||||
{
|
||||
pos = ToVector3(node.physical.position);
|
||||
return true;
|
||||
}
|
||||
|
||||
pos = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Vector3 ToVector3(Position p)
|
||||
{
|
||||
return new Vector3(p.x, p.z, -p.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user