mirror of
https://github.com/BotChain-Robots/ui.git
synced 2026-07-08 15:07:22 +02:00
Pipeline saving and View popup window added
Made-with: Cursor
This commit is contained in:
50
Assets/DiscoverOverlayController.cs
Normal file
50
Assets/DiscoverOverlayController.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public class DiscoverOverlayController : MonoBehaviour
|
||||||
|
{
|
||||||
|
public GameObject overlayPanel;
|
||||||
|
public RectTransform overlayContent;
|
||||||
|
|
||||||
|
public void ShowOverlay()
|
||||||
|
{
|
||||||
|
if (overlayPanel != null)
|
||||||
|
{
|
||||||
|
EnsureScrollRect();
|
||||||
|
overlayPanel.SetActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideOverlay()
|
||||||
|
{
|
||||||
|
ClearContent();
|
||||||
|
if (overlayPanel != null)
|
||||||
|
overlayPanel.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureScrollRect()
|
||||||
|
{
|
||||||
|
if (overlayContent == null) return;
|
||||||
|
var scrollArea = overlayContent.parent;
|
||||||
|
if (scrollArea == null) return;
|
||||||
|
var scrollGo = scrollArea.gameObject;
|
||||||
|
if (scrollGo.GetComponent<Mask>() == null)
|
||||||
|
scrollGo.AddComponent<Mask>().showMaskGraphic = false;
|
||||||
|
var sr = scrollGo.GetComponent<ScrollRect>();
|
||||||
|
if (sr == null)
|
||||||
|
{
|
||||||
|
sr = scrollGo.AddComponent<ScrollRect>();
|
||||||
|
sr.horizontal = false;
|
||||||
|
sr.vertical = true;
|
||||||
|
sr.content = overlayContent;
|
||||||
|
sr.movementType = ScrollRect.MovementType.Clamped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearContent()
|
||||||
|
{
|
||||||
|
if (overlayContent == null) return;
|
||||||
|
for (int i = overlayContent.childCount - 1; i >= 0; i--)
|
||||||
|
Destroy(overlayContent.GetChild(i).gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/DiscoverOverlayController.cs.meta
Normal file
11
Assets/DiscoverOverlayController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1c2b3a4e5f6071829a0b1c2d3e4f5a6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -25,7 +25,7 @@ Transform:
|
|||||||
m_GameObject: {fileID: 3292419760055380793}
|
m_GameObject: {fileID: 3292419760055380793}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0.010896653, y: -0.010896653, z: 0.7070228, w: 0.70702285}
|
m_LocalRotation: {x: 0.010896653, y: -0.010896653, z: 0.7070228, w: 0.70702285}
|
||||||
m_LocalPosition: {x: 0.0003, y: -0.0603, z: -0.0017}
|
m_LocalPosition: {x: 0.0003, y: -0.0518, z: -0.0014}
|
||||||
m_LocalScale: {x: 0.049999997, y: 0.049999997, z: 0.05}
|
m_LocalScale: {x: 0.049999997, y: 0.049999997, z: 0.05}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
|
|||||||
@@ -112,6 +112,15 @@ public class TopologyBuilder : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_skipControlLibraryCalls = false;
|
_skipControlLibraryCalls = false;
|
||||||
|
|
||||||
|
Debug.Log("=== Control Library Topology ===");
|
||||||
|
Debug.Log($"Modules ({graph.Modules.Count}):");
|
||||||
|
foreach (var m in graph.Modules)
|
||||||
|
Debug.Log($" Id={m.Id}, Type={m.Type}, Degree={m.Degree}");
|
||||||
|
Debug.Log($"Connections ({graph.Connections.Count}):");
|
||||||
|
foreach (var c in graph.Connections)
|
||||||
|
Debug.Log($" {c.FromModuleId} ({c.FromSocket}) -> {c.ToModuleId} ({c.ToSocket}), Orientation={c.Orientation}");
|
||||||
|
Debug.Log("================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildTopologyFromGraph(graph);
|
BuildTopologyFromGraph(graph);
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ public abstract class ModuleBase : MonoBehaviour
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (0 != ControlLibrary.send_angle_control(Int32.Parse(moduleID), angleRounded))
|
int result = ControlLibrary.send_angle_control(Int32.Parse(moduleID), angleRounded);
|
||||||
|
Debug.Log($"[ControlLibrary] send_angle_control(moduleId={moduleID}, angle={angleRounded}) returned {result}");
|
||||||
|
if (result != 0)
|
||||||
{
|
{
|
||||||
Debug.Log("Control library exited with error");
|
Debug.LogWarning($"[ControlLibrary] send_angle_control returned error code: {result}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (DllNotFoundException)
|
catch (DllNotFoundException)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
@@ -11,7 +12,7 @@ using UnityEngine.EventSystems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProgrammedMovementsController : MonoBehaviour
|
public class ProgrammedMovementsController : MonoBehaviour
|
||||||
{
|
{
|
||||||
public const int TracksCount = 3;
|
public const int TracksCount = 4;
|
||||||
public const int TimelineDurationSeconds = 10;
|
public const int TimelineDurationSeconds = 10;
|
||||||
public static readonly Color HighlightBlue = new Color(0.2f, 0.5f, 1f, 1f);
|
public static readonly Color HighlightBlue = new Color(0.2f, 0.5f, 1f, 1f);
|
||||||
|
|
||||||
@@ -19,6 +20,10 @@ public class ProgrammedMovementsController : MonoBehaviour
|
|||||||
public Camera mainCamera;
|
public Camera mainCamera;
|
||||||
public RectTransform timelineRoot;
|
public RectTransform timelineRoot;
|
||||||
public Button runButton;
|
public Button runButton;
|
||||||
|
public Button saveButton;
|
||||||
|
public Button loadButton;
|
||||||
|
public GameObject filePickerPanel;
|
||||||
|
public RectTransform filePickerContent;
|
||||||
|
|
||||||
private RectTransform _bannerRoot;
|
private RectTransform _bannerRoot;
|
||||||
private Transform _timelineView;
|
private Transform _timelineView;
|
||||||
@@ -45,6 +50,7 @@ public class ProgrammedMovementsController : MonoBehaviour
|
|||||||
}
|
}
|
||||||
EnsureTimelineViewShown();
|
EnsureTimelineViewShown();
|
||||||
WireUpButtons();
|
WireUpButtons();
|
||||||
|
WireSaveLoadButtons();
|
||||||
RefreshRunButton();
|
RefreshRunButton();
|
||||||
// Force layout rebuild so block positions align with timeline after view switch
|
// Force layout rebuild so block positions align with timeline after view switch
|
||||||
Canvas.ForceUpdateCanvases();
|
Canvas.ForceUpdateCanvases();
|
||||||
@@ -96,6 +102,18 @@ public class ProgrammedMovementsController : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _saveLoadWired;
|
||||||
|
|
||||||
|
void WireSaveLoadButtons()
|
||||||
|
{
|
||||||
|
if (_saveLoadWired) return;
|
||||||
|
_saveLoadWired = true;
|
||||||
|
if (saveButton != null)
|
||||||
|
saveButton.onClick.AddListener(SaveTimelineToFile);
|
||||||
|
if (loadButton != null)
|
||||||
|
loadButton.onClick.AddListener(LoadTimelineFromFile);
|
||||||
|
}
|
||||||
|
|
||||||
void WireUpButtons()
|
void WireUpButtons()
|
||||||
{
|
{
|
||||||
WireUpRunButton();
|
WireUpRunButton();
|
||||||
@@ -686,4 +704,255 @@ public class ProgrammedMovementsController : MonoBehaviour
|
|||||||
public int dcDirection = 1; // 1 = forward, -1 = backward
|
public int dcDirection = 1; // 1 = forward, -1 = backward
|
||||||
public GameObject uiRoot;
|
public GameObject uiRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private class SavedMovement
|
||||||
|
{
|
||||||
|
public string ModuleId;
|
||||||
|
public string ModuleType;
|
||||||
|
public float Degree;
|
||||||
|
public int Direction;
|
||||||
|
public int Track;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private class SavedSecond
|
||||||
|
{
|
||||||
|
public int Second;
|
||||||
|
public List<SavedMovement> Movements = new List<SavedMovement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private class SavedTimeline
|
||||||
|
{
|
||||||
|
public List<SavedSecond> Timeline = new List<SavedSecond>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveTimelineToFile()
|
||||||
|
{
|
||||||
|
var bySecond = new Dictionary<int, List<MovementBlock>>();
|
||||||
|
foreach (var b in _blocks)
|
||||||
|
{
|
||||||
|
if (!bySecond.ContainsKey(b.second))
|
||||||
|
bySecond[b.second] = new List<MovementBlock>();
|
||||||
|
bySecond[b.second].Add(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
var saved = new SavedTimeline();
|
||||||
|
var sortedSeconds = new List<int>(bySecond.Keys);
|
||||||
|
sortedSeconds.Sort();
|
||||||
|
foreach (int sec in sortedSeconds)
|
||||||
|
{
|
||||||
|
var entry = new SavedSecond { Second = sec };
|
||||||
|
foreach (var b in bySecond[sec])
|
||||||
|
{
|
||||||
|
var m = new SavedMovement
|
||||||
|
{
|
||||||
|
Degree = b.targetAngle,
|
||||||
|
Direction = b.dcDirection,
|
||||||
|
Track = b.trackIndex
|
||||||
|
};
|
||||||
|
if (b.moduleServo != null)
|
||||||
|
{
|
||||||
|
m.ModuleId = b.moduleServo.moduleID;
|
||||||
|
m.ModuleType = "Servo";
|
||||||
|
}
|
||||||
|
else if (b.moduleDC != null)
|
||||||
|
{
|
||||||
|
m.ModuleId = b.moduleDC.moduleID;
|
||||||
|
m.ModuleType = "DC";
|
||||||
|
}
|
||||||
|
entry.Movements.Add(m);
|
||||||
|
}
|
||||||
|
saved.Timeline.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
string json = JsonUtility.ToJson(saved, true);
|
||||||
|
string dir = Path.Combine(Application.dataPath, "ProgrammedMovements", "SavedTimelines");
|
||||||
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||||
|
string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
|
string filename = $"timeline_{timestamp}.json";
|
||||||
|
string path = Path.Combine(dir, filename);
|
||||||
|
File.WriteAllText(path, json);
|
||||||
|
Debug.Log($"[ProgrammedMovements] Saved {_blocks.Count} movements to {path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadTimelineFromFile()
|
||||||
|
{
|
||||||
|
string dir = Path.Combine(Application.dataPath, "ProgrammedMovements", "SavedTimelines");
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[ProgrammedMovements] No saved timelines directory: {dir}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var files = Directory.GetFiles(dir, "timeline_*.json");
|
||||||
|
if (files.Length == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[ProgrammedMovements] No saved timelines found in {dir}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Array.Sort(files);
|
||||||
|
Array.Reverse(files);
|
||||||
|
ShowFilePicker(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowFilePicker(string[] filePaths)
|
||||||
|
{
|
||||||
|
if (filePickerPanel == null || filePickerContent == null) return;
|
||||||
|
|
||||||
|
ClearFilePickerContent();
|
||||||
|
EnsureScrollRect();
|
||||||
|
|
||||||
|
float itemHeight = 32f;
|
||||||
|
float gap = 4f;
|
||||||
|
float totalHeight = filePaths.Length * (itemHeight + gap);
|
||||||
|
filePickerContent.sizeDelta = new Vector2(0, totalHeight);
|
||||||
|
|
||||||
|
for (int i = 0; i < filePaths.Length; i++)
|
||||||
|
{
|
||||||
|
string filePath = filePaths[i];
|
||||||
|
string fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||||
|
|
||||||
|
var itemGo = new GameObject("Item_" + i);
|
||||||
|
itemGo.transform.SetParent(filePickerContent, false);
|
||||||
|
var itemRect = itemGo.AddComponent<RectTransform>();
|
||||||
|
itemRect.anchorMin = new Vector2(0, 1);
|
||||||
|
itemRect.anchorMax = new Vector2(1, 1);
|
||||||
|
itemRect.pivot = new Vector2(0.5f, 1);
|
||||||
|
itemRect.anchoredPosition = new Vector2(0, -(i * (itemHeight + gap)));
|
||||||
|
itemRect.sizeDelta = new Vector2(-16, itemHeight);
|
||||||
|
var itemImg = itemGo.AddComponent<Image>();
|
||||||
|
itemImg.color = new Color(0.15f, 0.15f, 0.18f, 1f);
|
||||||
|
var itemBtn = itemGo.AddComponent<Button>();
|
||||||
|
itemBtn.targetGraphic = itemImg;
|
||||||
|
var colors = itemBtn.colors;
|
||||||
|
colors.highlightedColor = new Color(0.35f, 0.45f, 0.7f, 1f);
|
||||||
|
colors.pressedColor = new Color(0.2f, 0.3f, 0.6f, 1f);
|
||||||
|
itemBtn.colors = colors;
|
||||||
|
itemBtn.onClick.AddListener(() => OnFileSelected(filePath));
|
||||||
|
|
||||||
|
var labelGo = new GameObject("Label");
|
||||||
|
labelGo.transform.SetParent(itemGo.transform, false);
|
||||||
|
var labelRect = labelGo.AddComponent<RectTransform>();
|
||||||
|
labelRect.anchorMin = Vector2.zero;
|
||||||
|
labelRect.anchorMax = Vector2.one;
|
||||||
|
labelRect.offsetMin = new Vector2(10, 0);
|
||||||
|
labelRect.offsetMax = new Vector2(-10, 0);
|
||||||
|
var labelTxt = labelGo.AddComponent<Text>();
|
||||||
|
labelTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||||
|
labelTxt.text = fileName;
|
||||||
|
labelTxt.fontSize = 13;
|
||||||
|
labelTxt.alignment = TextAnchor.MiddleLeft;
|
||||||
|
labelTxt.color = Color.white;
|
||||||
|
labelTxt.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
filePickerPanel.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureScrollRect()
|
||||||
|
{
|
||||||
|
var scrollArea = filePickerContent.parent;
|
||||||
|
if (scrollArea == null) return;
|
||||||
|
var scrollGo = scrollArea.gameObject;
|
||||||
|
if (scrollGo.GetComponent<Mask>() == null)
|
||||||
|
scrollGo.AddComponent<Mask>().showMaskGraphic = false;
|
||||||
|
var sr = scrollGo.GetComponent<ScrollRect>();
|
||||||
|
if (sr == null)
|
||||||
|
{
|
||||||
|
sr = scrollGo.AddComponent<ScrollRect>();
|
||||||
|
sr.horizontal = false;
|
||||||
|
sr.vertical = true;
|
||||||
|
sr.content = filePickerContent;
|
||||||
|
sr.movementType = ScrollRect.MovementType.Clamped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearFilePickerContent()
|
||||||
|
{
|
||||||
|
if (filePickerContent == null) return;
|
||||||
|
for (int i = filePickerContent.childCount - 1; i >= 0; i--)
|
||||||
|
Destroy(filePickerContent.GetChild(i).gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFileSelected(string filePath)
|
||||||
|
{
|
||||||
|
DismissFilePicker();
|
||||||
|
LoadTimelineFromPath(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DismissFilePicker()
|
||||||
|
{
|
||||||
|
ClearFilePickerContent();
|
||||||
|
if (filePickerPanel != null)
|
||||||
|
filePickerPanel.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadTimelineFromPath(string path)
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText(path);
|
||||||
|
Debug.Log($"[ProgrammedMovements] Loading from: {path}");
|
||||||
|
|
||||||
|
var saved = JsonUtility.FromJson<SavedTimeline>(json);
|
||||||
|
if (saved == null || saved.Timeline == null || saved.Timeline.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[ProgrammedMovements] Save file is empty or invalid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var b in _blocks)
|
||||||
|
if (b.uiRoot != null) Destroy(b.uiRoot);
|
||||||
|
_blocks.Clear();
|
||||||
|
_blocksBySecond.Clear();
|
||||||
|
|
||||||
|
ResolveReferences();
|
||||||
|
EnsureTimelineViewShown();
|
||||||
|
|
||||||
|
var tracksRect = _timelineView?.Find("TracksContainer");
|
||||||
|
if (tracksRect == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[ProgrammedMovements] TracksContainer not found, cannot load.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int loaded = 0;
|
||||||
|
foreach (var entry in saved.Timeline)
|
||||||
|
{
|
||||||
|
foreach (var m in entry.Movements)
|
||||||
|
{
|
||||||
|
int trackIndex = Mathf.Clamp(m.Track, 0, TracksCount - 1);
|
||||||
|
|
||||||
|
var block = new MovementBlock
|
||||||
|
{
|
||||||
|
trackIndex = trackIndex,
|
||||||
|
second = Mathf.Clamp(entry.Second, 0, TimelineDurationSeconds - 1),
|
||||||
|
targetAngle = m.Degree,
|
||||||
|
dcDirection = m.Direction
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(m.ModuleId) && TopologyBuilder.idToInstance.TryGetValue(m.ModuleId, out var go))
|
||||||
|
{
|
||||||
|
if (m.ModuleType == "DC")
|
||||||
|
block.moduleDC = go.GetComponent<DCMotorModule>();
|
||||||
|
else
|
||||||
|
block.moduleServo = go.GetComponent<ServoMotorModule>();
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(m.ModuleId))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[ProgrammedMovements] Module '{m.ModuleId}' not found in topology.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_blocks.Add(block);
|
||||||
|
IndexBlock(block);
|
||||||
|
|
||||||
|
var track = tracksRect.GetChild(trackIndex);
|
||||||
|
var blocksHolder = track.Find("Blocks");
|
||||||
|
if (blocksHolder == null) continue;
|
||||||
|
CreateMovementBlockUI(blocksHolder, block);
|
||||||
|
loaded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[ProgrammedMovements] Loaded {loaded} movements from {Path.GetFileName(path)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
Assets/ProgrammedMovements/SavedTimelines.meta
Normal file
8
Assets/ProgrammedMovements/SavedTimelines.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 34ea3633f5ba44d789b01b6f3a211dbe
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
39
Assets/Resources/mockDataMMMMHub.json
Normal file
39
Assets/Resources/mockDataMMMMHub.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"Modules": [
|
||||||
|
{ "Id": "hub1", "Type": "Splitter2", "Degree": null },
|
||||||
|
{ "Id": "servo1", "Type": "Servo1", "Degree": 90 },
|
||||||
|
{ "Id": "servo2", "Type": "Servo1", "Degree": 90 },
|
||||||
|
{ "Id": "servo3", "Type": "Servo1", "Degree": 90 },
|
||||||
|
{ "Id": "servo4", "Type": "Servo1", "Degree": 90 }
|
||||||
|
],
|
||||||
|
"Connections": [
|
||||||
|
{
|
||||||
|
"FromModuleId": "hub1",
|
||||||
|
"ToModuleId": "servo1",
|
||||||
|
"FromSocket": "MaleSocket1",
|
||||||
|
"ToSocket": "FemaleSocket",
|
||||||
|
"Orientation": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FromModuleId": "hub1",
|
||||||
|
"ToModuleId": "servo2",
|
||||||
|
"FromSocket": "MaleSocket2",
|
||||||
|
"ToSocket": "FemaleSocket",
|
||||||
|
"Orientation": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FromModuleId": "hub1",
|
||||||
|
"ToModuleId": "servo3",
|
||||||
|
"FromSocket": "MaleSocket3",
|
||||||
|
"ToSocket": "FemaleSocket",
|
||||||
|
"Orientation": 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"FromModuleId": "hub1",
|
||||||
|
"ToModuleId": "servo4",
|
||||||
|
"FromSocket": "MaleSocket4",
|
||||||
|
"ToSocket": "FemaleSocket",
|
||||||
|
"Orientation": 270
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
Assets/Resources/mockDataMMMMHub.json.meta
Normal file
7
Assets/Resources/mockDataMMMMHub.json.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1044ba0a77f7e4e0fba382ec60755349
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user