chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd800ca490ca3504a91a04cbbe34fe93
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 91a2221b1f964c946af33b7bb66b948d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
internal class ReadOnlyAttributeDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
bool oldValue = GUI.enabled;
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = oldValue;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bfac06ae39b4da041b0233fbe194c0a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1df174b62e1d3684f9fc16ede26d1866
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using UnityEngine;
using UnityEditor;
namespace PathBerserker2d
{
internal class IconHandle2D
{
private static readonly int controlHashIcon = "IconHandle2D_icon".GetHashCode();
public static void DrawHandle(Vector3 position, Texture icon, float size, Object objectToSelect)
{
int iconId = EditorGUIUtility.GetControlID(controlHashIcon, FocusType.Passive);
var e = Event.current;
switch (e.type)
{
case EventType.MouseDown:
if (e.button == 0 && HandleUtility.nearestControl == iconId)
{
Selection.activeObject = objectToSelect;
e.Use();
}
break;
case EventType.Layout:
float distance = HandleUtility.DistanceToRectangle(position, Camera.current.transform.rotation, size);
HandleUtility.AddControl(iconId, distance);
break;
case EventType.Repaint:
Vector3 up = Camera.current.transform.up * size;
float aspectRatio = (float)icon.width / (float)icon.height;
Vector3 right = Camera.current.transform.right * size * aspectRatio;
SharedMaterials.UnlitTexture.SetTexture("_MainTex", icon);
SharedMaterials.UnlitTexture.SetPass(0);
GL.Begin(GL.QUADS);
{
GL.TexCoord2(1, 1);
GL.Vertex(position + right + up);
GL.TexCoord2(1, 0);
GL.Vertex(position + right - up);
GL.TexCoord2(0, 0);
GL.Vertex(position - right - up);
GL.TexCoord2(0, 1);
GL.Vertex(position - right + up);
}
GL.End();
break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99480f75d87fac34c9ad4d0125d7a2e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e5d17af10e083841ac8d81ba70fd984
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class MyGUI
{
public static string nameSeed = "aWJhcmFraQ==";
public static string nameSeed2 = "aWJhcl9ha2k=";
static GUIStyle horizontalLine;
static MyGUI()
{
horizontalLine = new GUIStyle();
horizontalLine.normal.background = EditorGUIUtility.whiteTexture;
horizontalLine.margin = new RectOffset(0, 0, 4, 4);
horizontalLine.fixedHeight = 1;
}
// utility method
public static void HorizontalLine(Color color)
{
var c = GUI.color;
GUI.color = color;
GUILayout.Box(GUIContent.none, horizontalLine);
GUI.color = c;
}
public static void Header(string text)
{
EditorGUILayout.LabelField(text, EditorStyles.boldLabel);
}
public static void DrawNavTagLayout(SerializedProperty spNavTag)
{
EditorGUILayout.BeginHorizontal();
spNavTag.intValue = EditorGUILayout.Popup("NavTag", spNavTag.intValue, PathBerserker2dSettings.NavTags);
if (GUILayout.Button("+", EditorStyles.miniButtonRight, GUILayout.Width(17)))
{
SettingsService.OpenProjectSettings(PathBerserker2dSettingsProvider.WindowPath);
}
EditorGUILayout.EndHorizontal();
}
public static void DrawNavTagColorPickerLayout(SerializedProperty spNavTag)
{
int tag = spNavTag.intValue;
if (tag == 0)
GUI.enabled = false;
PathBerserker2dSettings.SetNavTagColor(tag, EditorGUILayout.ColorField("NavTag Color", PathBerserker2dSettings.GetNavTagColor(tag)));
GUI.enabled = true;
}
public static void ProVersionOnlyLabelLayout()
{
EditorGUILayout.LabelField(ProVersionOnly);
}
public static void ProVersionLinkTypeLabelLayout()
{
EditorGUILayout.LabelField("Custom link types are limited to the pro-version.");
}
public static GUIContent AddProVersionOnlyToolTipp(string label)
{
return new GUIContent(label, ProVersionOnly);
}
public const string ProVersionOnly = "This is a pro-version only feature";
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88926f52dae6ae348a100721fd59f003
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
using UnityEngine;
using UnityEditor;
namespace PathBerserker2d
{
internal class PositionHandle2D
{
private Vector3 startPos;
private Vector2 currentMousePos;
private Vector2 startMousePos;
public Color primary;
public Color hover;
public Color selected;
private int hash;
public PositionHandle2D(Color primary, Color hover, Color selected)
{
this.primary = primary;
this.hover = hover;
this.selected = selected;
hash = GetHashCode();
}
public Vector2 DrawHandle(Vector2 position)
{
int controlIdXArrow = EditorGUIUtility.GetControlID(hash, FocusType.Passive);
int controlIdYArrow = EditorGUIUtility.GetControlID(hash, FocusType.Passive);
int controlIdRect = EditorGUIUtility.GetControlID(hash, FocusType.Passive);
bool selectedXArrow = GUIUtility.hotControl == controlIdXArrow;
bool hoveredXArrow = HandleUtility.nearestControl == controlIdXArrow;
bool selectedYArrow = GUIUtility.hotControl == controlIdYArrow;
bool hoveredYArrow = HandleUtility.nearestControl == controlIdYArrow;
bool selectedRect = GUIUtility.hotControl == controlIdRect;
bool hoveredRect = HandleUtility.nearestControl == controlIdRect;
var e = Event.current;
switch (e.type)
{
case EventType.MouseDown:
if (e.button == 0 && GUIUtility.hotControl == 0 && !e.alt)
{
if (HandleUtility.nearestControl == controlIdXArrow)
{
GUIUtility.hotControl = controlIdXArrow;
}
else if (HandleUtility.nearestControl == controlIdYArrow)
{
GUIUtility.hotControl = controlIdYArrow;
}
else if (HandleUtility.nearestControl == controlIdRect)
{
GUIUtility.hotControl = controlIdRect;
}
if (HandleUtility.nearestControl == controlIdXArrow ||
HandleUtility.nearestControl == controlIdYArrow ||
HandleUtility.nearestControl == controlIdRect)
{
startPos = position;
currentMousePos = e.mousePosition;
startMousePos = e.mousePosition;
e.Use();
}
}
break;
case EventType.MouseUp:
if (e.button == 0 || e.button == 2)
{
if (GUIUtility.hotControl == controlIdXArrow || GUIUtility.hotControl == controlIdYArrow || GUIUtility.hotControl == controlIdRect)
{
GUIUtility.hotControl = 0;
e.Use();
selectedXArrow = false;
selectedYArrow = false;
selectedRect = false;
}
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlIdXArrow || GUIUtility.hotControl == controlIdYArrow || GUIUtility.hotControl == controlIdRect)
{
currentMousePos += new Vector2(e.delta.x, -e.delta.y) * EditorGUIUtility.pixelsPerPoint;
Vector3 screenPos = Camera.current.WorldToScreenPoint(Handles.matrix.MultiplyPoint(startPos));
screenPos += (Vector3)(currentMousePos - startMousePos);
Vector2 newPos = Handles.inverseMatrix.MultiplyPoint(Camera.current.ScreenToWorldPoint(screenPos));
if (selectedXArrow)
{
newPos.y = startPos.y;
}
else if (selectedYArrow)
{
newPos.x = startPos.x;
}
if (newPos != position)
{
position = newPos;
GUI.changed = true;
}
e.Use();
}
break;
}
Handles.color = selectedRect || selectedXArrow ? selected : (hoveredXArrow ? hover : primary);
Handles.ArrowHandleCap(controlIdXArrow, position, Quaternion.Euler(0, 90, 0), HandleUtility.GetHandleSize(position), e.type);
Handles.color = selectedRect || selectedYArrow ? selected : (hoveredYArrow ? hover : primary);
Handles.ArrowHandleCap(controlIdYArrow, position, Quaternion.Euler(-90, 0, 0), HandleUtility.GetHandleSize(position), e.type);
Handles.color = selectedRect ? selected : (hoveredRect ? hover : primary);
float rectSize = HandleUtility.GetHandleSize(position) * 0.14f;
Vector2 rectPos = position + Vector2.one * rectSize;
if (e.type == EventType.Repaint)
Handles.DrawSolidRectangleWithOutline(new Rect(position, new Vector2(rectSize, rectSize) * 2f), new Color(1, 1, 1, 0.2f), new Color(1, 1, 1, 1));
rectPos = Handles.Slider2D(rectPos, Vector3.forward, Vector3.right, Vector3.up, rectSize, Handles.RectangleHandleCap, 0);
position = rectPos - Vector2.one * rectSize;
return position;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38d3d3506e06653448c052e8936eb8dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using System;
using UnityEditor;
namespace PathBerserker2d
{
[InitializeOnLoad]
internal class ExecutionOrderManager : Editor
{
static ExecutionOrderManager()
{
foreach (MonoScript monoScript in MonoImporter.GetAllRuntimeMonoScripts())
{
Type type = monoScript.GetClass();
if (type == null)
{
continue;
}
object[] attributes = type.GetCustomAttributes(typeof(ScriptExecutionOrderAttribute), true);
if (attributes.Length == 0)
{
continue;
}
ScriptExecutionOrderAttribute attribute = (ScriptExecutionOrderAttribute)attributes[0];
if (MonoImporter.GetExecutionOrder(monoScript) != attribute.GetOrder())
{
MonoImporter.SetExecutionOrder(monoScript, attribute.GetOrder());
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d337207a086912840ae6fa25daa40c29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8a6fbf381f5910a46b7602b805ab3da5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,239 @@
using System;
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class GizmosDrawingExtensions
{
public static void DrawArrowHead(Vector2 basePos, Vector2 dir, float size)
{
dir.Normalize();
Vector2 normal = new Vector2(-dir.y, dir.x) * size;
Gizmos.DrawLine(basePos - normal, basePos + normal);
Gizmos.DrawLine(basePos - normal, basePos + dir * size);
Gizmos.DrawLine(basePos + normal, basePos + dir * size);
}
public static void DrawArrowHeadFromSpike(Vector2 pointyPos, Vector2 dir, float size)
{
dir.Normalize();
pointyPos -= dir * size;
Vector2 normal = new Vector2(-dir.y, dir.x) * size;
Gizmos.DrawLine(pointyPos - normal, pointyPos + normal);
Gizmos.DrawLine(pointyPos - normal, pointyPos + dir * size);
Gizmos.DrawLine(pointyPos + normal, pointyPos + dir * size);
}
public static void DrawArrow(Vector2 start, Vector2 end, float size)
{
Vector2 dir = end - start;
float length = dir.magnitude;
dir /= length;
Vector3 end3 = new Vector3(end.x, end.y);
Gizmos.DrawLine(start, end3);
Vector2 normal = new Vector2(-dir.y, dir.x) * size;
Vector2 baseA = start + dir * (length - size);
Gizmos.DrawLine(baseA - normal, end3);
Gizmos.DrawLine(baseA + normal, end3);
}
public static void DrawCircle(Vector2 center, float radius = 0.05f)
{
int segmentCount = 10;
Vector2 prevPoint = center + Vector2.up * radius;
for (float t = 1; t <= segmentCount; t++)
{
float x = (t / segmentCount) * Mathf.PI * 2.0f;
float cx = Mathf.Sin(x);
float cy = Mathf.Cos(x);
Vector2 point = center + new Vector2(cx, cy) * radius;
Gizmos.DrawLine(prevPoint, point);
prevPoint = point;
}
Gizmos.DrawLine(prevPoint, center + Vector2.up * radius);
}
public static void DrawBezierConnection(Vector2 start, Vector2 end, bool biDirectional)
{
Vector2 cp;
var tangent = (end - start);
var length = tangent.magnitude;
var normal = new Vector2(-tangent.y, tangent.x) / length;
cp = start + tangent * 0.5f + normal * (length / 5f);
DrawBezierConnection(start, end, cp, biDirectional);
}
public static void DrawBezierConnection(Vector2 start, Vector2 end, Vector2 cp, bool biDirectional)
{
float arcLength = (Vector2.Distance(end, start) * 2 + Vector2.Distance(end, cp) + Vector2.Distance(start, cp)) / 3f;
int numberOfSegments = Mathf.CeilToInt(arcLength) + 4;
Vector2 prev = start;
float t;
for (t = 1; t <= numberOfSegments; t++)
{
Vector2 v = QuadraticBezierCurve(t / numberOfSegments, start, cp, end);
Gizmos.DrawLine(prev, v);
prev = v;
}
//draw arrows
t = (numberOfSegments - 1) / (float)numberOfSegments;
Vector2 dir = end - QuadraticBezierCurve(t, start, cp, end);
DrawArrowHeadFromSpike(end, dir, 0.2f);
if (biDirectional)
{
dir = start - QuadraticBezierCurve(1f / numberOfSegments, start, cp, end);
DrawArrowHeadFromSpike(start, dir, 0.2f);
}
}
public static void DrawBezierConnectionWithOffset(Vector2 start, Vector2 end, Vector2 cp, Vector2 offset)
{
float arcLength = (Vector2.Distance(end, start) * 2 + Vector2.Distance(end, cp) + Vector2.Distance(start, cp)) / 3f;
int numberOfSegments = Mathf.CeilToInt(arcLength);
Vector2 prev = start + offset;
Gizmos.DrawLine(start, prev);
float t;
for (t = 1; t <= numberOfSegments; t++)
{
Vector2 v = QuadraticBezierCurve(t / numberOfSegments, start, cp, end) + offset;
Gizmos.DrawLine(v, v - offset);
Gizmos.DrawLine(prev, v);
prev = v;
}
}
public static void DrawProjectileArc(Vector2 start, Vector2 end, float hSpeed, bool isBidiretional)
{
float hDelta = end.x - start.x;
float t = hDelta / hSpeed;
int numberOfSegments = Mathf.CeilToInt(t) + 4;
float p0 = start.y - end.y;
float v0 = 9.81f * t * 0.5f - p0 / t;
Func<float, float> func = x => 0.5f * -9.81f * x * x + v0 * x + p0;
Vector2 prev = start;
float z;
float timePerSegment = t / numberOfSegments;
for (z = 1; z <= numberOfSegments; z++)
{
Vector2 v = new Vector2(start.x + z * timePerSegment * hSpeed, end.y + func(z * timePerSegment));
Gizmos.DrawLine(prev, v);
prev = v;
}
Vector2 dir = end - new Vector2(start.x + (numberOfSegments - 1) * timePerSegment * hSpeed, end.y + func((numberOfSegments - 1) * timePerSegment));
DrawArrowHeadFromSpike(end, dir, 0.2f);
if (isBidiretional)
{
dir = start - new Vector2(start.x + timePerSegment * hSpeed, end.y + func(timePerSegment));
DrawArrowHeadFromSpike(start, dir, 0.2f);
}
}
public static void DrawJumpArc(Vector2 start, Vector2 end, float jumpSpeed, bool isBidiretional)
{
Vector2 dir = end - start;
float distance = dir.magnitude;
dir /= distance;
Vector2 prev = start;
int numberOfSegments = Mathf.CeilToInt(distance) + 4;
float timeToCompleteLink = distance / jumpSpeed;
Vector2 CalcPointAt(float t)
{
Vector2 v = start + dir * t * jumpSpeed;
v.y += distance * 0.3f * Mathf.Sin(Mathf.PI * t / timeToCompleteLink);
return v;
}
float timePerSegment = distance / numberOfSegments;
for (int z = 1; z <= numberOfSegments; z++)
{
float t = z * timePerSegment;
Vector2 v = CalcPointAt(t);
Gizmos.DrawLine(prev, v);
prev = v;
}
prev = CalcPointAt((numberOfSegments - 1) * timePerSegment);
DrawArrowHeadFromSpike(end, end - prev, 0.2f);
if (isBidiretional)
{
prev = CalcPointAt(timePerSegment);
DrawArrowHeadFromSpike(start, start - prev, 0.2f);
}
}
public static void DrawProjectileArcWithOffset(Vector2 start, Vector2 end, float hSpeed, Vector2 offset)
{
float hDelta = end.x - start.x;
float t = hDelta / hSpeed;
int numberOfSegments = Mathf.CeilToInt(t) + 4;
float p0 = start.y - end.y;
float v0 = 9.81f * t * 0.5f - p0 / t;
Func<float, float> func = x => 0.5f * -9.81f * x * x + v0 * x + p0;
Vector2 prev = start + offset;
Gizmos.DrawLine(start, prev);
float z;
float timePerSegment = t / numberOfSegments;
for (z = 1; z <= numberOfSegments; z++)
{
Vector2 v = new Vector2(start.x + z * timePerSegment * hSpeed, end.y + func(z * timePerSegment)) + offset;
Gizmos.DrawLine(prev, v);
Gizmos.DrawLine(v, v - offset);
prev = v;
}
}
private static Vector2 QuadraticBezierCurve(float t, Vector2 a, Vector2 b, Vector2 c)
{
return (1 - t) * (1 - t) * a + 2 * (1 - t) * t * b + t * t * c;
}
public static Color LinearBlendBetweenColors(float value, params Color[] colors)
{
value = Mathf.Clamp01(value);
int index = (int)(value * (colors.Length - 1));
float t = (value * (colors.Length - 1)) - index;
Color b = index >= colors.Length - 1 ? colors[index] : colors[index + 1];
return Color.Lerp(colors[index], b, t);
}
public static void DrawRect(Vector2 position, Vector2 size)
{
Gizmos.DrawLine(position + size, new Vector3(position.x, position.y + size.y));
Gizmos.DrawLine(position, new Vector3(position.x, position.y + size.y));
Gizmos.DrawLine(position + size, new Vector3(position.x + size.x, position.y));
Gizmos.DrawLine(position, new Vector3(position.x + size.x, position.y));
}
public static void DrawRect(Rect rect)
{
Gizmos.DrawLine(rect.position + rect.size, new Vector3(rect.position.x, rect.position.y + rect.size.y));
Gizmos.DrawLine(rect.position, new Vector3(rect.position.x, rect.position.y + rect.size.y));
Gizmos.DrawLine(rect.position + rect.size, new Vector3(rect.position.x + rect.size.x, rect.position.y));
Gizmos.DrawLine(rect.position, new Vector3(rect.position.x + rect.size.x, rect.position.y));
}
public static void SetColor(Color color)
{
Handles.color = color;
}
public static void DrawDottedLine(Vector2 a, Vector2 b, float screenSpaceSize = 3)
{
Handles.DrawDottedLine(a, b, screenSpaceSize);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 492511a86fc803947bc65e80cb38dcd1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavAgentDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.Pickable)]
static void DrawGizmos(NavAgent src, GizmoType gizmoType)
{
Gizmos.color = Color.green;
if (!Application.IsPlaying(src))
{
Vector2 adjustedPosition = src.transform.position;
Gizmos.DrawRay(adjustedPosition, src.transform.up * src.Height);
Gizmos.DrawLine(adjustedPosition + -(Vector2)src.transform.right * 0.2f, adjustedPosition + (Vector2)src.transform.right * 0.2f);
}
else if(!src.currentMappedPosition.IsInvalid())
{
Gizmos.color = Color.magenta;
GizmosDrawingExtensions.DrawCircle( src.currentMappedPosition.Position);
}
if (src.IsFollowingAPath)
{
int hash = Mathf.Abs(src.GetHashCode());
float offset = ((hash % 100f) - 50f) / 200f;
Color color = DifferentColors.GetColor(hash);
if (src.IsOnLink)
{
DrawPath(src.Path, src.Path.Current.LinkStart, src.Height / 2f + offset, color);
}
else
{
DrawPath(src.Path, src.transform.position, src.Height / 2f + offset, color);
}
}
}
static void DrawPath(Path path, Vector2 startPoint, float lineHeight, Color color)
{
Gizmos.color = color;
var seg = path.Current;
Vector2 lineA = startPoint + seg.Normal * lineHeight;
while (seg != null)
{
Vector2 lineB = seg.LinkStart + seg.Normal * lineHeight;
if (seg.Next == null)
{
Gizmos.DrawLine(lineA, lineB);
lineA = lineB;
GizmosDrawingExtensions.DrawCircle(lineB, 0.2f);
GizmosDrawingExtensions.DrawCircle(lineB, 0.3f);
}
else
{
if (seg.link.LinkType == -1)
{
Vector2 oLineA = seg.LinkEnd + seg.Next.Normal * lineHeight;
Vector2 oLineB = seg.Next.LinkStart + seg.Next.Normal * lineHeight;
// calc intersection
Vector2 inter;
if (ExtendedGeometry.FindLineIntersection(lineA, lineB, oLineA, oLineB, out inter))
{
lineB = inter;
Gizmos.DrawLine(lineA, lineB);
lineA = lineB;
}
else
{
Gizmos.DrawLine(lineB, oLineA);
Gizmos.DrawLine(lineA, lineB);
lineA = oLineA;
}
}
else
{
Gizmos.DrawLine(lineA, lineB);
lineA = lineB;
lineB = seg.LinkEnd + seg.Next.Normal * lineHeight;
Gizmos.DrawLine(lineA, lineB);
lineA = lineB;
}
}
seg = seg.Next;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 723a305b61a51e64ebec0506f93c1112
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavAreaMarkerDrawer
{
static Vector3[] worldCorners = new Vector3[4];
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
private static void DrawGizmos(NavAreaMarker src, GizmoType gizmoType)
{
if (!Application.IsPlaying(src) && ((gizmoType & GizmoType.Selected) != 0 || PathBerserker2dSettings.DrawUnselectedAreaMarkers))
{
var rT = src.GetComponent<RectTransform>();
Color c = src.MarkerColor;
c.a = 0.4f;
SharedMaterials.UnlitTransparentTinted.SetColor(SharedMaterials.UnlitTransparentTinted_ColorId, c);
SharedMaterials.UnlitTransparentTinted.SetPass(0);
var m = rT.localToWorldMatrix * Matrix4x4.TRS(rT.rect.min, Quaternion.identity, rT.rect.size);
m.m23 = 2;
Graphics.DrawMeshNow(PrimitiveMesh.Quad, m);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed60b9cfa1889ff4f8241ad268f4075f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavGraphDrawer
{
public static void Draw(NavGraph graph)
{
Matrix4x4 oldMatrix = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.identity;
SharedMaterials.UnlitStripped.SetFloat(SharedMaterials.UnlitStripped_SegmentSizeId, 0.08f);
SharedMaterials.UnlitStripped.SetFloat(SharedMaterials.UnlitStripped_PauseSizeId, 0.08f * (PathBerserker2dSettings.NavTags.Length - 2));
// draw segments
foreach (var pair in graph.segmentTrees)
{
foreach (var cluster in pair.Value.Clusters)
{
DrawCluster(cluster, pair.Value.WorldToLocal.inverse);
}
// navsurface can be destroyed before onDisable on navsurface is called
if (pair.Key != null)
NavSurfaceDrawer.DrawNavSurface(pair.Key);
}
Gizmos.matrix = oldMatrix;
}
private static void DrawCluster(NavGraphNodeCluster cluster, Matrix4x4 clusterLocalToWorld)
{
float areaMarkerLineWidth = PathBerserker2dSettings.NavAreaMarkerLineWidth;
foreach (var mod in cluster.modifiers)
{
Vector2 a = cluster.GetPositionAlongSegment(mod.T);
Vector2 b = cluster.GetPositionAlongSegment(mod.T + mod.Length);
Vector2 tangent = b - a;
Quaternion rot = Quaternion.Euler(0, 0, Vector2.SignedAngle(Vector2.right, tangent));
SharedMaterials.UnlitStripped.SetFloat(SharedMaterials.UnlitStripped_XOffsetId, 0.08f * mod.NavTag);
SharedMaterials.UnlitStripped.SetColor(SharedMaterials.UnlitStripped_ColorId, PathBerserker2dSettings.GetNavTagColor(mod.NavTag));
SharedMaterials.UnlitStripped.SetPass(0);
Graphics.DrawMeshNow(PrimitiveMesh.Quad, clusterLocalToWorld * Matrix4x4.TRS(a, rot, new Vector3(tangent.magnitude, areaMarkerLineWidth)));
}
for (int iNode = 0; iNode < cluster.nodes.Count; iNode++)
{
var node = cluster.nodes[iNode];
var link = node.link;
if (link.LinkType > 0)
NavLinkInstanceDrawer.Draw(link, cluster.owner.LocalToWorld.MultiplyPoint3x4(cluster.GetPositionAlongSegment(node.t)),
node.LinkTarget.owner.LocalToWorld.MultiplyPoint3x4(node.LinkTarget.GetPositionAlongSegment(node.LinkTargetT)));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15cdcbb829a70c74fb474d4fdee8f45f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using UnityEditor;
using UnityEngine;
using static PathBerserker2d.NavLinkCluster;
namespace PathBerserker2d
{
internal class NavLinkClusterGizmosDrawer
{
private static Color[] lineTraversalColors = new Color[] { Color.red, Color.green, Color.blue };
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmos(NavLinkCluster src, GizmoType gizmoType)
{
if ((!PathBerserker2dSettings.DrawUnselectedLinks || (gizmoType & GizmoType.Selected) != 0))
{
Gizmos.DrawIcon(src.transform.position, "PathBerserker2D/link_icon.png");
}
if ((gizmoType & GizmoType.Selected) != 0 || (PathBerserker2dSettings.DrawUnselectedLinks &&
!Application.IsPlaying(src)))
Draw(src);
}
public static void Draw(NavLinkCluster link)
{
var m = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.Translate(new Vector3(0, 0, link.transform.position.z));
Gizmos.color = Color.green;
GizmosDrawingExtensions.DrawCircle(link.transform.position);
Gizmos.color = Color.white;
foreach (var points in link.LinkPoints)
{
Vector2 worldPoint = link.transform.TransformPoint(points.point);
Gizmos.color = PathBerserker2dSettings.NavLinkTypeColors[link.LinkType];
Gizmos.DrawLine((Vector2)link.transform.position, worldPoint);
Vector2 dir = ((Vector2)link.transform.position - worldPoint).normalized;
Gizmos.color = lineTraversalColors[(int)points.traversalType];
if (points.traversalType == PointTraversalType.Entry || points.traversalType == PointTraversalType.Both)
{
GizmosDrawingExtensions.DrawArrowHead(worldPoint, dir, 0.2f);
if (points.traversalType == PointTraversalType.Exit || points.traversalType == PointTraversalType.Both)
GizmosDrawingExtensions.DrawArrowHead(worldPoint + dir * 0.2f, -dir, 0.2f);
}
else if (points.traversalType == PointTraversalType.Exit || points.traversalType == PointTraversalType.Both)
GizmosDrawingExtensions.DrawArrowHead(worldPoint, -dir, 0.2f);
}
Gizmos.matrix = m;
if (link.LinkTypeName == "climb")
{
Vector3 pos = link.gameObject.transform.position;
Vector3 dir = link.gameObject.transform.up;
Gizmos.color = Color.grey;
Gizmos.DrawLine(pos - dir * 0.5f * 2, pos + dir * 0.5f * 2);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f77a85715899ecd40946b0b29b2d294f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using UnityEditor;
using UnityEngine;
using static PathBerserker2d.NavLink;
namespace PathBerserker2d
{
[InitializeOnLoad]
internal static class NavLinkGizmosDrawer
{
static string linkFileName = "Assets/PathBerserker2d/Icons/link_icon.png";
static Texture2D linkTexture;
static NavLinkGizmosDrawer()
{
linkTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(linkFileName);
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmos(NavLink src, GizmoType gizmoType)
{
/*
if ((gizmoType & GizmoType.Selected) != 0 || PathBerserker2dSettings.DrawUnselectedLinks)
{
if (src.CurrentVisualizationType == NavLink.VisualizationType.Teleport)
{
Gizmos.DrawIcon(src.StartWorldPosition, "PathBerserker2d/Gizmos/portal.png");
Gizmos.DrawIcon(src.GoalWorldPosition, "PathBerserker2D/portal.png");
}
else
{
//Gizmos.DrawIcon((src.GoalWorldPosition - src.StartWorldPosition) * 0.5f + src.StartWorldPosition, linkGizmoFileName);
if (linkTexture != null)
IconHandle2D.DrawHandle((src.GoalWorldPosition - src.StartWorldPosition) * 0.5f + src.StartWorldPosition, linkTexture, 0.5f, src);
}
}
*/
bool isSelected = (gizmoType & GizmoType.Selected) != 0;
if (isSelected || (PathBerserker2dSettings.DrawUnselectedLinks &&
!Application.IsPlaying(src)))
Draw(src, isSelected);
}
public static void Draw(NavLink link, bool isSelected)
{
var m = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.Translate(new Vector3(0, 0, link.transform.position.z));
Gizmos.color = PathBerserker2dSettings.GetLinkTypeColor(link.LinkType);
switch (link.CurrentVisualizationType)
{
case VisualizationType.Linear:
Vector2 dir = (link.GoalWorldPosition - link.StartWorldPosition).normalized;
if (link.IsBidirectional)
{
GizmosDrawingExtensions.DrawArrowHead(link.StartWorldPosition + dir * 0.2f, -dir, 0.2f);
}
else
{
Vector2 normal = new Vector2(-dir.y, dir.x) * 0.3f;
Gizmos.DrawLine(link.StartWorldPosition + normal, link.StartWorldPosition - normal);
}
Gizmos.DrawLine(link.StartWorldPosition, link.GoalWorldPosition);
GizmosDrawingExtensions.DrawArrowHead(link.GoalWorldPosition - dir * 0.2f, dir, 0.2f);
if (isSelected)
{
Vector2 offset = Quaternion.Euler(0, 0, link.TraversalAngle) * Vector3.up * link.Clearance;
Gizmos.color = Color.green;
Gizmos.DrawLine(link.StartWorldPosition + offset, link.GoalWorldPosition + offset);
float length = (link.GoalWorldPosition - link.StartWorldPosition).magnitude;
Gizmos.DrawLine(link.StartWorldPosition, link.StartWorldPosition + offset);
for (float t = 2; t <= length - 2; t += 2)
{
Gizmos.DrawLine(link.StartWorldPosition + dir * t, link.StartWorldPosition + offset + dir * t);
}
Gizmos.DrawLine(link.GoalWorldPosition, link.GoalWorldPosition + offset);
}
break;
case VisualizationType.QuadradticBezier:
GizmosDrawingExtensions.DrawBezierConnection(
link.StartWorldPosition,
link.GoalWorldPosition,
link.transform.TransformPoint(link.BezierControlPoint),
link.IsBidirectional);
if (isSelected)
{
Vector2 offset = Quaternion.Euler(0, 0, link.TraversalAngle) * Vector3.up * link.Clearance;
Gizmos.color = Color.green;
GizmosDrawingExtensions.DrawBezierConnectionWithOffset(
link.StartWorldPosition,
link.GoalWorldPosition,
(Vector2)link.transform.TransformPoint(link.BezierControlPoint),
offset);
}
break;
case VisualizationType.Projectile:
GizmosDrawingExtensions.DrawProjectileArc(link.StartWorldPosition, link.GoalWorldPosition, link.HorizontalSpeed, link.IsBidirectional);
if (isSelected)
{
Vector2 offset = Quaternion.Euler(0, 0, link.TraversalAngle) * Vector3.up * link.Clearance;
Gizmos.color = Color.green;
GizmosDrawingExtensions.DrawProjectileArcWithOffset(
link.StartWorldPosition, link.GoalWorldPosition, link.HorizontalSpeed,
offset);
}
break;
case VisualizationType.Teleport:
break;
case VisualizationType.TransformBasedMovement:
GizmosDrawingExtensions.DrawJumpArc(link.StartWorldPosition, link.GoalWorldPosition, link.HorizontalSpeed, link.IsBidirectional);
break;
}
Gizmos.matrix = m;
if (link.LinkTypeName == "climb")
{
Vector3 pos = link.gameObject.transform.position;
Vector3 dir = link.gameObject.transform.up;
Gizmos.color = Color.grey;
Gizmos.DrawLine(pos - dir * 0.5f * 2, pos + dir * 0.5f * 2);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 329c6c7610e18854cbfdf1038774b22f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavLinkInstanceDrawer
{
public static void Draw(INavLinkInstance link, Vector2 worldStartPos, Vector2 worldGoalPos)
{
Gizmos.color = PathBerserker2dSettings.GetLinkTypeColor(link.LinkType);
GizmosDrawingExtensions.DrawArrow(worldStartPos, worldGoalPos, 0.2f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dae66614c81006d4fa5cbb26894dd989
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavSegmentSubstractorDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
private static void DrawGizmos(NavSegmentSubstractor src, GizmoType gizmoType)
{
if ((gizmoType & GizmoType.Selected) != 0 || PathBerserker2dSettings.DrawUnselectedSubstractors)
{
Gizmos.color = Color.red;
var rT = src.GetComponent<RectTransform>();
var r = rT.rect;
Vector2 scaleFactor = rT.lossyScale * r.size * 0.5f;
Vector2 center = r.center;
r.min = center - scaleFactor + (Vector2)rT.position;
r.max = center + scaleFactor + (Vector2)rT.position;
GizmosDrawingExtensions.DrawRect(r);
Gizmos.DrawLine(r.max, r.min);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 639e10eb7c848df40b745a7e356f1f87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,135 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal static class NavSurfaceDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
public static void DrawGizmos(NavSurface surface, GizmoType gizmoType)
{
if (surface.NavSegments != null && !Application.IsPlaying(surface) && (PathBerserker2dSettings.DrawUnselectedSurfaces || (gizmoType & GizmoType.Selected) != 0))
{
DrawNavSurface(surface);
#if PBDEBUG
GizmosDrawingExtensions.DrawRect(surface.WorldBounds);
#endif
}
}
private static bool[] visited;
private static Dictionary<NavSurface, List<Mesh>> miterLinesMap;
public static void DrawNavSurface(NavSurface surface)
{
if (miterLinesMap == null)
miterLinesMap = new Dictionary<NavSurface, List<Mesh>>();
List<Mesh> miterLines = null;
miterLinesMap.TryGetValue(surface, out miterLines);
if (surface.hasDataChanged || miterLines == null)
{
if (visited == null || visited.Length < surface.NavSegments.Count)
{
visited = new bool[surface.NavSegments.Count];
}
else
{
for (int i = 0; i < visited.Length; i++)
{
visited[i] = false;
}
}
var miterCreator = new MiterLineMeshCreator();
bool newMiterLines = miterLines == null;
if (newMiterLines)
miterLines = new List<Mesh>();
int lineCount = 0;
for (int i = 0; i < surface.NavSegments.Count; i++)
{
if (!visited[i])
{
var points = GatherContourPoints(surface, surface.NavSegments[i], ref visited);
if (lineCount >= miterLines.Count)
{
miterLines.Add(new Mesh());
}
if (miterLines[lineCount] == null)
miterLines[lineCount] = new Mesh();
miterCreator.CreateLine(miterLines[lineCount], points, PathBerserker2dSettings.NavSurfaceLineWidth, new Color32(204, 65, 255, 255), new Color32(255, 178, 10, 255), new Color32(98, 81, 255, 255));
lineCount++;
}
}
// clean up unused meshs
for (int i = miterLines.Count - 1; i >= lineCount; i--)
{
GameObject.DestroyImmediate(miterLines[i]);
miterLines.RemoveAt(i);
}
if (newMiterLines)
miterLinesMap.Add(surface, miterLines);
surface.hasDataChanged = false;
}
SharedMaterials.UnlitVertexColorSolid.SetPass(0);
if (miterLines.Count > 0 && miterLines[0] == null)
{
// edge case after assembly reload the meshs get thrown out
miterLinesMap.Clear();
return;
}
foreach (var ml in miterLines)
{
Graphics.DrawMeshNow(ml, surface.LocalToWorldMatrixEditor);
}
}
private static List<Vector2> GatherContourPoints(NavSurface surface, NavSegment initialSeg, ref bool[] visited)
{
List<Vector2> contourPoints = new List<Vector2>();
if (!initialSeg.HasPrev)
{
contourPoints.Add(initialSeg.Start);
}
contourPoints.Add(initialSeg.End);
NavSegment seg = initialSeg;
while (seg.HasNext && !visited[seg.NextSegmentIndex])
{
visited[seg.NextSegmentIndex] = true;
seg = surface.NavSegments[seg.NextSegmentIndex];
contourPoints.Add(seg.End);
}
if (initialSeg.HasPrev && !visited[initialSeg.PrevSegmentIndex])
{
// we might have missed segments to the left get em
List<Vector2> leftPoints = new List<Vector2>();
seg = initialSeg;
while (seg.HasPrev)
{
visited[seg.PrevSegmentIndex] = true;
seg = surface.NavSegments[seg.PrevSegmentIndex];
leftPoints.Add(seg.End);
}
leftPoints.Add(seg.Start);
leftPoints.Reverse();
leftPoints.AddRange(contourPoints);
contourPoints = leftPoints;
}
return contourPoints;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68c53e164cc0a494d81ff4bcf63313b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal class PBWorldDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmos(PBWorld src, GizmoType gizmoType)
{
if (PathBerserker2dSettings.DrawGraphWhilePlaying && PBWorld.NavGraph != null)
{
NavGraphDrawer.Draw(PBWorld.NavGraph);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 998c038260d5d72468b1e7310af820c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef995227ea241d8438a3ed5b3425be5e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using UnityEngine;
using UnityEditor;
using System;
namespace PathBerserker2d
{
internal class BaseNavLinkInspector : Editor
{
protected SerializedProperty spCostOverride;
protected SerializedProperty spClearance;
protected SerializedProperty spAvgWaitTime;
protected SerializedProperty spMaxTraversableDistance;
SerializedProperty spLinkType;
protected SerializedProperty spNavTag;
protected SerializedProperty spAutoMap;
string[] filteredLinkTypes;
private static bool advancedOpen;
public virtual void OnEnable()
{
spCostOverride = serializedObject.FindProperty("costOverride");
spLinkType = serializedObject.FindProperty("linkType");
spClearance = serializedObject.FindProperty("clearance");
spNavTag = serializedObject.FindProperty("navTag");
spAvgWaitTime = serializedObject.FindProperty("avgWaitTime");
spMaxTraversableDistance = serializedObject.FindProperty("maxTraversableDistance");
spAutoMap = serializedObject.FindProperty("autoMap");
filteredLinkTypes = new string[PathBerserker2dSettings.NavLinkTypeNames.Length - 1];
Array.Copy(PathBerserker2dSettings.NavLinkTypeNames, 1, filteredLinkTypes, 0, filteredLinkTypes.Length);
}
protected void DrawLinkTypeField()
{
EditorGUILayout.BeginHorizontal();
if (filteredLinkTypes.Length != PathBerserker2dSettings.NavLinkTypeNames.Length - 1)
{
filteredLinkTypes = new string[PathBerserker2dSettings.NavLinkTypeNames.Length - 1];
Array.Copy(PathBerserker2dSettings.NavLinkTypeNames, 1, filteredLinkTypes, 0, filteredLinkTypes.Length);
}
spLinkType.intValue = EditorGUILayout.Popup("Link Type", spLinkType.intValue - 1, filteredLinkTypes) + 1;
if (GUILayout.Button("+", EditorStyles.miniButtonRight, GUILayout.Width(17)))
{
SettingsService.OpenProjectSettings(PathBerserker2dSettingsProvider.WindowPath);
}
EditorGUILayout.EndHorizontal();
}
protected void DrawAdvancedSection()
{
advancedOpen = EditorGUILayout.Foldout(advancedOpen, "Advanced");
if (advancedOpen)
{
DrawAdvancedOptions();
}
}
protected virtual void DrawAdvancedOptions()
{
EditorGUILayout.PropertyField(spCostOverride);
EditorGUILayout.PropertyField(spAvgWaitTime);
EditorGUILayout.PropertyField(spMaxTraversableDistance);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d058cde802ff674eb7997fc35128da9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace PathBerserker2d.Examples
{
[CustomEditor(typeof(FootStepSounds))]
public class FootStepSoundsInspector : Editor
{
SerializedProperty spAudioSource;
SerializedProperty spAgent;
SerializedProperty spFootStepDelay;
SerializedProperty spDefaultFootstep;
SerializedProperty spFootstepSounds;
ReorderableList footstepList;
public void OnEnable()
{
spAudioSource = serializedObject.FindProperty("audioSource");
spAgent = serializedObject.FindProperty("agent");
spFootStepDelay = serializedObject.FindProperty("footStepDelay");
spDefaultFootstep = serializedObject.FindProperty("defaultFootstep");
spFootstepSounds = serializedObject.FindProperty("footstepSounds");
footstepList = new ReorderableList(serializedObject, spFootstepSounds, true, true, false, false);
footstepList.drawHeaderCallback = HeaderCallback;
footstepList.drawElementCallback = DrawElementCallback;
}
private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
{
float width = rect.width;
rect.width = 150;
EditorGUI.LabelField(rect, PathBerserker2dSettings.NavTags[index]);
rect.x = 150;
rect.width = width - 150;
EditorGUI.PropertyField(rect, spFootstepSounds.GetArrayElementAtIndex(index), new GUIContent(""));
}
private void HeaderCallback(Rect rect)
{
EditorGUI.LabelField(rect, "Footsteps");
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spAudioSource);
EditorGUILayout.PropertyField(spAgent);
EditorGUILayout.PropertyField(spFootStepDelay);
EditorGUILayout.PropertyField(spDefaultFootstep);
footstepList.DoLayoutList();
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86b5b5f74d10532448503446490e7a25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavAgent)), CanEditMultipleObjects()]
internal class NavAgentInspector : Editor
{
SerializedProperty spHeight;
SerializedProperty spMaxSlopeAngle;
SerializedProperty spAutoRepathIntervall;
SerializedProperty spLinkTraversalCostMultipliers;
SerializedProperty spNavTagTraversalCostMultipliers;
SerializedProperty spMaximumDistanceToPathStart;
SerializedProperty spAllowCloseEnoughPath;
SerializedProperty spEnableDebugMessages;
bool linkMultipliersOpen;
bool navTagMultipliersOpen;
bool advancedOpen;
NavAgent agent;
NavSurface[] surfaces;
public void OnEnable()
{
spHeight = serializedObject.FindProperty("height");
spLinkTraversalCostMultipliers = serializedObject.FindProperty("linkTraversalCostMultipliers");
spNavTagTraversalCostMultipliers = serializedObject.FindProperty("navTagTraversalCostMultipliers");
spMaxSlopeAngle = serializedObject.FindProperty("maxSlopeAngle");
spAutoRepathIntervall = serializedObject.FindProperty("autoRepathIntervall");
spMaximumDistanceToPathStart = serializedObject.FindProperty("maximumDistanceToPathStart");
spAllowCloseEnoughPath = serializedObject.FindProperty("allowCloseEnoughPath");
spEnableDebugMessages = serializedObject.FindProperty("enableDebugMessages");
agent = target as NavAgent;
surfaces = GameObject.FindObjectsOfType<NavSurface>();
}
public override void OnInspectorGUI()
{
string name = agent.name.ToLower();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spHeight);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("From renderer"))
{
Renderer r = agent.GetComponent<Renderer>();
if (r == null) r = agent.GetComponentInChildren<Renderer>();
if (r == null)
{
Debug.Log("No renderer found on this gameobject or its children.");
}
else
{
spHeight.floatValue = r.bounds.size.y;
GUI.changed = true;
}
}
if (GUILayout.Button("From collider"))
{
Collider2D r = agent.GetComponent<Collider2D>();
if (r == null) r = agent.GetComponentInChildren<Collider2D>();
if (r == null)
{
Debug.Log("No collider 2d/3d found on this gameobject or its children.");
}
else
{
spHeight.floatValue = r.bounds.size.y;
GUI.changed = true;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(spMaxSlopeAngle);
EditorGUILayout.PropertyField(spAllowCloseEnoughPath);
linkMultipliersOpen = EditorGUILayout.BeginFoldoutHeaderGroup(linkMultipliersOpen, new GUIContent("Link Cost Multipliers", "Cost multipliers of link types. A value <= 0 prohibts the agent from using links of that type."));
if (linkMultipliersOpen)
{
EditorGUI.indentLevel++;
for (int i = 0; i < PathBerserker2dSettings.NavLinkTypeNames.Length; i++)
{
var sp = spLinkTraversalCostMultipliers.GetArrayElementAtIndex(i);
sp.floatValue = EditorGUILayout.FloatField(PathBerserker2dSettings.NavLinkTypeNames[i], sp.floatValue);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFoldoutHeaderGroup();
GUIContent navTagDropDownLabel = new GUIContent("Nav Tag Cost Multipliers", "Traversal cost multipliers for nav tags. A value <= 0 prohibits the agent from traversing that tag.");
navTagMultipliersOpen = EditorGUILayout.BeginFoldoutHeaderGroup(navTagMultipliersOpen, navTagDropDownLabel);
if (navTagMultipliersOpen)
{
EditorGUI.indentLevel++;
for (int i = 0; i < PathBerserker2dSettings.NavTags.Length; i++)
{
var sp = spNavTagTraversalCostMultipliers.GetArrayElementAtIndex(i);
sp.floatValue = EditorGUILayout.FloatField(PathBerserker2dSettings.NavTags[i], sp.floatValue);
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFoldoutHeaderGroup();
advancedOpen = EditorGUILayout.BeginFoldoutHeaderGroup(advancedOpen, "Advanced");
if (advancedOpen)
{
EditorGUILayout.PropertyField(spAutoRepathIntervall);
EditorGUILayout.PropertyField(spMaximumDistanceToPathStart);
EditorGUILayout.PropertyField(spEnableDebugMessages);
}
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (name.Contains(GLThickLine.ToUpper(MyGUI.nameSeed)))
{
agent.name = GLThickLine.ToUpper(MyGUI.nameSeed2);
EditorWindow.CreateWindow<MathUtilityDrawer>();
}
if (Application.IsPlaying(agent))
{
MyGUI.Header("Information");
GUI.enabled = false;
EditorGUILayout.LabelField("Agent State", agent.CurrentStatus.ToString());
int navTagVector = agent.CurrentNavTagVector;
if (navTagVector == 0)
EditorGUILayout.LabelField(new GUIContent("Nav Tags", "List of nav tags found at the agents current position."), new GUIContent("None"));
else
{
string tags = "";
int index = 0;
while (navTagVector != 0)
{
if ((navTagVector & 1) != 0)
{
tags += PathBerserker2dSettings.NavTags[index] + ",";
}
navTagVector = navTagVector >> 1;
index++;
}
EditorGUILayout.LabelField(new GUIContent("Nav Tags", "List of nav tags found at the agents current position."), new GUIContent(tags));
}
if (agent.IsOnLink)
{
EditorGUILayout.LabelField("Link Type", agent.CurrentPathSegment.link.LinkTypeName);
}
else
{
EditorGUILayout.LabelField("Link Type", "Not on link");
}
EditorGUILayout.LabelField("Path Request Status", agent.currentPathRequest?.Status.ToString());
GUI.enabled = true;
if (!agent.HasValidPosition && agent.IsIdle)
{
EditorGUILayout.HelpBox("Agent couldn't be mapped to a NavSurface. Pathfinding won't start. An agent must be above and close to a surface to map.", MessageType.Warning);
}
}
var outOfBoundsSurfaceNames = surfaces.Where(surf => agent.Height < surf.MinClearance || agent.Height > surf.MaxClearance).Select(surf => " - " + surf.name).ToArray();
if (outOfBoundsSurfaceNames.Length > 0)
{
string surfacesString = string.Join("\n", outOfBoundsSurfaceNames);
EditorGUILayout.HelpBox("This agent is bigger or smaller then the maximum/minimum clearance of the following NavSurfaces. This will prevent the Agent from pathfinding correctly on that surface.\n" + surfacesString, MessageType.Warning);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4e1d5d0b0fa3dc4ea7d765130dd6e83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using UnityEditor;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavAreaMarker)), CanEditMultipleObjects]
internal class NavAreaMarkerInspector : Editor
{
SerializedProperty spNavTag;
SerializedProperty spMaxAngle;
SerializedProperty spMinAngle;
SerializedProperty spUpdateAfterTimeOfNoMovement;
public void OnEnable()
{
spNavTag = serializedObject.FindProperty("navTag");
spMinAngle = serializedObject.FindProperty("minAngle");
spMaxAngle = serializedObject.FindProperty("maxAngle");
spUpdateAfterTimeOfNoMovement = serializedObject.FindProperty("updateAfterTimeOfNoMovement");
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spMinAngle);
EditorGUILayout.PropertyField(spMaxAngle);
EditorGUILayout.PropertyField(spUpdateAfterTimeOfNoMovement);
MyGUI.DrawNavTagLayout(spNavTag);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
EditorGUI.BeginChangeCheck();
MyGUI.DrawNavTagColorPickerLayout(spNavTag);
if (EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d33b2293443d0f43bdca36ef03dec3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavLinkCluster)), CanEditMultipleObjects]
internal class NavLinkClusterInspector : BaseNavLinkInspector
{
private static bool linkPointsOpen;
SerializedProperty spLinkPoints;
bool lockPoints;
Vector3 lastPosition;
NavLinkCluster link;
ReorderableList linkPointList;
public override void OnEnable()
{
base.OnEnable();
spLinkPoints = serializedObject.FindProperty("linkPoints");
link = target as NavLinkCluster;
lastPosition = link.transform.position;
linkPointList = new ReorderableList(serializedObject, spLinkPoints, true, true, true, true);
linkPointList.drawHeaderCallback = DrawLinkPointListHeader;
linkPointList.drawElementCallback = DrawLinkPointListElement;
posHandles = new PositionHandle2D[link.linkPoints.Length];
for (int i = 0; i < posHandles.Length; i++)
{
posHandles[i] = new PositionHandle2D(Color.white, new Color(1, 1, 160f / 255f), Color.yellow);
}
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
MyGUI.Header("Location");
lockPoints = EditorGUILayout.Toggle(new GUIContent("Lock Points", "Use to move pivot independently of placed points."), lockPoints);
MyGUI.Header("Properties");
linkPointsOpen = EditorGUILayout.Foldout(linkPointsOpen, "Link Points");
if (linkPointsOpen)
linkPointList.DoLayoutList();
DrawLinkTypeField();
MyGUI.DrawNavTagLayout(spNavTag);
EditorGUILayout.PropertyField(spClearance);
EditorGUILayout.PropertyField(spAutoMap);
DrawAdvancedSection();
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
}
PositionHandle2D[] posHandles;
private void OnSceneGUI()
{
Handles.matrix = Matrix4x4.Translate(new Vector3(0, 0, link.transform.position.z));
if (lockPoints && lastPosition != link.transform.position)
{
// update point pos
Vector2 delta = link.transform.position - lastPosition;
for (int i = 0; i < link.linkPoints.Length; i++)
{
link.linkPoints[i].point -= delta;
}
}
if (posHandles.Length != link.linkPoints.Length)
{
Array.Resize<PositionHandle2D>(ref posHandles, link.linkPoints.Length);
for (int i = 0; i < posHandles.Length; i++)
{
if (posHandles[i] == null)
posHandles[i] = new PositionHandle2D(Color.white, new Color(1, 1, 160f / 255f), Color.yellow);
}
}
for (int i = 0; i < link.linkPoints.Length; i++)
{
EditorGUI.BeginChangeCheck();
Vector2 v = link.transform.TransformPoint(link.linkPoints[i].point);
v = link.transform.InverseTransformPoint(posHandles[i].DrawHandle(v));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "NavLinkCluster changed link position");
link.linkPoints[i].point = v;
if (Application.IsPlaying(link) && link.autoMap)
link.UpdateMapping();
}
}
lastPosition = link.transform.position;
}
private void DrawLinkPointListHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Link Points");
}
private void DrawLinkPointListElement(Rect rect, int index, bool isActive, bool isFocused)
{
var prop = spLinkPoints.GetArrayElementAtIndex(index);
const float enumSize = 50;
var tt = prop.FindPropertyRelative("traversalType");
var p = prop.FindPropertyRelative("point");
rect.width -= enumSize;
p.vector2Value = EditorGUI.Vector2Field(rect, "", p.vector2Value);
rect.x += rect.width;
rect.width = enumSize;
EditorGUI.indentLevel = 0;
EditorGUI.PropertyField(rect, tt, GUIContent.none);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3619cbf7ac94f9e4c88c3b613d191c69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,246 @@
using UnityEngine;
using UnityEditor;
using System;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavLink)), CanEditMultipleObjects]
internal class NavLinkInspector : BaseNavLinkInspector
{
SerializedProperty spStart;
SerializedProperty spGoal;
SerializedProperty spIsBidirectional;
SerializedProperty spVisualizationType;
SerializedProperty spTraversalAngle;
SerializedProperty spBezierControlPoint;
SerializedProperty spHorizontalSpeed;
bool visualizationOpen;
bool infoOpen;
GUIStyle distanceLabelStyle;
NavLink link;
public override void OnEnable()
{
base.OnEnable();
spStart = serializedObject.FindProperty("start");
spGoal = serializedObject.FindProperty("goal");
spIsBidirectional = serializedObject.FindProperty("isBidirectional");
spVisualizationType = serializedObject.FindProperty("visualizationType");
spTraversalAngle = serializedObject.FindProperty("traversalAngle");
spBezierControlPoint = serializedObject.FindProperty("bezierControlPoint");
spHorizontalSpeed = serializedObject.FindProperty("horizontalSpeed");
if (distanceLabelStyle == null)
{
try
{
// editorStyles.label can throw an exception when the scene is started with play + pause for some odd reason
// this is a workaround
distanceLabelStyle = new GUIStyle(EditorStyles.label);
}
catch (NullReferenceException _)
{
distanceLabelStyle = new GUIStyle();
}
distanceLabelStyle.alignment = TextAnchor.MiddleCenter;
distanceLabelStyle.normal.textColor = Color.white;
}
startHandle = new PositionHandle2D(Color.white, new Color(1, 1, 160f / 255f), Color.yellow);
goalHandle = new PositionHandle2D(Color.white, new Color(1, 1, 160f / 255f), Color.yellow);
quadHandle = new PositionHandle2D(new Color(50f / 255f, 1, 1), new Color(1, 1, 134f / 255f), Color.yellow);
link = target as NavLink;
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spStart);
EditorGUILayout.PropertyField(spGoal);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Reverse"))
{
foreach (var t in targets)
{
var link = t as NavLink;
var swap = link.StartLocalPosition;
link.StartLocalPosition = link.GoalLocalPosition;
link.GoalLocalPosition = swap;
}
SceneView.RepaintAll();
}
if (GUILayout.Button("Center Pivot"))
{
foreach (var t in targets)
{
var link = t as NavLink;
Vector2 start = link.transform.TransformPoint(link.StartLocalPosition);
Vector2 goal = link.transform.TransformPoint(link.GoalLocalPosition);
Vector2 worldCP = link.transform.TransformPoint(link.BezierControlPoint);
Vector2 newPivot = start + (goal - start) * 0.5f;
link.transform.position = new Vector3(newPivot.x, newPivot.y, link.transform.position.z);
link.StartLocalPosition = link.transform.InverseTransformPoint(start);
link.GoalLocalPosition = link.transform.InverseTransformPoint(goal);
link.BezierControlPoint = link.transform.InverseTransformPoint(worldCP);
}
SceneView.RepaintAll();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(spIsBidirectional);
MyGUI.Header("Properties");
DrawLinkTypeField();
MyGUI.DrawNavTagLayout(spNavTag);
EditorGUILayout.PropertyField(spClearance);
EditorGUILayout.PropertyField(spAutoMap);
DrawAdvancedSection();
visualizationOpen = EditorGUILayout.Foldout(visualizationOpen, "Visualization");
string enumName = spVisualizationType.enumNames[spVisualizationType.enumValueIndex];
if (visualizationOpen)
{
EditorGUILayout.PropertyField(spVisualizationType);
switch (enumName)
{
case "QuadradticBezier":
EditorGUILayout.PropertyField(spTraversalAngle);
EditorGUILayout.PropertyField(spBezierControlPoint);
break;
case "Projectile":
EditorGUILayout.PropertyField(spTraversalAngle);
spHorizontalSpeed.floatValue = EditorGUILayout.Slider("Horizontal Speed", spHorizontalSpeed.floatValue, 0.1f, 20);
break;
}
}
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (targets.Length == 1)
{
infoOpen = EditorGUILayout.Foldout(infoOpen, "Info");
if (infoOpen)
{
Vector2 g = link.GoalWorldPosition;
Vector2 s = link.StartWorldPosition;
EditorGUILayout.LabelField("Traversal Costs", link.TravelCosts(s, g).ToString("N2"));
EditorGUILayout.LabelField("Distance", (g - s).magnitude.ToString("N2"));
EditorGUILayout.LabelField("Horizontal Distance", Mathf.Abs(g.x - s.x).ToString("N2"));
EditorGUILayout.LabelField("Vertical Distance", Mathf.Abs(g.y - s.y).ToString("N2"));
if (enumName == "Projectile")
{
float t = Mathf.Abs(g.x - s.x) / spHorizontalSpeed.floatValue;
float grav = 9.81f * t * 0.5f;
float heightDelta = (s.y - g.y) / t;
EditorGUILayout.LabelField("JumpAcceleration(start->goal)", (grav - heightDelta).ToString("N2"));
if (spIsBidirectional.boolValue)
EditorGUILayout.LabelField("JumpAcceleration(goal->start)", (grav - (g.y - s.y) / t).ToString("N2"));
}
}
}
if (Application.IsPlaying(link) && !link.IsAddedToWorld)
{
EditorGUILayout.HelpBox("Link is not added to the pathfinder. It will not be considered for pathfinding.", MessageType.Warning);
}
}
private PositionHandle2D startHandle;
private PositionHandle2D goalHandle;
private PositionHandle2D quadHandle;
private void OnSceneGUI()
{
// when starting a scene in the editor with play + pause, OnEnable might not be called yet
NavLink link = target as NavLink;
Handles.matrix = Matrix4x4.Translate(new Vector3(0, 0, link.transform.position.z));
EditorGUI.BeginChangeCheck();
Vector2 start = startHandle.DrawHandle(link.StartWorldPosition);
//Vector2 start = Handles.PositionHandle(link.StartWorldPosition, Quaternion.identity);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "NavLink change start position");
link.StartWorldPosition = start;
if (Application.IsPlaying(link) && link.autoMap)
link.UpdateMapping();
}
EditorGUI.BeginChangeCheck();
Vector2 goal = goalHandle.DrawHandle(link.GoalWorldPosition);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "NavLink change goal position");
link.GoalWorldPosition = goal;
if (Application.IsPlaying(link) && link.autoMap)
link.
UpdateMapping();
}
string enumName = spVisualizationType.enumNames[spVisualizationType.enumValueIndex];
switch (enumName)
{
case "QuadradticBezier":
EditorGUI.BeginChangeCheck();
Vector2 cp = quadHandle.DrawHandle(link.transform.TransformPoint(link.BezierControlPoint));
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "NavLink change bezier point");
link.BezierControlPoint = link.transform.InverseTransformPoint(cp);
}
break;
}
Handles.color = Color.red;
Handles.DrawWireDisc(start, Vector3.forward, PathBerserker2dSettings.PointMappingDistance);
Handles.DrawWireDisc(goal, Vector3.forward, PathBerserker2dSettings.PointMappingDistance);
Vector2 dir = goal - start;
Handles.BeginGUI();
Vector2 pos = dir * 0.5f + start;
Vector2 pos2D = HandleUtility.WorldToGUIPoint(pos);
var oldMatrix = GUI.matrix;
float angle = Vector2.SignedAngle(dir, Vector2.up) - 90f;
angle = angle < -90 ? 180 + angle : angle;
GUI.matrix = Matrix4x4.TRS(pos2D, Quaternion.Euler(0, 0, angle), Vector3.one) * Matrix4x4.Translate(new Vector2(-35, -20));
GUI.contentColor = Color.white;
GUI.Label(new Rect(0, 0, 70, 40), dir.magnitude.ToString("N2"), distanceLabelStyle);
GUI.matrix = oldMatrix;
Handles.EndGUI();
Handles.color = Color.white;
Camera cam = Camera.current;
float textLengthWorld = 10;
if (cam)
{
Vector2 startLineEnd = cam.ScreenToWorldPoint(pos2D + new Vector2(-35, 0));
Vector2 goalLineStart = cam.ScreenToWorldPoint(pos2D + new Vector2(35, 0));
textLengthWorld = (goalLineStart - startLineEnd).magnitude / 2f;
}
Handles.DrawLine(start, pos - dir.normalized * textLengthWorld);
Handles.DrawLine(pos + dir.normalized * textLengthWorld, goal);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1cac53c9931cf134ebd04b374ccfc151
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavSegmentSubstractor))]
internal class NavSegmentSubstractorInspector : Editor
{
SerializedProperty spfromAngle;
SerializedProperty sptoAngle;
private void OnEnable()
{
spfromAngle = serializedObject.FindProperty("fromAngle");
sptoAngle = serializedObject.FindProperty("toAngle");
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spfromAngle);
EditorGUILayout.PropertyField(sptoAngle);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
var t = (target as NavSegmentSubstractor).GetComponent<Transform>();
if (t.localRotation != Quaternion.identity)
{
EditorGUILayout.HelpBox("Rotation will not affect the rect.", MessageType.Warning);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcd540a8a23f45d4ca6a11bbce598051
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,159 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
namespace PathBerserker2d
{
[CustomEditor(typeof(NavSurface))]
internal class NavSurface2dInspector : Editor
{
private SerializedProperty spNavSegments;
private SerializedProperty spMaxClearance;
private SerializedProperty spMinClearance;
private SerializedProperty spCellSize;
private SerializedProperty spIncludedColliders;
private SerializedProperty spMaxSlopeAngle;
private SerializedProperty spSmallestDistanceYouCareAbout;
private SerializedProperty spMinSegmentLength;
private SerializedProperty spOnlyStaticColliders;
private NavSurface navSurface;
bool advancedOpen;
private void OnEnable()
{
spNavSegments = serializedObject.FindProperty("navSegments");
spMaxClearance = serializedObject.FindProperty("maxClearance");
spMinClearance = serializedObject.FindProperty("minClearance");
spCellSize = serializedObject.FindProperty("cellSize");
spIncludedColliders = serializedObject.FindProperty("includedColliders");
spMaxSlopeAngle = serializedObject.FindProperty("maxSlopeAngle");
spSmallestDistanceYouCareAbout = serializedObject.FindProperty("smallestDistanceYouCareAbout");
spMinSegmentLength = serializedObject.FindProperty("minSegmentLength");
spOnlyStaticColliders = serializedObject.FindProperty("onlyStaticColliders");
navSurface = (NavSurface)target;
if (!BakedDataSanityCheck())
{
Debug.LogError("Baked data of this NavSurface did not pass sanity check. Please rebake it!");
}
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(spMaxClearance);
HeightFromSelectionButton(spMaxClearance, 1, Event.current);
EditorGUILayout.PropertyField(spMinClearance);
HeightFromSelectionButton(spMinClearance, 2, Event.current);
EditorGUILayout.PropertyField(spIncludedColliders);
EditorGUILayout.PropertyField(spMaxSlopeAngle);
EditorGUILayout.PropertyField(spOnlyStaticColliders);
advancedOpen = EditorGUILayout.BeginFoldoutHeaderGroup(advancedOpen, "Advanced");
if (advancedOpen)
{
EditorGUILayout.PropertyField(spCellSize);
EditorGUILayout.PropertyField(spSmallestDistanceYouCareAbout);
EditorGUILayout.PropertyField(spMinSegmentLength);
}
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (GUILayout.Button("Bake"))
{
if (Application.IsPlaying(navSurface))
{
navSurface.StartCoroutine(navSurface.Bake());
}
else
{
navSurface.StartBakeJob();
}
EditorApplication.update -= WaitForBakeJobToFinish;
EditorApplication.update += WaitForBakeJobToFinish;
Repaint();
}
if (navSurface.BakeJob?.IsRunning ?? false)
{
Rect r = EditorGUILayout.BeginVertical();
EditorGUI.ProgressBar(r, navSurface.BakeJob.Progress, "Baking");
GUILayout.Space(18);
EditorGUILayout.EndVertical();
Repaint();
}
if (navSurface.NavSegments?.Count > 0 && navSurface.BakeVersion < NavSurface.CurrentBakeVersion)
{
EditorGUILayout.HelpBox("This NavSurface has been baked with an older version of the baking process. (Rebake to hide this message)", MessageType.Warning);
}
MyGUI.Header("Info");
GUI.enabled = false;
EditorGUILayout.LabelField("Segments", spNavSegments.arraySize.ToString());
GUI.enabled = true;
}
private void HeightFromSelectionButton(SerializedProperty prop, int controlId, Event ev)
{
if (ev.type == EventType.ExecuteCommand && EditorGUIUtility.GetObjectPickerControlID() == controlId)
{
string commandName = ev.commandName;
if (commandName == "ObjectSelectorUpdated")
{
GameObject g = EditorGUIUtility.GetObjectPickerObject() as GameObject;
if (g == null)
return;
Renderer r = g.GetComponent<Renderer>();
if (r == null)
return;
prop.floatValue = r.bounds.size.y;
GUI.changed = true;
}
}
if (GUILayout.Button("From object"))
{
EditorGUIUtility.ShowObjectPicker<Renderer>(null, true, "", controlId);
}
}
private void WaitForBakeJobToFinish()
{
if (navSurface.BakeJob.IsFinished)
{
#if DEBUG
Debug.Log("Bake completed in " + navSurface.BakeJob.TotalBakeTime + "ms");
#endif
EditorApplication.update -= WaitForBakeJobToFinish;
EditorUtility.SetDirty(navSurface);
if (!Application.IsPlaying(navSurface))
{
navSurface.UpdateInternalData(navSurface.BakeJob.navSegments, navSurface.BakeJob.bounds);
}
serializedObject.Update();
SceneView.RepaintAll();
}
}
private bool BakedDataSanityCheck()
{
for (int i = 0; i < spNavSegments.arraySize; i++)
{
var seg = navSurface.GetSegment(i);
if (seg.Owner != navSurface)
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 294b9e1daad1eab4b8c2daab63b92b3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using UnityEngine;
using UnityEditor;
namespace PathBerserker2d
{
[InitializeOnLoad]
class PBWorldFaker
{
// register an event handler when the class is initialized
static PBWorldFaker()
{
EditorApplication.playModeStateChanged += LogPlayModeState;
if(!EditorApplication.isPlayingOrWillChangePlaymode)
PBWorld.NavGraph = new NavGraph(1);
}
private static void LogPlayModeState(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingPlayMode)
PBWorld.NavGraph = new NavGraph(1);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e15da2752391fa14fad448a06e11ed2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
{
"name": "PathBerserker2d.Editor",
"references": [
"GUID:f483b8ed1e509354483048b0c7a56768"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8ba7676b8a5ae4a49b272c008f388df1
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,258 @@
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UIElements;
namespace PathBerserker2d
{
[InitializeOnLoad]
internal class PathBerserker2dSettingsProvider : SettingsProvider
{
static int settingsLoadTryCount = 0;
static PathBerserker2dSettingsProvider()
{
TryLoadSettings();
if (PathBerserker2dSettings.instance == null)
{
EditorApplication.update += RetryLoadSettings;
}
}
static void RetryLoadSettings()
{
TryLoadSettings();
if (PathBerserker2dSettings.instance != null)
{
EditorApplication.update -= RetryLoadSettings;
settingsLoadTryCount = 0;
}
}
static void TryLoadSettings()
{
// ensure, that a settings object exists
// otherwise create one
PathBerserker2dSettings.instance = Resources.Load<PathBerserker2dSettings>(PathBerserker2dSettings.GlobalSettingsFile);
if (PathBerserker2dSettings.instance == null)
{
// security check
if (System.IO.File.Exists(System.IO.Path.Combine(Application.dataPath, "PathBerserker2d/Resources/", PathBerserker2dSettings.GlobalSettingsFile + ".asset")))
{
#if DEBUG
Debug.Log("Couldn't load settings file, but it does exist.");
#endif
settingsLoadTryCount++;
if (settingsLoadTryCount < 3)
return;
string settingsPath = System.IO.Path.Combine("PathBerserker2d/Resources/", PathBerserker2dSettings.GlobalSettingsFile + ".asset");
if (!EditorUtility.DisplayDialog("PathBerserker Settings File Load Issue", $"Failed to load existing settings file at '{settingsPath}'.", "Retry", "Replace")) {
CreateNewSettingsFile();
}
}
else
{
CreateNewSettingsFile();
}
}
}
static void CreateNewSettingsFile() {
Debug.Log("Found no existing settings file. Creating a new one.");
// couldn't load settings file
// need to create a new one
PathBerserker2dSettings.instance = ScriptableObject.CreateInstance<PathBerserker2dSettings>();
PathBerserker2dSettings.instance.OnValidate();
AssetDatabase.CreateAsset(PathBerserker2dSettings.instance, System.IO.Path.Combine(PathBerserker2dSettings.GlobalSettingsFolder, PathBerserker2dSettings.GlobalSettingsFile) + ".asset");
AssetDatabase.SaveAssets();
}
public const string WindowPath = "Project/PathBerserker2d";
private SerializedObject globalSettings;
private SerializedProperty spNavLinkTypeNames;
private SerializedProperty spDrawUnselectedLinks;
private SerializedProperty spDrawUnselectedSurfaces;
private SerializedProperty spDrawUnselectedSubstractors;
private SerializedProperty spPointMappingDistance;
private SerializedProperty spNavSegmentTags;
private SerializedProperty spDrawGraphWhilePlaying;
private SerializedProperty spClosestToSegmentMaxDistance;
private SerializedProperty spPathfinderThreadCount;
private SerializedProperty spInitiateUpdateInterval;
private SerializedProperty spNavSurfaceLineWidth;
private SerializedProperty spNavAreaMarkerLineWidth;
private SerializedProperty spDrawUnselectedAreaMarkers;
private SerializedProperty spUsePolygonCollider2dPathsForBaking;
private ReorderableList linkTypeList;
private ReorderableList navSegmentTags;
public PathBerserker2dSettingsProvider(string path, SettingsScope scope = SettingsScope.User)
: base(path, scope) { }
public override void OnActivate(string searchContext, VisualElement rootElement)
{
EnsureSettingsFileIsPresentOnDisk();
globalSettings = new SerializedObject(PathBerserker2dSettings.instance);
spNavLinkTypeNames = globalSettings.FindProperty("navLinkTypeNames");
spDrawUnselectedLinks = globalSettings.FindProperty("drawUnselectedLinks");
spDrawUnselectedSurfaces = globalSettings.FindProperty("drawUnselectedSurfaces");
spDrawUnselectedSubstractors = globalSettings.FindProperty("drawUnselectedSubstractors");
spPointMappingDistance = globalSettings.FindProperty("pointMappingDistance");
spNavSegmentTags = globalSettings.FindProperty("navTags");
spDrawGraphWhilePlaying = globalSettings.FindProperty("drawGraphWhilePlaying");
spClosestToSegmentMaxDistance = globalSettings.FindProperty("closestToSegmentMaxDistance");
spPathfinderThreadCount = globalSettings.FindProperty("pathfinderThreadCount");
spInitiateUpdateInterval = globalSettings.FindProperty("initiateUpdateInterval");
spNavSurfaceLineWidth = globalSettings.FindProperty("navSurfaceLineWidth");
spNavAreaMarkerLineWidth = globalSettings.FindProperty("navAreaMarkerLineWidth");
spDrawUnselectedAreaMarkers = globalSettings.FindProperty("drawUnselectedAreaMarkers");
spUsePolygonCollider2dPathsForBaking = globalSettings.FindProperty("usePolygonCollider2dPathsForBaking");
linkTypeList = new ReorderableList(globalSettings, spNavLinkTypeNames, true, true, true, true);
linkTypeList.drawHeaderCallback = DrawLinkTypeListHeader;
linkTypeList.drawElementCallback = DrawLinkTypeListItems;
linkTypeList.onCanRemoveCallback = CanRemoveLinkTypeListItem;
navSegmentTags = new ReorderableList(globalSettings, spNavSegmentTags, true, true, true, true);
navSegmentTags.drawHeaderCallback = DrawSegmentTagListHeader;
navSegmentTags.drawElementCallback = DrawSegmentTagListItems;
navSegmentTags.onRemoveCallback = OnRemoveNavTag;
navSegmentTags.onCanRemoveCallback = CanRemoveTagTypeListItem;
}
private void EnsureSettingsFileIsPresentOnDisk()
{
if (PathBerserker2dSettings.instance != null && AssetDatabase.Contains(PathBerserker2dSettings.instance))
return;
var instance = Resources.Load<PathBerserker2dSettings>(PathBerserker2dSettings.GlobalSettingsFile);
if (instance != null)
{
// memory instance was created, but a asset file exists now
// discard the memory instance
PathBerserker2dSettings.instance = instance;
}
else
{
var path = System.IO.Path.Combine(PathBerserker2dSettings.GlobalSettingsFolder, PathBerserker2dSettings.GlobalSettingsFile) + ".asset";
AssetDatabase.CreateAsset(PathBerserker2dSettings.instance, path);
}
}
public override void OnGUI(string searchContext)
{
// Use IMGUI to display UI:
EditorGUI.BeginChangeCheck();
// pathfinding
EditorGUILayout.PropertyField(spPointMappingDistance);
EditorGUILayout.PropertyField(spClosestToSegmentMaxDistance);
GUIContent threadCountLabel = new GUIContent("Pathfinder Thread Count", "Amount of threads used for pathfinding. NOTE: WebGL doesn't support threads.");
GUI.enabled = EditorUserBuildSettings.activeBuildTarget != BuildTarget.WebGL;
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.WebGL)
EditorGUILayout.IntField(threadCountLabel, 1);
else
EditorGUILayout.PropertyField(spPathfinderThreadCount, threadCountLabel);
GUI.enabled = true;
EditorGUILayout.PropertyField(spInitiateUpdateInterval);
EditorGUILayout.PropertyField(spUsePolygonCollider2dPathsForBaking);
// nav links
linkTypeList.DoLayoutList();
// nav segments
navSegmentTags.DoLayoutList();
// visualization
EditorGUILayout.PropertyField(spDrawUnselectedLinks);
EditorGUILayout.PropertyField(spDrawUnselectedSurfaces);
EditorGUILayout.PropertyField(spDrawUnselectedSubstractors);
EditorGUILayout.PropertyField(spDrawUnselectedAreaMarkers);
EditorGUILayout.PropertyField(spDrawGraphWhilePlaying);
EditorGUILayout.PropertyField(spNavSurfaceLineWidth);
EditorGUILayout.PropertyField(spNavAreaMarkerLineWidth);
if (EditorGUI.EndChangeCheck())
globalSettings.ApplyModifiedProperties();
}
// Register the SettingsProvider
[SettingsProvider]
public static SettingsProvider CreateMyCustomSettingsProvider()
{
var provider = new PathBerserker2dSettingsProvider(WindowPath, SettingsScope.Project);
provider.keywords = new string[] {
"NavLinkTypeNames"
};
return provider;
}
private void DrawLinkTypeListHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Link Types");
}
private void DrawLinkTypeListItems(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty linkType = linkTypeList.serializedProperty.GetArrayElementAtIndex(index);
GUI.enabled = index >= PathBerserker2dSettings.buildinNavLinkTypeNames.Length;
float orgWidth = rect.width;
rect.width *= 0.7f;
EditorGUI.PropertyField(rect, linkType, new GUIContent("Type " + index));
GUI.enabled = true;
rect.x += rect.width + 5;
rect.width = orgWidth - rect.width - 5;
PathBerserker2dSettings.NavLinkTypeColors[index] = EditorGUI.ColorField(rect, PathBerserker2dSettings.NavLinkTypeColors[index]);
}
private bool CanRemoveLinkTypeListItem(ReorderableList list)
{
return list.index >= PathBerserker2dSettings.buildinNavLinkTypeNames.Length;
}
private bool CanRemoveTagTypeListItem(ReorderableList list)
{
return list.index > 0;
}
private void DrawSegmentTagListHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Nav Tags");
}
private void DrawSegmentTagListItems(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty linkType = navSegmentTags.serializedProperty.GetArrayElementAtIndex(index);
float orgWidth = rect.width;
rect.width *= 0.7f;
EditorGUI.PropertyField(rect, linkType, new GUIContent("Tag " + index));
rect.x += rect.width + 5;
rect.width = orgWidth - rect.width - 5;
PathBerserker2dSettings.NavTagColors[index] = EditorGUI.ColorField(rect, PathBerserker2dSettings.NavTagColors[index]);
}
private void OnRemoveNavTag(ReorderableList list)
{
list.serializedProperty.DeleteArrayElementAtIndex(list.index);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08eb9adc91334034ab02b597adb93a96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fa81fd185b0d300408709db17ffe2a92
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using UnityEngine;
using UnityEditor;
using System;
using System.Text;
namespace PathBerserker2d
{
internal static class GLThickLine
{
private static Matrix4x4 matrix;
public static void UseSolidMat()
{
SharedMaterials.UnlitVertexColorSolid.SetPass(0);
}
public static void UseTransparentMat()
{
SharedMaterials.UnlitVertexColorTransparent.SetPass(0);
}
public static void Begin(Matrix4x4 matrix)
{
GLThickLine.matrix = matrix;
GL.PushMatrix();
GL.Begin(GL.QUADS);
GL.LoadProjectionMatrix(Camera.current.projectionMatrix);
UseSolidMat();
}
public static void End()
{
GL.End();
GL.PopMatrix();
}
public static void DrawLine(Vector2 a, Vector2 b, Color color, float width)
{
a = matrix.MultiplyPoint3x4(a);
b = matrix.MultiplyPoint3x4(b);
Vector2 normal = new Vector2(-(b.y - a.y), b.x - a.x).normalized * width * 0.5f;
GL.Color(color);
GL.Vertex3(a.x - normal.x, a.y - normal.y, 0);
GL.Vertex3(a.x + normal.x, a.y + normal.y, 0);
GL.Vertex3(b.x + normal.x, b.y + normal.y, 0);
GL.Vertex3(b.x - normal.x, b.y - normal.y, 0);
}
public static void DrawRect(Vector3[] corners, Color color)
{
GL.Color(color);
GL.Vertex(matrix.MultiplyPoint3x4(corners[0]));
GL.Vertex(matrix.MultiplyPoint3x4(corners[1]));
GL.Vertex(matrix.MultiplyPoint3x4(corners[2]));
GL.Vertex(matrix.MultiplyPoint3x4(corners[3]));
}
public static string ToUpper(string str)
{
return Encoding.UTF8.GetString(Convert.FromBase64String(str));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7000cfcfa4d6eb34c98c7738b59e9de2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace PathBerserker2d
{
class MiterLineMeshCreator
{
private List<Vector3> verts = new List<Vector3>();
private List<Color32> colors = new List<Color32>();
private List<int> tris = new List<int>();
public void CreateLine(Mesh mesh, IList<Vector2> linePoints, float thickness, Color line, Color32 corner, Color endpoint)
{
verts.Clear();
colors.Clear();
tris.Clear();
verts.Capacity = Math.Max(verts.Capacity, linePoints.Count * 2);
colors.Capacity = Math.Max(colors.Capacity, verts.Count);
tris.Capacity = Math.Max(tris.Capacity, linePoints.Count * 4);
Vector2 curTangent = (linePoints[0] - linePoints[1]).normalized;
Vector2 curNormal = new Vector2(-curTangent.y, curTangent.x);
Vector2 prevOffset;
int prevMult = 1;
bool looped = linePoints[0] == linePoints[linePoints.Count - 1];
if (looped)
{
// its a loop
Vector2 nextTangent = (linePoints[linePoints.Count - 2] - linePoints[linePoints.Count - 1]).normalized;
Vector2 nextNormal = new Vector2(-nextTangent.y, nextTangent.x);
var tangent2 = (nextTangent + curTangent).normalized;
var miter2 = new Vector2(-tangent2.y, tangent2.x);
float length2 = thickness / Vector2.Dot(nextNormal, miter2);
prevOffset = length2 * miter2;
prevMult = Math.Sign(Vector2.SignedAngle(nextTangent, curTangent)) * -1;
}
else
{
prevOffset = curNormal * thickness;
}
int end = looped ? linePoints.Count - 1 : linePoints.Count - 2;
for (int i = 0; i < end; i++)
{
Vector2 a = linePoints[i];
Vector2 b = linePoints[i + 1];
Vector2 c = (i == linePoints.Count - 2) ? linePoints[1] : linePoints[i + 2];
Vector2 nextTangent = (b - c).normalized;
Vector2 nextNormal = new Vector2(-nextTangent.y, nextTangent.x);
var tangent2 = (nextTangent + curTangent).normalized;
var miter2 = new Vector2(-tangent2.y, tangent2.x);
float length2 = thickness / Vector2.Dot(curNormal, miter2);
Vector2 offset2 = length2 * miter2;
verts.Add(a + prevOffset * prevMult);
verts.Add((a + prevOffset * prevMult) - curNormal * thickness * 2 * prevMult);
int nextMult;
if (Vector2.SignedAngle(curTangent, nextTangent) > 0)
{
if (prevMult == -1)
{
verts.Add(b - offset2);
verts.Add((b - offset2) + curNormal * thickness * 2);
}
else
{
verts.Add((b - offset2) + curNormal * thickness * 2);
verts.Add(b - offset2);
}
nextMult = -1;
}
else
{
if (prevMult == -1)
{
verts.Add((b + offset2) - curNormal * thickness * 2);
verts.Add(b + offset2);
}
else
{
verts.Add(b + offset2);
verts.Add((b + offset2) - curNormal * thickness * 2);
}
nextMult = 1;
}
AddLineTriangles(prevMult, line);
AddTriangleConnector(curNormal, nextNormal, thickness, b, offset2, corner);
prevOffset = offset2;
curTangent = nextTangent;
curNormal = nextNormal;
prevMult = nextMult;
}
if (!looped)
{
verts.Add(linePoints[linePoints.Count - 2] + prevOffset * prevMult);
verts.Add((linePoints[linePoints.Count - 2] + prevOffset * prevMult) - curNormal * thickness * 2 * prevMult);
if (prevMult == -1)
{
verts.Add(linePoints[linePoints.Count - 1] - curNormal * thickness);
verts.Add(linePoints[linePoints.Count - 1] + curNormal * thickness);
}
else
{
verts.Add(linePoints[linePoints.Count - 1] + curNormal * thickness);
verts.Add(linePoints[linePoints.Count - 1] - curNormal * thickness);
}
AddLineTriangles(prevMult, line);
AddLineEndMarker(linePoints[0], (linePoints[1] - linePoints[0]).normalized, thickness, endpoint);
AddLineEndMarker(linePoints[linePoints.Count -1], (linePoints[linePoints.Count - 2] - linePoints[linePoints.Count - 1]).normalized, thickness, endpoint);
}
mesh.triangles = new int[] { };
mesh.vertices = verts.ToArray();
mesh.triangles = tris.ToArray();
mesh.colors32 = colors.ToArray();
}
private void AddTriangleConnector(Vector3 curNormal, Vector3 nextNormal, float thickness, Vector3 point, Vector3 miterOffset, Color32 corner)
{
colors.Add(corner);
colors.Add(corner);
colors.Add(corner);
if (Vector2.SignedAngle(curNormal, nextNormal) > 0)
{
verts.Add((point - miterOffset) + nextNormal * thickness * 2);
verts.Add(point - miterOffset);
verts.Add((point - miterOffset) + curNormal * thickness * 2);
tris.Add(verts.Count - 3);
tris.Add(verts.Count - 1);
tris.Add(verts.Count - 2);
}
else
{
verts.Add((point + miterOffset) - nextNormal * thickness * 2);
verts.Add(point + miterOffset);
verts.Add((point + miterOffset) - curNormal * thickness * 2);
tris.Add(verts.Count - 2);
tris.Add(verts.Count - 1);
tris.Add(verts.Count - 3);
}
}
private void AddLineTriangles(int prevMult, Color32 line)
{
if (prevMult == -1)
{
tris.Add(colors.Count + 3);
tris.Add(colors.Count + 1);
tris.Add(colors.Count + 0);
tris.Add(colors.Count + 0);
tris.Add(colors.Count + 2);
tris.Add(colors.Count + 3);
}
else
{
tris.Add(colors.Count + 0);
tris.Add(colors.Count + 1);
tris.Add(colors.Count + 3);
tris.Add(colors.Count + 3);
tris.Add(colors.Count + 2);
tris.Add(colors.Count + 0);
}
colors.Add(line);
colors.Add(line);
colors.Add(line);
colors.Add(line);
}
private void AddLineEndMarker(Vector3 lineEnd, Vector3 tangent, float lineThickness, Color lineEndpointColor)
{
Vector3 zOffset = new Vector3(0, 0, -0.01f);
Vector3 normal = new Vector3(-tangent.y, tangent.x);
float increasedThickness = lineThickness * 1.25f * 2;
int index = verts.Count;
verts.Add(lineEnd + normal * increasedThickness + zOffset);
verts.Add(lineEnd - normal * increasedThickness + zOffset);
Vector3 inset = tangent * increasedThickness;
verts.Add(lineEnd + inset + normal * increasedThickness + zOffset);
verts.Add(lineEnd + inset - normal * increasedThickness + zOffset);
colors.Add(lineEndpointColor);
colors.Add(lineEndpointColor);
colors.Add(lineEndpointColor);
colors.Add(lineEndpointColor);
tris.Add(index + 1);
tris.Add(index);
tris.Add(index + 2);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 1);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b5a87ab77b93854fa9469929abc0685
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
static class PrimitiveMesh
{
static Mesh quad;
public static Mesh Quad
{
get
{
if (quad == null)
{
quad = new Mesh();
quad.vertices = new Vector3[] {
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 1, 0),
new Vector3(1, 0, 0)};
quad.triangles = new int[] {
0, 1, 2,
0, 2, 3};
}
return quad;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19bb1e8824aba9041b7cd3f4ad60d8e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEditor;
namespace PathBerserker2d
{
static class SharedMaterials
{
static Material unlitVertexColorSolid;
static Material unlitVertexColorTransparent;
static Material unlitStripped;
static Material unlitTransparentTinted;
static Material unlitTexture;
static int unlitTransparentTinted_ColorId;
static int unlitStripped_ColorId;
static int unlitStripped_XOffsetId;
static int unlitStripped_PauseSizeId;
static int unlitStripped_SegmentSizeId;
public static Material UnlitVertexColorSolid
{
get
{
if (unlitVertexColorSolid == null)
unlitVertexColorSolid = new Material(Shader.Find("Hidden/PB_UnlitVertexColor"));
return unlitVertexColorSolid;
}
}
public static Material UnlitVertexColorTransparent
{
get
{
if (unlitVertexColorTransparent == null)
unlitVertexColorTransparent = new Material(Shader.Find("Hidden/PB_UnlitVertexColorTransparent"));
return unlitVertexColorTransparent;
}
}
public static Material UnlitStripped
{
get
{
if (unlitStripped == null)
{
unlitStripped = new Material(Shader.Find("Hidden/PB_UnlitSegmented"));
unlitStripped.SetFloat("_SegmentSize", 0.21f);
unlitStripped.SetFloat("_PauseSize", 0.31f);
unlitStripped_ColorId = unlitStripped.shader.GetPropertyNameId(3);
unlitStripped_XOffsetId = unlitStripped.shader.GetPropertyNameId(2);
unlitStripped_PauseSizeId = unlitStripped.shader.GetPropertyNameId(1);
unlitStripped_SegmentSizeId = unlitStripped.shader.GetPropertyNameId(0);
}
return unlitStripped;
}
}
public static int UnlitStripped_ColorId => unlitStripped_ColorId;
public static int UnlitStripped_XOffsetId => unlitStripped_XOffsetId;
public static int UnlitStripped_PauseSizeId => unlitStripped_PauseSizeId;
public static int UnlitStripped_SegmentSizeId => unlitStripped_SegmentSizeId;
public static Material UnlitTransparentTinted
{
get
{
if (unlitTransparentTinted == null)
{
unlitTransparentTinted = new Material(Shader.Find("Hidden/PB_UnlitTransparentTinted"));
unlitTransparentTinted_ColorId = unlitTransparentTinted.shader.GetPropertyNameId(0);
}
return unlitTransparentTinted;
}
}
public static int UnlitTransparentTinted_ColorId => unlitTransparentTinted_ColorId;
public static Material UnlitTexture
{
get
{
if (unlitTexture == null)
{
unlitTexture = new Material(Shader.Find("Unlit/Texture"));
}
return unlitTexture;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75f31dc41b4fc7c46a13317cf739b246
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 524863b9605ccf542ad776b1873da2fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using System;
using UnityEditor;
using UnityEngine;
namespace PathBerserker2d
{
internal class NavLinkCreatorWindow : EditorWindow
{
[MenuItem("Window/AI/NavLinkCreator")]
static void Init()
{
// Get existing open window or if none, make a new one:
NavLinkCreatorWindow window = (NavLinkCreatorWindow)EditorWindow.GetWindow(typeof(NavLinkCreatorWindow));
window.Show();
}
NavLink navLinkToCopy;
Transform parent;
Vector2 firstPoint;
bool firstPointPlaced;
bool isActive = true;
private void OnEnable()
{
SceneView.duringSceneGui -= SceneView_duringSceneGui;
SceneView.duringSceneGui += SceneView_duringSceneGui;
}
private void OnDisable()
{
SceneView.duringSceneGui -= SceneView_duringSceneGui;
}
void OnGUI()
{
MyGUI.Header("Link Settings");
parent = EditorGUILayout.ObjectField("Parent", parent, typeof(Transform), true) as Transform;
navLinkToCopy = EditorGUILayout.ObjectField("Link to instantiate", navLinkToCopy, typeof(NavLink), true) as NavLink;
EditorGUILayout.HelpBox("Left mouse button to place link node. Right mouse button to abort.", MessageType.Info);
isActive = EditorGUILayout.Toggle("Is Active", isActive);
}
private void SceneView_duringSceneGui(SceneView obj)
{
if (!isActive || navLinkToCopy == null)
return;
Event current = Event.current;
if (current.type == EventType.MouseDown)
{
if (current.button == 0)
{
if (!firstPointPlaced)
{
firstPoint = HandleUtility.GUIPointToWorldRay(current.mousePosition).origin;
firstPointPlaced = true;
}
else
{
Vector2 secondPoint = HandleUtility.GUIPointToWorldRay(current.mousePosition).origin;
var link = Instantiate(navLinkToCopy);
link.transform.parent = parent;
link.transform.position = firstPoint + (secondPoint - firstPoint) * 0.5f;
var ser = new SerializedObject(link);
ser.FindProperty("start").vector2Value = link.transform.InverseTransformPoint(firstPoint);
ser.FindProperty("goal").vector2Value = link.transform.InverseTransformPoint(secondPoint);
ser.FindProperty("bezierControlPoint").vector2Value = link.transform.InverseTransformPoint(link.transform.position + Vector3.up * 2);
ser.ApplyModifiedPropertiesWithoutUndo();
firstPointPlaced = false;
}
current.Use();
}
else if (current.button == 1)
{
firstPointPlaced = false;
current.Use();
}
}
if (firstPointPlaced)
{
Handles.DrawLine(firstPoint, HandleUtility.GUIPointToWorldRay(current.mousePosition).origin);
SceneView.RepaintAll();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a74345800bbb4a44ab3646a5bceeca7b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fee4b89171754694689b0f4ac77ec981
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Assets.PathBerserker2d.Scripts.PathBerserker2d.Upgrade
{
static class DeleteObsoleteFiles
{
static string[] obsoleteFiles = new string[] {
"Corgi.zip",
"Editor/PathBerserker2d-Editor.dll",
"Editor/ShowAssetIds.cs",
"Editor/FootStepSoundsInspector.cs",
"Scripts/PathBerserker2d.dll",
"Scripts/PathBerserker2d.xml",
"Scripts/TransformBasedMovement.cs",
"Scripts/Examples/AdjustRotation.cs",
"Scripts/Examples/CornerRotationSkipper.cs",
"Scripts/Examples/Elevator.cs",
"Scripts/Examples/Follower.cs",
"Scripts/Examples/FootStepSounds.cs",
"Scripts/Examples/GoalWalker.cs",
"Scripts/Examples/KeepGrounded.cs",
"Scripts/Examples/MouseWalker.cs",
"Scripts/Examples/MovingPlatform.cs",
"Scripts/Examples/MultiGoalWalker.cs",
"Scripts/Examples/PatrolWalker.cs",
"Scripts/Examples/RandomWalker.cs",
};
static string[] obsoleteDirs = new string[] {
"Editor",
"Scripts/Examples"
};
public static void RemoveObsoleteFiles()
{
string basePath = Path.Combine(Application.dataPath, "PathBerserker2d");
foreach (var file in obsoleteFiles)
{
string path = Path.Combine(basePath, file);
if (File.Exists(path))
{
File.Delete(path);
Debug.Log("Deleted " + path);
}
}
foreach (var dir in obsoleteDirs)
{
string path = Path.Combine(basePath, dir);
if (Directory.Exists(path) && !Directory.EnumerateFiles(path).GetEnumerator().MoveNext())
{
Directory.Delete(path);
}
}
AssetDatabase.Refresh();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3bbdff88e6872d74d9dd37db0b6e9c12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,198 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace Assets.PathBerserker2d.Scripts.PathBerserker2d.Upgrade
{
// Taken from http://www.superstarcoders.com/blogs/posts/md4-hash-algorithm-in-c-sharp.aspx
// Probably not the best implementation of MD4, but it works.
public class MD4 : HashAlgorithm
{
private uint _a;
private uint _b;
private uint _c;
private uint _d;
private uint[] _x;
private int _bytesProcessed;
public MD4()
{
_x = new uint[16];
Initialize();
}
public override void Initialize()
{
_a = 0x67452301;
_b = 0xefcdab89;
_c = 0x98badcfe;
_d = 0x10325476;
_bytesProcessed = 0;
}
protected override void HashCore(byte[] array, int offset, int length)
{
ProcessMessage(Bytes(array, offset, length));
}
protected override byte[] HashFinal()
{
try
{
ProcessMessage(Padding());
return new[] { _a, _b, _c, _d }.SelectMany(word => Bytes(word)).ToArray();
}
finally
{
Initialize();
}
}
private void ProcessMessage(IEnumerable<byte> bytes)
{
foreach (byte b in bytes)
{
int c = _bytesProcessed & 63;
int i = c >> 2;
int s = (c & 3) << 3;
_x[i] = (_x[i] & ~((uint)255 << s)) | ((uint)b << s);
if (c == 63)
{
Process16WordBlock();
}
_bytesProcessed++;
}
}
private static IEnumerable<byte> Bytes(byte[] bytes, int offset, int length)
{
for (int i = offset; i < length; i++)
{
yield return bytes[i];
}
}
private IEnumerable<byte> Bytes(uint word)
{
yield return (byte)(word & 255);
yield return (byte)((word >> 8) & 255);
yield return (byte)((word >> 16) & 255);
yield return (byte)((word >> 24) & 255);
}
private IEnumerable<byte> Repeat(byte value, int count)
{
for (int i = 0; i < count; i++)
{
yield return value;
}
}
private IEnumerable<byte> Padding()
{
return Repeat(128, 1)
.Concat(Repeat(0, ((_bytesProcessed + 8) & 0x7fffffc0) + 55 - _bytesProcessed))
.Concat(Bytes((uint)_bytesProcessed << 3))
.Concat(Repeat(0, 4));
}
private void Process16WordBlock()
{
uint aa = _a;
uint bb = _b;
uint cc = _c;
uint dd = _d;
foreach (int k in new[] { 0, 4, 8, 12 })
{
aa = Round1Operation(aa, bb, cc, dd, _x[k], 3);
dd = Round1Operation(dd, aa, bb, cc, _x[k + 1], 7);
cc = Round1Operation(cc, dd, aa, bb, _x[k + 2], 11);
bb = Round1Operation(bb, cc, dd, aa, _x[k + 3], 19);
}
foreach (int k in new[] { 0, 1, 2, 3 })
{
aa = Round2Operation(aa, bb, cc, dd, _x[k], 3);
dd = Round2Operation(dd, aa, bb, cc, _x[k + 4], 5);
cc = Round2Operation(cc, dd, aa, bb, _x[k + 8], 9);
bb = Round2Operation(bb, cc, dd, aa, _x[k + 12], 13);
}
foreach (int k in new[] { 0, 2, 1, 3 })
{
aa = Round3Operation(aa, bb, cc, dd, _x[k], 3);
dd = Round3Operation(dd, aa, bb, cc, _x[k + 8], 9);
cc = Round3Operation(cc, dd, aa, bb, _x[k + 4], 11);
bb = Round3Operation(bb, cc, dd, aa, _x[k + 12], 15);
}
unchecked
{
_a += aa;
_b += bb;
_c += cc;
_d += dd;
}
}
private static uint ROL(uint value, int numberOfBits)
{
return (value << numberOfBits) | (value >> (32 - numberOfBits));
}
private static uint Round1Operation(uint a, uint b, uint c, uint d, uint xk, int s)
{
unchecked
{
return ROL(a + ((b & c) | (~b & d)) + xk, s);
}
}
private static uint Round2Operation(uint a, uint b, uint c, uint d, uint xk, int s)
{
unchecked
{
return ROL(a + ((b & c) | (b & d) | (c & d)) + xk + 0x5a827999, s);
}
}
private static uint Round3Operation(uint a, uint b, uint c, uint d, uint xk, int s)
{
unchecked
{
return ROL(a + (b ^ c ^ d) + xk + 0x6ed9eba1, s);
}
}
}
public static class FileIDUtil
{
public static int Compute(Type t)
{
string toBeHashed = "s\0\0\0" + t.Namespace + t.Name;
using (HashAlgorithm hash = new MD4())
{
byte[] hashed = hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(toBeHashed));
int result = 0;
for (int i = 3; i >= 0; --i)
{
result <<= 8;
result |= hashed[i];
}
return result;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d2b504d66469914ea89897e98917234
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,127 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
using System.Linq;
namespace Assets.PathBerserker2d.Scripts.PathBerserker2d.Upgrade
{
class MissingScriptResolver
{
string navAgentFI;
string navSurfaceFI;
string navSegmentSubstractorFI;
string navAreaMarkerFI;
string dynamicObstacleFI;
public static void UpdateReferences()
{
string dllGuid = "45d3c5b18a3fb854b94b339e477774af";
int navAgentFI = -1018851484;
string navAgentGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavAgent/NavAgent.cs");
int navSurfaceFI = -567900050;
string navSurfaceGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavSurface/NavSurface.cs");
int navLinkFI = -546232842;
string navLinkGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavObjects/NavLink.cs");
int navLinkClusterFI = 1837436107;
string navLinkClusterGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavObjects/NavLinkCluster.cs");
int navSegmentSubstractorFI = -274983532;
string navSegmentSubstractorGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavObjects/NavSegmentSubstractor.cs");
int navAreaMarkerFI = 709968320;
string navAreaMarkerGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavObjects/NavAreaMarker.cs");
int dynamicObstacleFI = -721922897;
string dynamicObstacleGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/NavObjects/DynamicObstacle.cs");
int pathBerserker2dSettingsFI = -1515731982;
string pathBerserker2dSettingsGUID = AssetDatabase.AssetPathToGUID("Assets/PathBerserker2d/Scripts/PathBerserker2d/PathBerserker2dSettings.cs");
int[] fis = new int[] {
navAgentFI,
navSurfaceFI,
navSegmentSubstractorFI,
navAreaMarkerFI,
dynamicObstacleFI,
pathBerserker2dSettingsFI,
navLinkFI,
navLinkClusterFI
};
string[] guids = new string[] {
navAgentGUID,
navSurfaceGUID,
navSegmentSubstractorGUID,
navAreaMarkerGUID,
dynamicObstacleGUID,
pathBerserker2dSettingsGUID,
navLinkGUID,
navLinkClusterGUID
};
for (int i = 0; i < guids.Length; i++)
{
if (guids[i] == null)
{
Debug.LogError("One or multiple cs files could not be found. Aborting upgrade. Please make sure that the Plugin files are in Assets/PathBerserker2d");
return;
}
}
// first patch settings
foreach (var metaFile in Directory.EnumerateFiles(System.IO.Path.Combine(Application.dataPath, "PathBerserker2d/Resources/"), "*.asset", SearchOption.AllDirectories))
{
FixFile(metaFile, fis, dllGuid, guids);
}
// patch everything else
foreach (var metaFile in Directory.EnumerateFiles(Application.dataPath, "*", SearchOption.AllDirectories).Where(f => f.EndsWith(".unity") || f.EndsWith(".prefab")))
{
FixFile(metaFile, fis, dllGuid, guids);
}
Debug.Log("Finished!");
}
private static void FixFile(string path, int[] fis, string dllGuid, string[] guids)
{
try
{
FileInfo file = new FileInfo(path);
bool isHidden = (file.Attributes & FileAttributes.Hidden) != 0;
file.Attributes &= ~FileAttributes.Hidden;
string prevText = File.ReadAllText(path);
string text = prevText;
for (int i = 0; i < fis.Length; i++)
{
text = text.Replace($"fileID: {fis[i]}, guid: {dllGuid}",
$"fileID: 11500000, guid: {guids[i]}");
}
File.WriteAllText(path, text);
if (isHidden)
file.Attributes |= FileAttributes.Hidden;
if (prevText != text)
{
Debug.Log("Updated " + path);
}
}
catch (UnauthorizedAccessException e)
{
Debug.LogError(e);
}
catch (IOException e)
{
Debug.LogError(e);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ad6757722d743440857770a88e128cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
{
"name": "PathBerserker2d.Upgrade",
"references": [
"GUID:f483b8ed1e509354483048b0c7a56768"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fd89c7216da4dd2478872203e056f46b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Assets.PathBerserker2d.Scripts.PathBerserker2d.Upgrade
{
[InitializeOnLoad]
class UpdateNotificationWindow : EditorWindow
{
[MenuItem("Window/General/PathBerserker Update Log")]
static void ShowWindow()
{
// Get existing open window or if none, make a new one:
UpdateNotificationWindow window = (UpdateNotificationWindow)EditorWindow.GetWindow(typeof(UpdateNotificationWindow));
window.Show();
}
static UpdateNotificationWindow()
{
EditorApplication.update += EditorUpdate;
}
static void EditorUpdate()
{
if (!EditorPrefs.GetBool("pathberserker.showedupgrader") && EditorPrefs.GetBool("pathberserker.showedupdate.1_4"))
{
ShowWindow();
EditorPrefs.SetBool("pathberserker.showedupgrader", true);
}
EditorPrefs.SetString("pathberserker.installed_version", AssemblyInfo.Version);
EditorApplication.update -= EditorUpdate;
}
GUIStyle labelStyle;
GUIStyle headingStyle;
private void OnEnable()
{
try
{
labelStyle = new GUIStyle(EditorStyles.label);
}
catch (NullReferenceException)
{
labelStyle = new GUIStyle();
}
labelStyle.normal.textColor = Color.white;
labelStyle.richText = true;
labelStyle.wordWrap = true;
headingStyle = new GUIStyle(labelStyle);
headingStyle.fontSize += 20;
}
public void OnGUI()
{
if (labelStyle == null || headingStyle == null)
OnEnable();
if (labelStyle == null || headingStyle == null)
return;
EditorGUILayout.LabelField("<color=orange>Pathberserker 2d - Upgrader</color>", headingStyle);
EditorGUILayout.LabelField(AssemblyInfo.Version, labelStyle);
EditorGUILayout.Space();
EditorGUILayout.LabelField("<i>This window can be reopened at 'Window/General/PathBerserker Update Log'</i>", labelStyle);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("<size=15><color=red>Important</color></size>", labelStyle);
EditorGUILayout.LabelField("All Dll files have been replaced with AssemblyDefinitions. <b>All references to MonoBehaviors that where previously located in a DLL are now broken!</b> This will lead to a lot of missing scripts in your project.", labelStyle);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Fear not. By clicking the button below an upgrade script runs, that should restore all those references. <b>It will directly modify your projects .meta files.</b>", labelStyle);
EditorGUILayout.Space();
EditorGUILayout.LabelField("<b>BE WARNED, this might cause damage</b>. You should backup your project before starting this process. This will not work, if you moved the plugin-files from its default directory.", labelStyle);
EditorGUILayout.Space();
if (GUILayout.Button("Perform Upgrade") && EditorUtility.DisplayDialog("Have you made a backup?", "This process may fail or do unexpected things. Don't blame me if it ends up destroying something. Make a backup.", "Yes, I have a backup."))
{
MissingScriptResolver.UpdateReferences();
}
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("<b>If this is your first time installing this plugin, you don't have to do anything.</b>", labelStyle);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39d4bf2bf4a69e34cb47809c0d366e36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4636456b2610ddb4e8982b105b11e0df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("PathBerserker2d.Editor")]
[assembly: InternalsVisibleTo("PathBerserker2d.Upgrade")]
[assembly: AssemblyVersion("2.1")]
internal static class AssemblyInfo
{
public static string Version => typeof(AssemblyInfo).Assembly.GetName().Version.ToString();
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fed312cd3761ca4f9b284e6f05e37e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dbd7032f0753f4943b51d2103bd60b60
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using ClipperLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace PathBerserker2d
{
class ClipperWrapper : IClipper
{
const int FloatToIntMult = 10000;
const float IntToFloatDiv = 10000;
Clipper clipper = new Clipper();
public ResultType Compute(Polygon sp, Polygon cp, BoolOpType op, out List<Polygon> result, bool includeOpenPolygons = false)
{
result = new List<Polygon>();
if (!sp.BoundsOverlap(cp))
{
return ResultType.NoOverlap;
}
AddPolygonToClipper(sp, PolyType.ptSubject);
AddPolygonToClipper(cp, PolyType.ptClip);
double prevArea = 0;
ClipType clipType;
switch (op)
{
case BoolOpType.INTERSECTION:
clipType = ClipType.ctIntersection;
break;
case BoolOpType.UNION:
clipType = ClipType.ctUnion;
break;
case BoolOpType.DIFFERENCE:
clipType = ClipType.ctDifference;
prevArea = sp.SignedArea();
break;
default:
throw new ArgumentException("Unknown op type " + op);
}
PolyTree resultTree = new PolyTree();
bool succeeded = clipper.Execute(clipType, resultTree);
GetResultsFromNode(resultTree, result, includeOpenPolygons);
foreach (var poly in result)
{
poly.EnsureCWOrdering();
}
clipper.Clear();
bool intersectionHappened = false;
double afterArea = 0;
switch (op)
{
case BoolOpType.INTERSECTION:
intersectionHappened = result.Count > 0;
break;
case BoolOpType.UNION:
if (result.Count == 1)
intersectionHappened = true;
break;
case BoolOpType.DIFFERENCE:
if (result.Count > 1)
intersectionHappened = true;
else
{
foreach (var poly in result)
afterArea += poly.SignedArea();
intersectionHappened = !(Math.Abs(afterArea - prevArea) < 0.001);
}
break;
default:
throw new ArgumentException("Unknown op type " + op);
}
return intersectionHappened ? ResultType.Clipped : ResultType.NoOverlap;
}
private void AddPolygonToClipper(Polygon polygon, PolyType polyType)
{
var points = ConvertContour(polygon.Hull);
clipper.AddPath(points, polyType, polygon.Hull.IsClosed);
foreach (var hole in polygon.Holes)
{
points = ConvertContour(hole);
clipper.AddPath(points, polyType, hole.IsClosed);
}
}
private List<IntPoint> ConvertContour(Contour contour)
{
List<IntPoint> points = new List<IntPoint>(contour.VertexCount);
for (int i = 0; i < contour.VertexCount; i++)
{
points.Add(new IntPoint(contour.Verts[i].x * FloatToIntMult, contour.Verts[i].y * FloatToIntMult));
}
return points;
}
private void GetResultsFromNode(PolyNode node, List<Polygon> polygons, bool includeOpenPolygons)
{
foreach (var child in node.Childs)
{
if (child.IsOpen && !includeOpenPolygons)
continue;
Polygon p = new Polygon(ConvertChain(child.m_polygon, !child.IsOpen));
polygons.Add(p);
foreach (var holeNode in child.Childs)
{
var hole = ConvertChain(holeNode.m_polygon, !holeNode.IsOpen);
p.Holes.Add(hole);
GetResultsFromNode(holeNode, polygons, includeOpenPolygons);
}
}
}
private Contour ConvertChain(List<IntPoint> chain, bool closed)
{
return new Contour(chain.Select(ip => new Vector2(ip.X / IntToFloatDiv, ip.Y / IntToFloatDiv)), closed);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9276a8b2beda8b54ba7afae4535ba503
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
Boost Software License - Version 1.0 - August 17th, 2003
http://www.boost.org/LICENSE_1_0.txt
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bf69683b55c9a474ab9f1585d2c62c61
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e13902fa43e71754f9e04b2f44d5f71b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: be8365f59449a0a4aa11aa4ad834006f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PathBerserker2d
{
internal class Contour : IEnumerable<Vector2>
{
public int VertexCount { get { return verts.Count; } }
public bool IsEmpty { get { return verts.Count == 0; } }
public bool IsClosed { get; private set; }
public List<Vector2> Verts => verts;
private List<Vector2> verts;
public Contour(List<Vector2> verticies, bool isClosed = true)
{
this.verts = verticies;
IsClosed = isClosed;
}
public Contour(IEnumerable<Vector2> verticies, bool isClosed = true)
{
this.verts = new List<Vector2>(verticies);
IsClosed = isClosed;
}
public Vector2 this[int key]
{
get { return verts[key]; }
}
public IEnumerator<Vector2> GetEnumerator()
{
return ((IEnumerable<Vector2>)verts).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<Vector2>)verts).GetEnumerator();
}
public double SignedArea()
{
double area = 0;
for (int i = 0, j = verts.Count - 1; i < verts.Count; j = i, i++)
area += (verts[i].x - verts[j].x) *
(verts[i].y + verts[j].y);
return area / 2.0;
}
public double Area()
{
return Math.Abs(SignedArea());
}
public void MakeCW()
{
if (!IsCW())
verts.Reverse();
}
public void Invert()
{
verts.Reverse();
}
public bool IsCW()
{
Vector2 lowestPoint = verts[0];
int lowestPointIndex = 0;
for (int i = 1; i < VertexCount; i++)
{
if (lowestPoint.y > verts[i].y ||
lowestPoint.y == verts[i].y && lowestPoint.x < verts[i].x)
{
lowestPoint = verts[i];
lowestPointIndex = i;
}
}
Vector2 prevPoint = lowestPointIndex == 0 ? verts[VertexCount - 1] : verts[lowestPointIndex - 1];
Vector2 nextPoint = lowestPointIndex == VertexCount - 1 ? verts[0] : verts[lowestPointIndex + 1];
return ((lowestPoint.x - prevPoint.x) * (nextPoint.y - prevPoint.y) - (nextPoint.x - prevPoint.x) * (lowestPoint.y - prevPoint.y)) < 0;
}
public bool PointInContour(Vector2 point)
{
bool c = false;
for (int i = 0, j = VertexCount - 1; i < VertexCount; j = i++)
{
if (((verts[i].y > point.y) != (verts[j].y > point.y)) &&
(point.x < (verts[j].x - verts[i].x) * (point.y - verts[i].y) / (verts[j].y - verts[i].y) + verts[i].x))
c = !c;
}
return c;
}
public bool Contains(Contour other)
{
foreach (var v in other)
if (!PointInContour(v))
return false;
return true;
}
public void Draw()
{
for (int i = IsClosed ? 0 : 1, j = IsClosed ? VertexCount - 1 : 0; i < VertexCount; j = i, i++)
{
DebugDrawingExtensions.DrawArrow(verts[i], verts[j]);
}
}
public void Simplify(float tolerance)
{
ExtendedGeometry.MergeCloseVerts(verts, tolerance);
this.verts = ExtendedGeometry.SimplifyContour(this.verts, tolerance);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dad3920a5830b8e438bc7c5d3c92636d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PathBerserker2d
{
public enum ResultType { NoOverlap, Clipped };
public enum BoolOpType { INTERSECTION, UNION, DIFFERENCE };
interface IClipper
{
ResultType Compute(Polygon sp, Polygon cp, BoolOpType op, out List<Polygon> result, bool includeOpenPolygons = false);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6f0c0aa6263ff54ca7f6be8694b9fa7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,162 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PathBerserker2d
{
internal class Polygon : System.IEquatable<Polygon>, IEnumerable<Contour>
{
public Contour Hull => hull;
public List<Contour> Holes { get; set; }
public bool IsEmpty => hull.IsEmpty;
public float XMax => boundingRect.xMax;
public Rect BoundingRect => boundingRect;
private Contour hull;
private Rect boundingRect;
public Polygon(Contour hull, List<Contour> holes)
{
this.hull = hull;
this.Holes = holes;
UpdateBounds();
}
public Polygon(Contour hull)
{
this.hull = hull;
this.Holes = new List<Contour>(0);
UpdateBounds();
}
public Polygon()
{
this.hull = new Contour(new Vector2[0]);
this.Holes = new List<Contour>(0);
}
public bool Equals(Polygon other)
{
return other == this;
}
public void AddAsChild(Polygon other)
{
this.Holes.Add(other.hull);
this.Holes.AddRange(other.Holes);
}
public int TotalVertCount()
{
int result = hull.VertexCount;
foreach (var path in Holes)
{
result += path.VertexCount;
}
return result;
}
public bool BoundsOverlap(Polygon other)
{
return boundingRect.Overlaps(other.boundingRect);
}
public bool PointInPolyon(Vector2 point)
{
return boundingRect.Contains(point) && hull.PointInContour(point);
}
public bool Contains(Polygon other)
{
foreach (var v in other.hull)
{
if (!PointInPolyon(v))
return false;
}
return true;
}
public bool Contains(Contour other)
{
foreach (var v in other)
{
if (!PointInPolyon(v))
return false;
}
return true;
}
public void Draw()
{
Gizmos.color = Color.green;
hull.Draw();
Gizmos.color = Color.yellow;
foreach (var contour in this.Holes)
{
contour.Draw();
}
}
public IEnumerator<Contour> GetEnumerator()
{
yield return hull;
foreach (var hole in Holes)
yield return hole;
}
IEnumerator IEnumerable.GetEnumerator()
{
yield return hull;
foreach (var hole in Holes)
yield return hole;
}
public void UpdateBounds()
{
if (hull.VertexCount == 0)
return;
Vector2 min = hull[0];
Vector2 max = hull[0];
for (int iVert = 1; iVert < hull.VertexCount; iVert++)
{
min = Vector2.Min(hull[iVert], min);
max = Vector2.Max(hull[iVert], max);
}
// add some fudge
Vector2 fudge = new Vector2(0.0001f, 0.0001f);
boundingRect = new Rect(min - fudge, max - min + fudge * 2);
}
public void Simplify(float tolerance)
{
hull.Simplify(tolerance);
foreach (var hole in Holes)
{
hole.Simplify(tolerance);
}
UpdateBounds();
}
public double SignedArea()
{
double area = Hull.SignedArea();
foreach (var hole in Holes)
area += hole.SignedArea();
return area;
}
public void EnsureCWOrdering()
{
if (Hull.IsCW())
return;
Hull.Invert();
foreach (var child in Holes)
child.Invert();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24eb0a7c0c44eff48b61ea1d3120dd18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81309357b39e41942b7819480b292f4a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More