forked from zxl/LaboratoryProtection
618 lines
20 KiB
C#
618 lines
20 KiB
C#
using Cysharp.Threading.Tasks;
|
|
using Cysharp.Threading.Tasks.Triggers;
|
|
|
|
using PMaker.Await;
|
|
using PMaker.Extension;
|
|
|
|
using Sirenix.OdinInspector;
|
|
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
|
|
using UniRx;
|
|
using UniRx.Triggers;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.UI.Extensions;
|
|
|
|
public class UIWiring : AwaitBehaviour
|
|
{
|
|
//#if UNITY_EDITOR
|
|
// private static bool DrawColoredEnumElement(Rect rect, bool value)
|
|
// {
|
|
// if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition))
|
|
// {
|
|
// value = !value;
|
|
// GUI.changed = true;
|
|
// Event.current.Use();
|
|
// }
|
|
//#if UNITY_EDITOR
|
|
// UnityEditor.EditorGUI.DrawRect(rect.Padding(1), value ? new Color(0.1f, 0.8f, 0.2f) : new Color(0, 0, 0, 0.5f));
|
|
//#endif
|
|
|
|
// return value;
|
|
// }
|
|
// [TableMatrix(VerticalTitle = "序号", HorizontalTitle = "答案", DrawElementMethod = "DrawColoredEnumElement", ResizableColumns = false)]
|
|
// // 0 2 0 1 3
|
|
// public bool[,] answers;
|
|
|
|
//[Button]
|
|
//public void SetAnswers()
|
|
//{
|
|
// Debug.Log(answers.GetLength(0));
|
|
// var length = answers.GetLength(0);
|
|
// intAnswers = new int[length];
|
|
// for (int row = 0; row < length; row++)
|
|
// {
|
|
// for (int i = 0; i < answers.GetLength(1); i++)
|
|
// {
|
|
// if (answers[row, i] == true)
|
|
// {
|
|
// intAnswers[row] = i;
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
// this.SetDirty();
|
|
//}
|
|
//#endif
|
|
|
|
public int[] intAnswers;
|
|
|
|
public Button confirmBtn;
|
|
|
|
public bool isSingle = true;
|
|
|
|
public UILineRenderer lineRendererPrefab;
|
|
public UILineRenderer currentLine;
|
|
|
|
public List<Transform> roots;
|
|
|
|
public List<Button> allbtns;
|
|
public HashSet<Transform> items;
|
|
|
|
[Sirenix.OdinInspector.ReadOnly]
|
|
public SegmentContainer<Button, UILineRenderer> segmentContainer;
|
|
|
|
#if UNITY_EDITOR
|
|
private void Reset()
|
|
{
|
|
confirmBtn = this.GetComponentInChildren<Button>(true);
|
|
lineRendererPrefab = Resources.Load<UILineRenderer>("Prefabs/lineRender");
|
|
|
|
this.roots = this
|
|
.transform
|
|
.Children()
|
|
.Where(_ => _.name.Contains("Root"))
|
|
.ToList();
|
|
|
|
this.items = new HashSet<Transform>();
|
|
this.items.UnionWith(this.roots.SelectMany(_ => _.Children()));
|
|
|
|
var row = this.roots[0].childCount;
|
|
var col = this.roots[1].childCount;
|
|
//this.answers = new bool[row, col];
|
|
}
|
|
|
|
[Button]
|
|
private void ResetValue()
|
|
{
|
|
confirmBtn = this.GetComponentInChildren<Button>(true);
|
|
lineRendererPrefab = Resources.Load<UILineRenderer>("Prefabs/lineRender");
|
|
|
|
this.roots = this
|
|
.transform
|
|
.Children()
|
|
.Where(_ => _.name.Contains("Root"))
|
|
.ToList();
|
|
//this.items = new HashSet<Transform>();
|
|
//this.items.UnionWith(this.roots.SelectMany(_ => _.Children()));
|
|
this.allbtns = this.roots.SelectMany(_ => _.Children()).Select(_ => _.GetComponent<Button>()).ToList();
|
|
}
|
|
#endif
|
|
|
|
private bool IsSameParent(Transform a, Transform b)
|
|
{
|
|
var result = a.parent == b.parent;
|
|
Debug.Log(result);
|
|
return result;
|
|
}
|
|
|
|
private Vector3 GetPos(Transform transform)
|
|
{
|
|
//var pos = btn.transform.position;
|
|
var pos = transform.Find("pos").position;
|
|
return pos;
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
this.items = new HashSet<Transform>();
|
|
this.items.UnionWith(this.allbtns.Select(_ => _.transform));
|
|
|
|
var lineRoot = this.transform.Find("LinePool");
|
|
this.segmentContainer = new SegmentContainer<Button, UILineRenderer>();
|
|
var canvas = this.transform.GetComponentInParent<Canvas>();
|
|
var camera = canvas.worldCamera;
|
|
|
|
Vector2 GetRealPos(Vector2 vector)
|
|
{
|
|
var pos = RectTransformUtility.WorldToScreenPoint(camera, vector);
|
|
pos = this.CoordinateTransform(currentLine, pos);
|
|
return pos;
|
|
}
|
|
|
|
bool OnEndDrag(Button startNode, Button endNode)
|
|
{
|
|
// 是否为节点
|
|
var result1 = this.items.Contains(endNode.transform);
|
|
if (result1 != true)
|
|
{
|
|
return false;
|
|
}
|
|
// 是否为同一父物体
|
|
var result2 = IsSameParent(startNode.transform, endNode.transform);
|
|
if (result2 == true)
|
|
{
|
|
return false;
|
|
}
|
|
// 是否可以连线
|
|
var result3 = this.segmentContainer.IsJoinable(startNode, endNode);
|
|
if (result3 != true)
|
|
{
|
|
return false;
|
|
}
|
|
//var pos = RectTransformUtility.WorldToScreenPoint(camera, GetPos(endNode.transform));
|
|
//currentLine.Points[1] = camera.WorldToScreenPoint(GetPos(endNode.transform));
|
|
var pos = GetRealPos(GetPos(endNode.transform));
|
|
currentLine.Points[1] = pos;
|
|
currentLine.SetAllDirty();
|
|
|
|
var segment = new SegmentContainer<Button, UILineRenderer>.Segment()
|
|
{
|
|
startNode = startNode,
|
|
endNode = endNode,
|
|
line = currentLine,
|
|
};
|
|
this.segmentContainer.AddSegment(segment);
|
|
return true;
|
|
}
|
|
|
|
//var key = false;
|
|
//foreach (var item in allbtns)
|
|
//{
|
|
// var btn = item;
|
|
|
|
// 点击后移除所有与该节点相连的对象
|
|
//btn.OnClickAsObservable()
|
|
// .Where(_ => key == false)
|
|
// .Where(_ => currentLine == false)
|
|
// .Subscribe(_ => {
|
|
// var allLines = this.segmentContainer.GetSegments(btn)?.Select(value => value.line).ToArray();
|
|
// if (allLines == null)
|
|
// {
|
|
// return;
|
|
// }
|
|
// foreach (var line in allLines)
|
|
// {
|
|
// Destroy(line.gameObject);
|
|
// }
|
|
// this.segmentContainer.ClearNode(btn);
|
|
// })
|
|
// .AddTo(this);
|
|
|
|
// // 点击生成线段
|
|
// btn//.OnEndDragAsObservable()
|
|
// .OnClickAsObservable()
|
|
// //.Where(_ => key == true)
|
|
// //.Where(_ => currentLine == true)
|
|
// .Subscribe(_ => {
|
|
// if (key == true && currentLine == true)
|
|
// {
|
|
// var endNode = GetHover()?.GetComponentInParent<Button>();
|
|
// Debug.Log(endNode);
|
|
// var result = false;
|
|
// if (endNode == true)
|
|
// {
|
|
// result = OnEndDrag(btn, endNode);
|
|
// }
|
|
|
|
// if (result != true)
|
|
// {
|
|
// GameObject.Destroy(currentLine.gameObject);
|
|
// }
|
|
// currentLine = null;
|
|
// key = false;
|
|
// }
|
|
// else if (key == false && currentLine == false)
|
|
// {
|
|
// if (this.isSingle == true)
|
|
// {
|
|
// var s = this.segmentContainer.GetSegments(btn);
|
|
// if (s != null && s.Count > 0)
|
|
// {
|
|
// Debug.Log("已连线");
|
|
|
|
// var allLines = s.Select(value => value.line).ToArray();
|
|
// foreach (var line in allLines)
|
|
// {
|
|
// Destroy(line.gameObject);
|
|
// }
|
|
// this.segmentContainer.ClearNode(btn);
|
|
// return;
|
|
// }
|
|
// }
|
|
// //var pos = camera.WorldToScreenPoint(GetPos(btn.transform));
|
|
// //Debug.Log(pos);
|
|
// //var pos = RectTransformUtility.WorldToScreenPoint(camera, GetPos(btn.transform));
|
|
// currentLine = Instantiate(lineRendererPrefab, lineRoot);
|
|
// //pos = this.CoordinateTransform(currentLine, pos);
|
|
// var pos = GetRealPos(GetPos(btn.transform));
|
|
// Debug.Log(pos);
|
|
// currentLine.Points[0] = pos;
|
|
// key = true;
|
|
// }
|
|
// //else
|
|
// //{
|
|
// // var allLines = this.segmentContainer.GetSegments(btn)?.Select(value => value.line).ToArray();
|
|
// // if (allLines == null)
|
|
// // {
|
|
// // return;
|
|
// // }
|
|
// // foreach (var line in allLines)
|
|
// // {
|
|
// // Destroy(line.gameObject);
|
|
// // }
|
|
// // this.segmentContainer.ClearNode(btn);
|
|
// //}
|
|
// })
|
|
// .AddTo(this);
|
|
|
|
// //btn.OnDragAsObservable()
|
|
// // .Where(_ => currentLine == true)
|
|
// // .Subscribe(_ => {
|
|
// // currentLine.Points[1] = this.CoordinateTransform(currentLine, _.position);
|
|
// // currentLine.SetAllDirty();
|
|
// // })
|
|
// // .AddTo(this);
|
|
|
|
// //btn//.OnBeginDragAsObservable()
|
|
// // .OnClickAsObservable()
|
|
// // .Where(_ => key == false)
|
|
// // .Subscribe(_ => {
|
|
// // if (this.isSingle == true)
|
|
// // {
|
|
// // var s = this.segmentContainer.GetSegments(btn);
|
|
// // if (s != null && s.Count > 0)
|
|
// // {
|
|
// // Debug.Log("已连线");
|
|
// // return;
|
|
// // }
|
|
// // }
|
|
// // //var pos = camera.WorldToScreenPoint(GetPos(btn.transform));
|
|
// // //Debug.Log(pos);
|
|
// // //var pos = RectTransformUtility.WorldToScreenPoint(camera, GetPos(btn.transform));
|
|
// // currentLine = Instantiate(lineRendererPrefab, lineRoot);
|
|
// // //pos = this.CoordinateTransform(currentLine, pos);
|
|
// // var pos = GetRealPos(GetPos(btn.transform));
|
|
// // Debug.Log(pos);
|
|
// // currentLine.Points[0] = pos;
|
|
// // key = true;
|
|
// // })
|
|
// // .AddTo(this);
|
|
//}
|
|
|
|
|
|
var root1 = this.roots[0].transform.GetComponentsInChildren<Button>(true);
|
|
var root2 = this.roots[1].transform.GetComponentsInChildren<Button>(true);
|
|
var all = this.allbtns;
|
|
var token = this.GetCancellationTokenOnDestroy();
|
|
|
|
UniTask.Create(async () => {
|
|
while (token.IsCancellationRequested != true)
|
|
{
|
|
var index = await UniTask.WhenAny(root1.Select(_ => _.OnClickAsync(token)));
|
|
var btn = root1[index];
|
|
if (this.isSingle == true)
|
|
{
|
|
var s = this.segmentContainer.GetSegments(btn);
|
|
if (s != null && s.Count > 0)
|
|
{
|
|
Debug.Log("已连线");
|
|
|
|
var allLines = s.Select(value => value.line).ToArray();
|
|
foreach (var line in allLines)
|
|
{
|
|
Destroy(line.gameObject);
|
|
}
|
|
this.segmentContainer.ClearNode(btn);
|
|
continue;
|
|
}
|
|
}
|
|
currentLine = Instantiate(lineRendererPrefab, lineRoot);
|
|
var pos = GetRealPos(GetPos(btn.transform));
|
|
Debug.Log(pos);
|
|
currentLine.Points[0] = pos;
|
|
|
|
await UniTask.WhenAny(all.Select(_ => _.OnClickAsync(token)));
|
|
|
|
var endNode = GetHover()?.GetComponentInParent<Button>();
|
|
Debug.Log(endNode);
|
|
var result = false;
|
|
if (endNode == true)
|
|
{
|
|
result = OnEndDrag(btn, endNode);
|
|
}
|
|
|
|
if (result != true)
|
|
{
|
|
GameObject.Destroy(currentLine.gameObject);
|
|
}
|
|
currentLine = null;
|
|
}
|
|
});
|
|
|
|
//foreach (var item in root1)
|
|
//{
|
|
|
|
//}
|
|
|
|
this.UpdateAsObservable()
|
|
.Where(_ => currentLine == true)
|
|
.Subscribe(_ => {
|
|
currentLine.Points[1] = this.CoordinateTransform(currentLine, Input.mousePosition);
|
|
currentLine.SetAllDirty();
|
|
})
|
|
.AddTo(this);
|
|
}
|
|
|
|
private GameObject GetHover()
|
|
{
|
|
var data = new PointerEventData(EventSystem.current)
|
|
{
|
|
position = Input.mousePosition,
|
|
};
|
|
var results = new List<RaycastResult>();
|
|
EventSystem.current.RaycastAll(data, results);
|
|
return results.FirstOrDefault().gameObject;
|
|
}
|
|
|
|
public bool[] GetRight()
|
|
{
|
|
var btns = this.roots[0].Children().Select(_ => _.GetComponent<Button>()).ToArray();
|
|
var outs = new bool[btns.Length];
|
|
//var col = this.answers.GetLength(1);
|
|
for (int i = 0; i < btns.Length; i++)
|
|
{
|
|
var item = btns[i];
|
|
var set = this.segmentContainer.GetNodes(item);
|
|
if (set != null)
|
|
{
|
|
var result = true;
|
|
var answer = this.intAnswers[i];
|
|
Debug.Log($"答案{answer}");
|
|
var btn = this.roots[1].GetChild(answer).GetComponent<Button>();
|
|
if (set.Contains(btn) != true)
|
|
{
|
|
result = false;
|
|
}
|
|
//for (int j = 0; j < col; j++)
|
|
//{
|
|
// var btn = this.roots[1].GetChild(j).GetComponent<Button>();
|
|
// Debug.Log($"答案{this.answers[i, j]}");
|
|
|
|
//}
|
|
outs[i] = result;
|
|
}
|
|
}
|
|
return outs;
|
|
}
|
|
|
|
public override async UniTask WaitAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
await this.StartAsync();
|
|
|
|
foreach (var item in this.segmentContainer.Clear())
|
|
{
|
|
Debug.Log(item);
|
|
Destroy(item.line);
|
|
}
|
|
|
|
confirmBtn.gameObject.SetActive(true);
|
|
await this.confirmBtn.OnClickAsync(cancellationToken);
|
|
var isRight = this.GetRight();
|
|
foreach (var item in isRight)
|
|
{
|
|
Debug.Log(item);
|
|
}
|
|
// 改变线颜色 TODO
|
|
var btns = this.roots[0].Children().Select(_ => _.GetComponent<Button>());
|
|
var index = 0;
|
|
foreach (var item in btns)
|
|
{
|
|
var s = this.segmentContainer.GetSegments(item);
|
|
if (s == null)
|
|
{
|
|
break;
|
|
}
|
|
if (isRight[index] == true)
|
|
{
|
|
foreach (var tempS in s)
|
|
{
|
|
tempS.line.color = Color.green;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var tempS in s)
|
|
{
|
|
tempS.line.color = Color.red;
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
//while (cancellationToken.IsCancellationRequested != true)
|
|
//{
|
|
// await this.confirmBtn.OnClickAsync(cancellationToken);
|
|
// foreach (var item in this.GetRight())
|
|
// {
|
|
// Debug.Log(item);
|
|
// }
|
|
//}
|
|
confirmBtn.gameObject.SetActive(false);
|
|
}
|
|
|
|
private Vector2 CoordinateTransform(UILineRenderer lineRenderer, Vector2 position)
|
|
{
|
|
var rect = lineRenderer.GetComponent<RectTransform>().rect;
|
|
var x = rect.width / Screen.width;
|
|
var y = rect.height / Screen.height;
|
|
position.x *= x;
|
|
position.y *= y;
|
|
return position;
|
|
}
|
|
}
|
|
|
|
[Searchable]
|
|
public class SegmentContainer<TNode, TLine>
|
|
{
|
|
[SerializeField]
|
|
[DictionaryDrawerSettings]
|
|
private Dictionary<TNode, HashSet<Segment>> _segmentMapper = new Dictionary<TNode, HashSet<Segment>>();
|
|
[SerializeField]
|
|
[DictionaryDrawerSettings]
|
|
private Dictionary<TNode, HashSet<TNode>> _nodeMapper = new Dictionary<TNode, HashSet<TNode>>();
|
|
|
|
private void AddSegmentMapper(TNode node, Segment segment)
|
|
{
|
|
if (_segmentMapper.ContainsKey(node) == false)
|
|
{
|
|
_segmentMapper.Add(node, new HashSet<Segment>());
|
|
}
|
|
var set = _segmentMapper[node];
|
|
set.Add(segment);
|
|
}
|
|
|
|
private void RemoveSegmentMapper(TNode node, Segment segment)
|
|
{
|
|
if (_segmentMapper.ContainsKey(node) == false)
|
|
{
|
|
return;
|
|
}
|
|
var set = _segmentMapper[node];
|
|
if (set == null)
|
|
{
|
|
return;
|
|
}
|
|
set.Remove(segment);
|
|
}
|
|
|
|
private void AddNodeMapper(TNode startNode, TNode endNode)
|
|
{
|
|
if (_nodeMapper.ContainsKey(startNode) == false)
|
|
{
|
|
_nodeMapper.Add(startNode, new HashSet<TNode>());
|
|
}
|
|
var set = _nodeMapper[startNode];
|
|
set.Add(endNode);
|
|
}
|
|
|
|
private void RemoveNodeMapper(TNode startNode, TNode endNode)
|
|
{
|
|
if (_nodeMapper.ContainsKey(startNode) == false)
|
|
{
|
|
return;
|
|
}
|
|
var set = _nodeMapper[startNode];
|
|
if (set == null)
|
|
{
|
|
return;
|
|
}
|
|
set.Remove(endNode);
|
|
}
|
|
|
|
public void AddSegment(Segment segment)
|
|
{
|
|
AddSegmentMapper(segment.startNode, segment);
|
|
AddSegmentMapper(segment.endNode, segment);
|
|
|
|
AddNodeMapper(segment.startNode, segment.endNode);
|
|
AddNodeMapper(segment.endNode, segment.startNode);
|
|
}
|
|
|
|
public void RemoveSegment(Segment segment)
|
|
{
|
|
RemoveSegmentMapper(segment.startNode, segment);
|
|
RemoveSegmentMapper(segment.endNode, segment);
|
|
|
|
RemoveNodeMapper(segment.startNode, segment.endNode);
|
|
RemoveNodeMapper(segment.endNode, segment.startNode);
|
|
}
|
|
|
|
public HashSet<Segment> GetSegments(TNode node)
|
|
{
|
|
_segmentMapper.TryGetValue(node, out var segments);
|
|
return segments;
|
|
}
|
|
|
|
public HashSet<TNode> GetNodes(TNode node)
|
|
{
|
|
_nodeMapper.TryGetValue(node, out var nodes);
|
|
return nodes;
|
|
}
|
|
|
|
public void ClearNode(TNode node)
|
|
{
|
|
if (_segmentMapper.ContainsKey(node) != true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var item in _segmentMapper[node].ToArray())
|
|
{
|
|
RemoveSegment(item);
|
|
}
|
|
_segmentMapper[node].Clear();
|
|
}
|
|
|
|
public bool IsJoinable(TNode startNode, TNode endNode)
|
|
{
|
|
if (this._nodeMapper.ContainsKey(startNode) == false)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
var nodes = this._nodeMapper[startNode];
|
|
if (nodes == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (nodes.Contains(endNode) == true)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public IEnumerable<Segment> Clear()
|
|
{
|
|
var allSegment = new List<Segment>();
|
|
foreach (var item in _segmentMapper)
|
|
{
|
|
allSegment.AddRange(item.Value);
|
|
this.ClearNode(item.Key);
|
|
}
|
|
return allSegment.Distinct();
|
|
}
|
|
|
|
public struct Segment
|
|
{
|
|
public TNode startNode;
|
|
public TNode endNode;
|
|
public TLine line;
|
|
}
|
|
} |