update local packages
This commit is contained in:
Родитель
39c5a892c5
Коммит
056c3fc5ff
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cee0b306a665f174998de09b82121deb
|
||||
folderAsset: yes
|
||||
timeCreated: 1492708210
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dbf0f4f237a0b5e7cbd0b84acd1f5fa3372df2d05308997444d82413f08851fd
|
||||
size 7147
|
|
@ -0,0 +1,103 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8ef34d7d3b3b45b4d923405b38d36f35
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,70 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace LightingTools.LightProbesVolumes
|
||||
{
|
||||
[CustomEditor(typeof(LightProbesVolumeSettings))]
|
||||
public class LightProbesVolumeEditor : Editor
|
||||
{
|
||||
SerializedProperty horizontalSpacing;
|
||||
SerializedProperty verticalSpacing;
|
||||
SerializedProperty offsetFromFloor;
|
||||
SerializedProperty numberOfLayers;
|
||||
SerializedProperty fillVolume;
|
||||
SerializedProperty followFloor;
|
||||
SerializedProperty discardInsideGeometry;
|
||||
SerializedProperty drawDebug;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
horizontalSpacing = serializedObject.FindProperty("horizontalSpacing");
|
||||
verticalSpacing = serializedObject.FindProperty("verticalSpacing");
|
||||
offsetFromFloor = serializedObject.FindProperty("offsetFromFloor");
|
||||
numberOfLayers = serializedObject.FindProperty("numberOfLayers");
|
||||
fillVolume = serializedObject.FindProperty("fillVolume");
|
||||
followFloor = serializedObject.FindProperty("followFloor");
|
||||
discardInsideGeometry = serializedObject.FindProperty("discardInsideGeometry");
|
||||
drawDebug = serializedObject.FindProperty("drawDebug");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
var volume = (LightProbesVolumeSettings)target;
|
||||
|
||||
serializedObject.Update();
|
||||
EditorGUILayout.DelayedFloatField(horizontalSpacing);
|
||||
EditorGUILayout.DelayedFloatField(verticalSpacing);
|
||||
EditorGUILayout.PropertyField(offsetFromFloor);
|
||||
EditorGUILayout.PropertyField(fillVolume);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
EditorGUI.BeginDisabledGroup(fillVolume.boolValue);
|
||||
EditorGUILayout.PropertyField(numberOfLayers);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EditorGUILayout.PropertyField(followFloor);
|
||||
EditorGUILayout.PropertyField(discardInsideGeometry);
|
||||
EditorGUILayout.PropertyField(drawDebug);
|
||||
|
||||
if (GUILayout.Button("Create Light Probes in Selected Volume"))
|
||||
{
|
||||
volume.Populate();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Light/Lightprobes Volume", false, 10)]
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand)
|
||||
{
|
||||
// Create a custom game object
|
||||
GameObject volume = new GameObject("LightprobeVolume");
|
||||
// Ensure it gets reparented if this was a context click (otherwise does nothing)
|
||||
GameObjectUtility.SetParentAndAlign(volume, menuCommand.context as GameObject);
|
||||
// Register the creation in the undo system
|
||||
Undo.RegisterCreatedObjectUndo(volume, "Create " + volume.name);
|
||||
Selection.activeObject = volume;
|
||||
volume.AddComponent<LightProbesVolumeSettings>();
|
||||
volume.GetComponent<BoxCollider>().size = new Vector3(5, 2, 5);
|
||||
volume.GetComponent<BoxCollider>().isTrigger = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f046a008730fb8f40aca66fe59632d95
|
||||
timeCreated: 1490885515
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "LightingTools.LightProbesVolumes.Editor",
|
||||
"references": [
|
||||
"LightingTools.LightProbesVolumes"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 087ff6b4289583947b41b0c03d628d1f
|
||||
timeCreated: 1546438390
|
||||
licenseType: Pro
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace LightingTools.LightProbesVolumes
|
||||
{
|
||||
public class RefreshLightProbesVolumes
|
||||
{
|
||||
[MenuItem("Lighting/Refresh lightprobes volumes")]
|
||||
static void Refresh()
|
||||
{
|
||||
var volumes = GameObject.FindObjectsOfType<LightProbesVolumeSettings>();
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
volume.Populate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 69d120c0b1360954680fd20bf7e055e2
|
||||
timeCreated: 1490885515
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,40 @@
|
|||
# LightingTools.LightProbesVolumes
|
||||
Light probes volumes
|
||||
|
||||
Requires Unity 2018.1 or above.
|
||||
|
||||
# Setup instructions :
|
||||
|
||||
- In your project folder create a "LocalPackages" folder next to your "Assets" folder
|
||||
- In the LocalPackages folder extract this repo under a "LightingTools.LightProbesVolumes" folder
|
||||
- In the "Packages" folder open the "manifest.json" in a text editing software
|
||||
- in "manifest.json" under "dependencies" add the line :
|
||||
"li.lightingtools.core": "file:../LocalPackages/LightingTools.LightProbesVolumes" (you need to add a "," if this is not the last dependency)
|
||||
- open the project and profit !
|
||||
|
||||
# How to use it :
|
||||
|
||||
- In the hierarchy view click : Create / Light / Lightprobe Volume
|
||||
- Set the size of the box collider to the size of the area you want to place lightprobes in ( if you have a ceiling it is recommended to set the vertical bounds lower to the ceiling, or it will spawn probes on top of it).
|
||||
- Set the " Light probe volume settings" :
|
||||
- vertical and horizontal spacing ( one probe every X meters )
|
||||
- offset from floor is at which vertical distance from the collision you want to spawn the first layer of probes
|
||||
- number of layers is the number of probes that will be placed vertically above the hit collider
|
||||
- follow floor : when enabled the script performs raycast in order to place lightprobes above existing static geometry that has a collider. When disabled the lightprobes are just placed above the lower face of the volume.
|
||||
- fill volume enabled will fill the whole height of the volume instead of just doing X number of layer. When this is enabled the number of layers is ignored.
|
||||
- discard inside geometry will test if your probe is inside an object with collisions. This will only work if the top face of your volume is not itself inside an object with collisions. In order to check this enable "draw Debug" and fill the volume : the green cross at the top has to be located in the air and not inside a geometry.
|
||||
- Click the button !
|
||||
- When you have several volumes setup in your scene and you want to refresh them all :
|
||||
- Go to lighting / Refresh lightprobes volumes. This will place again the probes in all the volumes in the scene.
|
||||
|
||||
# Improvements I would like to do :
|
||||
|
||||
- Replace the raycast to colliders by raycast to meshrenderers
|
||||
|
||||
# Troubleshoot :
|
||||
|
||||
- if the script doesn't place any lightprobe, make sure your geometric is marked as static, and that it has a collider. Using colliders isn't ideal but I haven't found a good solution that would work without them.
|
||||
|
||||
# Contributions :
|
||||
|
||||
This was originally based on the script shared by ghostmantis games : http://ghostmantis.com/?p=332
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0e68ecda44ce2814bb8206e78e262818
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 814405171c7b05741a1b7aa8a09674eb
|
||||
folderAsset: yes
|
||||
timeCreated: 1546438065
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,194 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace LightingTools.LightProbesVolumes
|
||||
{
|
||||
public static class LightProbesPlacement
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public static void Populate (GameObject gameObject, float horizontalSpacing, float verticalSpacing, float offsetFromFloor, int numberOfLayers, bool drawDebug, bool fillVolume, bool discardInsideGeometry, bool followFloor)
|
||||
{
|
||||
BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
|
||||
if (boxCollider == null)
|
||||
{
|
||||
Debug.LogWarning("Box collider not found on " + gameObject.name);
|
||||
return;
|
||||
}
|
||||
//Make sure collider is a trigger
|
||||
boxCollider.isTrigger = true;
|
||||
|
||||
//avoid division by 0
|
||||
horizontalSpacing = Mathf.Max(horizontalSpacing, 0.01f);
|
||||
verticalSpacing = Mathf.Max(verticalSpacing, 0.01f);
|
||||
|
||||
//Check if there is already a lightprobegroup component
|
||||
// if there is destroy it
|
||||
LightProbeGroup oldLightprobes = gameObject.GetComponent<LightProbeGroup>();
|
||||
|
||||
//Calculate Start Points at the top of the collider
|
||||
Vector3[] startPositions = StartPoints(boxCollider.size, boxCollider.center, boxCollider.transform, horizontalSpacing);
|
||||
|
||||
float minY = boxCollider.bounds.min.y;
|
||||
float maxY = boxCollider.bounds.max.y;
|
||||
|
||||
float sizeY = boxCollider.size.y;
|
||||
int ycount = Mathf.FloorToInt((sizeY-offsetFromFloor) / verticalSpacing) + 1;
|
||||
|
||||
List<Vector3> VertPositions = new List<Vector3>();
|
||||
|
||||
int currentTrace = 0;
|
||||
|
||||
//if followFloor we raycast from top to down in order to follow the static geometry (with colliders)
|
||||
if(followFloor)
|
||||
{
|
||||
foreach (Vector3 startPos in startPositions)
|
||||
{
|
||||
//RaycastHit hit;
|
||||
RaycastHit[] hits;
|
||||
Ray ray = new Ray();
|
||||
ray.origin = startPos;
|
||||
ray.direction = -Vector3.up;
|
||||
hits = Physics.RaycastAll(ray, sizeY + 1, -1, QueryTriggerInteraction.Ignore);
|
||||
|
||||
//Validate hits
|
||||
foreach (var hit in hits)
|
||||
{
|
||||
if (!hit.collider.gameObject.isStatic)
|
||||
break;
|
||||
if (hit.point.y + offsetFromFloor < maxY && hit.point.y + offsetFromFloor > minY)
|
||||
VertPositions.Add(hit.point + new Vector3(0, offsetFromFloor, 0));
|
||||
|
||||
int maxLayer = fillVolume ? ycount : numberOfLayers;
|
||||
|
||||
for (int i = 1; i < maxLayer; i++)
|
||||
{
|
||||
if (hit.point.y + offsetFromFloor + i * verticalSpacing < maxY && hit.point.y + offsetFromFloor + verticalSpacing > minY)
|
||||
VertPositions.Add(hit.point + new Vector3(0, offsetFromFloor + i * verticalSpacing, 0));
|
||||
}
|
||||
}
|
||||
EditorUtility.DisplayProgressBar("Tracing floor collisions", currentTrace.ToString() + "/" + startPositions.Length.ToString(), (float)currentTrace / (float)startPositions.Length);
|
||||
currentTrace++;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int maxLayer = fillVolume ? ycount : numberOfLayers;
|
||||
|
||||
for (int i = 0; i< maxLayer; i++)
|
||||
{
|
||||
foreach(Vector3 position in startPositions)
|
||||
{
|
||||
VertPositions.Add(position + Vector3.up * verticalSpacing * i - Vector3.up*sizeY + Vector3.up * offsetFromFloor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(drawDebug)
|
||||
{
|
||||
foreach(Vector3 position in VertPositions)
|
||||
{
|
||||
Debug.DrawLine(position, position + Vector3.up * 0.5f,Color.red,3);
|
||||
}
|
||||
}
|
||||
|
||||
List<Vector3> validVertPositions = new List<Vector3>();
|
||||
|
||||
//Inside Geometry test : take an arbitrary position in space and trace from that position to the probe position and back from the probe position to the arbitrary position. If the number of hits is different for both raycasts the probe is considered to be inside an object.
|
||||
//When using Draw Debug the arbitrary position is the Green cross in the air.
|
||||
if (discardInsideGeometry)
|
||||
{
|
||||
int j = 0;
|
||||
Vector3 insideTestPosition = gameObject.transform.position + gameObject.GetComponent<BoxCollider>().center + new Vector3(0, maxY / 2, 0);
|
||||
if (drawDebug)
|
||||
{
|
||||
Debug.DrawLine(insideTestPosition + Vector3.up, insideTestPosition - Vector3.up, Color.green, 5);
|
||||
Debug.DrawLine(insideTestPosition + Vector3.right, insideTestPosition - Vector3.right, Color.green, 5);
|
||||
Debug.DrawLine(insideTestPosition + Vector3.forward, insideTestPosition - Vector3.forward, Color.green, 5);
|
||||
}
|
||||
foreach (Vector3 positionCandidate in VertPositions)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Checking probes inside geometry", j.ToString() + "/" + VertPositions.Count, (float)j / (float)VertPositions.Count);
|
||||
|
||||
Ray forwardRay = new Ray(insideTestPosition, Vector3.Normalize(positionCandidate - insideTestPosition));
|
||||
Ray backwardRay = new Ray(positionCandidate, Vector3.Normalize(insideTestPosition - positionCandidate));
|
||||
RaycastHit[] hitsForward;
|
||||
RaycastHit[] hitsBackward;
|
||||
hitsForward = Physics.RaycastAll(forwardRay, Vector3.Distance(positionCandidate, insideTestPosition), -1, QueryTriggerInteraction.Ignore);
|
||||
hitsBackward = Physics.RaycastAll(backwardRay, Vector3.Distance(positionCandidate, insideTestPosition), -1, QueryTriggerInteraction.Ignore);
|
||||
if (hitsForward.Length == hitsBackward.Length) validVertPositions.Add(positionCandidate);
|
||||
else if (drawDebug)
|
||||
Debug.DrawRay(backwardRay.origin, backwardRay.direction * Vector3.Distance(positionCandidate, insideTestPosition), Color.cyan, 5);
|
||||
j++;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
else
|
||||
validVertPositions = VertPositions;
|
||||
|
||||
|
||||
// Check if we have any hits
|
||||
if (validVertPositions.Count < 1)
|
||||
{
|
||||
Debug.Log("no valid hit for " + gameObject.name);
|
||||
return;
|
||||
}
|
||||
|
||||
LightProbeGroup LPGroup = oldLightprobes != null ? oldLightprobes : gameObject.AddComponent<LightProbeGroup>();
|
||||
|
||||
// Feed lightprobe positions
|
||||
Vector3[] ProbePos = new Vector3[validVertPositions.Count];
|
||||
for (int i = 0; i < validVertPositions.Count; i++)
|
||||
{
|
||||
ProbePos[i] = gameObject.transform.InverseTransformPoint(validVertPositions[i]);
|
||||
}
|
||||
LPGroup.probePositions = ProbePos;
|
||||
|
||||
//Finish
|
||||
Debug.Log("Finished placing " + ProbePos.Length + " probes for " + gameObject.name);
|
||||
}
|
||||
|
||||
static Vector3[] StartPoints(Vector3 size, Vector3 offset, Transform transform, float horizontalSpacing)
|
||||
{
|
||||
// Calculate count and start offset
|
||||
int xCount = Mathf.FloorToInt(size.x / horizontalSpacing) + 1;
|
||||
int zCount = Mathf.FloorToInt(size.z / horizontalSpacing) + 1;
|
||||
float startxoffset = (size.x - (xCount-1) * horizontalSpacing)/2;
|
||||
float startzoffset = (size.z - (zCount-1) * horizontalSpacing)/2;
|
||||
|
||||
//if lightprobe count fits exactly in bounds, I know the probes at the maximum bounds will be rejected, so add offset
|
||||
if (startxoffset == 0)
|
||||
startxoffset = horizontalSpacing / 2;
|
||||
if (startzoffset == 0)
|
||||
startzoffset = horizontalSpacing / 2;
|
||||
|
||||
Vector3[] vertPositions = new Vector3[ xCount * zCount ];
|
||||
|
||||
int vertexnumber = 0;
|
||||
|
||||
for (int i = 0; i < xCount; i++)
|
||||
{
|
||||
for (int j = 0; j < zCount; j++ )
|
||||
{
|
||||
Vector3 position = new Vector3
|
||||
{
|
||||
y = size.y / 2,
|
||||
x = startxoffset + (i * horizontalSpacing) - (size.x / 2),
|
||||
z = startzoffset + (j * horizontalSpacing) - (size.z / 2)
|
||||
};
|
||||
|
||||
vertPositions[vertexnumber] = transform.TransformPoint(position + offset);
|
||||
|
||||
vertexnumber++;
|
||||
}
|
||||
}
|
||||
|
||||
return vertPositions;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3bfe7209b5cff4f49b13217ed25f16dd
|
||||
timeCreated: 1504691534
|
||||
guid: 82da266145fa8e8449852cf58020c13d
|
||||
timeCreated: 1546423551
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
|
@ -0,0 +1,31 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace LightingTools.LightProbesVolumes
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(BoxCollider))]
|
||||
public class LightProbesVolumeSettings : MonoBehaviour
|
||||
{
|
||||
public float horizontalSpacing = 2.0f;
|
||||
public float verticalSpacing = 2.0f;
|
||||
public float offsetFromFloor = 0.5f;
|
||||
public int numberOfLayers = 2;
|
||||
public bool fillVolume = false;
|
||||
public bool followFloor = true;
|
||||
public bool discardInsideGeometry;
|
||||
public bool drawDebug = false;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var boxCollider = GetComponent<BoxCollider>();
|
||||
boxCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void Populate()
|
||||
{
|
||||
LightProbesPlacement.Populate(gameObject,horizontalSpacing,verticalSpacing,offsetFromFloor,numberOfLayers,drawDebug,fillVolume,discardInsideGeometry, followFloor);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fc7eebf330e67854398f83add3b50749
|
||||
timeCreated: 1490885515
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 8ef34d7d3b3b45b4d923405b38d36f35, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "LightingTools.LightProbesVolumes",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 636c3e62138bf8548a29334166578537
|
||||
timeCreated: 1546438430
|
||||
licenseType: Pro
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "li.lightingtools.lightprobesvolumes",
|
||||
"displayName": "Light Probes Volumes",
|
||||
"version": "0.0.1",
|
||||
"unity": "2018.3",
|
||||
"description": "Lighting tools test."
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1d6b995f4d143ea40a626591e25d164e
|
||||
timeCreated: 1546438320
|
||||
licenseType: Pro
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
# Changelog
|
||||
All notable changes to this package will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.1.0] - 2017-MM-DD
|
||||
|
||||
### This is the first release of *Unity Package \<Your package name\>*.
|
||||
|
||||
*Short description of this release*
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 73d00c311b68c0b43b290792f9229142
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
# Contributing
|
||||
|
||||
## If you are interested in contributing, here are some ground rules:
|
||||
* ... Define guidelines & rules for what contributors need to know to successfully make Pull requests against your repo ...
|
||||
|
||||
## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/licenses/Unity_Contribution_Agreement)
|
||||
By making a pull request, you are confirming agreement to the terms and conditions of the UCA, including that your Contributions are your original creation and that you have complete right and authority to make your Contributions.
|
||||
|
||||
## Once you have a change ready following these ground rules. Simply make a pull request
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 12e422220e727d14aba019c24b31c69c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b1ebd9449aa245a42b8cf1842f144df3
|
||||
folderAsset: yes
|
||||
timeCreated: 1479375522
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,126 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Collections;
|
||||
using Unity.EditorCoroutines.Editor;
|
||||
|
||||
[CustomEditor(typeof(LevelLightmapData))]
|
||||
public class LevelLightmapDataEditor : Editor
|
||||
{
|
||||
public SerializedProperty lightingScenariosScenes;
|
||||
public SerializedProperty lightingScenesNames;
|
||||
public SerializedProperty allowLoadingLightingScenes;
|
||||
|
||||
GUIContent allowLoading = new GUIContent("Allow loading Lighting Scenes", "Allow the Level Lightmap Data script to load a lighting scene additively at runtime if the lighting scenario contains realtime lights.");
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
lightingScenariosScenes = serializedObject.FindProperty("lightingScenariosScenes");
|
||||
lightingScenesNames = serializedObject.FindProperty("lightingScenesNames");
|
||||
allowLoadingLightingScenes = serializedObject.FindProperty("allowLoadingLightingScenes");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
LevelLightmapData lightmapData = (LevelLightmapData)target;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(lightingScenariosScenes, new GUIContent("Lighting Scenarios Scenes"), includeChildren:true);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
lightingScenesNames.arraySize = lightingScenariosScenes.arraySize;
|
||||
|
||||
for (int i=0; i<lightingScenariosScenes.arraySize; i++)
|
||||
{
|
||||
lightingScenesNames.GetArrayElementAtIndex(i).stringValue = lightingScenariosScenes.GetArrayElementAtIndex(i).objectReferenceValue == null ? "" : lightingScenariosScenes.GetArrayElementAtIndex(i).objectReferenceValue.name;
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
EditorGUILayout.PropertyField(allowLoadingLightingScenes, allowLoading);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
for (int i = 0; i < lightmapData.lightingScenariosScenes.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if ( lightmapData.lightingScenariosScenes[i] != null )
|
||||
{
|
||||
EditorGUILayout.LabelField(lightmapData.lightingScenariosScenes[i].name.ToString(), EditorStyles.boldLabel);
|
||||
if (GUILayout.Button("Build "))
|
||||
{
|
||||
if(UnityEditor.Lightmapping.giWorkflowMode != UnityEditor.Lightmapping.GIWorkflowMode.OnDemand)
|
||||
{
|
||||
Debug.LogError("ExtractLightmapData requires that you have baked you lightmaps and Auto mode is disabled.");
|
||||
}
|
||||
else
|
||||
BuildLightingScenario(i, lightmapData);
|
||||
}
|
||||
if (GUILayout.Button("Store "))
|
||||
{
|
||||
lightmapData.StoreLightmapInfos(i);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
public void BuildLightingScenario(int ScenarioID, LevelLightmapData levelLightmapData)
|
||||
{
|
||||
//Remove reference to LightingDataAsset so that Unity doesn't delete the previous bake
|
||||
Lightmapping.lightingDataAsset = null;
|
||||
|
||||
string currentBuildScenename = lightingScenariosScenes.GetArrayElementAtIndex(ScenarioID).objectReferenceValue.name;
|
||||
|
||||
Debug.Log("Loading " + currentBuildScenename);
|
||||
|
||||
string lightingSceneGUID = AssetDatabase.FindAssets(currentBuildScenename)[0];
|
||||
string lightingScenePath = AssetDatabase.GUIDToAssetPath(lightingSceneGUID);
|
||||
if (!lightingScenePath.EndsWith(".unity"))
|
||||
lightingScenePath = lightingScenePath + ".unity";
|
||||
|
||||
EditorSceneManager.OpenScene(lightingScenePath, OpenSceneMode.Additive);
|
||||
|
||||
Scene lightingScene = SceneManager.GetSceneByName(currentBuildScenename);
|
||||
EditorSceneManager.SetActiveScene(lightingScene);
|
||||
|
||||
SearchLightsNeededRealtime(levelLightmapData);
|
||||
|
||||
Debug.Log("Start baking");
|
||||
EditorCoroutineUtility.StartCoroutine(BuildLightingAsync(lightingScene), this);
|
||||
}
|
||||
|
||||
private IEnumerator BuildLightingAsync(Scene lightingScene)
|
||||
{
|
||||
var newLightmapMode = new LightmapsMode();
|
||||
newLightmapMode = LightmapSettings.lightmapsMode;
|
||||
Lightmapping.BakeAsync();
|
||||
while (Lightmapping.isRunning) { yield return null; }
|
||||
//Lightmapping.lightingDataAsset = null;
|
||||
EditorSceneManager.SaveScene(lightingScene);
|
||||
EditorSceneManager.CloseScene(lightingScene, true);
|
||||
LightmapSettings.lightmapsMode = newLightmapMode;
|
||||
}
|
||||
|
||||
public void SearchLightsNeededRealtime(LevelLightmapData levelLightmapData)
|
||||
{
|
||||
bool latestBuildHasRealtimeLights = false;
|
||||
|
||||
var lights = FindObjectsOfType<Light>();
|
||||
var reflectionProbes = FindObjectsOfType<ReflectionProbe>();
|
||||
|
||||
foreach (Light light in lights)
|
||||
{
|
||||
if (light.lightmapBakeType == LightmapBakeType.Mixed || light.lightmapBakeType == LightmapBakeType.Realtime)
|
||||
latestBuildHasRealtimeLights = true;
|
||||
}
|
||||
if (reflectionProbes.Length > 0)
|
||||
latestBuildHasRealtimeLights = true;
|
||||
|
||||
levelLightmapData.latestBuildHasReltimeLights = latestBuildHasRealtimeLights;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0f3a223fa901e8b4e96df48bf5dd78ad
|
||||
timeCreated: 1479375604
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "LightingTools.LightmapSwitcher.Editor",
|
||||
"references": [
|
||||
"LightingTools.LightmapSwitcher",
|
||||
"Unity.EditorCoroutines.Editor"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 73cd762c1d4bdaf4891d2710c1f58ae2
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1656bfbc25c2cc43d6a2cc683fd240120c03b5f06803dbe91c28c8e48e02b67b
|
||||
size 3715
|
|
@ -0,0 +1,103 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4021dabdeeef1824b871b34d975991b8
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 10
|
||||
mipmaps:
|
||||
mipMapMode: 1
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 0
|
||||
aniso: 1
|
||||
mipBias: -100
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 0
|
||||
alphaUsage: 0
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,5 @@
|
|||
[MyPackageName] copyright © [YEAR] Unity Technologies ApS
|
||||
|
||||
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
|
||||
|
||||
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e85d55989cd833f4ea31cdc5fb0189d3
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,514 @@
|
|||
# UPM Package Starter Kit
|
||||
|
||||
The purpose of this package starter kit is to provide the data structure and development guidelines for new packages meant for the **Unity Package Manager (UPM)**.
|
||||
|
||||
This is the first of many steps towards an automated package publishing experience within Unity. This package starter kit is merely a fraction of the creation, edition, validation, and publishing tools that we will end up with.
|
||||
|
||||
We hope you enjoy your experience. You can use **#devs-packman** on Slack to provide feedback or ask questions regarding your package development efforts.
|
||||
|
||||
## Are you ready to become a package?
|
||||
The Package Manager is a work-in-progress for Unity and, in that sense, there are a few criteria that must be met for your package to be considered on the package list at this time:
|
||||
- **Your code accesses public Unity C# APIs only.** If you have a native code component, it will need to ship with an official editor release. Internal API access might eventually be possible for Unity made packages, but not at this time.
|
||||
- **Your code doesn't require security, obfuscation, or conditional access control.** Anyone should be able to download your package and access the source code.
|
||||
|
||||
|
||||
## Package structure
|
||||
|
||||
```none
|
||||
<root>
|
||||
├── package.json
|
||||
├── README.md
|
||||
├── CHANGELOG.md
|
||||
├── LICENSE.md
|
||||
├── Third Party Notices.md
|
||||
├── QAReport.md
|
||||
├── Editor
|
||||
│ ├── Unity.[YourPackageName].Editor.asmdef
|
||||
│ └── EditorExample.cs
|
||||
├── Runtime
|
||||
│ ├── Unity.[YourPackageName].asmdef
|
||||
│ └── RuntimeExample.cs
|
||||
├── Tests
|
||||
│ ├── .tests.json
|
||||
│ ├── Editor
|
||||
│ │ ├── Unity.[YourPackageName].Editor.Tests.asmdef
|
||||
│ │ └── EditorExampleTest.cs
|
||||
│ └── Runtime
|
||||
│ ├── Unity.[YourPackageName].Tests.asmdef
|
||||
│ └── RuntimeExampleTest.cs
|
||||
├── Samples
|
||||
│ └── Example
|
||||
│ ├── .sample.json
|
||||
│ └── SampleExample.cs
|
||||
└── Documentation~
|
||||
├── your-package-name.md
|
||||
└── Images
|
||||
```
|
||||
|
||||
## Develop your package
|
||||
Package development works best within the Unity Editor. Here's how to set that up:
|
||||
|
||||
1. Clone the Package Starter Kit repository locally.
|
||||
|
||||
- In a console (or terminal) application, choose a place to clone the repository and enter the following:
|
||||
```
|
||||
git clone git@github.cds.internal.unity3d.com:unity/com.unity.package-starter-kit.git
|
||||
```
|
||||
|
||||
1. Create a new repository for your package and clone to your desktop.
|
||||
|
||||
- On Github.cds create a new repository with the name of your package (Example: `"com.unity.terrain-builder"`).
|
||||
|
||||
- In a console (or terminal) application, choose a place to clone the repository and perform the following:
|
||||
```
|
||||
git clone git@github.cds.internal.unity3d.com:unity/com.unity.[your-package-name]
|
||||
```
|
||||
|
||||
1. Copy the contents of the Package Starter Kit folder to your new package. Be careful not to copy the Package Starter Kit *.git* folder over.
|
||||
|
||||
1. **Fill in your package information.**
|
||||
|
||||
Follow the instructions for [filling out your package manifest](#fill-out-your-package-manifest) (*package.json*).
|
||||
|
||||
Then update the `"createSeparatePackage"` field in the *Tests/.tests.json* file to set up testing for Continuous Integration (CI):
|
||||
|
||||
* Set it to false if you want the tests to remain part of the published package. This is the default value.
|
||||
|
||||
* Set it to true if you want the CI to create a separate package for these tests, and add the metadata at publish time to link the packages together. This allows you to have a large number of tests, or assets, etc. that you don't want to include in your main package, while making it easy to test your package with those tests & fixtures.
|
||||
|
||||
1. Start **Unity**, create a local empty project and import your package into the project.
|
||||
|
||||
1. In a console (or terminal) application, push the package starter kit files you copied in your new package repository to its remote.
|
||||
- Add them to your repository's list to version
|
||||
```git add .```
|
||||
- Commit to your new package's remote master
|
||||
```git commit```
|
||||
- Push to your new package's remote master
|
||||
```git push```
|
||||
|
||||
1. Restart **Unity**. This forces the Package Manager to rescan your project so that it can find the new package that you just embedded. For more information on embedded packages see [Confluence](https://confluence.hq.unity3d.com/display/PAK/How+to+embed+a+package+in+your+project).
|
||||
|
||||
1. **Update the *README.md* file.**
|
||||
|
||||
It should contain all pertinent information for developers using your package, such as:
|
||||
|
||||
* Prerequistes
|
||||
* External tools or development libraries
|
||||
* Required installed Software
|
||||
* Command line examples to build, test, and run your package.
|
||||
|
||||
1. **Rename and update your documentation file(s).**
|
||||
|
||||
Use the samples included in this starter kit to create preliminary, high-level documentation. Your documentation should introduce users to the features and sample files included in your package. For more information, see [Document your package](#document-your-package).
|
||||
|
||||
1. **Rename and update assembly definition files.**
|
||||
|
||||
|
||||
Choose a name schema to ensure that the name of the assembly built from the assembly definition file (_.asmdef_) will follow the .Net [Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/index). For more information, see [Name your assembly definition files](#name-your-assembly-definition-files).
|
||||
|
||||
1. **Add samples to your package (code & assets).**
|
||||
|
||||
The Package Manager recognizes the *Samples* directory in a package but does not import samples into Unity when the package is added to a project by default. Users can import samples into their */Assets* directory by clicking the **Import in project** button from the [Details view](https://docs.unity3d.com/Manual/upm-ui-details.html) of your package in the Package Manager window.
|
||||
|
||||
If your package contains a sample:
|
||||
|
||||
* Rename the *Samples/Example* folder, and update the *.sample.json* file in it.
|
||||
|
||||
* If your package contains multiple samples, make a copy of the *Samples/Example* folder for each sample, and update each *.sample.json* file accordingly.
|
||||
|
||||
Delete the *Samples* folder altogether if your package does not need samples.
|
||||
|
||||
1. **Validate your package using the Validation Suite.**
|
||||
|
||||
Before you publish your package, you need to make sure that it passes all the necessary validation checks by using the [Package Validation Suite](https://github.cds.internal.unity3d.com/unity/com.unity.package-validation-suite) extension. This is *required*.
|
||||
|
||||
For more information, see [Validate your package](#validate-your-package).
|
||||
|
||||
1. **Follow our design guidelines**
|
||||
|
||||
Follow these design guidelines when creating your package:
|
||||
|
||||
* The [package design standards](https://confluence.hq.unity3d.com/display/UX/Packages) on Confluence.
|
||||
|
||||
* The [design checklist](https://unitytech.github.io/unityeditor-hig/topics/checklist.html) from Unity's Human Interface Guidelines.
|
||||
|
||||
* The namespace for code in the asmdef *must* match the asmdef name, except the initial `Unity`, which should be replaced with `UnityEngine` or `UnityEditor`.
|
||||
|
||||
* For **Runtime code**, only use the `Unity` namespace for code that has no dependency on anything in `UnityEngine` or `UnityEditor` and instead uses `ECS` and other `Unity`-namespace systems.
|
||||
|
||||
1. **Add tests to your package.**
|
||||
|
||||
For **Editor tests**:
|
||||
* Write all your Editor Tests in *Tests/Editor*
|
||||
* If your tests require access to internal methods, add an *AssemblyInfo.cs* file to your Editor code and use `[assembly: InternalsVisibleTo("Unity.[YourPackageName].Editor.Tests")]`.
|
||||
|
||||
For **Playmode Tests**:
|
||||
* Write all your Playmode Tests in *Tests/Runtime*.
|
||||
* If your tests require access to internal methods, add an *AssemblyInfo.cs* file to your Runtime code and use `[assembly: InternalsVisibleTo("Unity.[YourPackageName].Tests")]`.
|
||||
|
||||
1. **Setup your package CI.**
|
||||
|
||||
Make sure your package continues to work against trunk or any other branch by setting up automated testing on every commit. See the [Confluence page](https://confluence.hq.unity3d.com/display/PAK/Setting+up+your+package+CI) that explains how to set up your package CI.
|
||||
|
||||
This starter kit contains the minimum recommended workflow for package CI, which provides the barebones to: `pack`, `test` and `publish` your packages. It also contains the required configuration to `promote` your **preview** packages to production.
|
||||
|
||||
1. **Update *CHANGELOG.md*.**
|
||||
|
||||
Every new feature or bug fix should have a trace in this file. For more details on the chosen changelog format, see [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
|
||||
|
||||
### Fill out your package manifest
|
||||
|
||||
1. Update the following required fields in file *package.json* (see [Confluence](https://confluence.hq.unity3d.com/pages/viewpage.action?pageId=39065257) for more information):
|
||||
|
||||
| **Attribute name** | **Description** |
|
||||
|:---|:---|
|
||||
| `"name"` | Set the package name, following this naming convention: `"com.unity.[your-package-name]"`, without capital letters. For example, `"com.unity.2d.animation"`. |
|
||||
| `"displayName"` | Set the package's user-friendly display name. For example, `"Terrain Builder SDK"`. <br><br>__Note:__ Use a display name that will help users understand what your package is intended for. |
|
||||
| `"version"` | Set the package version in `"X.Y.Z"` format, following these [Semantic Versioning](http://semver.org/spec/v2.0.0.html) guidelines:<br>- To introduce a breaking API change, increment the major version (**X**.Y.Z).<br>- To introduce a new feature, increment the minor version (X.**Y**.Z).<br>- To introduce a bug fix, increment the patch version (X.Y.**Z**) |
|
||||
| `"unity"` | Set the Unity version your package is compatible with. For example: `"2018.1"`. |
|
||||
| `"unityRelease"` | Specify the Unity patch release your package is compatible with. For example: `"0a8"`.<br/><br/>__Note:__ This field is only required when the specific Unity version has a patch release. |
|
||||
| `"description"` | This description appears in the Package Manager window when the user selects this package from the list. For best results, use this text to summarize what the package does and how it can benefit the user.<br><br>Special formatting characters are supported, including line breaks (`\n`) and unicode characters such as bullets (`\u25AA`). For more information, see the [Writing Package docs](https://confluence.hq.unity3d.com/display/DOCS/Writing+Package+docs) page on Confluence. |
|
||||
|
||||
1. Update the following recommended fields in file **package.json**:
|
||||
|
||||
| **Attribute name** | **Description** |
|
||||
|:---|:---|
|
||||
| `"dependencies"` | List of packages this package depends on. All dependencies will also be downloaded and loaded in a project with your package. Here's an example:<br/><br/>`dependencies: {`<br/> `"com.unity.ads": "1.0.0",`<br/> `"com.unity.analytics": "2.0.0"`<br/>`}` |
|
||||
| `"keywords"` | An array of keywords related to the package. This field is currently purely informational. |
|
||||
| `"type"` | The type of your package. This is used to determine the visibility of your package in the Project Browser and the visibility of its Assets in the Object Picker. The `"tool"` and `"library"` types are used to set your package and its Assets as hidden by default. If not present or set to another value, your package and its Assets are visible by default. |
|
||||
| `"hideInEditor"` | A boolean value that overrides the package visibility set by the package type. If set to `false`, the default value, your package and its Assets are **always** visible by default; if set to `true`, your package and its Assets are **always** hidden by default. |
|
||||
|
||||
**Notes**:
|
||||
- For packages in development, neither `"type"` nor `"hideInEditor"` are used. The package is **always** visible in the Project Browser and its Assets are **always** visible in the Object Picker.
|
||||
- The user is **always** able to toggle the package visibility in the Project Browser, as well as their Assets visibility in the Object Picker.
|
||||
|
||||
### Document your package
|
||||
|
||||
You need to document your public APIs and provide task-oriented documentation for your features:
|
||||
|
||||
1. Document all of [your public APIs](#document-your-public-apis) and [your features](#document-your-features).
|
||||
2. [Test your documentation locally](#test-your-documentation-locally).
|
||||
3. [Get your documentation published](#get-your-documentation-published).
|
||||
|
||||
Your package should include the documentation source in your package, but not the generated HTML.
|
||||
|
||||
The page in the user manual that links to package documentation is [Packages documentation](http://docs.hq.unity3d.com/Documentation/Manual/PackagesList.html).
|
||||
|
||||
#### Document your public APIs
|
||||
|
||||
API documentation is generated from any [XmlDoc tagged comments](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/xml-documentation-comments) found in the *.cs* files included in the package.
|
||||
|
||||
You can use Visual Studio to autogenerate the correct tags by entering the documentation comments (`///`) in the empty line above your code. Visual Studio automatically detects which tags your code needs and inserts them. For example, if you write a method with two parameters that returns a value, Visual Studio gives you the **summary** tag (for the method description), two **param** tags, and one **returns** tag:
|
||||
|
||||
```c#
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="One"></param>
|
||||
/// <param name="Two"></param>
|
||||
/// <returns></returns>
|
||||
public bool TestingXmlDoc(int One, int Two)
|
||||
{
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
See the [Editor/EditorExample.cs](Editor/EditorExample.cs) file in this package for examples of how to document classes, methods, properties, and enums.
|
||||
|
||||
You need to document all public APIs. If you don't need an API to be accessed by clients, mark it as internal instead:
|
||||
|
||||
1. Add a custom *filter.yml* file to the package *Documentation~* folder.
|
||||
|
||||
2. Add these rules to your custom *filter.yml* file. These rules make the DocFX configuration consistent with packages that don't have a custom filter:
|
||||
|
||||
```yaml
|
||||
apiRules:
|
||||
- exclude:
|
||||
# inherited Object methods
|
||||
uidRegex: ^System\.Object\..*$
|
||||
type: Method
|
||||
- exclude:
|
||||
# mentioning types from System.* namespace
|
||||
uidRegex: ^System\..*$
|
||||
type: Type
|
||||
- exclude:
|
||||
hasAttribute:
|
||||
uid: System.ObsoleteAttribute
|
||||
type: Member
|
||||
- exclude:
|
||||
hasAttribute:
|
||||
uid: System.ObsoleteAttribute
|
||||
type: Type
|
||||
```
|
||||
|
||||
3. Specify classes to exclude by UID or by specifying regular expressions. For information about the rules for the filters, see the [DocFX guide](https://dotnet.github.io/docfx/tutorial/howto_filter_out_unwanted_apis_attributes.html).
|
||||
|
||||
#### Document your features
|
||||
|
||||
Write the Manual documentation using [GitHub-flavored Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) in *.md* files stored under the *Documentation~* folder. The *Documentation~* folder is suffixed with `~` to prevent its contents from being loaded in the Editor (recommended). Alternatively, you could use *.Documentation* for the folder name.
|
||||
|
||||
This preliminary, high-level documentation should introduce users to the features and sample files included in your package.
|
||||
|
||||
All packages that expose UI in editor or runtime features should use one of the appropriate documentation example guides under the *Documentation~* folder:
|
||||
|
||||
* [tools-package-guide.md](Documentation~/tools-package-guide.md) for a package that includes features that augment the Unity Editor or Runtime (modules, tools, and libraries)
|
||||
* [sample-package-guide.md](Documentation~/sample-package-guide.md) for a package that includes sample files
|
||||
* [test-package-guide.md](Documentation~/test-package-guide.md) for a package that provides tests
|
||||
|
||||
For instructions, see the [documentation guidelines](Documentation~/index.md).
|
||||
|
||||
#### Test your documentation locally
|
||||
|
||||
As you are developing your documentation, you can see what your documentation will look like by using the DocTools package: **com.unity.package-manager-doctools** (optional).
|
||||
|
||||
Once the DocTools package is installed, it displays a **Generate Documentation** button in the Package Manager window's Details view for any installed or embedded packages. To install the extension, see [Installing the DocTools package](https://confluence.hq.unity3d.com/display/DOCS/Package+documentation+with+Git#PackagedocumentationwithGit-InstallingDocTools) on Confluence.
|
||||
|
||||
The DocTools extension is still in preview, if you come across arguable results, please discuss them on **#docs-packman**.
|
||||
|
||||
#### Get your documentation published
|
||||
|
||||
When the documentation is complete, notify the technical writer assigned to your team or project. If you don't know which technical writer is assigned, fill out the [Package Docs Questions](https://docs.google.com/document/d/1vI0zbNX9OausEwYhg_kl9CIDNf4taExjON0fyp2cHS0) form, [log a JIRA DOC ticket](https://unity3d.atlassian.net/secure/CreateIssue!default.jspa?selectedProjectId=12200&issuetype=3) and attach the form to that ticket.
|
||||
|
||||
You can also ask for help or clarification on **#devs-documentation**.
|
||||
|
||||
> **Note:** The package will remain in **preview** mode until the final documentation is completed. Users will have access to the developer-generated documentation only in preview packages.
|
||||
|
||||
After the technical writer reviews the documentation, they will create a pull request in the package git repository. The package's development team will then need to submit a new package version with the updated docs.
|
||||
|
||||
|
||||
|
||||
### Name your assembly definition files
|
||||
|
||||
If your package contains Editor code, rename and modify [Editor/Unity.YourPackageName.Editor.asmdef](Editor/Unity.YourPackageName.Editor.asmdef). Otherwise, delete the *Editor* directory.
|
||||
|
||||
* Name **must** match your package name, suffixed by `.Editor` (for example, `Unity.[YourPackageName].Editor`).
|
||||
* Assembly **must** reference `Unity.[YourPackageName]` (if you have any runtime code).
|
||||
* Platforms **must** include `"Editor"`.
|
||||
|
||||
If your package contains code that needs to be included in Unity runtime builds, rename and modify [Runtime/Unity.YourPackageName.asmdef](Runtime/Unity.YourPackageName.asmdef). Otherwise, delete the *Runtime* directory.
|
||||
|
||||
* Name **must** match your package name (for example,`Unity.[YourPackageName]`)
|
||||
|
||||
If your package has Editor code, you **must** have Editor Tests. In that case, rename and modify [Tests/Editor/Unity.YourPackageName.Editor.Tests.asmdef](Tests/Editor/Unity.YourPackageName.Editor.Tests.asmdef).
|
||||
|
||||
* Name **must** match your package name, suffixed by `.Editor.Tests` (for example, `Unity.[YourPackageName].Editor.Tests`)
|
||||
* Assembly **must** reference `Unity.[YourPackageName].Editor` and `Unity.[YourPackageName]` (if you have any Runtime code).
|
||||
* Platforms **must** include `"Editor"`.
|
||||
* Optional Unity references **must** include `"TestAssemblies"` to allow your Editor Tests to show up in the Test Runner or run on Katana when your package is listed in the project manifest's `testables` field.
|
||||
|
||||
If your package has Runtime code, you **must** have Playmode Tests. In that case, rename and modify [Tests/Runtime/Unity.YourPackageName.Tests.asmdef](Tests/Runtime/Unity.YourPackageName.Tests.asmdef).
|
||||
|
||||
* Name **must** match your package name, suffixed by `.Tests` (for example, `Unity.[YourPackageName].Tests`)
|
||||
* Assembly **must** reference `Unity.[YourPackageName]`.
|
||||
* Optional Unity references **must** include `"TestAssemblies"` to allow your Playmode Tests to show up in the Test Runner or run on Katana when your package is listed in the project manifest's `testables` field.
|
||||
|
||||
|
||||
|
||||
### Validate your package
|
||||
|
||||
To install the extension, follow these steps:
|
||||
|
||||
1. Make sure you have `Package Manager UI v1.9.6` or above.
|
||||
|
||||
2. Make sure your project manifest points to the Candidates registry, which you can do by changing the **registry** line to:
|
||||
|
||||
```json
|
||||
"registry": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates"
|
||||
```
|
||||
|
||||
3. From the Package Manager window, install the latest version of the **Package Validation Suite** with the scope set to **All Packages**. If you don't see it in the list, make sure **Advanced** > **Show preview packages** is enabled.
|
||||
|
||||
Once the Validation Suite package is installed, a **Validate** button appears in the details pane when you select your installed package from the Package Manager window.
|
||||
|
||||
To run the tests:
|
||||
|
||||
1. Click the **Validate** button to run a series of tests. A **See Results** button appears after the test run.
|
||||
2. Click the **See Results** button for additional explanation:
|
||||
* If it succeeds, a green bar displays a **Success** message.
|
||||
* If it fails, a red bar displays a **Failed** message.
|
||||
|
||||
The validation suite is still in preview, so if you come across arguable results, please discuss them on **#devs-packman**.
|
||||
|
||||
|
||||
|
||||
## Create a Pre-Release Package
|
||||
Pre-Release Packages are a great way of getting your features in front of Unity Developers in order to get early feedback on functionality and UI designs. Pre-Release packages need to go through the publishing to production flow, as would any other package, but with diminished requirements:
|
||||
|
||||
* Expected Package structure respected
|
||||
* Package loads in Unity Editor without errors
|
||||
* License file present - With third party notices file if necessary
|
||||
* Test coverage is good - Optional but preferred
|
||||
* Public APIs documented, minimal feature docs exists- Optional but preferred
|
||||
|
||||
The only supported Pre-Release tag is **preview** which you suffix to the version number of the package in the *package.json* manifest file. For example:
|
||||
|
||||
```json
|
||||
"version" : "1.2.0-preview"
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Register your package
|
||||
|
||||
If you think you are working on a feature that is a good package candidate, please take a minute to tell Release Management about it in the **#devs-pkg-promotion** channel.
|
||||
|
||||
Working with the board of dev directors and with product management, we will schedule the entry of the candidates in the ecosystem, based on technical challenges and on our feature roadmap.
|
||||
Don’t hesitate to reach out and join us on **#devs-packman** on Slack.
|
||||
|
||||
## Share your package
|
||||
|
||||
If you want to share your project with other developers, the steps are similar to what's presented above. On the other developer's machine:
|
||||
|
||||
1. Start **Unity** and create a local empty project.
|
||||
|
||||
1. Launch the console (or terminal) application, navigate to the newly created project folder, and then clone your repository in the `Packages` directory:
|
||||
|
||||
```shell
|
||||
cd <YourProjectPath>/Packages
|
||||
git clone https://github.cdsinternal.unity3d.com/unity/[your-package-name].git com.unity.[sub-group].[your-package-name]
|
||||
```
|
||||
> __Note:__ Your directory name must be the name of your package (Example: `"com.unity.terrain-builder"`).
|
||||
|
||||
## Make sure your package meets all legal requirements
|
||||
|
||||
All packages must COMPLETE AND SUBMIT [THIS FORM](https://docs.google.com/forms/d/e/1FAIpQLSe3H6PARLPIkWVjdB_zMvuIuIVtrqNiGlEt1yshkMCmCMirvA/viewform) to receive approval. It is a simple, streamlined form that tells legal if there are any potential issues that need to be addressed prior to publication.
|
||||
|
||||
If your package has third-party elements and its licenses are approved, then all the licenses must be added to the **Third Party Notices.md** file. If you have more than one license, duplicate the `Component Name/License Type/Provide License Details` section for each additional license.
|
||||
|
||||
> **Note:** A URL can work as long as it actually points to the reproduced license and the copyright information _(if applicable)_.
|
||||
|
||||
If your package does not have third party elements, you can remove the *Third Party Notices.md* file from your package.
|
||||
|
||||
## Preparing your package for the Candidates registry
|
||||
|
||||
Before publishing your package to production, you must send your package on the Package Manager's internal **candidates** repository. The candidates repository is monitored by QA and release management, and is where package validation will take place before it is accepted in production.
|
||||
|
||||
1. Publishing your changes to the Package Manager's **Candidates** registry happens from Github.cds. To do so, set up your project's Continuous integration (CI), which will be triggered by "Tags" on your branches.
|
||||
|
||||
For information see [the Confluence page](https://confluence.hq.unity3d.com/display/PAK/Setting+up+your+package+CI) that describes how to set up CI for your package.
|
||||
|
||||
1. Test your package locally. Once your package is published on the **Candidates** registry, you can test your package in the editor by creating a new project, and editing the project's *manifest.json* file to point to your candidate package:
|
||||
|
||||
```json
|
||||
dependencies: {
|
||||
"com.unity.[sub-group].[your-package-name]": "0.1.0"
|
||||
},
|
||||
"registry": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates"
|
||||
```
|
||||
|
||||
## Get your package published to Production
|
||||
|
||||
Packages are promoted to the **Production** registry from the **Candidates** registry, as described above. Your package must meet [certain criteria](https://docs.google.com/document/d/1TSnlSKJ6_h0C-CYO2LvV0fyGxJvH6OxC2-heyN8o-Gw/edit#heading=h.xxfb5jk2jda2) before you can submit a request to promote a package to production.
|
||||
|
||||
Once you feel comfortable that your package meets the list of Release Management Criteria, ask to be promoted from candidates to production by going to **#devs-pkg-promotion**.
|
||||
|
||||
Also, once you have a package in production for a few versions and it was used by real users, you can ask to be whitelisted to appear in the Package Manager window. Your package will not be visible in the Package Manager window until it is published AND whitelisted by Release Management.
|
||||
|
||||
**_Release management will validate your package content, and check that the editor/playmode tests pass before promoting the package to production._**
|
||||
|
||||
### Verified status and bundled packages
|
||||
If your package is meant to ship with a release of the editor (**Verified Packages** and **Bundled Packages**), follow these steps:
|
||||
1. To be marked as verified, in trunk, modify the editor manifest (*[root]\External\PackageManager\Editor\manifest.json*) to include your package in the **verified** list.
|
||||
|
||||
1. If your package is not verified, but only bundled with the editor, submit one or more Test Project(s) in Ono, so that your new package can be tested in all ABVs moving forward.
|
||||
|
||||
The following steps will create a test project that will run in ABVs, load your package into the project, and run all the tests found in your package. The better your test coverage, the more confident you'll be that your package works with trunk.
|
||||
|
||||
* Create a branch in Ono, based on the latest branch this package must be compatible with (trunk, or release branch).
|
||||
* If your package contains **Editor Tests**:
|
||||
* In *[root]\Tests\Editor.Tests*, create a new EditorTest Project (for new packages use **YourPackageName**) or use an existing project (for new versions of existing package).
|
||||
|
||||
To get a bare package for an EditorTest Project, click [here](https://oc.unity3d.com/index.php/s/Cldvuy6NpxqYy8y).
|
||||
|
||||
* Modify the project’s *manifest.json* file to include the production version of the package (`name@version`).
|
||||
|
||||
* Your project's *manifest.json* file should contain the following line:
|
||||
|
||||
```json
|
||||
"testables" : [ "com.unity.[sub-group].[your-package-name]" ]
|
||||
```
|
||||
* If your package contains **PlaymodeTests**:
|
||||
* In *[root]\Tests\PlaymodeTests*, create a new PlaymodeTest Project (for new packages use **YourPackageName**) or use an existing project (for new versions of existing package).
|
||||
|
||||
* Modify the project’s *manifest.json* file to include the candidate version of the package (`name@version`).
|
||||
|
||||
* Your project's manifest.json file should contain the following line:
|
||||
|
||||
```json
|
||||
"testables" : [ "com.unity.[sub-group].[your-package-name]" ]
|
||||
```
|
||||
|
||||
* Commit your branch changes to Ono, and run all Windows & Mac Editor/PlayMode tests (not full ABV) in Katana.
|
||||
|
||||
1. Once the tests are green on Katana, create your PR, add both **Latest Release Manager** and **Trunk Merge Queue** as reviewers.
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
**What’s the difference between a preview package and a verified package?**
|
||||
|
||||
A preview package is a great way to develop and get feedback on new features and functionality. Preview package can be created against any version of Unity 2018.1+, and can be made discoverable through the Package Manager UI by issuing a request in #devs-packman. Quality and release schedule is up to the package owner, although minimum bars are set in place to ensure the package contains the right licenses, documentation, and a comprehensive set of tests.
|
||||
|
||||
Once a preview package has been in the field for 2-3 release cycles of the editor, that package can be considered for Verification. Verified packages are tested with a specific version of the editor, and offer our users a compatibility guarantee. Verified packages are the only packages that can be included in the set of templates we ship with the editor (Verified Templates). Code for these packages must follow core development guidelines, including code cutoff dates, and are tested in katana for continued compatibility.
|
||||
|
||||
**What’s the difference between a core package and a default package?**
|
||||
|
||||
A core package is a package that has its code included with the Editor’s core code. This is interesting for packages that plan to change enormously in parallel to editor APIs. By moving package code to the editor’s repo, both core API\functionality changes can be made along with required packages changes in the same PR.
|
||||
https://docs.google.com/document/d/1CMoanjR3KAdew-6n39JdCFmHkTp1oshs3vkpejapf4Q/edit
|
||||
|
||||
A default package is a verified package that gets installed with every new project users create, regardless of the template they use. We should limit the number of default packages we support, as each default package adds to the project loading time. The list of default packages can be found in the editor manifest (https://ono.unity3d.com/unity/unity/files/de904b9ed9b44580ecd1e883f510daaa08182cc5/External/PackageManager/Editor/manifest.json).
|
||||
|
||||
**What are the requirement for me to publish a preview package?**
|
||||
|
||||
https://docs.google.com/document/d/1epGkAJRayJLN89_weA_-G5LFT_1uFifFZqBzAgvp_Zs/
|
||||
|
||||
|
||||
**What are the requirements for me to get my package verified for a version of unity?**
|
||||
|
||||
https://docs.google.com/document/d/1oWC9XArVfkGMnqN9azR4hW4Pcd7-kQQw8Oy7ckP43JE/
|
||||
|
||||
**How is my verified package tested in Katana?**
|
||||
|
||||
https://docs.google.com/document/d/1jwTh71ZGtB2vF0SsHEwivt2FunaJWMGDdQJTpYRj3EE/edit
|
||||
|
||||
**How is my template tested in Katana?**
|
||||
|
||||
https://docs.google.com/document/d/1jwTh71ZGtB2vF0SsHEwivt2FunaJWMGDdQJTpYRj3EE/edit
|
||||
|
||||
**How do I add samples to my package?**
|
||||
|
||||
https://docs.google.com/document/d/1rmxGh6Z9gtbQlGUKCsVBaR0RyHvzq_gsWoYs6sttzYA/edit#heading=h.fg1e3sz56048
|
||||
|
||||
**How do I setup CI or publishing options for my package?**
|
||||
https://confluence.hq.unity3d.com/display/PAK/Setting+up+your+package+CI
|
||||
|
||||
**How can I add tests to my package?**
|
||||
|
||||
There’s a “Tests” directory in the package starter kit. If you add editor and playmode tests in that directory, they will make up the list of tests for your package.
|
||||
|
||||
**The tests in my package bloat my package too much, what are my options?**
|
||||
|
||||
https://docs.google.com/document/d/19kKIGFetde5ES-gKXQp_P7bxQ9UgBnBUu58-y7c1rTA/edit
|
||||
|
||||
**Can I automate my package publishing yet?**
|
||||
|
||||
Not just yet, but we’re working on it. The first automated publishing we will enable is the push to production for preview packages. Basically, when your package passes validation (loads in the editor without error, the tests in the package pass, validation suite run success), the package will be pushed to production automatically. Other publishing flows will soon be available as well, here’s the full list of internal package publishing flows Unity will support. https://docs.google.com/document/d/1zdMzAtfi-vgM8NMPmwL40yinBeL3YImwTO5gSfGNCgs/edit
|
||||
|
||||
**How do I get a template package started?**
|
||||
|
||||
Start with the Project Template Starter Kit (you can request access in **#devs-packman**).
|
||||
https://github.cds.internal.unity3d.com/unity/com.unity.template-starter-kit
|
||||
|
||||
**How do I get my package included in a template?**
|
||||
|
||||
First and foremost, your package needs to be on the verified list of packages. Only verified packages can get added to templates we ship with the editor. Then reach out to the templates community in **#devs-template** to open discussions on adding your package to one or more of our existing templates.
|
||||
|
||||
**How can I test my package locally, as a user would?**
|
||||
|
||||
https://confluence.hq.unity3d.com/display/PAK/How+to+add+a+git+package+to+your+project
|
||||
|
||||
**What tests are included by the validation suite?**
|
||||
|
||||
https://docs.google.com/spreadsheets/d/1CdO7D0WSirbZhjnVsdJxJwOPK4UdUDxSRBIqwyjm70w/edit#gid=0
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fbde06cfbf0952043b92f05755959f4c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4187919837946ea479e9dd7d67ba328c
|
||||
guid: 025606af85fbe2846b479c744861150a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
|
@ -0,0 +1,383 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Collections;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class LevelLightmapData : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class SphericalHarmonics
|
||||
{
|
||||
public float[] coefficients = new float[27];
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class RendererInfo
|
||||
{
|
||||
public Renderer renderer;
|
||||
public int lightmapIndex;
|
||||
public Vector4 lightmapOffsetScale;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class LightingScenarioData {
|
||||
public RendererInfo[] rendererInfos;
|
||||
public Texture2D[] lightmaps;
|
||||
public Texture2D[] lightmapsDir;
|
||||
public Texture2D[] shadowMasks;
|
||||
public LightmapsMode lightmapsMode;
|
||||
public SphericalHarmonics[] lightProbes;
|
||||
public bool hasRealtimeLights;
|
||||
}
|
||||
|
||||
public bool latestBuildHasReltimeLights;
|
||||
public bool allowLoadingLightingScenes = true;
|
||||
|
||||
[SerializeField]
|
||||
List<LightingScenarioData> lightingScenariosData;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
public List<SceneAsset> lightingScenariosScenes;
|
||||
#endif
|
||||
[SerializeField]
|
||||
public String[] lightingScenesNames = new string[1];
|
||||
public int currentLightingScenario = -1;
|
||||
public int previousLightingScenario = -1;
|
||||
|
||||
private Coroutine m_SwitchSceneCoroutine;
|
||||
|
||||
[SerializeField]
|
||||
public int lightingScenariosCount;
|
||||
|
||||
//TODO : enable logs only when verbose enabled
|
||||
public bool verbose = false;
|
||||
|
||||
private List<SphericalHarmonicsL2[]> lightProbesRuntime = new List<SphericalHarmonicsL2[]>();
|
||||
|
||||
public void LoadLightingScenario(int index)
|
||||
{
|
||||
if(index != currentLightingScenario)
|
||||
{
|
||||
previousLightingScenario = currentLightingScenario == -1 ? index : currentLightingScenario;
|
||||
|
||||
currentLightingScenario = index;
|
||||
|
||||
LightmapSettings.lightmapsMode = lightingScenariosData[index].lightmapsMode;
|
||||
|
||||
if(allowLoadingLightingScenes)
|
||||
m_SwitchSceneCoroutine = StartCoroutine(SwitchSceneCoroutine(lightingScenesNames[previousLightingScenario], lightingScenesNames[currentLightingScenario]));
|
||||
|
||||
var newLightmaps = LoadLightmaps(index);
|
||||
|
||||
ApplyRendererInfo(lightingScenariosData[index].rendererInfos);
|
||||
|
||||
LightmapSettings.lightmaps = newLightmaps;
|
||||
|
||||
LoadLightProbes(currentLightingScenario);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
PrepareLightProbeArrays();
|
||||
}
|
||||
|
||||
private void PrepareLightProbeArrays()
|
||||
{
|
||||
for (int x = 0; x < lightingScenariosCount; x++)
|
||||
{
|
||||
lightProbesRuntime.Add(DeserializeLightProbes(x));
|
||||
}
|
||||
}
|
||||
|
||||
private SphericalHarmonicsL2[] DeserializeLightProbes(int index)
|
||||
{
|
||||
var sphericalHarmonicsArray = new SphericalHarmonicsL2[lightingScenariosData[index].lightProbes.Length];
|
||||
|
||||
for (int i = 0; i < lightingScenariosData[index].lightProbes.Length; i++)
|
||||
{
|
||||
var sphericalHarmonics = new SphericalHarmonicsL2();
|
||||
|
||||
// j is coefficient
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
//k is channel ( r g b )
|
||||
for (int k = 0; k < 9; k++)
|
||||
{
|
||||
sphericalHarmonics[j, k] = lightingScenariosData[index].lightProbes[i].coefficients[j * 9 + k];
|
||||
}
|
||||
}
|
||||
|
||||
sphericalHarmonicsArray[i] = sphericalHarmonics;
|
||||
}
|
||||
return sphericalHarmonicsArray;
|
||||
}
|
||||
|
||||
IEnumerator SwitchSceneCoroutine(string sceneToUnload, string sceneToLoad)
|
||||
{
|
||||
AsyncOperation unloadop = null;
|
||||
AsyncOperation loadop = null;
|
||||
|
||||
if (sceneToUnload != null && sceneToUnload != string.Empty && sceneToUnload != sceneToLoad)
|
||||
{
|
||||
unloadop = SceneManager.UnloadSceneAsync(sceneToUnload);
|
||||
while (!unloadop.isDone)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if(sceneToLoad != null && sceneToLoad != string.Empty && sceneToLoad != "")
|
||||
{
|
||||
loadop = SceneManager.LoadSceneAsync(sceneToLoad, LoadSceneMode.Additive);
|
||||
while ((!loadop.isDone || loadop == null))
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneToLoad));
|
||||
}
|
||||
LoadLightProbes(currentLightingScenario);
|
||||
}
|
||||
|
||||
LightmapData[] LoadLightmaps(int index)
|
||||
{
|
||||
if (lightingScenariosData[index].lightmaps == null
|
||||
|| lightingScenariosData[index].lightmaps.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("No lightmaps stored in scenario " + index);
|
||||
return null;
|
||||
}
|
||||
|
||||
var newLightmaps = new LightmapData[lightingScenariosData[index].lightmaps.Length];
|
||||
|
||||
for (int i = 0; i < newLightmaps.Length; i++)
|
||||
{
|
||||
newLightmaps[i] = new LightmapData();
|
||||
newLightmaps[i].lightmapColor = lightingScenariosData[index].lightmaps[i];
|
||||
|
||||
if (lightingScenariosData[index].lightmapsMode != LightmapsMode.NonDirectional)
|
||||
{
|
||||
newLightmaps[i].lightmapDir = lightingScenariosData[index].lightmapsDir[i];
|
||||
}
|
||||
if (lightingScenariosData[index].shadowMasks.Length > 0)
|
||||
{
|
||||
newLightmaps[i].shadowMask = lightingScenariosData[index].shadowMasks[i];
|
||||
}
|
||||
}
|
||||
|
||||
return newLightmaps;
|
||||
}
|
||||
|
||||
public void ApplyRendererInfo(RendererInfo[] infos)
|
||||
{
|
||||
try
|
||||
{
|
||||
Terrain terrain = FindObjectOfType<Terrain>();
|
||||
int i = 0;
|
||||
if (terrain != null)
|
||||
{
|
||||
terrain.lightmapIndex = infos[i].lightmapIndex;
|
||||
terrain.lightmapScaleOffset = infos[i].lightmapOffsetScale;
|
||||
i++;
|
||||
}
|
||||
|
||||
for (int j = i; j < infos.Length; j++)
|
||||
{
|
||||
RendererInfo info = infos[j];
|
||||
//if (info.renderer == null)
|
||||
//continue;
|
||||
info.renderer.lightmapIndex = infos[j].lightmapIndex;
|
||||
if (!info.renderer.isPartOfStaticBatch)
|
||||
{
|
||||
info.renderer.lightmapScaleOffset = infos[j].lightmapOffsetScale;
|
||||
}
|
||||
if (info.renderer.isPartOfStaticBatch && verbose == true && Application.isEditor)
|
||||
{
|
||||
Debug.Log("Object " + info.renderer.gameObject.name + " is part of static batch, skipping lightmap offset and scale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Error in ApplyRendererInfo:" + e.GetType().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadLightProbes(int index)
|
||||
{
|
||||
if (Application.isEditor && !Application.isPlaying)
|
||||
{
|
||||
PrepareLightProbeArrays();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LightmapSettings.lightProbes.bakedProbes = lightProbesRuntime[index];
|
||||
}
|
||||
catch { Debug.LogWarning("Warning, error when trying to load lightprobes for scenario " + index); }
|
||||
}
|
||||
|
||||
public void StoreLightmapInfos(int index)
|
||||
{
|
||||
var newLightingScenarioData = new LightingScenarioData ();
|
||||
var newRendererInfos = new List<RendererInfo>();
|
||||
var newLightmapsTextures = new List<Texture2D>();
|
||||
var newLightmapsTexturesDir = new List<Texture2D>();
|
||||
var newLightmapsMode = new LightmapsMode();
|
||||
var newSphericalHarmonicsList = new List<SphericalHarmonics>();
|
||||
var newLightmapsShadowMasks = new List<Texture2D>();
|
||||
|
||||
newLightmapsMode = LightmapSettings.lightmapsMode;
|
||||
|
||||
GenerateLightmapInfo(gameObject, newRendererInfos, newLightmapsTextures, newLightmapsTexturesDir, newLightmapsShadowMasks, newLightmapsMode);
|
||||
|
||||
newLightingScenarioData.lightmapsMode = newLightmapsMode;
|
||||
|
||||
newLightingScenarioData.lightmaps = newLightmapsTextures.ToArray();
|
||||
|
||||
if (newLightmapsMode != LightmapsMode.NonDirectional)
|
||||
{
|
||||
newLightingScenarioData.lightmapsDir = newLightmapsTexturesDir.ToArray();
|
||||
}
|
||||
|
||||
//Mixed or realtime support
|
||||
newLightingScenarioData.hasRealtimeLights = latestBuildHasReltimeLights;
|
||||
|
||||
newLightingScenarioData.shadowMasks = newLightmapsShadowMasks.ToArray();
|
||||
|
||||
newLightingScenarioData.rendererInfos = newRendererInfos.ToArray();
|
||||
|
||||
var scene_LightProbes = new SphericalHarmonicsL2[LightmapSettings.lightProbes.bakedProbes.Length];
|
||||
scene_LightProbes = LightmapSettings.lightProbes.bakedProbes;
|
||||
|
||||
for (int i = 0; i < scene_LightProbes.Length; i++)
|
||||
{
|
||||
var SHCoeff = new SphericalHarmonics();
|
||||
|
||||
// j is coefficient
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
//k is channel ( r g b )
|
||||
for (int k = 0; k < 9; k++)
|
||||
{
|
||||
SHCoeff.coefficients[j*9+k] = scene_LightProbes[i][j, k];
|
||||
}
|
||||
}
|
||||
|
||||
newSphericalHarmonicsList.Add(SHCoeff);
|
||||
}
|
||||
|
||||
newLightingScenarioData.lightProbes = newSphericalHarmonicsList.ToArray ();
|
||||
|
||||
if (lightingScenariosData.Count < index + 1)
|
||||
{
|
||||
lightingScenariosData.Insert(index, newLightingScenarioData);
|
||||
}
|
||||
else
|
||||
{
|
||||
lightingScenariosData[index] = newLightingScenarioData;
|
||||
}
|
||||
|
||||
lightingScenariosCount = lightingScenariosData.Count;
|
||||
|
||||
if (lightingScenesNames == null || lightingScenesNames.Length< lightingScenariosCount)
|
||||
{
|
||||
lightingScenesNames = new string[lightingScenariosCount];
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateLightmapInfo(GameObject root, List<RendererInfo> newRendererInfos, List<Texture2D> newLightmapsLight, List<Texture2D> newLightmapsDir, List<Texture2D> newLightmapsShadow, LightmapsMode newLightmapsMode)
|
||||
{
|
||||
Terrain terrain = FindObjectOfType<Terrain>();
|
||||
if (terrain != null && terrain.lightmapIndex != -1 && terrain.lightmapIndex != 65534)
|
||||
{
|
||||
RendererInfo terrainRendererInfo = new RendererInfo();
|
||||
terrainRendererInfo.lightmapOffsetScale = terrain.lightmapScaleOffset;
|
||||
|
||||
Texture2D lightmaplight = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapColor;
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsLight.IndexOf(lightmaplight);
|
||||
if (terrainRendererInfo.lightmapIndex == -1)
|
||||
{
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsLight.Count;
|
||||
newLightmapsLight.Add(lightmaplight);
|
||||
}
|
||||
|
||||
if (newLightmapsMode != LightmapsMode.NonDirectional)
|
||||
{
|
||||
Texture2D lightmapdir = LightmapSettings.lightmaps[terrain.lightmapIndex].lightmapDir;
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsDir.IndexOf(lightmapdir);
|
||||
if (terrainRendererInfo.lightmapIndex == -1)
|
||||
{
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsDir.Count;
|
||||
newLightmapsDir.Add(lightmapdir);
|
||||
}
|
||||
}
|
||||
if (LightmapSettings.lightmaps[terrain.lightmapIndex].shadowMask != null)
|
||||
{
|
||||
Texture2D lightmapShadow = LightmapSettings.lightmaps[terrain.lightmapIndex].shadowMask;
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsShadow.IndexOf(lightmapShadow);
|
||||
if (terrainRendererInfo.lightmapIndex == -1)
|
||||
{
|
||||
terrainRendererInfo.lightmapIndex = newLightmapsShadow.Count;
|
||||
newLightmapsShadow.Add(lightmapShadow);
|
||||
}
|
||||
}
|
||||
newRendererInfos.Add(terrainRendererInfo);
|
||||
|
||||
if (Application.isEditor)
|
||||
Debug.Log("Terrain lightmap stored in" + terrainRendererInfo.lightmapIndex.ToString());
|
||||
}
|
||||
|
||||
var renderers = FindObjectsOfType(typeof(Renderer));
|
||||
|
||||
if (Application.isEditor)
|
||||
Debug.Log("stored info for " + renderers.Length + " meshrenderers");
|
||||
|
||||
foreach (Renderer renderer in renderers)
|
||||
{
|
||||
if (renderer.lightmapIndex != -1 && renderer.lightmapIndex != 65534)
|
||||
{
|
||||
RendererInfo info = new RendererInfo();
|
||||
info.renderer = renderer;
|
||||
info.lightmapOffsetScale = renderer.lightmapScaleOffset;
|
||||
|
||||
Texture2D lightmaplight = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapColor;
|
||||
info.lightmapIndex = newLightmapsLight.IndexOf(lightmaplight);
|
||||
if (info.lightmapIndex == -1)
|
||||
{
|
||||
info.lightmapIndex = newLightmapsLight.Count;
|
||||
newLightmapsLight.Add(lightmaplight);
|
||||
}
|
||||
|
||||
if (newLightmapsMode != LightmapsMode.NonDirectional)
|
||||
{
|
||||
Texture2D lightmapdir = LightmapSettings.lightmaps[renderer.lightmapIndex].lightmapDir;
|
||||
info.lightmapIndex = newLightmapsDir.IndexOf(lightmapdir);
|
||||
if (info.lightmapIndex == -1)
|
||||
{
|
||||
info.lightmapIndex = newLightmapsDir.Count;
|
||||
newLightmapsDir.Add(lightmapdir);
|
||||
}
|
||||
}
|
||||
if (LightmapSettings.lightmaps[renderer.lightmapIndex].shadowMask != null)
|
||||
{
|
||||
Texture2D lightmapShadow = LightmapSettings.lightmaps[renderer.lightmapIndex].shadowMask;
|
||||
info.lightmapIndex = newLightmapsShadow.IndexOf(lightmapShadow);
|
||||
if (info.lightmapIndex == -1)
|
||||
{
|
||||
info.lightmapIndex = newLightmapsShadow.Count;
|
||||
newLightmapsShadow.Add(lightmapShadow);
|
||||
}
|
||||
}
|
||||
newRendererInfos.Add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 311c76c9ffea5554c82aa90000c874ac
|
||||
timeCreated: 1435326575
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4021dabdeeef1824b871b34d975991b8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "LightingTools.LightmapSwitcher",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": []
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 802596836aa83094ea9d1166a80d7e4e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "li.lightingtools.lightmapswitcher",
|
||||
"displayName": "Lightmap Switcher",
|
||||
"version": "0.1.0-preview",
|
||||
"unity": "2019.1",
|
||||
"description": "Allows you to switch lightmaps at runtime.",
|
||||
"dependencies": {
|
||||
"com.unity.editorcoroutines": "0.0.2-preview"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 243e8b565ec911f4dbcf88980a8510af
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,42 @@
|
|||
# Changelog
|
||||
All notable changes to this package will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [7.1.6] - 2019-11-22
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.1.5] - 2019-11-15
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.1.4] - 2019-11-13
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.1.3] - 2019-11-04
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.1.2] - 2019-09-19
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.1.1] - 2019-09-05
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
## [7.0.1] - 2019-07-25
|
||||
|
||||
Version Updated
|
||||
The version number for this package has increased due to a version update of a related graphics package.
|
||||
|
||||
Started Changelog
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b3f7006c7834664ba28a1fa6b51f9fe
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,5 @@
|
|||
Render Pipeline Core copyright © 2019 Unity Technologies ApS
|
||||
|
||||
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
|
||||
|
||||
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e797817a7447e1f42816cf2c2fa8765d
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d299198a792a964e9d443aa47506e2c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Configuration
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.Rendering.HighDefinition
|
||||
{
|
||||
[GenerateHLSL(PackingRules.Exact)]
|
||||
public enum HDShadowFilteringQuality
|
||||
{
|
||||
Low = 0,
|
||||
Medium = 1,
|
||||
High = 2,
|
||||
}
|
||||
|
||||
[GenerateHLSL(PackingRules.Exact)]
|
||||
public enum ShaderOptions
|
||||
{
|
||||
CameraRelativeRendering = 1, // Rendering sets the origin of the world to the position of the primary (scene view) camera
|
||||
PreExposition = 1,
|
||||
PrecomputedAtmosphericAttenuation = 0, // Precomputes atmospheric attenuation for the directional light on the CPU, which makes it independent from the fragment's position, which is faster but wrong
|
||||
#if ENABLE_RAYTRACING
|
||||
Raytracing = 1,
|
||||
#else
|
||||
Raytracing = 0,
|
||||
#endif
|
||||
#if ENABLE_VR
|
||||
XrMaxViews = 2, // Used for single-pass rendering (with fast path in vertex shader code when forced to 2)
|
||||
#else
|
||||
XrMaxViews = 1,
|
||||
#endif
|
||||
AreaLights = 0,
|
||||
|
||||
DeferredShadowFiltering = HDShadowFilteringQuality.Medium,
|
||||
BarnDoor = 0
|
||||
};
|
||||
|
||||
// Note: #define can't be use in include file in C# so we chose this way to configure both C# and hlsl
|
||||
// Changing a value in this enum Config here require to regenerate the hlsl include and recompile C# and shaders
|
||||
public class ShaderConfig
|
||||
{
|
||||
public static int s_CameraRelativeRendering = (int)ShaderOptions.CameraRelativeRendering;
|
||||
public static int s_PreExposition = (int)ShaderOptions.PreExposition;
|
||||
public static int s_XrMaxViews = (int)ShaderOptions.XrMaxViews;
|
||||
public static int s_PrecomputedAtmosphericAttenuation = (int)ShaderOptions.PrecomputedAtmosphericAttenuation;
|
||||
public static int s_AreaLights = (int)ShaderOptions.AreaLights;
|
||||
public static int s_BarnDoor = (int)ShaderOptions.BarnDoor;
|
||||
public static HDShadowFilteringQuality s_DeferredShadowFiltering = (HDShadowFilteringQuality)ShaderOptions.DeferredShadowFiltering;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// This file was automatically generated. Please don't edit by hand.
|
||||
//
|
||||
|
||||
#ifndef SHADERCONFIG_CS_HLSL
|
||||
#define SHADERCONFIG_CS_HLSL
|
||||
//
|
||||
// UnityEngine.Rendering.HighDefinition.HDShadowFilteringQuality: static fields
|
||||
//
|
||||
#define HDSHADOWFILTERINGQUALITY_LOW (0)
|
||||
#define HDSHADOWFILTERINGQUALITY_MEDIUM (1)
|
||||
#define HDSHADOWFILTERINGQUALITY_HIGH (2)
|
||||
|
||||
//
|
||||
// UnityEngine.Rendering.HighDefinition.ShaderOptions: static fields
|
||||
//
|
||||
#define SHADEROPTIONS_CAMERA_RELATIVE_RENDERING (1)
|
||||
#define SHADEROPTIONS_PRE_EXPOSITION (1)
|
||||
#define SHADEROPTIONS_PRECOMPUTED_ATMOSPHERIC_ATTENUATION (0)
|
||||
#define SHADEROPTIONS_RAYTRACING (0)
|
||||
#define SHADEROPTIONS_XR_MAX_VIEWS (2)
|
||||
#define SHADEROPTIONS_AREA_LIGHTS (1)
|
||||
#define SHADEROPTIONS_DEFERRED_SHADOW_FILTERING (1)
|
||||
#define SHADEROPTIONS_BARN_DOOR (0)
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bcb8aa9f314d49b4c97aa1f3f3511e7b
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 488b9213a64c77540bca3fe167edbe6c
|
||||
timeCreated: 1475742183
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Unity.RenderPipelines.HighDefinition.Config.Runtime",
|
||||
"references": [
|
||||
"GUID:df380645f10b7bc4b97d4f5eb6303d95"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a075b55b404a34748ac14ea9b6039911
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "com.unity.render-pipelines.high-definition-config",
|
||||
"description": "Configuration files for the High Definition Render Pipeline.",
|
||||
"version": "7.1.6",
|
||||
"unity": "2019.3",
|
||||
"unityRelease": "0b13",
|
||||
"displayName": "High Definition RP Config",
|
||||
"dependencies": {
|
||||
"com.unity.render-pipelines.core": "7.1.6"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:Unity-Technologies/ScriptableRenderPipeline.git",
|
||||
"revision": "462033f359f783f5d71dc578159bf5827abb41e2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af6e0e6bb9a468845bfb9c9381e3219b
|
||||
PackageManifestImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,152 @@
|
|||
# Changelog
|
||||
|
||||
## 2019.3.1
|
||||
|
||||
#### Changed
|
||||
|
||||
* **Messager** is now able to pass instigator Game Object through message broadcast.
|
||||
* **OnMessageEvent** now passes the optional instigator instead of itself as instigator to the Calls. In order to pass itself use an intermediate **SetInstigatorLogic** that targets the OnMessageEvent owner to replicate the former behaviour.
|
||||
* **SendMessageAction** now passes its instigator game object to the **Messager**
|
||||
|
||||
#### Added
|
||||
|
||||
* **Call Tree Explorer**: Added Category for Erroneous Calls
|
||||
* Added **ToggleBehaviourAction** working the same as ToggleGameObjectAction, but for behaviour components instead.
|
||||
* **SendMessageBehaviour** (Timeline Send Message Tracks/Clips) now displays an instigator game object field in the inspector to be attached to the sent message.
|
||||
* Added **VFXSetPropertyAction**
|
||||
* Added **VFXSendEventAction**
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed `OnValidate` for FirstPersonController leading to infinite import loop when displaying the inspector for a Prefab Asset.
|
||||
* Fix for null Callables in Callable Tree Window.
|
||||
|
||||
## 2019.3.0
|
||||
|
||||
* Feature Release
|
||||
* Requires Unity 2019.3.0 or newer
|
||||
|
||||
#### Added
|
||||
|
||||
* **Call Tree Explorer :** Using Window/Gameplay Ingredients/Call Tree Explorer , opens a window that lists the tree of Events, Logic and Actions, State Machines and Event Calling Actions
|
||||
* **Folders:** In the Game Object creation Menu, Select folder to add a folder in the hierarchy. Automatically adds Static Game Objects with colored icon (Displayed using Advanced Hierarchy View)
|
||||
* **Global Variables System**:
|
||||
- Added Global Variables (Globals + Local Scope)
|
||||
- Added Global Variable Debug Window (`Window/Gameplay Ingredients/Globals Debug`)
|
||||
- Added Global Variable Set Action
|
||||
- Added Global Variable Logic
|
||||
- Added Global Variables Reset Action
|
||||
* **Timers**:
|
||||
* Added Timer Component
|
||||
* Added TimerAction to control Timer
|
||||
* Added TimerDisplayRig
|
||||
* Added option in GameplayIngredientsSettings to disable visibility of Callable[] bound to Update Loops.
|
||||
* Added OnUpdate Event : Perform calls every update
|
||||
* Added OnColider Event : Perform calls upon collisions
|
||||
* Added OnJoinBreak Event : Perform calls upon Rigid body joint break
|
||||
* Added FlipFlop Logic : Two-state latch logic
|
||||
* Added State Logic : Perform logic based on State Machine current state.
|
||||
* Added Audio Mix Snapshot Action : Set Mixer Snapshots
|
||||
* Added RigidBody Action : Perform actions on a rigidbody
|
||||
* Added SetAnimatorParameterAction : Perform parameter setting on Animators
|
||||
* Added Sacrifice Oldest option to Factory : When needing a new spawn but no slots left, sacrifices the first spawn of the list
|
||||
* Added Context Menu in ToggleGameObjectAction to update entries based on current enabled state in scene.
|
||||
|
||||
#### Changed
|
||||
|
||||
- Improved **Find & Replace** window, with a selection-based criteria.
|
||||
- Moved Menu Items in Window menu into a Gameplay Ingredients Subfolder
|
||||
- GameManager Resets Global Variables Local Scope on Level Load
|
||||
- Updated NaughtyAttributes to a more recent version
|
||||
- Renamed the Add Game Object menu category from `'GameplayIngredients' to 'Gameplay Ingredients'` and adjusted its contents
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed LinkGameView not working in play mode when excluding VirtualCameraManager.
|
||||
* Fixed Performance issue in GameplayIngredientsSettings when having a big list of Excluded managers.
|
||||
* Fixed ApplicationExitAction : Exits play mode when in Editor.
|
||||
|
||||
## 2019.1.2
|
||||
|
||||
#### Changed
|
||||
|
||||
* **[Breaking Change]** Discover Assets now reference many Scenes/SceneSetups
|
||||
* Action to take: have to re-reference scenes in Discover Asset
|
||||
|
||||
#### Added
|
||||
|
||||
* Added Screenshot Manager (Defaults to F11 to take screenshots)
|
||||
* Added OnMouseDownEvent
|
||||
* Added OnMouseHoverEvent
|
||||
* Added OnVisibilityEvent
|
||||
* Added SaveDataSwitchOnIntLogic
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed warning in CycleResolutionsAction
|
||||
|
||||
|
||||
|
||||
## 2019.1.1
|
||||
|
||||
#### Changed
|
||||
|
||||
#### Added
|
||||
|
||||
* Log Action
|
||||
* Added Playable Director to objects in discover (to open atimeline at a give playable director)
|
||||
* Added support of Game Save Value index for Factories (in order to select a blueprint object from a saved value)
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed Import Errors at first project load, including the way we load discover and GameplayIngredients project settings
|
||||
|
||||
* Secure checks in Gathering Manager classes from assembly (skips protected assemblies now)
|
||||
|
||||
|
||||
|
||||
## 2019.1.0
|
||||
|
||||
#### Changed
|
||||
|
||||
* Removed counts in OnTriggerEvent
|
||||
* Callables can now be friendly-named (with default formatting)
|
||||
* Updated Starter Packages
|
||||
|
||||
#### Added
|
||||
|
||||
- Added NTimesLogic (split from OnTriggerEvent)
|
||||
- Added Replace Mode for Level Streaming Manager
|
||||
- Added UIToggle Action and Property Drawer
|
||||
- Added Audio Play Clip Action
|
||||
- Added Platform Logic
|
||||
|
||||
- New Welcome Screen, with Wizard
|
||||
- New optional GameplayIngredients Project Configuration asset
|
||||
- Toggles for verbose callable logging
|
||||
- Manager Exclusion List
|
||||
- New Scene from Template Window + Config SceneTemplateLists Assets
|
||||
- Helps creating new scenes from user-made templates
|
||||
- New Discover Window System:
|
||||
- Adds a new DiscoverAsset to reference Levels / Scene Setups
|
||||
- Adds new Discover components in scenes
|
||||
- Discover window helps navigate scenes while in editor and discover content.
|
||||
- Added improved Game Manager
|
||||
- Manages loading of main menu & levels directly instead of using LevelStreamingManager
|
||||
- Manages Level Startup in sync after all scenes have started.
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed code to run on Unity 2019.1
|
||||
* Fixed factory managed objects upon destroy
|
||||
* Fixes in LinkGameView when application is not playing
|
||||
* Fix in LevelStreamingManager incorrect computation of Scene Counts
|
||||
* Fixes in VirtualCameraManager
|
||||
* Fixes in Find/Replace window
|
||||
* Fixes in Hierarchy View Hints for Unity 2019.3 new skin
|
||||
|
||||
|
||||
|
||||
## 2018.3.0
|
||||
|
||||
Initial Version
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a2d80afb13247c646b079b5f51dcb6ac
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dedb43ed8dff48a4ea95d29002e41ca4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e78d538974cc9924792b2707919e382a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,43 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEditor.ProjectWindowCallback;
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class AssetFactory
|
||||
{
|
||||
public static void CreateAssetInProjectWindow<T>(string iconName, string fileName) where T: ScriptableObject
|
||||
{
|
||||
var icon = EditorGUIUtility.FindTexture(iconName);
|
||||
|
||||
var namingInstance = new DoCreateGenericAsset();
|
||||
namingInstance.type = typeof(T);
|
||||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, namingInstance, fileName, icon, null);
|
||||
}
|
||||
|
||||
public static ScriptableObject CreateAssetAtPath(string path, Type type)
|
||||
{
|
||||
Debug.Log("CreateAssetAtPath (" + type.Name + ")");
|
||||
|
||||
ScriptableObject asset = ScriptableObject.CreateInstance(type);
|
||||
asset.name = Path.GetFileName(path);
|
||||
AssetDatabase.CreateAsset(asset, path);
|
||||
return asset;
|
||||
}
|
||||
|
||||
class DoCreateGenericAsset : EndNameEditAction
|
||||
{
|
||||
public Type type;
|
||||
|
||||
public override void Action(int instanceId, string pathName, string resourceFile)
|
||||
{
|
||||
ScriptableObject asset = AssetFactory.CreateAssetAtPath(pathName, type);
|
||||
ProjectWindowUtil.ShowCreatedAsset(asset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e93b674f69eb054c8514bd7c501f0e3
|
||||
guid: 4833ee2adf8a9cd4c96cbd6dac04fe42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37d3d697167f89145a81aa7bb6c0acc4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,562 @@
|
|||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEditor.SceneManagement;
|
||||
using GameplayIngredients.Events;
|
||||
using GameplayIngredients.Logic;
|
||||
using GameplayIngredients.Actions;
|
||||
using GameplayIngredients.StateMachines;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class CallTreeWindow : EditorWindow
|
||||
{
|
||||
CallTreeView m_TreeView;
|
||||
[MenuItem("Window/Gameplay Ingredients/Callable Tree Explorer", priority = MenuItems.kWindowMenuPriority)]
|
||||
static void OpenWindow()
|
||||
{
|
||||
s_Instance = GetWindow<CallTreeWindow>();
|
||||
}
|
||||
|
||||
public static bool visible = false;
|
||||
static CallTreeWindow s_Instance;
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
visible = false;
|
||||
s_Instance = null;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
nodeRoots = new Dictionary<string, List<CallTreeNode>>();
|
||||
m_TreeView = new CallTreeView(nodeRoots);
|
||||
titleContent = new GUIContent("Callable Tree Explorer", CallTreeView.Styles.Callable);
|
||||
ReloadCallHierarchy();
|
||||
EditorSceneManager.sceneOpened += Reload;
|
||||
EditorSceneSetup.onSetupLoaded += ReloadSetup;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
void Reload(Scene scene, OpenSceneMode mode)
|
||||
{
|
||||
ReloadCallHierarchy();
|
||||
}
|
||||
|
||||
void ReloadSetup(EditorSceneSetup setup)
|
||||
{
|
||||
ReloadCallHierarchy();
|
||||
}
|
||||
|
||||
public static void Refresh()
|
||||
{
|
||||
s_Instance.ReloadCallHierarchy();
|
||||
s_Instance.Repaint();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
int tbHeight = 24;
|
||||
using (new GUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Height(tbHeight)))
|
||||
{
|
||||
if (GUILayout.Button("Reload", EditorStyles.toolbarButton))
|
||||
{
|
||||
ReloadCallHierarchy();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
string filter = EditorGUILayout.DelayedTextField(m_TreeView.stringFilter, EditorStyles.toolbarSearchField);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_TreeView.SetStringFilter(filter);
|
||||
}
|
||||
|
||||
|
||||
Rect buttonRect = GUILayoutUtility.GetRect(52, 16);
|
||||
if (GUI.Button(buttonRect, "Filter", EditorStyles.toolbarDropDown))
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent("Filter Selected"), false, () => {
|
||||
m_TreeView.SetAutoFilter(false);
|
||||
m_TreeView.SetObjectFilter(Selection.activeGameObject);
|
||||
});
|
||||
menu.AddItem(new GUIContent("Clear Filter"), false, () => {
|
||||
m_TreeView.SetAutoFilter(false);
|
||||
m_TreeView.SetObjectFilter(null);
|
||||
m_TreeView.SetStringFilter(string.Empty);
|
||||
});
|
||||
menu.AddSeparator("");
|
||||
menu.AddItem(new GUIContent("Automatic Filter"), m_TreeView.AutoFilter, () => {
|
||||
m_TreeView.ToggleAutoFilter();
|
||||
});
|
||||
menu.DropDown(buttonRect);
|
||||
}
|
||||
|
||||
}
|
||||
Rect r = GUILayoutUtility.GetRect(position.width, position.height - tbHeight);
|
||||
m_TreeView.OnGUI(r);
|
||||
}
|
||||
|
||||
Dictionary<string, List<CallTreeNode>> nodeRoots;
|
||||
|
||||
List<MonoBehaviour> erroneous;
|
||||
|
||||
void ReloadCallHierarchy()
|
||||
{
|
||||
if (nodeRoots == null)
|
||||
nodeRoots = new Dictionary<string, List<CallTreeNode>>();
|
||||
else
|
||||
nodeRoots.Clear();
|
||||
|
||||
erroneous = new List<MonoBehaviour>();
|
||||
|
||||
AddToCategory<EventBase>("Events");
|
||||
AddToCategory<StateMachine>("State Machines");
|
||||
AddToCategory<Factory>("Factories");
|
||||
AddToCategory<SendMessageAction>("Messages");
|
||||
CollectErroneousCallables();
|
||||
m_TreeView.Reload();
|
||||
}
|
||||
|
||||
void CollectErroneousCallables()
|
||||
{
|
||||
if (erroneous == null || erroneous.Count == 0)
|
||||
return;
|
||||
var root = new List<CallTreeNode>();
|
||||
nodeRoots.Add("Erroneous Callables", root);
|
||||
|
||||
foreach(var callable in erroneous)
|
||||
{
|
||||
root.Add(new CallTreeNode(callable, CallTreeNodeType.Callable, callable.name));
|
||||
}
|
||||
}
|
||||
|
||||
void AddErroneous(MonoBehaviour bhv)
|
||||
{
|
||||
if (!erroneous.Contains(bhv))
|
||||
erroneous.Add(bhv);
|
||||
}
|
||||
|
||||
void AddToCategory<T>(string name) where T:MonoBehaviour
|
||||
{
|
||||
var list = Resources.FindObjectsOfTypeAll<T>().ToList();
|
||||
|
||||
if (list.Count > 0)
|
||||
nodeRoots.Add(name, new List<CallTreeNode>());
|
||||
else
|
||||
return;
|
||||
|
||||
var listRoot = nodeRoots[name];
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (item.gameObject.scene == null || !item.gameObject.scene.isLoaded)
|
||||
continue;
|
||||
|
||||
var stack = new Stack<object>();
|
||||
|
||||
if(typeof(T) == typeof(StateMachine))
|
||||
{
|
||||
listRoot.Add(GetStateMachineNode(item as StateMachine, stack));
|
||||
}
|
||||
else if(typeof(T) == typeof(SendMessageAction))
|
||||
{
|
||||
listRoot.Add(GetMessageNode(item as SendMessageAction, stack));
|
||||
}
|
||||
else
|
||||
{
|
||||
listRoot.Add(GetNode(item, stack));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CallTreeNode GetNode(MonoBehaviour bhv, Stack<object> stack)
|
||||
{
|
||||
if(!stack.Contains(bhv))
|
||||
{
|
||||
stack.Push(bhv);
|
||||
var rootNode = new CallTreeNode(bhv, GetType(bhv), $"{bhv.gameObject.name} ({bhv.GetType().Name})");
|
||||
var type = bhv.GetType();
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
// Find Fields that are Callable[]
|
||||
if (field.FieldType.IsAssignableFrom(typeof(Callable[])))
|
||||
{
|
||||
var node = new CallTreeNode(bhv, CallTreeNodeType.Callable, field.Name);
|
||||
var value = (Callable[])field.GetValue(bhv);
|
||||
|
||||
if (value != null && value.Length > 0)
|
||||
{
|
||||
rootNode.Children.Add(node);
|
||||
// Add Callables from this Callable[] array
|
||||
foreach (var call in value)
|
||||
{
|
||||
if (call != null)
|
||||
node.Children.Add(GetCallableNode(call, stack));
|
||||
else
|
||||
AddErroneous(node.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CallTreeNode(bhv, GetType(bhv), $"RECURSED : {bhv.gameObject.name} ({bhv.GetType().Name})");
|
||||
}
|
||||
}
|
||||
|
||||
CallTreeNode GetCallableNode(Callable c, Stack<object> stack)
|
||||
{
|
||||
if (!stack.Contains(c))
|
||||
{
|
||||
stack.Push(c);
|
||||
var rootNode = new CallTreeNode(c, GetType(c), $"{c.Name} ({c.gameObject.name} : {c.GetType().Name})");
|
||||
var type = c.GetType();
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
// Find Fields that are Callable[]
|
||||
if (field.FieldType.IsAssignableFrom(typeof(Callable[])))
|
||||
{
|
||||
var node = new CallTreeNode(c, CallTreeNodeType.Callable, field.Name);
|
||||
var value = (Callable[])field.GetValue(c);
|
||||
|
||||
if (value != null && value.Length > 0)
|
||||
{
|
||||
rootNode.Children.Add(node);
|
||||
// Add Callables from this Callable[] array
|
||||
foreach (var call in value)
|
||||
{
|
||||
if (call != null)
|
||||
node.Children.Add(GetCallableNode(call, stack));
|
||||
else
|
||||
AddErroneous(node.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CallTreeNode(c, GetType(c), $"RECURSED : {c.Name} ({c.gameObject.name} : {c.GetType().Name})");
|
||||
}
|
||||
}
|
||||
|
||||
CallTreeNode GetMessageNode(SendMessageAction msg, Stack<object> stack)
|
||||
{
|
||||
if (!stack.Contains(msg))
|
||||
{
|
||||
stack.Push(msg);
|
||||
var rootNode = new CallTreeNode(msg, CallTreeNodeType.Message, $"{msg.MessageToSend} : ({msg.gameObject.name}.{msg.Name})");
|
||||
var all = Resources.FindObjectsOfTypeAll<OnMessageEvent>().Where(o=> o.MessageName == msg.MessageToSend).ToList();
|
||||
|
||||
foreach(var evt in all)
|
||||
{
|
||||
rootNode.Children.Add(GetNode(evt, stack));
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CallTreeNode(msg, GetType(msg), $"RECURSED :{msg.MessageToSend} : ({msg.gameObject.name}.{msg.Name})");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CallTreeNode GetStateMachineNode(StateMachine sm, Stack<object> stack)
|
||||
{
|
||||
if (!stack.Contains(sm))
|
||||
{
|
||||
stack.Push(sm);
|
||||
var rootNode = new CallTreeNode(sm, CallTreeNodeType.StateMachine, sm.gameObject.name);
|
||||
var type = sm.GetType();
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
// Find Fields that are State[]
|
||||
if (field.FieldType.IsAssignableFrom(typeof(State[])))
|
||||
{
|
||||
// Add Callables from this Callable[] array
|
||||
var value = (State[])field.GetValue(sm);
|
||||
foreach (var state in value)
|
||||
{
|
||||
if (state != null)
|
||||
rootNode.Children.Add(GetStateNode(state, stack));
|
||||
else
|
||||
AddErroneous(rootNode.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CallTreeNode(sm, GetType(sm), $"RECURSED :{sm.gameObject.name}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CallTreeNode GetStateNode(State st, Stack<object> stack)
|
||||
{
|
||||
if (!stack.Contains(st))
|
||||
{
|
||||
stack.Push(st);
|
||||
var rootNode = new CallTreeNode(st, CallTreeNodeType.State, st.gameObject.name);
|
||||
var type = st.GetType();
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
// Find Fields that are Callable[]
|
||||
if (field.FieldType.IsAssignableFrom(typeof(Callable[])))
|
||||
{
|
||||
var node = new CallTreeNode(st, CallTreeNodeType.Callable, field.Name);
|
||||
rootNode.Children.Add(node);
|
||||
// Add Callables from this Callable[] array
|
||||
var value = (Callable[])field.GetValue(st);
|
||||
foreach (var call in value)
|
||||
{
|
||||
if (call != null)
|
||||
node.Children.Add(GetNode(call, stack));
|
||||
else
|
||||
AddErroneous(rootNode.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CallTreeNode(st, GetType(st), $"RECURSED :{st.gameObject.name}");
|
||||
}
|
||||
}
|
||||
|
||||
CallTreeNodeType GetType(MonoBehaviour bhv)
|
||||
{
|
||||
if (bhv == null)
|
||||
return CallTreeNodeType.Callable;
|
||||
else if (bhv is EventBase)
|
||||
return CallTreeNodeType.Event;
|
||||
else if (bhv is LogicBase)
|
||||
return CallTreeNodeType.Logic;
|
||||
else if (bhv is ActionBase)
|
||||
return CallTreeNodeType.Action;
|
||||
else if (bhv is StateMachine)
|
||||
return CallTreeNodeType.StateMachine;
|
||||
else if (bhv is State)
|
||||
return CallTreeNodeType.State;
|
||||
else if (bhv is Factory)
|
||||
return CallTreeNodeType.Factory;
|
||||
else if (bhv is OnMessageEvent || bhv is SendMessageAction)
|
||||
return CallTreeNodeType.Message;
|
||||
else
|
||||
return CallTreeNodeType.Callable;
|
||||
}
|
||||
|
||||
class CallTreeNode
|
||||
{
|
||||
public string Name;
|
||||
public MonoBehaviour Target;
|
||||
public List<CallTreeNode> Children;
|
||||
public CallTreeNodeType Type;
|
||||
public CallTreeNode(MonoBehaviour target, CallTreeNodeType type, string name = "")
|
||||
{
|
||||
Name = string.IsNullOrEmpty(name) ? target.GetType().Name : name;
|
||||
Target = target;
|
||||
Type = type;
|
||||
Children = new List<CallTreeNode>();
|
||||
}
|
||||
|
||||
public bool Filter(GameObject go, string filter)
|
||||
{
|
||||
bool keep = (go == null || this.Target.gameObject == go)
|
||||
&& (string.IsNullOrEmpty(filter) ? true : this.Name.Contains(filter));
|
||||
|
||||
if(!keep)
|
||||
{
|
||||
foreach (var node in Children)
|
||||
keep = keep || node.Filter(go, filter);
|
||||
}
|
||||
|
||||
return keep;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallTreeNodeType
|
||||
{
|
||||
Callable,
|
||||
Event,
|
||||
Logic,
|
||||
Action,
|
||||
Message,
|
||||
StateMachine,
|
||||
State,
|
||||
Factory
|
||||
}
|
||||
|
||||
class CallTreeView : TreeView
|
||||
{
|
||||
Dictionary<string, List<CallTreeNode>> m_Roots;
|
||||
Dictionary<int, CallTreeNode> m_Bindings;
|
||||
|
||||
public CallTreeView(Dictionary<string, List<CallTreeNode>> roots) : base(new TreeViewState())
|
||||
{
|
||||
m_Roots = roots;
|
||||
m_Bindings = new Dictionary<int, CallTreeNode>();
|
||||
}
|
||||
|
||||
public string stringFilter { get { return m_StringFilter; } }
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_filter = null;
|
||||
[SerializeField]
|
||||
string m_StringFilter = "";
|
||||
|
||||
public bool AutoFilter { get; private set; }
|
||||
public void ToggleAutoFilter()
|
||||
{
|
||||
SetAutoFilter(!AutoFilter);
|
||||
}
|
||||
|
||||
public void SetAutoFilter(bool value)
|
||||
{
|
||||
AutoFilter = value;
|
||||
if (AutoFilter)
|
||||
{
|
||||
Selection.selectionChanged += UpdateAutoFilter;
|
||||
if(this.HasSelection())
|
||||
{
|
||||
SetObjectFilter(m_Bindings[this.GetSelection()[0]].Target.gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
Selection.selectionChanged -= UpdateAutoFilter;
|
||||
}
|
||||
|
||||
void UpdateAutoFilter()
|
||||
{
|
||||
if (Selection.activeGameObject != null)
|
||||
SetObjectFilter(Selection.activeGameObject);
|
||||
}
|
||||
|
||||
public void SetObjectFilter(GameObject filter = null)
|
||||
{
|
||||
m_filter = filter;
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void SetStringFilter(string stringFilter)
|
||||
{
|
||||
m_StringFilter = stringFilter;
|
||||
Reload();
|
||||
}
|
||||
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
int id = -1;
|
||||
m_Bindings.Clear();
|
||||
var treeRoot = new TreeViewItem(++id, -1, "~Root");
|
||||
|
||||
foreach(var kvp in m_Roots)
|
||||
{
|
||||
if (kvp.Value == null || kvp.Value.Count == 0)
|
||||
continue;
|
||||
|
||||
var currentRoot = new TreeViewItem(++id, 0, kvp.Key);
|
||||
treeRoot.AddChild(currentRoot);
|
||||
foreach (var node in kvp.Value)
|
||||
{
|
||||
if (node.Filter(m_filter, m_StringFilter))
|
||||
{
|
||||
currentRoot.AddChild(GetNode(node, ref id, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (treeRoot.children == null)
|
||||
{
|
||||
treeRoot.AddChild(new TreeViewItem(1, 0, "(No Results)"));
|
||||
}
|
||||
|
||||
return treeRoot;
|
||||
}
|
||||
|
||||
TreeViewItem GetNode(CallTreeNode node, ref int id, int depth)
|
||||
{
|
||||
id++;
|
||||
var item = new TreeViewItem(id, depth, $"{node.Name}");
|
||||
item.icon = GetIcon(node.Target, node.Type);
|
||||
m_Bindings.Add(id, node);
|
||||
|
||||
foreach(var child in node.Children)
|
||||
{
|
||||
item.AddChild(GetNode(child, ref id, depth + 1));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
Texture2D GetIcon(MonoBehaviour bhv, CallTreeNodeType type)
|
||||
{
|
||||
if(bhv != null && type != CallTreeNodeType.Callable)
|
||||
{
|
||||
var texture = EditorGUIUtility.ObjectContent(bhv, bhv.GetType()).image;
|
||||
if (texture != null)
|
||||
return texture as Texture2D;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
default:
|
||||
case CallTreeNodeType.Callable:
|
||||
return Styles.Callable;
|
||||
case CallTreeNodeType.Action:
|
||||
return Styles.Action;
|
||||
case CallTreeNodeType.Logic:
|
||||
return Styles.Logic;
|
||||
case CallTreeNodeType.Event:
|
||||
return Styles.Event;
|
||||
case CallTreeNodeType.Message:
|
||||
return Styles.Message;
|
||||
case CallTreeNodeType.State:
|
||||
return Styles.State;
|
||||
case CallTreeNodeType.Factory:
|
||||
return Styles.Factory;
|
||||
case CallTreeNodeType.StateMachine:
|
||||
return Styles.StateMachine;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
if (AutoFilter)
|
||||
return;
|
||||
|
||||
base.SelectionChanged(selectedIds);
|
||||
if (selectedIds.Count > 0 && m_Bindings.ContainsKey(selectedIds[0]))
|
||||
Selection.activeObject = m_Bindings[selectedIds[0]].Target;
|
||||
}
|
||||
|
||||
public static class Styles
|
||||
{
|
||||
public static Texture2D Callable = Icon("Misc/ic-callable.png");
|
||||
public static Texture2D Action = Icon("Actions/ic-action-generic.png");
|
||||
public static Texture2D Logic = Icon("Logic/ic-generic-logic.png");
|
||||
public static Texture2D Event = Icon("Events/ic-event-generic.png");
|
||||
public static Texture2D Message = Icon("Events/ic-event-message .png");
|
||||
public static Texture2D StateMachine = Icon("Misc/ic-StateMachine.png");
|
||||
public static Texture2D State = Icon("Misc/ic-State.png");
|
||||
public static Texture2D Factory = Icon("Misc/ic-Factory.png");
|
||||
|
||||
static Texture2D Icon(string path)
|
||||
{
|
||||
return AssetDatabase.LoadAssetAtPath<Texture2D>($"Packages/net.peeweek.gameplay-ingredients/Icons/{path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 446b3e7e2cf55584fa1555b54658d4ab
|
||||
guid: 07aa515d40867f64686276ce5db0e51d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b3467fc18385f3f41a4050759dc020e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
[CustomEditor(typeof(Folder))]
|
||||
public class FolderEditor : UnityEditor.Editor
|
||||
{
|
||||
[MenuItem("GameObject/Folder", false, 10)]
|
||||
static void CreateFolder()
|
||||
{
|
||||
var go = new GameObject("Folder", typeof(Folder));
|
||||
if(Selection.activeGameObject != null && Selection.activeGameObject.scene != null)
|
||||
{
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
}
|
||||
|
||||
SerializedProperty m_Color;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_Color = serializedObject.FindProperty("Color");
|
||||
}
|
||||
|
||||
public override bool HasPreviewGUI()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var color = EditorGUILayout.ColorField("Folder Color", m_Color.colorValue);
|
||||
if(EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_Color.colorValue = color;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 483b9101733f40747b8c858a4e30c737
|
||||
guid: 474b95a56281dc44483708a7186ba29d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1654aa6a98f49614c87efd8f50c2785c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,68 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class DiscoverAsset : ScriptableObject
|
||||
{
|
||||
[MenuItem("Assets/Create/Discover Asset", priority = 202)]
|
||||
static void Create()
|
||||
{
|
||||
AssetFactory.CreateAssetInProjectWindow<DiscoverAsset>(null, "New DiscoverAsset.asset");
|
||||
}
|
||||
|
||||
[OnOpenAsset]
|
||||
static bool OpenAsset(int instanceID, int line)
|
||||
{
|
||||
var asset = EditorUtility.InstanceIDToObject(instanceID);
|
||||
if (asset is DiscoverAsset)
|
||||
{
|
||||
DiscoverWindow.ShowDiscoverWindow(asset as DiscoverAsset);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
[Header("General Properties")]
|
||||
public string WindowTitle = "Discover";
|
||||
public Texture2D HeaderTexture;
|
||||
|
||||
[Tooltip("Width of the Window, in pixels")]
|
||||
public int WindowWidth = 640;
|
||||
[Tooltip("Height of the Window, in pixels")]
|
||||
public int WindowHeight = 520;
|
||||
[Tooltip("Width of the Discover List, in pixels")]
|
||||
public int DiscoverListWidth = 180;
|
||||
|
||||
[Header("Show At Startup")]
|
||||
public bool EnableShowAtStartup = true;
|
||||
[Tooltip("The name of the preference for auto showing at startup, will be ")]
|
||||
public string PreferenceName = "Discover";
|
||||
|
||||
[Header("Content")]
|
||||
public string Title = "Welcome!";
|
||||
[Multiline]
|
||||
public string Description = "This is a sample body for your discover window.";
|
||||
[Header("Scenes")]
|
||||
public DiscoverSceneInfo[] Scenes;
|
||||
|
||||
[Header("Debug")]
|
||||
public bool Debug = false;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct DiscoverSceneInfo
|
||||
{
|
||||
public string Title;
|
||||
[Multiline]
|
||||
public string Description;
|
||||
public EditorSceneSetup[] SceneSetups;
|
||||
public SceneAsset[] SingleScenes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 99c49654b9bada340bfd165d70dc8296
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: e61da4d7fe9ddd647bd4fd18999ff0f0, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,215 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Playables;
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.VFX;
|
||||
#else
|
||||
using UnityEngine.Experimental.VFX;
|
||||
#endif
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
[CustomEditor(typeof(Discover))]
|
||||
public class DiscoverEditor : UnityEditor.Editor
|
||||
{
|
||||
const string kEditPreferenceName = "GameplayIngredients.DiscoverEditor.Editing";
|
||||
|
||||
static bool editing
|
||||
{
|
||||
get { return EditorPrefs.GetBool(kEditPreferenceName, false); }
|
||||
set { if (value != editing) EditorPrefs.SetBool(kEditPreferenceName, value); }
|
||||
}
|
||||
|
||||
Discover m_Discover;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_Discover = serializedObject.targetObject as Discover;
|
||||
if (m_Discover.transform.hideFlags != HideFlags.HideInInspector)
|
||||
m_Discover.transform.hideFlags = HideFlags.HideInInspector;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Align Discover to View"))
|
||||
{
|
||||
var transform = (serializedObject.targetObject as Discover).gameObject.transform;
|
||||
var svTransform = SceneView.lastActiveSceneView.camera.transform;
|
||||
|
||||
transform.position = svTransform.position;
|
||||
transform.rotation = svTransform.rotation;
|
||||
transform.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
editing = GUILayout.Toggle(editing, "Edit", EditorStyles.miniButton, GUILayout.Width(48));
|
||||
}
|
||||
|
||||
if (editing)
|
||||
DrawDefaultInspector();
|
||||
else
|
||||
DrawDiscoverContentGUI(m_Discover);
|
||||
}
|
||||
|
||||
public static void DrawDiscoverContentGUI(Discover discover)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(discover.Category))
|
||||
GUILayout.Label(discover.Category, DiscoverWindow.Styles.subHeader);
|
||||
|
||||
GUILayout.Label(discover.Name, DiscoverWindow.Styles.header);
|
||||
|
||||
using (new GUILayout.VerticalScope(DiscoverWindow.Styles.indent))
|
||||
{
|
||||
if (discover.Description != null && discover.Description != string.Empty)
|
||||
{
|
||||
GUILayout.Label(discover.Description, DiscoverWindow.Styles.body);
|
||||
}
|
||||
|
||||
GUILayout.Space(8);
|
||||
|
||||
foreach (var section in discover.Sections)
|
||||
{
|
||||
SectionGUI(section);
|
||||
GUILayout.Space(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SectionGUI(DiscoverSection section)
|
||||
{
|
||||
using (new DiscoverWindow.GroupLabelScope(section.SectionName))
|
||||
{
|
||||
using (new GUILayout.VerticalScope(DiscoverWindow.Styles.slightIndent))
|
||||
{
|
||||
GUILayout.Label(section.SectionContent, DiscoverWindow.Styles.body);
|
||||
|
||||
if (section.Actions != null && section.Actions.Length > 0)
|
||||
{
|
||||
GUILayout.Space(8);
|
||||
|
||||
using (new GUILayout.VerticalScope(GUI.skin.box))
|
||||
{
|
||||
foreach (var action in section.Actions)
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(action.Description);
|
||||
GUILayout.FlexibleSpace();
|
||||
using (new GUILayout.HorizontalScope(GUILayout.MinWidth(160), GUILayout.Height(22)))
|
||||
{
|
||||
ActionButtonGUI(action.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ActionButtonGUI(UnityEngine.Object target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
GUILayout.Button("(No Object)");
|
||||
EditorGUI.EndDisabledGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Type t = target.GetType();
|
||||
|
||||
if (t == typeof(GameObject))
|
||||
{
|
||||
GameObject go = target as GameObject;
|
||||
|
||||
if (GUILayout.Button(" Select ", DiscoverWindow.Styles.buttonLeft))
|
||||
{
|
||||
Selection.activeObject = go;
|
||||
}
|
||||
|
||||
if(PrefabUtility.GetPrefabAssetType(go) == PrefabAssetType.NotAPrefab)
|
||||
{
|
||||
if (GUILayout.Button(" Go to ", DiscoverWindow.Styles.buttonRight))
|
||||
{
|
||||
|
||||
Selection.activeObject = go;
|
||||
SceneView.lastActiveSceneView.FrameSelected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button(" Open ", DiscoverWindow.Styles.buttonRight))
|
||||
{
|
||||
AssetDatabase.OpenAsset(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t == typeof(Discover))
|
||||
{
|
||||
if (GUILayout.Button("Discover"))
|
||||
{
|
||||
var discover = target as Discover;
|
||||
Selection.activeGameObject = discover.gameObject;
|
||||
DiscoverWindow.SelectDiscover(discover);
|
||||
}
|
||||
}
|
||||
else if (t == typeof(VisualEffectAsset))
|
||||
{
|
||||
if (GUILayout.Button("Open VFX Graph"))
|
||||
{
|
||||
VisualEffectAsset graph = target as VisualEffectAsset;
|
||||
AssetDatabase.OpenAsset(graph);
|
||||
}
|
||||
}
|
||||
else if (t == typeof(Animation))
|
||||
{
|
||||
if (GUILayout.Button("Open Animation"))
|
||||
{
|
||||
Animation animation = target as Animation;
|
||||
AssetDatabase.OpenAsset(animation);
|
||||
}
|
||||
}
|
||||
else if (t == typeof(TimelineAsset))
|
||||
{
|
||||
if (GUILayout.Button("Open Timeline"))
|
||||
{
|
||||
TimelineAsset timeline = target as TimelineAsset;
|
||||
AssetDatabase.OpenAsset(timeline);
|
||||
}
|
||||
}
|
||||
else if (t == typeof(PlayableDirector))
|
||||
{
|
||||
if (GUILayout.Button("Open Director"))
|
||||
{
|
||||
PlayableDirector director = target as PlayableDirector;
|
||||
|
||||
AssetDatabase.OpenAsset(director.playableAsset);
|
||||
Selection.activeObject = director.gameObject;
|
||||
}
|
||||
}
|
||||
else if (t == typeof(Shader))
|
||||
{
|
||||
if (GUILayout.Button("Open Shader"))
|
||||
{
|
||||
Shader shader = target as Shader;
|
||||
AssetDatabase.OpenAsset(shader);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button("Select"))
|
||||
{
|
||||
Selection.activeObject = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ec5c4aa8c38a45743b935b39eceb9b44
|
||||
guid: b1c258c43239c974080376d0bf372421
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -0,0 +1,604 @@
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using UnityEngine.Experimental.VFX;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class DiscoverWindow : EditorWindow
|
||||
{
|
||||
static List<DiscoverAsset> s_StartupDiscoverAssets;
|
||||
|
||||
static bool GetShowOnStartup(string name)
|
||||
{
|
||||
return EditorPrefs.GetBool($"{name}.ShowAtStartup", true);
|
||||
}
|
||||
|
||||
static void SetShowOnStartup(string name, bool value)
|
||||
{
|
||||
if (value != GetShowOnStartup(name)) EditorPrefs.SetBool($"{name}.ShowAtStartup", value);
|
||||
}
|
||||
|
||||
public static void SelectDiscover(Discover discover)
|
||||
{
|
||||
foreach(var window in s_Windows)
|
||||
{
|
||||
foreach(var categoryKvp in window.discoverObjects)
|
||||
{
|
||||
if (categoryKvp.Value.Contains(discover))
|
||||
{
|
||||
window.SetSelectedDiscover(discover);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reload()
|
||||
{
|
||||
EditorApplication.update -= ShowAtStartup;
|
||||
s_StartupDiscoverAssets = null;
|
||||
InitShowAtStartup();
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void InitShowAtStartup()
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("t:DiscoverAsset");
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
DiscoverAsset asset = AssetDatabase.LoadAssetAtPath<DiscoverAsset>(AssetDatabase.GUIDToAssetPath(guid));
|
||||
if (asset.EnableShowAtStartup)
|
||||
{
|
||||
if (s_StartupDiscoverAssets == null)
|
||||
s_StartupDiscoverAssets = new List<DiscoverAsset>();
|
||||
|
||||
s_StartupDiscoverAssets.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_StartupDiscoverAssets != null && s_StartupDiscoverAssets.Count > 0)
|
||||
EditorApplication.update += ShowAtStartup;
|
||||
}
|
||||
|
||||
static void ShowAtStartup()
|
||||
{
|
||||
if (!Application.isPlaying && s_StartupDiscoverAssets != null)
|
||||
{
|
||||
foreach (var discoverAsset in s_StartupDiscoverAssets)
|
||||
{
|
||||
if (GetShowOnStartup(discoverAsset.PreferenceName))
|
||||
ShowDiscoverWindow(discoverAsset);
|
||||
}
|
||||
}
|
||||
EditorApplication.update -= ShowAtStartup;
|
||||
}
|
||||
|
||||
static List<DiscoverWindow> s_Windows;
|
||||
|
||||
public static void ShowDiscoverWindow(DiscoverAsset discoverAsset)
|
||||
{
|
||||
if (discoverAsset != null)
|
||||
{
|
||||
var window = GetWindow<DiscoverWindow>(true);
|
||||
window.SetDiscoverAsset(discoverAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Could not open Discover Window : discoverAsset is null");
|
||||
}
|
||||
}
|
||||
|
||||
public DiscoverAsset discoverAsset { get; private set; }
|
||||
Texture2D header;
|
||||
bool forceGlobal;
|
||||
|
||||
void SetDiscoverAsset(DiscoverAsset discover)
|
||||
{
|
||||
discoverAsset = discover;
|
||||
titleContent = new GUIContent(discoverAsset.WindowTitle);
|
||||
minSize = new Vector2(discoverAsset.WindowWidth, discoverAsset.WindowHeight);
|
||||
maxSize = new Vector2(discoverAsset.WindowWidth, discoverAsset.WindowHeight);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
UpdateDiscoverObjects();
|
||||
EditorSceneManager.newSceneCreated += UpdateDiscoverObjectsOnCreate;
|
||||
EditorSceneManager.sceneOpened += UpdateDiscoverObjectsOnLoad;
|
||||
EditorSceneSetup.onSetupLoaded += UpdateDiscoverObjectsOnLoadSetup;
|
||||
|
||||
if (s_Windows == null)
|
||||
s_Windows = new List<DiscoverWindow>();
|
||||
|
||||
if(!s_Windows.Contains(this))
|
||||
s_Windows.Add(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorSceneManager.newSceneCreated -= UpdateDiscoverObjectsOnCreate;
|
||||
EditorSceneManager.sceneOpened -= UpdateDiscoverObjectsOnLoad;
|
||||
EditorSceneSetup.onSetupLoaded -= UpdateDiscoverObjectsOnLoadSetup;
|
||||
|
||||
if (s_Windows.Contains(this))
|
||||
s_Windows.Remove(this);
|
||||
}
|
||||
|
||||
Dictionary<string, List<Discover>> discoverObjects = null;
|
||||
|
||||
void UpdateDiscoverObjectsOnLoadSetup(EditorSceneSetup setup)
|
||||
{
|
||||
forceGlobal = false;
|
||||
UpdateDiscoverObjects();
|
||||
}
|
||||
|
||||
void UpdateDiscoverObjectsOnCreate(Scene scene, NewSceneSetup setup, NewSceneMode mode)
|
||||
{
|
||||
forceGlobal = false;
|
||||
UpdateDiscoverObjects();
|
||||
}
|
||||
|
||||
void UpdateDiscoverObjectsOnLoad(Scene s, OpenSceneMode s2)
|
||||
{
|
||||
forceGlobal = false;
|
||||
UpdateDiscoverObjects();
|
||||
}
|
||||
|
||||
void UpdateDiscoverObjects(bool clear = false)
|
||||
{
|
||||
if (discoverObjects == null)
|
||||
discoverObjects = new Dictionary<string, List<Discover>>();
|
||||
|
||||
if (clear)
|
||||
discoverObjects.Clear();
|
||||
|
||||
Discover[] newOnes = FindObjectsOfType<Discover>();
|
||||
|
||||
// Add new ones
|
||||
foreach (var item in newOnes)
|
||||
{
|
||||
if (!discoverObjects.ContainsKey(item.Category))
|
||||
{
|
||||
discoverObjects.Add(item.Category, new List<Discover>());
|
||||
}
|
||||
|
||||
if (!discoverObjects[item.Category].Contains(item))
|
||||
{
|
||||
discoverObjects[item.Category].Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup Empty Entries
|
||||
Dictionary<string, List<Discover>> cleanedUpLists = new Dictionary<string, List<Discover>>();
|
||||
|
||||
foreach (var categoryKvp in discoverObjects)
|
||||
{
|
||||
cleanedUpLists.Add(categoryKvp.Key, categoryKvp.Value.Where((o) => o != null).ToList());
|
||||
}
|
||||
foreach (var categoryKvp in cleanedUpLists)
|
||||
{
|
||||
discoverObjects[categoryKvp.Key] = categoryKvp.Value;
|
||||
}
|
||||
|
||||
// Cleanup Empty Categories
|
||||
List<string> toDelete = new List<string>();
|
||||
foreach (var categoryKvp in discoverObjects)
|
||||
{
|
||||
if (categoryKvp.Value == null || categoryKvp.Value.Count == 0)
|
||||
toDelete.Add(categoryKvp.Key);
|
||||
}
|
||||
foreach (var category in toDelete)
|
||||
{
|
||||
discoverObjects.Remove(category);
|
||||
}
|
||||
|
||||
// Finally, sort items in each category
|
||||
foreach (var categoryKvp in discoverObjects)
|
||||
{
|
||||
discoverObjects[categoryKvp.Key].Sort((a, b) => { return Comparer<int>.Default.Compare(a.Priority, b.Priority); });
|
||||
}
|
||||
|
||||
// Ensure something is selected is possible
|
||||
|
||||
if (selectedDiscover == null) // Try Fetching a default
|
||||
{
|
||||
foreach (var categoryKvp in discoverObjects)
|
||||
{
|
||||
selectedDiscover = categoryKvp.Value.FirstOrDefault(o => o.DefaultSelected == true);
|
||||
if (selectedDiscover != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedDiscover == null && discoverObjects != null && discoverObjects.Count > 0)
|
||||
{
|
||||
selectedDiscover = discoverObjects.First().Value.First();
|
||||
}
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
// Draw Header Image
|
||||
if (discoverAsset.HeaderTexture != null)
|
||||
{
|
||||
if (header == null || header != discoverAsset.HeaderTexture)
|
||||
header = discoverAsset.HeaderTexture;
|
||||
|
||||
Rect headerRect = GUILayoutUtility.GetRect(header.width, header.height);
|
||||
GUI.DrawTexture(headerRect, header);
|
||||
}
|
||||
else
|
||||
{
|
||||
Rect headerRect = GUILayoutUtility.GetRect(discoverAsset.WindowWidth, 80);
|
||||
EditorGUI.DrawRect(headerRect, new Color(0,0,0,0.2f));
|
||||
headerRect.xMin += 16;
|
||||
headerRect.yMin += 16;
|
||||
GUI.Label(headerRect, discoverAsset.WindowTitle, Styles.header);
|
||||
}
|
||||
|
||||
bool hasContent = discoverObjects != null && discoverObjects.Count > 0;
|
||||
|
||||
// Draw Navigation Bar
|
||||
EditorGUI.BeginDisabledGroup(!hasContent);
|
||||
using (new GUILayout.AreaScope(new Rect(discoverAsset.WindowWidth - 168, 8, 160, 20)))
|
||||
{
|
||||
using (new GUILayout.HorizontalScope(Styles.tabContainer))
|
||||
{
|
||||
bool value = forceGlobal;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
value = GUILayout.Toggle(forceGlobal || !hasContent, "Levels", Styles.buttonLeft);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
forceGlobal = true;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
value = GUILayout.Toggle(!forceGlobal && hasContent, "Discover", Styles.buttonRight);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
forceGlobal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// Draw Content
|
||||
if (!hasContent || forceGlobal)
|
||||
GlobalContentGUI();
|
||||
else
|
||||
SceneContentGUI();
|
||||
|
||||
// Draw Footer
|
||||
Rect line = GUILayoutUtility.GetRect(discoverAsset.WindowWidth, 1);
|
||||
EditorGUI.DrawRect(line, Color.black);
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
if(discoverAsset.EnableShowAtStartup)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool showOnStartup = GUILayout.Toggle(GetShowOnStartup(discoverAsset.PreferenceName), " Show this window on startup");
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
SetShowOnStartup(discoverAsset.PreferenceName, showOnStartup);
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if(discoverAsset.Debug)
|
||||
{
|
||||
if (GUILayout.Button("Select DiscoverAsset"))
|
||||
Selection.activeObject = discoverAsset;
|
||||
|
||||
if (GUILayout.Button("Reload"))
|
||||
UpdateDiscoverObjects(true);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Close"))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 globalContentScroll;
|
||||
|
||||
void GlobalContentGUI()
|
||||
{
|
||||
globalContentScroll = GUILayout.BeginScrollView(globalContentScroll);
|
||||
using (new GUILayout.VerticalScope(Styles.indent))
|
||||
{
|
||||
GUILayout.Label(discoverAsset.Title, Styles.header);
|
||||
using (new GUILayout.VerticalScope(Styles.indent))
|
||||
{
|
||||
GUILayout.Label(discoverAsset.Description, Styles.body);
|
||||
|
||||
if(discoverAsset.Scenes != null)
|
||||
{
|
||||
foreach (var map in discoverAsset.Scenes)
|
||||
{
|
||||
using (new GroupLabelScope(map.Title))
|
||||
{
|
||||
GUILayout.Label(map.Description, Styles.body);
|
||||
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (map.SceneSetups != null)
|
||||
{
|
||||
foreach(var sceneSetup in map.SceneSetups)
|
||||
{
|
||||
if (sceneSetup != null && GUILayout.Button($"Open {sceneSetup.name}"))
|
||||
LoadSceneSetup(sceneSetup);
|
||||
}
|
||||
}
|
||||
if(map.SingleScenes != null)
|
||||
{
|
||||
foreach(var singleScene in map.SingleScenes)
|
||||
{
|
||||
if (singleScene != null && GUILayout.Button($"Open {singleScene.name}"))
|
||||
LoadSingleScene(singleScene);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
Discover selectedDiscover;
|
||||
Vector2 listScroll;
|
||||
Vector2 contentScroll;
|
||||
|
||||
void SceneContentGUI()
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
using (new GUILayout.VerticalScope())
|
||||
{
|
||||
listScroll = GUILayout.BeginScrollView(listScroll, GUI.skin.box, GUILayout.Width(discoverAsset.DiscoverListWidth));
|
||||
using (new GUILayout.VerticalScope(GUILayout.ExpandHeight(true)))
|
||||
{
|
||||
foreach (var category in discoverObjects.Keys.OrderBy((x) => x.ToString()))
|
||||
{
|
||||
if(!string.IsNullOrEmpty(category))
|
||||
GUILayout.Label(category, EditorStyles.boldLabel);
|
||||
|
||||
foreach (var item in discoverObjects[category])
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool value = GUILayout.Toggle(item == selectedDiscover, item.Name, Styles.listItem);
|
||||
|
||||
if (value)
|
||||
{
|
||||
// Select the new one if not selected
|
||||
if(selectedDiscover != item)
|
||||
{
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (discoverAsset.Debug)
|
||||
Selection.activeObject = item;
|
||||
|
||||
SetSelectedDiscover(item);
|
||||
}
|
||||
}
|
||||
|
||||
Rect r = GUILayoutUtility.GetLastRect();
|
||||
int c = EditorGUIUtility.isProSkin ? 1 : 0;
|
||||
EditorGUI.DrawRect(r, new Color(c, c, c, 0.1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
GUILayout.Space(4);
|
||||
|
||||
using (new GUILayout.VerticalScope(GUILayout.Width(440)))
|
||||
{
|
||||
contentScroll = GUILayout.BeginScrollView(contentScroll);
|
||||
GUILayout.Space(8);
|
||||
|
||||
DiscoverEditor.DrawDiscoverContentGUI(selectedDiscover);
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetSelectedDiscover(Discover newSelection)
|
||||
{
|
||||
|
||||
if (SceneView.lastActiveSceneView != null && newSelection.AlignViewToTransform)
|
||||
{
|
||||
SceneView.lastActiveSceneView.AlignViewToObject(newSelection.transform);
|
||||
}
|
||||
|
||||
if (selectedDiscover.ObjectsToToggle != null)
|
||||
{
|
||||
// Reverse Toggle previous GameObjects state
|
||||
foreach (var go in selectedDiscover.ObjectsToToggle)
|
||||
{
|
||||
if (go.GameObject == null)
|
||||
continue;
|
||||
|
||||
switch (go.State)
|
||||
{
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Disable:
|
||||
go.GameObject.SetActive(true);
|
||||
break;
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Enable:
|
||||
go.GameObject.SetActive(false);
|
||||
break;
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Toggle:
|
||||
go.GameObject.SetActive(go.GameObject.activeSelf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new item
|
||||
selectedDiscover = newSelection;
|
||||
|
||||
if (selectedDiscover.ObjectsToToggle != null)
|
||||
{
|
||||
// Toggle Next GameObjects State
|
||||
foreach (var go in selectedDiscover.ObjectsToToggle)
|
||||
{
|
||||
if (go.GameObject == null)
|
||||
continue;
|
||||
|
||||
switch (go.State)
|
||||
{
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Disable:
|
||||
go.GameObject.SetActive(false);
|
||||
break;
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Enable:
|
||||
go.GameObject.SetActive(true);
|
||||
break;
|
||||
case Actions.ToggleGameObjectAction.GameObjectToggle.GameObjectToggleState.Toggle:
|
||||
go.GameObject.SetActive(go.GameObject.activeSelf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
contentScroll = Vector2.zero;
|
||||
}
|
||||
|
||||
void LoadSceneSetup(EditorSceneSetup setup)
|
||||
{
|
||||
try
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Discover", $"Opening {setup.name}...", 0.9f);
|
||||
forceGlobal = false;
|
||||
EditorSceneSetup.RestoreSetup(setup);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.LogError($"Could not load EditorSceneSetup : {setup.name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
UpdateDiscoverObjects();
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSingleScene(SceneAsset scene)
|
||||
{
|
||||
try
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Discover", $"Opening {scene.name}...", 0.9f);
|
||||
forceGlobal = false;
|
||||
EditorSceneManager.OpenScene(AssetDatabase.GetAssetPath(scene), OpenSceneMode.Single);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.LogError($"Could not load Scene : {scene.name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
UpdateDiscoverObjects();
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupLabelScope : GUILayout.VerticalScope
|
||||
{
|
||||
public GroupLabelScope(string name) : base(Styles.box)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
GUIContent n = new GUIContent(name);
|
||||
Rect r = GUILayoutUtility.GetRect(n, Styles.boxHeader, GUILayout.ExpandWidth(true));
|
||||
GUI.Label(r, n, Styles.boxHeader);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class Styles
|
||||
{
|
||||
public static GUIStyle indent;
|
||||
public static GUIStyle slightIndent;
|
||||
|
||||
public static GUIStyle header;
|
||||
public static GUIStyle subHeader;
|
||||
public static GUIStyle body;
|
||||
|
||||
public static GUIStyle box;
|
||||
public static GUIStyle boxHeader;
|
||||
|
||||
public static GUIStyle listItem;
|
||||
|
||||
public static GUIStyle buttonLeft;
|
||||
public static GUIStyle buttonMid;
|
||||
public static GUIStyle buttonRight;
|
||||
|
||||
public static GUIStyle tabContainer;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
header = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||
header.fontSize = 24;
|
||||
header.padding = new RectOffset(0, 0, -4, -4);
|
||||
header.richText = true;
|
||||
|
||||
subHeader = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||
subHeader.fontSize = 11;
|
||||
subHeader.fontStyle = FontStyle.Italic;
|
||||
|
||||
body = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||
body.fontSize = 11;
|
||||
body.richText = true;
|
||||
|
||||
indent = new GUIStyle();
|
||||
indent.padding = new RectOffset(12, 12, 12, 12);
|
||||
|
||||
slightIndent = new GUIStyle();
|
||||
slightIndent.padding = new RectOffset(6, 6, 0, 6);
|
||||
|
||||
box = new GUIStyle(EditorStyles.helpBox);
|
||||
|
||||
boxHeader = new GUIStyle(GUI.skin.box);
|
||||
boxHeader.normal.textColor = GUI.skin.label.normal.textColor;
|
||||
boxHeader.fixedHeight = 20;
|
||||
boxHeader.fontSize = 11;
|
||||
boxHeader.fontStyle = FontStyle.Bold;
|
||||
boxHeader.alignment = TextAnchor.UpperLeft;
|
||||
boxHeader.margin = new RectOffset(0, 0, 0, 6);
|
||||
|
||||
listItem = new GUIStyle(EditorStyles.label);
|
||||
listItem.padding = new RectOffset(12, 0, 2, 2);
|
||||
|
||||
buttonLeft = new GUIStyle(EditorStyles.miniButtonLeft);
|
||||
buttonLeft.fontSize = 11;
|
||||
buttonMid = new GUIStyle(EditorStyles.miniButtonMid);
|
||||
buttonMid.fontSize = 11;
|
||||
buttonRight = new GUIStyle(EditorStyles.miniButtonRight);
|
||||
buttonRight.fontSize = 11;
|
||||
|
||||
tabContainer = new GUIStyle(EditorStyles.miniButton);
|
||||
tabContainer.padding = new RectOffset(4, 4, 0, 0);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6206e16563e844a4f897a8cc92efc86a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da253dff53554a5498211c4caae380cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,108 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditor.Callbacks;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class EditorSceneSetup : ScriptableObject
|
||||
{
|
||||
[MenuItem("File/Save Scene Setup As... #%&S", priority = 171)]
|
||||
static void SaveSetup()
|
||||
{
|
||||
string path = EditorUtility.SaveFilePanelInProject("Save EditorSceneSetup", "New EditorSceneSetup", "asset", "Save EditorSceneSetup?");
|
||||
if(path != string.Empty)
|
||||
{
|
||||
EditorSceneSetup setup = GetCurrentSetup();
|
||||
AssetDatabase.CreateAsset(setup, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public delegate void EditorSceneSetupLoadedDelegate(EditorSceneSetup setup);
|
||||
public static event EditorSceneSetupLoadedDelegate onSetupLoaded;
|
||||
|
||||
[OnOpenAsset]
|
||||
static bool OnOpenAsset(int instanceID, int line)
|
||||
{
|
||||
var obj = EditorUtility.InstanceIDToObject(instanceID);
|
||||
if(obj is EditorSceneSetup)
|
||||
{
|
||||
EditorSceneSetup setup = (EditorSceneSetup)obj;
|
||||
int active = setup.ActiveScene;
|
||||
|
||||
try
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Loading Scenes", string.Format("Loading Scene Setup {0}....", setup.name), 1.0f);
|
||||
RestoreSetup(setup);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/Editor Scene Setup", priority = 200)]
|
||||
static void CreateAsset()
|
||||
{
|
||||
AssetFactory.CreateAssetInProjectWindow<EditorSceneSetup>("SceneSet Icon", "New SceneSetup.asset");
|
||||
}
|
||||
|
||||
public int ActiveScene;
|
||||
public EditorScene[] LoadedScenes;
|
||||
|
||||
[System.Serializable]
|
||||
public struct EditorScene
|
||||
{
|
||||
public SceneAsset Scene;
|
||||
public bool Loaded;
|
||||
}
|
||||
|
||||
public static EditorSceneSetup GetCurrentSetup()
|
||||
{
|
||||
var scenesetups = EditorSceneManager.GetSceneManagerSetup();
|
||||
|
||||
var editorSetup = CreateInstance<EditorSceneSetup>();
|
||||
|
||||
int i = 0;
|
||||
editorSetup.LoadedScenes = new EditorScene[scenesetups.Length];
|
||||
foreach(var setup in scenesetups)
|
||||
{
|
||||
if (setup.isActive)
|
||||
editorSetup.ActiveScene = i;
|
||||
|
||||
editorSetup.LoadedScenes[i].Scene = AssetDatabase.LoadAssetAtPath<SceneAsset>(setup.path);
|
||||
editorSetup.LoadedScenes[i].Loaded = setup.isLoaded;
|
||||
|
||||
i++;
|
||||
}
|
||||
return editorSetup;
|
||||
}
|
||||
|
||||
public static void RestoreSetup(EditorSceneSetup editorSetup)
|
||||
{
|
||||
SceneSetup[] setups = new SceneSetup[editorSetup.LoadedScenes.Length];
|
||||
|
||||
for(int i = 0; i < setups.Length; i++)
|
||||
{
|
||||
setups[i] = new SceneSetup();
|
||||
string path = AssetDatabase.GetAssetPath(editorSetup.LoadedScenes[i].Scene);
|
||||
setups[i].path = path;
|
||||
setups[i].isLoaded = editorSetup.LoadedScenes[i].Loaded;
|
||||
setups[i].isActive = (editorSetup.ActiveScene == i);
|
||||
}
|
||||
|
||||
EditorSceneManager.RestoreSceneManagerSetup(setups);
|
||||
|
||||
if(onSetupLoaded != null)
|
||||
onSetupLoaded.Invoke(editorSetup);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8bf8857f1af056d4894bee553e091ceb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 5498606499726036565, guid: 0000000000000000d000000000000000, type: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,88 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using System;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
[CustomEditor(typeof(EditorSceneSetup))]
|
||||
public class EditorSceneSetupEditor : UnityEditor.Editor
|
||||
{
|
||||
ReorderableList m_List;
|
||||
|
||||
SerializedProperty m_LoadedScenes;
|
||||
SerializedProperty m_ActiveScene;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_ActiveScene = serializedObject.FindProperty("ActiveScene");
|
||||
m_LoadedScenes = serializedObject.FindProperty("LoadedScenes");
|
||||
|
||||
m_List = new ReorderableList(serializedObject, m_LoadedScenes, true, true, true, true);
|
||||
m_List.drawElementCallback = OnDrawElement;
|
||||
m_List.drawHeaderCallback = OnDrawHeader;
|
||||
|
||||
}
|
||||
|
||||
private void OnDrawHeader(Rect rect)
|
||||
{
|
||||
GUI.Label(rect, "Scene List");
|
||||
}
|
||||
|
||||
private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
var toggleRect = rect;
|
||||
toggleRect.width = 16;
|
||||
toggleRect.yMin += 2;
|
||||
|
||||
var sceneRect = rect;
|
||||
sceneRect.xMin += 24;
|
||||
sceneRect.xMax -= 80;
|
||||
sceneRect.yMin += 2;
|
||||
sceneRect.height = 16;
|
||||
|
||||
var loadedRect = rect;
|
||||
loadedRect.xMin = rect.xMax - 80;
|
||||
loadedRect.yMin += 2;
|
||||
|
||||
bool active = m_ActiveScene.intValue == index;
|
||||
bool newActive = GUI.Toggle(toggleRect, active, GUIContent.none);
|
||||
if(GUI.changed && newActive != active)
|
||||
{
|
||||
m_ActiveScene.intValue = index;
|
||||
}
|
||||
|
||||
var sceneAsset = (SceneAsset)EditorGUI.ObjectField(sceneRect, m_LoadedScenes.GetArrayElementAtIndex(index).FindPropertyRelative("Scene").objectReferenceValue, typeof(SceneAsset), false);
|
||||
if (GUI.changed)
|
||||
{
|
||||
m_LoadedScenes.GetArrayElementAtIndex(index).FindPropertyRelative("Scene").objectReferenceValue = sceneAsset;
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(index == 0);
|
||||
int visible = m_LoadedScenes.GetArrayElementAtIndex(index).FindPropertyRelative("Loaded").boolValue ? 1 : 0;
|
||||
visible = EditorGUI.IntPopup(loadedRect, visible, kLoadedItems, kLoadedIndices);
|
||||
|
||||
if(GUI.changed)
|
||||
{
|
||||
m_LoadedScenes.GetArrayElementAtIndex(index).FindPropertyRelative("Loaded").boolValue = visible == 1 ? true : false;
|
||||
} else if(index == 0)
|
||||
{
|
||||
m_LoadedScenes.GetArrayElementAtIndex(index).FindPropertyRelative("Loaded").boolValue = true;
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
static readonly int[] kLoadedIndices = new int[2] { 0, 1 };
|
||||
static readonly GUIContent[] kLoadedItems = new GUIContent[2] { new GUIContent("Not Loaded"), new GUIContent("Loaded") };
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
m_List.DoLayoutList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a0875758aceaa3a4b96c65bf4fb85f50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 869fd8f9cbfa0cd468609db02f580990
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,462 @@
|
|||
using System.Reflection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class FindAndReplaceWindow : EditorWindow
|
||||
{
|
||||
[MenuItem("Edit/Find And Replace... %&#F", priority = 144)]
|
||||
static void OpenWindow()
|
||||
{
|
||||
GetWindow<FindAndReplaceWindow>();
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, Type> s_assemblyTypes = GetTypes();
|
||||
|
||||
private static Dictionary<string, Type> GetTypes()
|
||||
{
|
||||
Dictionary<string, Type> all = new Dictionary<string, Type>();
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (Type t in assembly.GetTypes())
|
||||
{
|
||||
if (typeof(Component).IsAssignableFrom(t) && !all.ContainsKey(t.Name))
|
||||
all.Add(t.Name, t);
|
||||
}
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum SearchBy
|
||||
{
|
||||
Name,
|
||||
ComponentType,
|
||||
Tag,
|
||||
Layer,
|
||||
Mesh,
|
||||
Material,
|
||||
Selection
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
titleContent = Contents.title;
|
||||
minSize = new Vector2(640, 280);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
using (new GUILayout.VerticalScope(GUILayout.Width(320)))
|
||||
{
|
||||
SearchControlsGUI();
|
||||
}
|
||||
|
||||
using (new GUILayout.VerticalScope(GUILayout.Width(4)))
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
Rect r = GUILayoutUtility.GetLastRect();
|
||||
EditorGUI.DrawRect(r, Color.black);
|
||||
}
|
||||
|
||||
using (new GUILayout.VerticalScope())
|
||||
{
|
||||
SearchResultsGUI();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
GameObject prefabReplacement;
|
||||
[SerializeField]
|
||||
SearchBy searchBy;
|
||||
|
||||
[SerializeField]
|
||||
string nameSearch = "GameObject";
|
||||
[SerializeField]
|
||||
string tagSearch = "Player";
|
||||
[SerializeField]
|
||||
string layerSearch = "PostProcessing";
|
||||
[SerializeField]
|
||||
string componentSearch = "Light";
|
||||
[SerializeField]
|
||||
Mesh meshSearch;
|
||||
[SerializeField]
|
||||
Material materialSearch;
|
||||
[SerializeField]
|
||||
bool selectionRecurse = false;
|
||||
|
||||
[SerializeField]
|
||||
bool keepPosition = true;
|
||||
[SerializeField]
|
||||
bool keepRotation = true;
|
||||
[SerializeField]
|
||||
bool keepScale = false;
|
||||
[SerializeField]
|
||||
bool keepParenting = true;
|
||||
[SerializeField]
|
||||
bool keepName = true;
|
||||
[SerializeField]
|
||||
bool keepTag = false;
|
||||
[SerializeField]
|
||||
bool keepLayer = true;
|
||||
[SerializeField]
|
||||
bool keepStatic = false;
|
||||
[SerializeField]
|
||||
bool unpackPrefab = false;
|
||||
|
||||
enum SearchOp
|
||||
{
|
||||
Find,
|
||||
Add,
|
||||
Refine
|
||||
}
|
||||
|
||||
void SearchControlsGUI()
|
||||
{
|
||||
EditorGUIUtility.labelWidth = 120;
|
||||
GUILayout.Space(4);
|
||||
GUILayout.Label("Search Scene Objects", Styles.boldLabel);
|
||||
searchBy = (SearchBy)EditorGUILayout.EnumPopup(Contents.criteria, searchBy);
|
||||
switch (searchBy)
|
||||
{
|
||||
case SearchBy.Name:
|
||||
nameSearch = EditorGUILayout.TextField(Contents.nameSearch, nameSearch);
|
||||
SearchButtonsGUI(searchBy, nameSearch);
|
||||
break;
|
||||
case SearchBy.Tag:
|
||||
tagSearch = EditorGUILayout.TextField(Contents.tagSearch, tagSearch);
|
||||
SearchButtonsGUI(searchBy, tagSearch);
|
||||
break;
|
||||
case SearchBy.Layer:
|
||||
layerSearch = EditorGUILayout.TextField(Contents.layerSearch, layerSearch);
|
||||
SearchButtonsGUI(searchBy, layerSearch);
|
||||
break;
|
||||
case SearchBy.ComponentType:
|
||||
componentSearch = EditorGUILayout.TextField(Contents.componentSearch, componentSearch);
|
||||
SearchButtonsGUI(searchBy, componentSearch);
|
||||
break;
|
||||
case SearchBy.Mesh:
|
||||
meshSearch = (Mesh)EditorGUILayout.ObjectField(Contents.meshSearch, meshSearch, typeof(Mesh), true);
|
||||
SearchButtonsGUI(searchBy, meshSearch);
|
||||
break;
|
||||
case SearchBy.Material:
|
||||
materialSearch = (Material)EditorGUILayout.ObjectField(Contents.materialSearch, materialSearch, typeof(Material), true);
|
||||
SearchButtonsGUI(searchBy, materialSearch);
|
||||
break;
|
||||
case SearchBy.Selection:
|
||||
selectionRecurse = EditorGUILayout.Toggle(Contents.selectionRecurse, selectionRecurse);
|
||||
SearchButtonsGUI(searchBy, selectionRecurse);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Label("Replace Results", Styles.boldLabel);
|
||||
prefabReplacement = (GameObject)EditorGUILayout.ObjectField(Contents.prefabReplacement, prefabReplacement, typeof(GameObject), true);
|
||||
|
||||
if (prefabReplacement != null)
|
||||
{
|
||||
PrefabAssetType type = PrefabUtility.GetPrefabAssetType(prefabReplacement);
|
||||
bool isAPrefab = type == PrefabAssetType.Model || type == PrefabAssetType.Regular || type == PrefabAssetType.Variant;
|
||||
if(isAPrefab)
|
||||
unpackPrefab = EditorGUILayout.Toggle("Unpack Prefab", unpackPrefab);
|
||||
}
|
||||
EditorGUI.BeginDisabledGroup(prefabReplacement == null);
|
||||
|
||||
GUILayout.Label("Keep Properties from Original:");
|
||||
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
keepPosition = GUILayout.Toggle(keepPosition, "Position", EditorStyles.miniButtonLeft, GUILayout.Height(16));
|
||||
keepRotation = GUILayout.Toggle(keepRotation, "Rotation", EditorStyles.miniButtonMid, GUILayout.Height(16));
|
||||
keepScale = GUILayout.Toggle(keepScale, "Scale", EditorStyles.miniButtonMid, GUILayout.Height(16));
|
||||
keepParenting = GUILayout.Toggle(keepParenting, "Parenting", EditorStyles.miniButtonRight, GUILayout.Height(16));
|
||||
}
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
keepName = GUILayout.Toggle(keepName, "Name", EditorStyles.miniButtonLeft, GUILayout.Height(16));
|
||||
keepTag = GUILayout.Toggle(keepTag, "Tag", EditorStyles.miniButtonMid, GUILayout.Height(16));
|
||||
keepLayer = GUILayout.Toggle(keepLayer, "Layer", EditorStyles.miniButtonMid, GUILayout.Height(16));
|
||||
keepStatic = GUILayout.Toggle(keepStatic, "Static", EditorStyles.miniButtonRight, GUILayout.Height(16));
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Replace All", Styles.bigButton, GUILayout.Height(24)) && prefabReplacement != null)
|
||||
{
|
||||
Undo.RecordObjects(searchResults.ToArray(), "Replace Objects");
|
||||
|
||||
for(int i = 0; i < searchResults.Count; i++)
|
||||
{
|
||||
var obj = searchResults[i];
|
||||
var newObj = SwapObject(obj, prefabReplacement, searchResults);
|
||||
searchResults[i] = newObj;
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
GUILayout.Space(8);
|
||||
}
|
||||
|
||||
GameObject SwapObject(GameObject toReplace, GameObject replacement, List<GameObject> others)
|
||||
{
|
||||
GameObject newObj;
|
||||
|
||||
if (PrefabUtility.GetPrefabAssetType(replacement) != PrefabAssetType.NotAPrefab && !unpackPrefab)
|
||||
newObj = (GameObject)PrefabUtility.InstantiatePrefab(replacement);
|
||||
else
|
||||
newObj = Instantiate(replacement);
|
||||
|
||||
if (keepName)
|
||||
newObj.name = toReplace.name;
|
||||
|
||||
if(keepPosition)
|
||||
newObj.transform.position = toReplace.transform.position;
|
||||
if(keepRotation)
|
||||
newObj.transform.rotation = toReplace.transform.rotation;
|
||||
if(keepParenting)
|
||||
newObj.transform.parent = toReplace.transform.parent;
|
||||
if(keepScale)
|
||||
newObj.transform.localScale = toReplace.transform.localScale;
|
||||
|
||||
if(keepTag)
|
||||
newObj.tag = toReplace.tag;
|
||||
if(keepLayer)
|
||||
newObj.layer = toReplace.layer;
|
||||
if(keepStatic)
|
||||
newObj.isStatic = toReplace.isStatic;
|
||||
|
||||
foreach(var other in others)
|
||||
{
|
||||
if(other.transform.parent == toReplace.transform)
|
||||
{
|
||||
other.transform.parent = newObj.transform;
|
||||
}
|
||||
}
|
||||
|
||||
DestroyImmediate(toReplace);
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
void SearchButtonsGUI(SearchBy by, object criteria)
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Find", Styles.bigButton, GUILayout.Height(24)))
|
||||
Search(SearchOp.Find, by, criteria);
|
||||
|
||||
if (GUILayout.Button("Add", Styles.bigButton, GUILayout.Height(24)))
|
||||
Search(SearchOp.Add, by, criteria);
|
||||
|
||||
if (GUILayout.Button("Refine", Styles.bigButton, GUILayout.Height(24)))
|
||||
Search(SearchOp.Refine, by, criteria);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static GameObject[] FindAllSceneObjects()
|
||||
{
|
||||
var all = Resources.FindObjectsOfTypeAll<GameObject>();
|
||||
all = all.Where(o => o.scene.isLoaded).ToArray();
|
||||
return all;
|
||||
}
|
||||
|
||||
void Search(SearchOp op, SearchBy by, object criteria)
|
||||
{
|
||||
List<GameObject> query = new List<GameObject>();
|
||||
|
||||
var all = FindAllSceneObjects();
|
||||
|
||||
switch (by)
|
||||
{
|
||||
case SearchBy.Name:
|
||||
foreach(var go in all)
|
||||
{
|
||||
if (go.name.Contains((string)criteria))
|
||||
query.Add(go);
|
||||
}
|
||||
break;
|
||||
case SearchBy.Tag:
|
||||
query.AddRange(GameObject.FindGameObjectsWithTag((string)criteria));
|
||||
break;
|
||||
case SearchBy.Layer:
|
||||
foreach (var go in all)
|
||||
{
|
||||
if (go.layer == LayerMask.NameToLayer((string)criteria))
|
||||
query.Add(go);
|
||||
}
|
||||
break;
|
||||
case SearchBy.ComponentType:
|
||||
if(s_assemblyTypes.ContainsKey((string)criteria))
|
||||
{
|
||||
Type t = s_assemblyTypes[(string)criteria];
|
||||
if( typeof(Component).IsAssignableFrom(t))
|
||||
{
|
||||
Component[] components = (Component[])Resources.FindObjectsOfTypeAll(t);
|
||||
if(components != null)
|
||||
{
|
||||
foreach(var c in components)
|
||||
{
|
||||
if (c.gameObject.scene != null && !query.Contains(c.gameObject))
|
||||
query.Add(c.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SearchBy.Mesh:
|
||||
Mesh mesh = (Mesh)criteria;
|
||||
foreach (var go in all)
|
||||
{
|
||||
MeshFilter filter = go.GetComponent<MeshFilter>();
|
||||
if (filter != null && filter.sharedMesh == mesh)
|
||||
{
|
||||
query.Add(go);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SearchBy.Material:
|
||||
Material mat = (Material)criteria;
|
||||
foreach (var go in all)
|
||||
{
|
||||
Renderer renderer = go.GetComponent<Renderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
if(renderer.sharedMaterials.Contains(mat))
|
||||
{
|
||||
query.Add(go);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SearchBy.Selection:
|
||||
|
||||
foreach(var selected in Selection.gameObjects)
|
||||
{
|
||||
bool recurse = (bool)criteria;
|
||||
if(!recurse)
|
||||
query.Add(selected);
|
||||
else
|
||||
query.AddRange(selected.GetAllChildren());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case SearchOp.Find:
|
||||
searchResults = query;
|
||||
break;
|
||||
case SearchOp.Add:
|
||||
foreach(var item in query)
|
||||
{
|
||||
if (!searchResults.Contains(item))
|
||||
searchResults.Add(item);
|
||||
}
|
||||
break;
|
||||
case SearchOp.Refine:
|
||||
List<GameObject> refined = new List<GameObject>();
|
||||
foreach (var item in searchResults)
|
||||
{
|
||||
if (query.Contains(item))
|
||||
refined.Add(item);
|
||||
}
|
||||
searchResults = refined;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
List<GameObject> searchResults= new List<GameObject>();
|
||||
Vector2 scroll;
|
||||
|
||||
void SearchResultsGUI()
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Search Results", Styles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if(GUILayout.Button("Select in Scene", GUILayout.Height(24)))
|
||||
{
|
||||
Selection.objects = searchResults.ToArray();
|
||||
}
|
||||
if(GUILayout.Button("Clear", GUILayout.Height(24)))
|
||||
{
|
||||
searchResults.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
scroll = GUILayout.BeginScrollView(scroll, EditorStyles.helpBox);
|
||||
{
|
||||
GameObject toRemove = null;
|
||||
|
||||
// Trim all nulls
|
||||
searchResults = searchResults.Where(o => o != null).ToList();
|
||||
|
||||
foreach(var obj in searchResults)
|
||||
{
|
||||
using (new GUILayout.HorizontalScope(EditorStyles.textField))
|
||||
{
|
||||
GUILayout.Label(obj.name, EditorStyles.label);
|
||||
if(GUILayout.Button("X", GUILayout.Width(32)))
|
||||
{
|
||||
toRemove = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove != null)
|
||||
searchResults.Remove(toRemove);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
}
|
||||
|
||||
static class Contents
|
||||
{
|
||||
public static GUIContent title = new GUIContent("Find and Replace", (Texture)EditorGUIUtility.LoadRequired("ViewToolZoom On"));
|
||||
public static GUIContent criteria = new GUIContent("Criteria");
|
||||
public static GUIContent nameSearch = new GUIContent("GameObject Name");
|
||||
public static GUIContent tagSearch = new GUIContent("Tag");
|
||||
public static GUIContent layerSearch = new GUIContent("Layer");
|
||||
public static GUIContent componentSearch = new GUIContent("Component Type");
|
||||
public static GUIContent meshSearch = new GUIContent("Mesh");
|
||||
public static GUIContent materialSearch = new GUIContent("Material");
|
||||
public static GUIContent selectionRecurse = new GUIContent("Include Children");
|
||||
public static GUIContent prefabReplacement = new GUIContent("Prefab Replacement");
|
||||
}
|
||||
|
||||
static class Styles
|
||||
{
|
||||
public static readonly GUIStyle boldLabel = GetBoldLabel();
|
||||
public static readonly GUIStyle bigButton = GetBigButton();
|
||||
|
||||
static GUIStyle GetBoldLabel()
|
||||
{
|
||||
var style = new GUIStyle(EditorStyles.boldLabel);
|
||||
style.fontSize = 14;
|
||||
return style;
|
||||
}
|
||||
|
||||
static GUIStyle GetBigButton()
|
||||
{
|
||||
var style = new GUIStyle(EditorStyles.miniButton);
|
||||
style.fontSize = 14;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2ee173722a41c4c4c8388b7206e59a83
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b748787917f8d7459fa7973629a9afe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,203 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public static class LinkGameView
|
||||
{
|
||||
static readonly string kPreferenceName = "GameplayIngredients.LinkGameView";
|
||||
static readonly string kLinkCameraName = "___LINK__SCENE__VIEW__CAMERA___";
|
||||
|
||||
public static bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
// Get preference only when not playing
|
||||
if (!Application.isPlaying)
|
||||
m_Active = EditorPrefs.GetBool(kPreferenceName, false);
|
||||
|
||||
return m_Active;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
// Update preference only when not playing
|
||||
if(!Application.isPlaying)
|
||||
EditorPrefs.SetBool(kPreferenceName, value);
|
||||
|
||||
m_Active = value;
|
||||
|
||||
if(s_GameObject != null)
|
||||
s_GameObject.SetActive(value);
|
||||
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
static bool m_Active = false;
|
||||
|
||||
|
||||
public static SceneView LockedSceneView
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_LockedSceneView;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
s_LockedSceneView = value;
|
||||
}
|
||||
}
|
||||
|
||||
static SceneView s_LockedSceneView;
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void Initialize()
|
||||
{
|
||||
SceneView.duringSceneGui += Update;
|
||||
EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
||||
}
|
||||
|
||||
static void OnPlayModeChanged(PlayModeStateChange state)
|
||||
{
|
||||
// Reset State when entering editmode or play mode
|
||||
if(state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
if (Active)
|
||||
Active = true;
|
||||
else
|
||||
Active = false;
|
||||
}
|
||||
else // Cleanup before switching state
|
||||
{
|
||||
if (s_GameObject != null)
|
||||
Object.DestroyImmediate(s_GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
const string kMenuPath = "Edit/Link SceneView and GameView %,";
|
||||
public const int kMenuPriority = 230;
|
||||
|
||||
[MenuItem(kMenuPath, priority = kMenuPriority, validate = false)]
|
||||
static void Toggle()
|
||||
{
|
||||
if (Active)
|
||||
Active = false;
|
||||
else
|
||||
Active = true;
|
||||
}
|
||||
|
||||
[MenuItem(kMenuPath, priority = kMenuPriority, validate = true)]
|
||||
static bool ToggleCheck()
|
||||
{
|
||||
Menu.SetChecked(kMenuPath, Active);
|
||||
return SceneView.sceneViews.Count > 0;
|
||||
}
|
||||
|
||||
static GameObject s_GameObject;
|
||||
|
||||
|
||||
static void Update(SceneView sceneView)
|
||||
{
|
||||
// Check if camera Exists
|
||||
if (s_GameObject == null)
|
||||
{
|
||||
// If disconnected (should not happen, but hey...)
|
||||
var result = GameObject.Find(kLinkCameraName);
|
||||
|
||||
if (result != null) // reconnect
|
||||
s_GameObject = result;
|
||||
else // Create the camera if it does not exist
|
||||
s_GameObject = CreateLinkedCamera();
|
||||
|
||||
if (Application.isPlaying)
|
||||
Active = false;
|
||||
}
|
||||
|
||||
// If we have a VirtualCameraManager, manage its state here
|
||||
if(Application.isPlaying && Manager.Has<VirtualCameraManager>())
|
||||
{
|
||||
var camera = Manager.Get<VirtualCameraManager>().gameObject;
|
||||
|
||||
if(camera.activeInHierarchy && Active) // We need to disable the VirtualCameraManager
|
||||
{
|
||||
camera.SetActive(false);
|
||||
}
|
||||
else if (!camera.activeInHierarchy && !Active) // We need to re-enable the VirtualCameraManager
|
||||
{
|
||||
camera.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (Active)
|
||||
{
|
||||
var sv = s_LockedSceneView == null ? SceneView.lastActiveSceneView : s_LockedSceneView;
|
||||
var sceneCamera = sv.camera;
|
||||
var camera = s_GameObject.GetComponent<Camera>();
|
||||
bool needRepaint = sceneCamera.transform.position != camera.transform.position
|
||||
|| sceneCamera.transform.rotation != camera.transform.rotation
|
||||
|| sceneCamera.fieldOfView != camera.fieldOfView;
|
||||
|
||||
if(needRepaint)
|
||||
{
|
||||
s_GameObject.transform.position = sceneCamera.transform.position;
|
||||
s_GameObject.transform.rotation = sceneCamera.transform.rotation;
|
||||
camera.orthographic = sceneCamera.orthographic;
|
||||
camera.fieldOfView = sceneCamera.fieldOfView;
|
||||
camera.orthographicSize = sceneCamera.orthographicSize;
|
||||
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
needRepaint = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string kDefaultPrefabName = "LinkGameViewCamera";
|
||||
|
||||
static GameObject CreateLinkedCamera()
|
||||
{
|
||||
// Try to find an Asset named as the default name
|
||||
string[] assets = AssetDatabase.FindAssets(kDefaultPrefabName);
|
||||
if(assets.Length > 0)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(assets[0]);
|
||||
GameObject obj = (GameObject)AssetDatabase.LoadAssetAtPath(path, typeof(GameObject));
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
var instance = GameObject.Instantiate(obj);
|
||||
if(instance.GetComponent<Camera>() != null)
|
||||
{
|
||||
instance.hideFlags = HideFlags.HideAndDontSave;
|
||||
instance.tag = "MainCamera";
|
||||
instance.name = kLinkCameraName;
|
||||
instance.SetActive(Active);
|
||||
instance.GetComponent<Camera>().depth = int.MaxValue;
|
||||
return instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("LinkGameView Found default prefab but has no camera!");
|
||||
}
|
||||
}
|
||||
else
|
||||
Debug.LogWarning("LinkGameView Found default prefab but is not gameobject!");
|
||||
}
|
||||
|
||||
|
||||
// Otherwise ... Create default from code
|
||||
var go = new GameObject(kLinkCameraName);
|
||||
go.hideFlags = HideFlags.HideAndDontSave;
|
||||
go.tag = "MainCamera";
|
||||
var camera = go.AddComponent<Camera>();
|
||||
camera.depth = int.MaxValue;
|
||||
go.SetActive(Active);
|
||||
return go;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b80d5428a5e868b41ba603471f379a02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "GameplayIngredients-Editor",
|
||||
"references": [
|
||||
"NaughtyAttributes.Core",
|
||||
"NaughtyAttributes>Editor",
|
||||
"GameplayIngredients",
|
||||
"Unity.ugui",
|
||||
"Unity.Timeline"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": []
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4e1a37fe12446f47976df6a3019ef5f
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class GameplayIngredientsAssetPostprocessor : AssetPostprocessor
|
||||
{
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
if (importedAssets.Contains(WelcomeScreen.kSettingsAssetPath))
|
||||
{
|
||||
Debug.Log("Imported GameplayIngredientsSettings");
|
||||
WelcomeScreen.Reload();
|
||||
}
|
||||
|
||||
string[] allDiscovery = AssetDatabase.FindAssets("t:DiscoverAsset");
|
||||
bool needDiscoveryReload = false;
|
||||
foreach(var guid in allDiscovery)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if(importedAssets.Contains(path))
|
||||
{
|
||||
needDiscoveryReload = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needDiscoveryReload)
|
||||
DiscoverWindow.Reload();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a13118b22a04c0d4d95bb500a88e59ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e3cfe5bb18bcf554b83a3f0646547273
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,122 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public class GlobalsDebugWindow : EditorWindow
|
||||
{
|
||||
[MenuItem("Window/Gameplay Ingredients/Globals Debug")]
|
||||
static void Open()
|
||||
{
|
||||
GetWindow<GlobalsDebugWindow>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
titleContent = new GUIContent("Globals Debug");
|
||||
minSize = new Vector2(360, 140);
|
||||
Globals.OnGlobalsUpdated += Globals_OnGlobalsUpdated;
|
||||
}
|
||||
|
||||
private void Globals_OnGlobalsUpdated (Globals.Type t, string name, object value)
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Globals.OnGlobalsUpdated -= Globals_OnGlobalsUpdated;
|
||||
}
|
||||
|
||||
Vector2 scroll;
|
||||
private void OnGUI()
|
||||
{
|
||||
var localBools = Globals.GetBoolNames(Globals.Scope.Local);
|
||||
var globalBools = Globals.GetBoolNames(Globals.Scope.Global);
|
||||
var localInts = Globals.GetIntNames(Globals.Scope.Local);
|
||||
var globalInts = Globals.GetIntNames(Globals.Scope.Global);
|
||||
var localFloats = Globals.GetFloatNames(Globals.Scope.Local);
|
||||
var globalFloats = Globals.GetFloatNames(Globals.Scope.Global);
|
||||
var localStrings = Globals.GetStringNames(Globals.Scope.Local);
|
||||
var globalStrings = Globals.GetStringNames(Globals.Scope.Global);
|
||||
var localObjects = Globals.GetObjectNames(Globals.Scope.Local);
|
||||
var globalObjects = Globals.GetObjectNames(Globals.Scope.Global);
|
||||
|
||||
GUI.backgroundColor = new Color(0.8f, 0.8f, 0.8f, 1);
|
||||
using(new GUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
GUILayout.Label("Name", Styles.header);
|
||||
GUILayout.Label("Global", Styles.header, GUILayout.Width(64));
|
||||
GUILayout.Label("Type", Styles.header, GUILayout.Width(64));
|
||||
GUILayout.Label("Value", Styles.header, GUILayout.Width(128));
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
scroll = EditorGUILayout.BeginScrollView(scroll);
|
||||
|
||||
foreach (var item in globalBools.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Global, Globals.Type.Boolean); }
|
||||
foreach (var item in localBools.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Local, Globals.Type.Boolean); }
|
||||
foreach (var item in globalInts.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Global, Globals.Type.Integer); }
|
||||
foreach (var item in localInts.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Local, Globals.Type.Integer); }
|
||||
foreach (var item in globalFloats.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Global, Globals.Type.Float); }
|
||||
foreach (var item in localFloats.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Local, Globals.Type.Float); }
|
||||
foreach (var item in globalStrings.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Global, Globals.Type.String); }
|
||||
foreach (var item in localStrings.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Local, Globals.Type.String); }
|
||||
foreach (var item in globalObjects.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Global, Globals.Type.GameObject); }
|
||||
foreach (var item in localObjects.OrderBy(o => o)) { DrawItem(item, Globals.Scope.Local, Globals.Type.GameObject); }
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
void DrawItem(string name, Globals.Scope scope, Globals.Type type)
|
||||
{
|
||||
using(new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(name, Styles.cell);
|
||||
GUILayout.Label(scope.ToString(), Styles.cell, GUILayout.Width(64));
|
||||
GUILayout.Label(type.ToString(), Styles.cell, GUILayout.Width(64));
|
||||
switch (type)
|
||||
{
|
||||
case Globals.Type.Boolean:
|
||||
GUILayout.Toggle(Globals.GetBool(name, scope),"", GUILayout.Width(128));
|
||||
break;
|
||||
case Globals.Type.Integer:
|
||||
GUILayout.TextField(Globals.GetInt(name, scope).ToString(), GUILayout.Width(128));
|
||||
break;
|
||||
case Globals.Type.String:
|
||||
GUILayout.TextField(Globals.GetString(name, scope).ToString(), GUILayout.Width(128));
|
||||
break;
|
||||
case Globals.Type.Float:
|
||||
GUILayout.TextField(Globals.GetFloat(name, scope).ToString(), GUILayout.Width(128));
|
||||
break;
|
||||
case Globals.Type.GameObject:
|
||||
EditorGUILayout.ObjectField("",Globals.GetObject(name, scope), typeof(GameObject), true, GUILayout.Width(128));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Styles
|
||||
{
|
||||
public static GUIStyle header;
|
||||
public static GUIStyle cell;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
header = new GUIStyle(EditorStyles.toolbarButton);
|
||||
header.alignment = TextAnchor.MiddleLeft;
|
||||
header.fontStyle = FontStyle.Bold;
|
||||
|
||||
cell = new GUIStyle(EditorStyles.toolbarButton);
|
||||
cell.alignment = TextAnchor.MiddleLeft;
|
||||
cell.fontSize = 10;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: afc22b24e415f0d4a87bcfd086d002cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,85 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GameplayIngredients
|
||||
{
|
||||
static class HiearchyItems
|
||||
{
|
||||
#region TRIGGERS
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/Trigger (Box)", false, 10)]
|
||||
static void CreateTriggerBox()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var col = go.AddComponent<BoxCollider>();
|
||||
col.isTrigger = true;
|
||||
var evt = go.AddComponent<Events.OnTriggerEvent>();
|
||||
go.name = "Box Trigger";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/Trigger (Sphere)", false, 10)]
|
||||
static void CreateTriggerSphere()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var col = go.AddComponent<SphereCollider>();
|
||||
col.isTrigger = true;
|
||||
var evt = go.AddComponent<Events.OnTriggerEvent>();
|
||||
go.name = "Sphere Trigger";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/Trigger (Capsule)", false, 10)]
|
||||
static void CreateTriggerCapsule()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var col = go.AddComponent<CapsuleCollider>();
|
||||
col.isTrigger = true;
|
||||
var evt = go.AddComponent<Events.OnTriggerEvent>();
|
||||
go.name = "Capsule Trigger";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/On Awake", false, 10)]
|
||||
static void CreateOnAwake()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var evt = go.AddComponent<Events.OnAwakeEvent>();
|
||||
go.name = "On Awake";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/On Enable", false, 10)]
|
||||
static void CreateOnEnableDisable()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var evt = go.AddComponent<Events.OnEnableDisableEvent>();
|
||||
go.name = "On Enable/Disable";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Gameplay Ingredients/Events/On Start", false, 10)]
|
||||
static void CreateOnStart()
|
||||
{
|
||||
var go = new GameObject();
|
||||
var evt = go.AddComponent<Events.OnStartEvent>();
|
||||
go.name = "On Start";
|
||||
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.parent = Selection.activeGameObject.transform;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 28d66973767a30643a54411070050920
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d5dcb83e338ada74c9a36ec453e5fa26
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,212 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
using UnityEngine.VFX;
|
||||
#else
|
||||
using UnityEngine.Experimental.VFX;
|
||||
#endif
|
||||
using UnityEditor;
|
||||
using GameplayIngredients.StateMachines;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class HierarchyHints
|
||||
{
|
||||
const string kMenuPath = "Edit/Advanced Hierarchy View %.";
|
||||
public const int kMenuPriority = 230;
|
||||
|
||||
[MenuItem(kMenuPath, priority = kMenuPriority, validate = false)]
|
||||
static void Toggle()
|
||||
{
|
||||
if (Active)
|
||||
Active = false;
|
||||
else
|
||||
Active = true;
|
||||
}
|
||||
|
||||
[MenuItem(kMenuPath, priority = kMenuPriority, validate = true)]
|
||||
static bool ToggleCheck()
|
||||
{
|
||||
Menu.SetChecked(kMenuPath, Active);
|
||||
return SceneView.sceneViews.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
static readonly string kPreferenceName = "GameplayIngredients.HierarchyHints";
|
||||
|
||||
public static bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
return EditorPrefs.GetBool(kPreferenceName, false);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
EditorPrefs.SetBool(kPreferenceName, value);
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
static HierarchyHints()
|
||||
{
|
||||
EditorApplication.hierarchyWindowItemOnGUI -= HierarchyOnGUI;
|
||||
EditorApplication.hierarchyWindowItemOnGUI += HierarchyOnGUI;
|
||||
}
|
||||
|
||||
static Dictionary<Type, string> s_Definitions = new Dictionary<Type, string>()
|
||||
{
|
||||
{ typeof(Folder), "Folder Icon"},
|
||||
{ typeof(MonoBehaviour), "cs Script Icon"},
|
||||
{ typeof(Camera), "Camera Icon"},
|
||||
{ typeof(MeshRenderer), "MeshRenderer Icon"},
|
||||
{ typeof(SkinnedMeshRenderer), "SkinnedMeshRenderer Icon"},
|
||||
{ typeof(BoxCollider), "BoxCollider Icon"},
|
||||
{ typeof(SphereCollider), "SphereCollider Icon"},
|
||||
{ typeof(CapsuleCollider), "CapsuleCollider Icon"},
|
||||
{ typeof(MeshCollider), "MeshCollider Icon"},
|
||||
{ typeof(AudioSource), "AudioSource Icon"},
|
||||
{ typeof(Animation), "Animation Icon"},
|
||||
{ typeof(Animator), "Animator Icon"},
|
||||
{ typeof(PlayableDirector), "PlayableDirector Icon"},
|
||||
{ typeof(Light), "Light Icon"},
|
||||
{ typeof(LightProbeGroup), "LightProbeGroup Icon"},
|
||||
{ typeof(LightProbeProxyVolume), "LightProbeProxyVolume Icon"},
|
||||
{ typeof(ReflectionProbe), "ReflectionProbe Icon"},
|
||||
{ typeof(VisualEffect), "VisualEffect Icon"},
|
||||
{ typeof(ParticleSystem), "ParticleSystem Icon"},
|
||||
{ typeof(Canvas), "Canvas Icon"},
|
||||
{ typeof(Image), "Image Icon"},
|
||||
{ typeof(Text), "Text Icon"},
|
||||
{ typeof(Button), "Button Icon"},
|
||||
{ typeof(StateMachine), "Packages/net.peeweek.gameplay-ingredients/Icons/Misc/ic-StateMachine.png"},
|
||||
{ typeof(State), "Packages/net.peeweek.gameplay-ingredients/Icons/Misc/ic-State.png"},
|
||||
};
|
||||
|
||||
static void HierarchyOnGUI(int instanceID, Rect selectionRect)
|
||||
{
|
||||
if (!Active) return;
|
||||
|
||||
var fullRect = selectionRect;
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
fullRect.xMin = 32;
|
||||
#else
|
||||
fullRect.xMin = 16;
|
||||
#endif
|
||||
fullRect.xMax = EditorGUIUtility.currentViewWidth;
|
||||
GameObject o = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
|
||||
if (o == null) return;
|
||||
|
||||
var c = GUI.color;
|
||||
|
||||
bool isFolder = o.GetComponent<Folder>() != null;
|
||||
|
||||
if(isFolder)
|
||||
{
|
||||
fullRect.xMin += 28 + 14 * GetObjectDepth(o.transform);
|
||||
fullRect.width = 16;
|
||||
EditorGUI.DrawRect(fullRect, EditorGUIUtility.isProSkin? Styles.proBackground : Styles.personalBackground);
|
||||
DrawIcon(fullRect, Contents.GetContent(typeof(Folder)), o.GetComponent<Folder>().Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o.isStatic)
|
||||
{
|
||||
GUI.Label(fullRect, " S");
|
||||
EditorGUI.DrawRect(fullRect, Colors.dimGray);
|
||||
}
|
||||
|
||||
foreach (var type in s_Definitions.Keys)
|
||||
{
|
||||
if (o.GetComponents(type).Length > 0) selectionRect = DrawIcon(selectionRect, Contents.GetContent(type), Color.white);
|
||||
}
|
||||
}
|
||||
GUI.color = c;
|
||||
}
|
||||
|
||||
static int GetObjectDepth(Transform t, int depth=0)
|
||||
{
|
||||
if (t.parent == null)
|
||||
return depth;
|
||||
else
|
||||
return GetObjectDepth(t.parent, depth + 1);
|
||||
}
|
||||
|
||||
|
||||
static Rect DrawIcon(Rect rect, GUIContent content, Color color, int size = 16)
|
||||
{
|
||||
GUI.color = color;
|
||||
GUI.Label(rect, content, Styles.icon);
|
||||
rect.width = rect.width - size;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static class Contents
|
||||
{
|
||||
static Dictionary<Type, GUIContent> s_Icons = new Dictionary<Type, GUIContent>();
|
||||
|
||||
public static void AddIcon(Type type, string IconName)
|
||||
{
|
||||
GUIContent icon;
|
||||
|
||||
Texture texture = AssetDatabase.LoadAssetAtPath<Texture>(IconName);
|
||||
|
||||
if (texture == null)
|
||||
icon = EditorGUIUtility.IconContent(IconName);
|
||||
else
|
||||
icon = new GUIContent(texture);
|
||||
|
||||
s_Icons.Add(type, icon);
|
||||
}
|
||||
|
||||
public static GUIContent GetContent(Type t)
|
||||
{
|
||||
if (!s_Icons.ContainsKey(t) && s_Definitions.ContainsKey(t))
|
||||
AddIcon(t,s_Definitions[t]);
|
||||
|
||||
return s_Icons[t];
|
||||
}
|
||||
}
|
||||
|
||||
static class Colors
|
||||
{
|
||||
public static Color orange = new Color(1.0f, 0.7f, 0.1f);
|
||||
public static Color red = new Color(1.0f, 0.4f, 0.3f);
|
||||
public static Color yellow = new Color(0.8f, 1.0f, 0.1f);
|
||||
public static Color green = new Color(0.2f, 1.0f, 0.1f);
|
||||
public static Color blue = new Color(0.5f, 0.8f, 1.0f);
|
||||
public static Color violet = new Color(0.8f, 0.5f, 1.0f);
|
||||
public static Color purple = new Color(1.0f, 0.5f, 0.8f);
|
||||
public static Color dimGray = new Color(0.4f, 0.4f, 0.4f, 0.2f);
|
||||
}
|
||||
|
||||
static class Styles
|
||||
{
|
||||
public static GUIStyle rightLabel;
|
||||
public static GUIStyle icon;
|
||||
|
||||
public static Color proBackground = new Color(0.25f, 0.25f, 0.25f, 1.0f);
|
||||
public static Color personalBackground = new Color(0.75f, 0.75f, 0.75f, 1.0f);
|
||||
|
||||
static Styles()
|
||||
{
|
||||
rightLabel = new GUIStyle(EditorStyles.label);
|
||||
rightLabel.alignment = TextAnchor.MiddleRight;
|
||||
rightLabel.normal.textColor = Color.white;
|
||||
rightLabel.onNormal.textColor = Color.white;
|
||||
|
||||
rightLabel.active.textColor = Color.white;
|
||||
rightLabel.onActive.textColor = Color.white;
|
||||
|
||||
icon = new GUIStyle(rightLabel);
|
||||
icon.padding = new RectOffset();
|
||||
icon.margin = new RectOffset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a6ca563f0dba0814c804f2cdbef717d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,146 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace GameplayIngredients.Editor
|
||||
{
|
||||
public static class MenuItems
|
||||
{
|
||||
public const int kWindowMenuPriority = 100;
|
||||
public const int kPlayMenuPriority = 160;
|
||||
public const int kMenuPriority = 330;
|
||||
|
||||
#region PLAY HERE
|
||||
|
||||
[MenuItem("Edit/Play from SceneView Position #%&P", priority = kPlayMenuPriority)]
|
||||
static void PlayHere()
|
||||
{
|
||||
EditorApplication.isPlaying = true;
|
||||
}
|
||||
|
||||
[MenuItem("Edit/Play from SceneView Position #%&P", priority = kPlayMenuPriority, validate = true)]
|
||||
static bool PlayHereValidate()
|
||||
{
|
||||
return PlayFromHere.IsReady;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GROUP_UNGROUP
|
||||
|
||||
const int kGroupMenuIndex = 500;
|
||||
const string kGroupMenuString = "Edit/Group Selected %G";
|
||||
const string kUnGroupMenuString = "Edit/Un-Group Selected %#G";
|
||||
|
||||
[MenuItem(kGroupMenuString, priority = kGroupMenuIndex, validate = false)]
|
||||
static void Group()
|
||||
{
|
||||
if (Selection.gameObjects.Length <= 1)
|
||||
return;
|
||||
|
||||
var selected = Selection.gameObjects;
|
||||
Transform parent = selected[0].transform.parent;
|
||||
Scene scene = selected[0].scene;
|
||||
|
||||
bool sparseParents = false;
|
||||
|
||||
foreach (var obj in selected)
|
||||
{
|
||||
if (obj.transform.parent != parent || obj.scene != scene)
|
||||
{
|
||||
sparseParents = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sparseParents)
|
||||
{
|
||||
parent = null;
|
||||
scene = SceneManager.GetActiveScene();
|
||||
}
|
||||
|
||||
Vector3 posSum = Vector3.zero;
|
||||
|
||||
foreach (var go in selected)
|
||||
{
|
||||
posSum += go.transform.position;
|
||||
}
|
||||
|
||||
GameObject groupObj = new GameObject("Group");
|
||||
groupObj.transform.position = posSum / selected.Length;
|
||||
groupObj.transform.parent = parent;
|
||||
groupObj.isStatic = true;
|
||||
|
||||
foreach (var go in selected)
|
||||
go.transform.parent = groupObj.transform;
|
||||
|
||||
// Expand by pinging the first object
|
||||
EditorGUIUtility.PingObject(selected[0]);
|
||||
|
||||
}
|
||||
|
||||
[MenuItem(kGroupMenuString, priority = kGroupMenuIndex, validate = true)]
|
||||
static bool GroupCheck()
|
||||
{
|
||||
return (Selection.gameObjects.Length > 1);
|
||||
}
|
||||
|
||||
|
||||
[MenuItem(kUnGroupMenuString, priority = kGroupMenuIndex+1, validate = false)]
|
||||
static void UnGroup()
|
||||
{
|
||||
if (Selection.gameObjects.Length == 0)
|
||||
return;
|
||||
|
||||
var selected = Selection.gameObjects;
|
||||
List<Transform> oldParents = new List<Transform>();
|
||||
foreach(var go in selected)
|
||||
{
|
||||
if(go.transform.parent != null)
|
||||
{
|
||||
if(!oldParents.Contains(go.transform.parent))
|
||||
oldParents.Add(go.transform.parent);
|
||||
|
||||
go.transform.parent = go.transform.parent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
List<GameObject> toDelete = new List<GameObject>();
|
||||
|
||||
// Cleanup old parents
|
||||
foreach(var parent in oldParents)
|
||||
{
|
||||
var go = parent.gameObject;
|
||||
if(parent.childCount == 0 && parent.GetComponents<Component>().Length == 1) // if no more children and only transform/rectTransform
|
||||
{
|
||||
toDelete.Add(go);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var trash in toDelete)
|
||||
GameObject.DestroyImmediate(trash);
|
||||
|
||||
}
|
||||
|
||||
[MenuItem(kUnGroupMenuString, priority = kGroupMenuIndex+1, validate = true)]
|
||||
static bool UnGroupCheck()
|
||||
{
|
||||
return (Selection.gameObjects.Length > 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ASSETS
|
||||
|
||||
[UnityEditor.MenuItem("Assets/Create/Game Level")]
|
||||
static void CreateGameLevel()
|
||||
{
|
||||
GameplayIngredients.Editor.AssetFactory.CreateAssetInProjectWindow<GameLevel>("", "New Game Level.asset");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b95ff60e46051544a400d4a0afa962b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: abc8c99e8458f7f468eee26216bd1879
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче