diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta
new file mode 100644
index 0000000..83c741b
--- /dev/null
+++ b/Assets/Scenes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6ea315d0fd7389c41b19996891e99ae3
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity
new file mode 100644
index 0000000..c39e581
--- /dev/null
+++ b/Assets/Scenes/SampleScene.unity
@@ -0,0 +1,267 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 705507994}
+ m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 12
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 500
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 500
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 2
+ m_PVRDenoiserTypeDirect: 0
+ m_PVRDenoiserTypeIndirect: 0
+ m_PVRDenoiserTypeAO: 0
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 0
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &705507993
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 705507995}
+ - component: {fileID: 705507994}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &705507994
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 705507993}
+ m_Enabled: 1
+ serializedVersion: 8
+ m_Type: 1
+ m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+ m_Intensity: 1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_Lightmapping: 1
+ m_LightShadowCasterMode: 0
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 705507993}
+ m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 3, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1 &963194225
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 963194228}
+ - component: {fileID: 963194227}
+ - component: {fileID: 963194226}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_GateFitMode: 2
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 1, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/SampleScene.unity.meta
new file mode 100644
index 0000000..952bd1e
--- /dev/null
+++ b/Assets/Scenes/SampleScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9fc0d4010bbf28b4594072e72b8655ab
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets.meta b/Assets/StarterAssets.meta
new file mode 100644
index 0000000..b674322
--- /dev/null
+++ b/Assets/StarterAssets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c470efa886f34f140baf6172ea9ec956
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets/Editor.meta b/Assets/StarterAssets/Editor.meta
new file mode 100644
index 0000000..0d34018
--- /dev/null
+++ b/Assets/StarterAssets/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 577651ee23b1b8342a827136803910fb
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs b/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs
new file mode 100644
index 0000000..2555600
--- /dev/null
+++ b/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs
@@ -0,0 +1,35 @@
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace StarterAssets
+{
+ public partial class StarterAssetsDeployMenu : ScriptableObject
+ {
+ // prefab paths
+ private const string FirstPersonPrefabPath = "/FirstPersonController/Prefabs/";
+
+#if STARTER_ASSETS_PACKAGES_CHECKED
+ ///
+ /// Check the capsule, main camera, cinemachine virtual camera, camera target and references
+ ///
+ [MenuItem(MenuRoot + "/Reset First Person Controller", false)]
+ static void ResetFirstPersonControllerCapsule()
+ {
+ var firstPersonControllers = FindObjectsOfType();
+ var player = firstPersonControllers.FirstOrDefault(controller => controller.CompareTag(PlayerTag));
+ GameObject playerGameObject;
+
+ // player
+ if (player == null)
+ HandleInstantiatingPrefab(StarterAssetsPath + FirstPersonPrefabPath,
+ PlayerCapsulePrefabName, out playerGameObject);
+ else
+ playerGameObject = player.gameObject;
+
+ // cameras
+ CheckCameras(FirstPersonPrefabPath, playerGameObject.transform);
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs.meta b/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs.meta
new file mode 100644
index 0000000..bce9765
--- /dev/null
+++ b/Assets/StarterAssets/Editor/FirstPersonStarterAssetsDeployMenu.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 9387ada430244cac953616bcaca61424
+timeCreated: 1621533817
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/PackageChecker.meta b/Assets/StarterAssets/Editor/PackageChecker.meta
new file mode 100644
index 0000000..5602757
--- /dev/null
+++ b/Assets/StarterAssets/Editor/PackageChecker.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 14bd68b8c6966124487020c02c9b7f9d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs b/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs
new file mode 100644
index 0000000..73fe17d
--- /dev/null
+++ b/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityEditor;
+using UnityEditor.PackageManager;
+using UnityEditor.PackageManager.Requests;
+using UnityEngine;
+using PackageInfo = UnityEditor.PackageManager.PackageInfo;
+
+namespace StarterAssets
+{
+ public static class PackageChecker
+ {
+ private static ListRequest clientList;
+ private static SearchRequest compatibleList;
+ private static List packagesToAdd;
+
+ private static AddRequest[] addRequests;
+ private static bool[] installRequired;
+
+ private static readonly string EditorFolderRoot = "Assets/StarterAssets/";
+ private static readonly string PackagesToImportDataFile = "PackageImportList.txt";
+ public static readonly string PackageCheckerScriptingDefine = "STARTER_ASSETS_PACKAGES_CHECKED";
+
+ [InitializeOnLoadMethod]
+ private static void CheckPackage()
+ {
+ // if we dont have the scripting define, it means the check has not been done
+ if (!ScriptingDefineUtils.CheckScriptingDefine(PackageCheckerScriptingDefine))
+ {
+ packagesToAdd = new List();
+ clientList = null;
+ compatibleList = null;
+
+ // find the projects required package list
+ var requiredPackagesListFile = Directory.GetFiles(Application.dataPath, PackagesToImportDataFile,
+ SearchOption.AllDirectories);
+
+ // if no PackageImportList.txt exists
+ if (requiredPackagesListFile.Length == 0)
+ {
+ Debug.LogError(
+ "[Auto Package] : Couldn't find the packages list. Be sure there is a file called PackageImportList in your project");
+ }
+ else
+ {
+ packagesToAdd = new List();
+
+ string packageListPath = requiredPackagesListFile[0];
+ string[] content = File.ReadAllLines(packageListPath);
+
+ foreach (string line in content)
+ {
+ string[] split = line.Split('@');
+
+ // if no version is given, return null
+ PackageEntry entry = new PackageEntry
+ {name = split[0], version = split.Length > 1 ? split[1] : null};
+
+ packagesToAdd.Add(entry);
+ }
+
+ // Create a file in library that is queried to see if CheckPackage() has been run already
+ ScriptingDefineUtils.SetScriptingDefine(PackageCheckerScriptingDefine);
+
+ // create a list of compatible packages for current engine version
+ compatibleList = Client.SearchAll();
+
+ while (!compatibleList.IsCompleted)
+ {
+ if (compatibleList.Status == StatusCode.Failure || compatibleList.Error != null)
+ {
+ Debug.LogError(compatibleList.Error.message);
+ break;
+ }
+ }
+
+ // create a list of packages found in the engine
+ clientList = Client.List();
+
+ while (!clientList.IsCompleted)
+ {
+ if (clientList.Status == StatusCode.Failure || clientList.Error != null)
+ {
+ Debug.LogError(clientList.Error.message);
+ break;
+ }
+ }
+
+ addRequests = new AddRequest[packagesToAdd.Count];
+ installRequired = new bool[packagesToAdd.Count];
+
+ // default new packages to install = false. we will mark true after validating they're required
+ for (int i = 0; i < installRequired.Length; i++)
+ {
+ installRequired[i] = false;
+ }
+
+ // build data collections compatible packages for this project, and packages within the project
+ List compatiblePackages =
+ new List();
+ List clientPackages =
+ new List();
+
+ foreach (var result in compatibleList.Result)
+ {
+ compatiblePackages.Add(result);
+ }
+
+ foreach (var result in clientList.Result)
+ {
+ clientPackages.Add(result);
+ }
+
+ // check for the latest verified package version for each package that is missing a version
+ for (int i = 0; i < packagesToAdd.Count; i++)
+ {
+ // if a version number is not provided
+ if (packagesToAdd[i].version == null)
+ {
+ foreach (var package in compatiblePackages)
+ {
+ // if no latest verified version found, PackageChecker will just install latest release
+ if (packagesToAdd[i].name == package.name && package.versions.verified != string.Empty)
+ {
+ // add latest verified version number to the packagetoadd list version
+ // so that we get the latest verified version only
+ packagesToAdd[i].version = package.versions.verified;
+
+ // add to our install list
+ installRequired[i] = true;
+
+ //Debug.Log(string.Format("Requested {0}. Latest verified compatible package found: {1}",
+ // packagesToAdd[i].name, packagesToAdd[i].version));
+ }
+ }
+ }
+
+ // we don't need to catch packages that are not installed as their latest version has been collected
+ // from the campatiblelist result
+ foreach (var package in clientPackages)
+ {
+ if (packagesToAdd[i].name == package.name)
+ {
+ // see what version we have installed
+ switch (CompareVersion(packagesToAdd[i].version, package.version))
+ {
+ // latest verified is ahead of installed version
+ case 1:
+ installRequired[i] = EditorUtility.DisplayDialog("Confirm Package Upgrade",
+ $"The version of \"{packagesToAdd[i].name}\" in this project is {package.version}. The latest verified " +
+ $"version is {packagesToAdd[i].version}. Would you like to upgrade it to the latest version? (Recommended)",
+ "Yes", "No");
+
+ Debug.Log(
+ $"Package version behind: {package.packageId} is behind latest verified " +
+ $"version {package.versions.verified}. prompting user install");
+ break;
+
+ // latest verified matches installed version
+ case 0:
+ installRequired[i] = false;
+
+ Debug.Log(
+ $"Package version match: {package.packageId} matches latest verified version " +
+ $"{package.versions.verified}. Skipped install");
+ break;
+
+ // latest verified is behind installed version
+ case -1:
+ installRequired[i] = EditorUtility.DisplayDialog("Confirm Package Downgrade",
+ $"The version of \"{packagesToAdd[i].name}\" in this project is {package.version}. The latest verified version is {packagesToAdd[i].version}. " +
+ $"{package.version} is unverified. Would you like to downgrade it to the latest verified version? " +
+ "(Recommended)", "Yes", "No");
+
+ Debug.Log(
+ $"Package version ahead: {package.packageId} is newer than latest verified " +
+ $"version {package.versions.verified}, skipped install");
+ break;
+ }
+ }
+ }
+ }
+
+ // install our packages and versions
+ for (int i = 0; i < packagesToAdd.Count; i++)
+ {
+ if (installRequired[i])
+ {
+ addRequests[i] = InstallSelectedPackage(packagesToAdd[i].name, packagesToAdd[i].version);
+ }
+ }
+
+ ReimportPackagesByKeyword();
+ }
+ }
+ }
+
+ private static AddRequest InstallSelectedPackage(string packageName, string packageVersion)
+ {
+ if (packageVersion != null)
+ {
+ packageName = packageName + "@" + packageVersion;
+ Debug.Log($"Adding package: {packageName}");
+ }
+
+ AddRequest newPackage = Client.Add(packageName);
+
+ while (!newPackage.IsCompleted)
+ {
+ if (newPackage.Status == StatusCode.Failure || newPackage.Error != null)
+ {
+ Debug.LogError(newPackage.Error.message);
+ return null;
+ }
+ }
+
+ return newPackage;
+ }
+
+ private static void ReimportPackagesByKeyword()
+ {
+ AssetDatabase.Refresh();
+ AssetDatabase.ImportAsset(EditorFolderRoot, ImportAssetOptions.ImportRecursive);
+ }
+
+ public static int CompareVersion(string latestVerifiedVersion, string projectVersion)
+ {
+ string[] latestVersionSplit = latestVerifiedVersion.Split('.');
+ string[] projectVersionSplit = projectVersion.Split('.');
+ int iteratorA = 0;
+ int iteratorB = 0;
+
+ while (iteratorA < latestVersionSplit.Length || iteratorB < projectVersionSplit.Length)
+ {
+ int latestVerified = 0;
+ int installed = 0;
+
+ if (iteratorA < latestVersionSplit.Length)
+ {
+ latestVerified = Convert.ToInt32(latestVersionSplit[iteratorA]);
+ }
+
+ if (iteratorB < projectVersionSplit.Length)
+ {
+ installed = Convert.ToInt32(projectVersionSplit[iteratorB]);
+ }
+
+ // latest verified is ahead of installed version
+ if (latestVerified > installed) return 1;
+
+ // latest verified is behind installed version
+ if (latestVerified < installed) return -1;
+
+ iteratorA++;
+ iteratorB++;
+ }
+
+ // if the version is the same
+ return 0;
+ }
+
+ public class PackageEntry
+ {
+ public string name;
+ public string version;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs.meta b/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs.meta
new file mode 100644
index 0000000..8e51255
--- /dev/null
+++ b/Assets/StarterAssets/Editor/PackageChecker/PackageChecker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7315d24058889bb4da8c959c3f2ebaa2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt b/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt
new file mode 100644
index 0000000..c73c715
--- /dev/null
+++ b/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt
@@ -0,0 +1,2 @@
+com.unity.cinemachine
+com.unity.inputsystem
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt.meta b/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt.meta
new file mode 100644
index 0000000..45c609b
--- /dev/null
+++ b/Assets/StarterAssets/Editor/PackageChecker/PackageImportList.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 77db531f6c7d0ce4da51f6017f51c622
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs b/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs
new file mode 100644
index 0000000..099303b
--- /dev/null
+++ b/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs
@@ -0,0 +1,36 @@
+using UnityEditor;
+
+namespace StarterAssets
+{
+ public static class ScriptingDefineUtils
+ {
+ public static bool CheckScriptingDefine(string scriptingDefine)
+ {
+ BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
+ var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup);
+ return defines.Contains(scriptingDefine);
+ }
+
+ public static void SetScriptingDefine(string scriptingDefine)
+ {
+ BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
+ var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup);
+ if (!defines.Contains(scriptingDefine))
+ {
+ defines += $";{scriptingDefine}";
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, defines);
+ }
+ }
+
+ public static void RemoveScriptingDefine(string scriptingDefine)
+ {
+ BuildTargetGroup buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
+ var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup);
+ if (defines.Contains(scriptingDefine))
+ {
+ string newDefines = defines.Replace(scriptingDefine, "");
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, newDefines);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs.meta b/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs.meta
new file mode 100644
index 0000000..5abe5d2
--- /dev/null
+++ b/Assets/StarterAssets/Editor/ScriptingDefineUtils.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 19d5e2567c974ac6817cbc0ae63e638c
+timeCreated: 1620921242
\ No newline at end of file
diff --git a/Assets/StarterAssets/Editor/StarterAssetsDeployMenu.cs b/Assets/StarterAssets/Editor/StarterAssetsDeployMenu.cs
new file mode 100644
index 0000000..1e364bb
--- /dev/null
+++ b/Assets/StarterAssets/Editor/StarterAssetsDeployMenu.cs
@@ -0,0 +1,140 @@
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+#if STARTER_ASSETS_PACKAGES_CHECKED
+using Cinemachine;
+#endif
+
+namespace StarterAssets
+{
+ // This class needs to be a scriptable object to support dynamic determination of StarterAssets install path
+ public partial class StarterAssetsDeployMenu : ScriptableObject
+ {
+ public const string MenuRoot = "Tools/Starter Assets";
+
+ // prefab names
+ private const string MainCameraPrefabName = "MainCamera";
+ private const string PlayerCapsulePrefabName = "PlayerCapsule";
+
+ // names in hierarchy
+ private const string CinemachineVirtualCameraName = "PlayerFollowCamera";
+
+ // tags
+ private const string PlayerTag = "Player";
+ private const string MainCameraTag = "MainCamera";
+ private const string CinemachineTargetTag = "CinemachineTarget";
+
+ // Get the path to the template prefabs
+ private static string StarterAssetsPath => PathToThisFile;
+
+ private static GameObject _cinemachineVirtualCamera;
+
+ ///
+ /// Get the relative root path of the StarterAssets install - works even if user has
+ /// moved it within Assets, so long as user does not mess with the internal hierarchy
+ /// of the StarterAssets folder
+ ///
+ public static string StarterAssetsInstallPath
+ {
+ get
+ {
+ string path = PathToThisFile;
+ // where this file is relative to install path:
+ return path.Substring(0, path.LastIndexOf("StarterAssets"));
+ }
+ }
+
+ private static string PathToThisFile
+ {
+ get
+ {
+ var dummy = CreateInstance();
+ string path = AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(dummy));
+ DestroyImmediate(dummy);
+ return path.Substring(0, path.LastIndexOf("/Editor/StarterAssetsDeployMenu.cs"));
+ }
+ }
+
+ ///
+ /// Deletes the scripting define set by the Package Checker.
+ /// See Assets/Editor/PackageChecker/PackageChecker.cs for more information
+ ///
+ [MenuItem(MenuRoot + "/Reinstall Dependencies", false)]
+ static void ResetPackageChecker()
+ {
+ ScriptingDefineUtils.RemoveScriptingDefine(PackageChecker.PackageCheckerScriptingDefine);
+ }
+
+#if STARTER_ASSETS_PACKAGES_CHECKED
+ private static void CheckCameras(string prefabPath, Transform targetParent)
+ {
+ CheckMainCamera(prefabPath);
+
+ GameObject vcam = GameObject.Find(CinemachineVirtualCameraName);
+
+ if (!vcam)
+ {
+ HandleInstantiatingPrefab(StarterAssetsPath + prefabPath,
+ CinemachineVirtualCameraName,
+ out GameObject vcamPrefab);
+ _cinemachineVirtualCamera = vcamPrefab;
+ }
+ else
+ {
+ _cinemachineVirtualCamera = vcam;
+ }
+
+ GameObject[] targets = GameObject.FindGameObjectsWithTag(CinemachineTargetTag);
+ GameObject target = targets.FirstOrDefault(t => t.transform.IsChildOf(targetParent));
+ if (target == null)
+ {
+ target = new GameObject("PlayerCameraRoot");
+ target.transform.SetParent(targetParent);
+ target.transform.localPosition = new Vector3(0f, 1.375f, 0f);
+ target.tag = CinemachineTargetTag;
+ Undo.RegisterCreatedObjectUndo(target, "Created new cinemachine target");
+ }
+ CheckVirtualCameraFollowReference(target, _cinemachineVirtualCamera);
+ }
+
+ private static void CheckMainCamera(string prefabPath)
+ {
+ GameObject[] mainCameras = GameObject.FindGameObjectsWithTag(MainCameraTag);
+
+ if (mainCameras.Length < 1)
+ {
+ // if there are no MainCameras, add one
+ HandleInstantiatingPrefab(StarterAssetsPath + prefabPath, MainCameraPrefabName,
+ out _);
+ }
+ else
+ {
+ // make sure the found camera has a cinemachine brain (we only need 1)
+ if (!mainCameras[0].TryGetComponent(out CinemachineBrain cinemachineBrain))
+ mainCameras[0].AddComponent();
+ }
+ }
+
+ private static void CheckVirtualCameraFollowReference(GameObject target,
+ GameObject cinemachineVirtualCamera)
+ {
+ var serializedObject =
+ new SerializedObject(cinemachineVirtualCamera.GetComponent());
+ var serializedProperty = serializedObject.FindProperty("m_Follow");
+ serializedProperty.objectReferenceValue = target.transform;
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ private static void HandleInstantiatingPrefab(string path, string prefabName, out GameObject prefab)
+ {
+ prefab = (GameObject) PrefabUtility.InstantiatePrefab(
+ AssetDatabase.LoadAssetAtPath