Skip to content

Commit

Permalink
优化了共享变量的显示逻辑
Browse files Browse the repository at this point in the history
1.优化了共享变量的显示逻辑,使用ForcedSharedAttribute对共享变量进行强制共享
2.加入方便的拖拽复制功能
  • Loading branch information
AkiKurisu committed Feb 14, 2023
1 parent de15008 commit 6c90841
Show file tree
Hide file tree
Showing 84 changed files with 613 additions and 832 deletions.
143 changes: 51 additions & 92 deletions AkiBT/Editor/Core/BehaviorTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
using System;
namespace Kurisu.AkiBT.Editor
{
public class BehaviorTreeView: GraphView
public class BehaviorTreeView: GraphView,ITreeView
{
internal readonly StyleSheet nodeSheet;
private readonly struct EdgePair
{
public readonly NodeBehavior NodeBehavior;
Expand All @@ -25,7 +24,8 @@ public EdgePair(NodeBehavior nodeBehavior, Port parentOutputPort)
public Blackboard _blackboard;
protected readonly IBehaviorTree behaviorTree;
protected RootNode root;
public List<SharedVariable> ExposedProperties=new List<SharedVariable>();
private List<SharedVariable> exposedProperties=new List<SharedVariable>();
public List<SharedVariable> ExposedProperties=>exposedProperties;
private FieldResolverFactory fieldResolverFactory = new FieldResolverFactory();
protected NodeSearchWindowProvider provider;
public event System.Action<SharedVariable> OnPropertyNameChangeEvent;
Expand All @@ -44,21 +44,16 @@ public string SavePath
/// 是否可以保存为SO,如果可以会在ToolBar中提供按钮
/// </summary>
public virtual bool CanSaveToSO=>behaviorTree is BehaviorTree;
/// <summary>
/// 编辑器名称
/// </summary>
public virtual string treeEditorName=>"AkiBT";
private readonly NodeResolver nodeResolver = new NodeResolver();
/// <summary>
/// 结点选择委托
/// </summary>
public System.Action<BehaviorTreeNode> onSelectAction;
private readonly EditorWindow _window;
/// <summary>
/// 是否在Restore中
/// </summary>
/// <value></value>
public bool IsRestored{get;private set;}
private readonly BehaviorNodeConverter converter=new BehaviorNodeConverter();
private readonly DragDropManipulator dragDropManipulator;
/// <summary>
/// 黑板
/// </summary>
Expand All @@ -84,7 +79,9 @@ public BehaviorTreeView(IBehaviorTree bt, EditorWindow editor)
this.AddManipulator(new RectangleSelector());
this.AddManipulator(new FreehandSelector());
this.AddManipulator(contentDragger);

dragDropManipulator=new DragDropManipulator(this);
dragDropManipulator.OnDragOverEvent+=CopyFromObject;
this.AddManipulator(dragDropManipulator);
provider = ScriptableObject.CreateInstance<NodeSearchWindowProvider>();
provider.Initialize(this, editor,BehaviorTreeSetting.GetMask(treeEditorName));

Expand All @@ -95,7 +92,6 @@ public BehaviorTreeView(IBehaviorTree bt, EditorWindow editor)
serializeGraphElements += CopyOperation;
canPasteSerializedData+=(data)=>true;
unserializeAndPaste+=OnPaste;
nodeSheet=BehaviorTreeSetting.GetNodeStyle(treeEditorName);
}
private string CopyOperation(IEnumerable<GraphElement> elements)
{
Expand All @@ -122,7 +118,7 @@ private void OnPaste(string a,string b)
node.Select(this,true);
});
}
internal BehaviorTreeNode DuplicateNode(BehaviorTreeNode node)
public BehaviorTreeNode DuplicateNode(BehaviorTreeNode node)
{
var newNode =nodeResolver.CreateNodeInstance(node.GetBehavior(),this) as BehaviorTreeNode;
Rect newRect=node.GetPosition();
Expand All @@ -133,7 +129,7 @@ internal BehaviorTreeNode DuplicateNode(BehaviorTreeNode node)
newNode.CopyFrom(node);
return newNode;
}
internal GroupBlock CreateBlock(Rect rect, GroupBlockData blockData = null)
public GroupBlock CreateBlock(Rect rect, GroupBlockData blockData = null)
{
if(blockData==null)blockData = new GroupBlockData();
var group = new GroupBlock
Expand Down Expand Up @@ -165,11 +161,6 @@ public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
evt.menu.MenuItems().Clear();
remainTargets.ForEach(evt.menu.MenuItems().Add);
}
/// <summary>
/// 添加共享变量到黑板
/// </summary>
/// <param name="variable"></param>
/// <typeparam name="T"></typeparam>
public void AddPropertyToBlackBoard(SharedVariable variable)
{
var localPropertyValue = variable.GetValue();
Expand Down Expand Up @@ -226,93 +217,61 @@ private void BuildBlackboardMenu(ContextualMenuPopulateEvent evt,VisualElement e
}
return compatiblePorts;
}
private void CopyFromObject(UnityEngine.Object data)
{
if(data is GameObject)
{
if((data as GameObject).TryGetComponent<IBehaviorTree>(out IBehaviorTree tree))
{
_window.ShowNotification(new GUIContent("GameObject Dropped Successfully!"));
CopyFromOtherTree(tree);
return;
}
_window.ShowNotification(new GUIContent("Invalid Drag GameObject!"));
return;
}
if(data is not IBehaviorTree)
{
_window.ShowNotification(new GUIContent("Invalid Drag Data!"));
return;
}
_window.ShowNotification(new GUIContent("Data Dropped Successfully!"));
CopyFromOtherTree(data as IBehaviorTree);
}
internal void CopyFromOtherTree(IBehaviorTree otherTree)
{
IsRestored=true;
IEnumerable<BehaviorTreeNode>nodes;
RootNode rootNode;
(rootNode,nodes)=converter.ConvertToNode(otherTree,this);
foreach(var node in nodes)node.onSelectAction=onSelectAction;
var edge=rootNode.Child.connections.First();
RemoveElement(edge);
RemoveElement(rootNode);
IsRestored=false;
}
/// <summary>
/// 重铸行为树
/// </summary>
internal void Restore()
{
var stack = new Stack<EdgePair>();
IBehaviorTree tree=behaviorTree;
if(behaviorTree.ExternalBehaviorTree!=null)tree=behaviorTree.ExternalBehaviorTree;
IBehaviorTree tree=behaviorTree.ExternalBehaviorTree??behaviorTree;
foreach(var variable in tree.SharedVariables)
{
AddPropertyToBlackBoard(variable.Clone() as SharedVariable);//Clone新的共享变量
}
stack.Push(new EdgePair(tree.Root, null));
var nodes=new List<BehaviorTreeNode>();
IsRestored=true;
while (stack.Count > 0)
{
// create node
var edgePair = stack.Pop();
if (edgePair.NodeBehavior == null)
{
continue;
}
var node = nodeResolver.CreateNodeInstance(edgePair.NodeBehavior.GetType(),this);
node.Restore(edgePair.NodeBehavior);
node.onSelectAction=onSelectAction;// 添加选中委托
AddElement(node);
nodes.Add(node);
node.SetPosition( edgePair.NodeBehavior.graphPosition);

// connect parent
if (edgePair.ParentOutputPort != null)
{
var edge = ConnectPorts(edgePair.ParentOutputPort, node.Parent);
AddElement(edge);
}

// seek child
switch (edgePair.NodeBehavior)
{
case Composite nb:
{
var compositeNode = node as CompositeNode;
var addible = nb.Children.Count - compositeNode.ChildPorts.Count;
for (var i = 0; i < addible; i++)
{
compositeNode.AddChild();
}

for (var i = 0; i < nb.Children.Count; i++)
{
stack.Push(new EdgePair(nb.Children[i], compositeNode.ChildPorts[i]));
}
break;
}
case Conditional nb:
{
var conditionalNode = node as ConditionalNode;
stack.Push(new EdgePair(nb.Child, conditionalNode.Child));
break;
}
case Decorator nb:
{
var decoratorNode = node as DecoratorNode;
stack.Push(new EdgePair(nb.Child, decoratorNode.Child));
break;
}
case Root nb:
{
root = node as RootNode;
if (nb.Child != null)
{
stack.Push(new EdgePair(nb.Child, root.Child));
}
break;
}
}
}
IEnumerable<BehaviorTreeNode>nodes;
(this.root,nodes)=converter.ConvertToNode(tree,this);
foreach(var node in nodes)node.onSelectAction=onSelectAction;
foreach (var nodeBlockData in tree.BlockData)
{
var block =CreateBlock(new Rect(nodeBlockData.Position, new Vector2(100, 100)),
nodeBlockData);
block.AddElements(nodes.Where(x=>nodeBlockData.ChildNodes.Contains(x.GUID)));
CreateBlock(new Rect(nodeBlockData.Position, new Vector2(100, 100)),nodeBlockData)
.AddElements(nodes.Where(x=>nodeBlockData.ChildNodes.Contains(x.GUID)));
}
IsRestored=false;
}
internal void SelectGroup(BehaviorTreeNode node)
void ITreeView.SelectGroup(BehaviorTreeNode node)
{
var block =CreateBlock(new Rect(node.transform.position, new Vector2(100, 100)));
foreach(var select in selection)
Expand All @@ -321,7 +280,7 @@ internal void SelectGroup(BehaviorTreeNode node)
block.AddElement(select as BehaviorTreeNode);
}
}
internal void UnSelectGroup()
void ITreeView.UnSelectGroup()
{
foreach(var select in selection)
{
Expand Down
36 changes: 35 additions & 1 deletion AkiBT/Editor/Core/GraphEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class GraphEditorWindow : EditorWindow
public BehaviorTreeView GraphView=>graphView;
private UnityEngine.Object key { get; set; }
InfoView infoView;
/// <summary>
/// 用于判断SO类型
/// </summary>
/// <returns></returns>
protected virtual Type SOType=>typeof(BehaviorTreeSO);
protected virtual string TreeName=>"行为树";
protected virtual string InfoText=>"欢迎使用AkiBT,一个超简单的行为树!";
Expand All @@ -33,10 +37,21 @@ public static void Show(IBehaviorTree bt)
window.Show();
window.Focus();
}
/// <summary>
/// 创建GraphView实例
/// </summary>
/// <param name="behaviorTree"></param>
/// <returns></returns>
protected virtual BehaviorTreeView CreateView(IBehaviorTree behaviorTree)
{
return new BehaviorTreeView(behaviorTree, this);
}
/// <summary>
/// 创建EditorWindow实例
/// </summary>
/// <param name="bt"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
protected static T Create<T>(IBehaviorTree bt)where T:GraphEditorWindow
{

Expand All @@ -62,7 +77,7 @@ private static void StructGraphView(GraphEditorWindow window, IBehaviorTree beha
window.rootVisualElement.Clear();
window.graphView=window.CreateView(behaviorTree);
window.infoView=new InfoView(window.InfoText);
window.infoView.styleSheets.Add((StyleSheet)Resources.Load("AkiBT/Info", typeof(StyleSheet)));
window.infoView.styleSheets.Add(Resources.Load<StyleSheet>("AkiBT/Info"));
window.graphView.Add( window.infoView);
window.graphView.onSelectAction=window.OnNodeSelectionChange;//绑定委托
GenerateBlackBoard(window.graphView);
Expand Down Expand Up @@ -211,12 +226,31 @@ private VisualElement CreateToolBar(BehaviorTreeView graphView)
SaveDataToSO();
}
}
if (GUILayout.Button("从SO复制", EditorStyles.toolbarButton))
{
string path=EditorUtility.OpenFilePanel("选择复制文件",Application.dataPath,"asset");
var data=LoadDataFromFile(path.Replace(Application.dataPath,string.Empty));
if(data!=null)graphView.CopyFromOtherTree(data);
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
);
}
private IBehaviorTree LoadDataFromFile(string path)
{
try
{
return AssetDatabase.LoadAssetAtPath<BehaviorTreeSO>($"Assets/{path}");

}
catch
{
this.ShowNotification(new GUIContent($"无效路径:Assets/{path},请选择BehaviorTreeSO"));
return null;
}
}
void OnNodeSelectionChange(BehaviorTreeNode node)
{
infoView.UpdateSelection(node);
Expand Down
2 changes: 1 addition & 1 deletion AkiST/Runtime.meta → AkiBT/Editor/Core/Interface.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions AkiBT/Editor/Core/Interface/ITreeView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
namespace Kurisu.AkiBT.Editor
{
public interface ITreeView
{
/// <summary>
/// 将选中结点加入Group并创建Block
/// </summary>
/// <param name="node"></param>
void SelectGroup(BehaviorTreeNode node);
/// <summary>
/// 取消Group
/// </summary>
void UnSelectGroup();
/// <summary>
/// 复制结点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
BehaviorTreeNode DuplicateNode(BehaviorTreeNode node);
/// <summary>
/// 编辑器名称
/// </summary>
string treeEditorName{get;}
/// <summary>
/// 共享变量名称修改事件(手动触发)
/// </summary>
event System.Action<SharedVariable> OnPropertyNameChangeEvent;
/// <summary>
/// 共享变量名称编辑事件(自动触发)
/// </summary>
event System.Action<SharedVariable> OnPropertyNameEditingEvent;
List<SharedVariable> ExposedProperties{get;}
/// <summary>
/// 是否在Restore中
/// </summary>
/// <value></value>
bool IsRestored{get;}
/// <summary>
/// 添加共享变量到黑板
/// </summary>
/// <param name="variable"></param>
/// <typeparam name="T"></typeparam>
void AddPropertyToBlackBoard(SharedVariable variable);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion AkiBT/Editor/Core/Member/Field/ListField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected virtual ListView CreateListView()
if(field is BaseField<T>)(field as BaseField<T>).label=string.Empty;
return field;
};
const int itemHeight = 16;
const int itemHeight = 20;
var view = new ListView(value, itemHeight, makeItem, bindItem);
return view;
}
Expand Down
Loading

0 comments on commit 6c90841

Please sign in to comment.