Files
CartoonFPS/Assets/FPS Framework/Scripts/Editor/FirearmCreatorWindow.cs
2025-08-06 23:18:38 +01:00

522 lines
18 KiB
C#

using Akila.FPSFramework.Internal;
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;
namespace Akila.FPSFramework
{
public class FirearmCreatorWindow : EditorWindow
{
public enum FirearmCreationPreset
{
AssaultRifle,
Shotgun,
Sniper,
Pistol
}
private Vector2 scrollPosition; // To keep track of the scrollbar position
private string firearmName = "Untitled Firearm";
private FirearmCreationPreset preset;
private static FirearmCreationPreset currentPreset;
private static GameObject replacement;
private static GameObject hud;
private static GameObject crosshair;
private bool useAdvanced;
private Firearm.ShootingMechanism shootingMechanism;
private Firearm.ShootingDirection shootingDirection;
private static GameObject projectile;
private float fireRate = 800;
private float muzzleVelocity = 500;
private float damage = 25;
private float range = 200;
private static bool autoGenerateAudioProfiles;
private static bool autoGeneratePrefab;
private static AudioProfile fireSound;
private static AudioProfile reloadSound;
private static AudioClip fireClip;
private static AudioClip reloadClip;
private static ParticleSystem muzzleFlash;
// Add a menu item to open the window
[MenuItem(MenuPaths.CreateFirearm)]
public static void ShowWindow()
{
// Show existing window instance. If none exists, create one.
EditorWindow window = GetWindow<FirearmCreatorWindow>(true, "Firearm Wizard", true);
window.minSize = new Vector2(425, 606);
hud = FindPrefab("0336a53be1116fe48935ea7a38bfc26e", "Firearm HUD");
crosshair = FindPrefab("84e86c88a365a1c4ebfdf835fd34e678", "Crosshair");
projectile = FindPrefab("788a3d3b8ecc33d4989926185282c05d", "Projectile");
LoadAudioProfiles();
}
private void OnGUI()
{
// Define the mask area (exclude the last 10 units)
Rect maskRect = new Rect(0, 0, position.width, position.height - 34);
// Apply the mask
GUI.BeginClip(maskRect);
// Optional: Draw a subtle line to visualize the mask boundary
Handles.color = Color.gray;
Handles.DrawLine(new Vector3(0, position.height - 10), new Vector3(position.width, position.height - 10));
// Start the scroll view
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Width(position.width), GUILayout.Height(position.height));
currentPreset = preset;
//Draw default and most basic settings
DisplayBasic();
//Draw the fileds that need to be assgined, fields like crosshair and HUD
DisplayAssignables();
//Draw advanced settings for people who want to create more diffrente guns
DisplayAdvanced();
DisplayAudioAndFX();
DisplayUtilities();
// End the scroll view
EditorGUILayout.EndScrollView();
GUI.EndClip();
//Finally draw create button which opens a folder panel and creates a gun
DisplayCreateButton();
}
private void DisplayBasic()
{
EditorGUILayout.Space();
GUILayout.Label("BASIC", EditorStyles.boldLabel);
firearmName = EditorGUILayout.TextField(new GUIContent("Name", "The name of the firearm to create. This name will be used to name the firearm preset and the firearm object."), firearmName);
preset = (FirearmCreationPreset)EditorGUILayout.EnumPopup("Preset", preset);
}
private void DisplayAssignables()
{
EditorGUILayout.Space();
GUILayout.Label("ASSIGNABLES", EditorStyles.boldLabel);
replacement = (GameObject)EditorGUILayout.ObjectField("Replacement", replacement, typeof(GameObject), true);
if (replacement?.TryGetComponent<Pickable>(out Pickable p) == false)
{
Debug.LogError($"'{replacement?.name}' is not a valid Pickable. Please assign a prefab with the 'Pickable' component attached.");
replacement = null;
}
hud = (GameObject)EditorGUILayout.ObjectField("Firearm HUD", hud, typeof(GameObject), true);
if (hud?.TryGetComponent<FirearmHUD>(out FirearmHUD h) == false)
{
Debug.LogError($"'{hud?.name}' is not a valid FirearmHUD. Please assign a prefab with the 'FirearmHUD' component attached.");
hud = null;
}
crosshair = (GameObject)EditorGUILayout.ObjectField("Crosshair", crosshair, typeof(GameObject), true);
if (crosshair?.TryGetComponent<Crosshair>(out Crosshair c) == false)
{
Debug.LogError($"'{crosshair?.name}' is not a valid Crosshair. Please assign a prefab with the 'Crosshair' component attached.");
crosshair = null;
}
EditorGUILayout.Space();
}
private void DisplayAdvanced()
{
EditorGUILayout.BeginVertical("Box");
useAdvanced = EditorGUILayout.ToggleLeft("ADVANCED", useAdvanced, EditorStyles.boldLabel);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("Box");
EditorGUI.BeginDisabledGroup(!useAdvanced);
shootingMechanism = (Firearm.ShootingMechanism)EditorGUILayout.EnumPopup("Shooting Mechanism", shootingMechanism);
shootingDirection = (Firearm.ShootingDirection)EditorGUILayout.EnumPopup("Shooting Direction", shootingDirection);
if(shootingMechanism == InventoryItem.ShootingMechanism.Projectiles)
{
projectile = (GameObject)EditorGUILayout.ObjectField("Projectile", projectile, typeof(GameObject), true);
}
if (projectile?.TryGetComponent<Projectile>(out Projectile p) == false)
{
Debug.LogError($"'{projectile?.name}' is not a valid Projectile. Please assign a prefab with the 'Projectile' component attached.");
projectile = null;
}
EditorGUI.indentLevel++;
fireRate = EditorGUILayout.FloatField("Fire Rate", fireRate);
muzzleVelocity = EditorGUILayout.FloatField("Muzzle Velocity", muzzleVelocity);
damage = EditorGUILayout.FloatField("Damage", damage);
range = EditorGUILayout.FloatField("Range", range);
EditorGUI.indentLevel--;
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
}
private FirearmCreationPreset prevPreset;
private void DisplayAudioAndFX()
{
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("Audio & FX", EditorStyles.boldLabel);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("Box");
if(prevPreset != preset)
{
LoadAudioProfiles();
}
EditorGUI.BeginDisabledGroup(autoGenerateAudioProfiles);
fireSound = (AudioProfile)EditorGUILayout.ObjectField("Fire Sound", fireSound, typeof(AudioProfile), true);
reloadSound = (AudioProfile)EditorGUILayout.ObjectField("Reload Sound", reloadSound, typeof(AudioProfile), true);
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
autoGenerateAudioProfiles = EditorGUILayout.Toggle("Auto Generate", autoGenerateAudioProfiles);
EditorGUI.indentLevel++;
EditorGUI.BeginDisabledGroup(!autoGenerateAudioProfiles);
fireClip = (AudioClip)EditorGUILayout.ObjectField("Fire Audio Clip", fireClip, typeof(AudioClip), true);
reloadClip = (AudioClip)EditorGUILayout.ObjectField("Reload Audio Clip", reloadClip, typeof(AudioClip), true);
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
EditorGUILayout.EndVertical();
prevPreset = preset;
}
private void DisplayUtilities()
{
EditorGUILayout.BeginVertical("Box");
GUILayout.Label("Utilities", EditorStyles.boldLabel);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("Box");
autoGeneratePrefab = EditorGUILayout.Toggle("Generate Prefab", autoGeneratePrefab);
EditorGUILayout.HelpBox("Generating a prefab is usful because then you can use that prefab in a pickable and make your firearm a pickable weapon. Use the pickable wizard for faster results.", MessageType.Info);
EditorGUILayout.EndVertical();
}
private void DisplayCreateButton()
{
float buttonWidth = 70,
buttonHeight = 20,
padding = 7;
Rect buttonRect = new Rect(
position.width - buttonWidth - padding,
position.height - buttonHeight - padding,
buttonWidth,
buttonHeight);
if (GUI.Button(buttonRect, "Create"))
Create();
}
public void Create()
{
FirearmPreset asset = ScriptableObject.CreateInstance<FirearmPreset>();
string path = EditorUtility.SaveFilePanelInProject(
"Save ScriptableObject",
firearmName,
"asset",
"Please enter a file name to save the ScriptableObject."
);
if (string.IsNullOrEmpty(path)) return;
ApplyFirearmPreset(asset);
if (replacement)
asset.replacement = replacement.GetComponent<Pickable>();
else
asset.replacement = null;
if (crosshair)
asset.crosshair = crosshair.GetComponent<Crosshair>();
else
asset.crosshair = null;
if (hud)
asset.firearmHud = hud.GetComponent<FirearmHUD>();
else
asset.firearmHud = null;
AssetDatabase.CreateAsset(asset, path);
AssetDatabase.SaveAssets();
GameObject defaultFirearm = FindPrefab("e3adfec6173060940ba6ad6097b94972", "Default Firearm");
GameObject newFirearm = Instantiate(defaultFirearm);
newFirearm.GetComponent<Firearm>().preset = asset;
newFirearm.GetComponent<Firearm>().name = firearmName;
newFirearm.transform.SetParent(Selection.activeTransform, false);
newFirearm.transform.Reset();
newFirearm.name = firearmName;
if (autoGeneratePrefab)
{
bool result = false;
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(newFirearm, $"{System.IO.Path.GetDirectoryName(path)}/{firearmName}.prefab", out result);
AssetDatabase.SaveAssets();
DestroyImmediate(newFirearm.gameObject);
GameObject newFirearmAsPrefab = (GameObject)PrefabUtility.InstantiatePrefab(prefab, Selection.activeTransform);
Selection.activeObject = newFirearmAsPrefab;
}
else
{
Selection.activeObject = newFirearm;
}
if (autoGenerateAudioProfiles)
{
GenerateAudioProfiles(fireClip, reloadClip, System.IO.Path.GetDirectoryName(path), asset);
}
else
{
asset.fireSound = fireSound;
asset.reloadSound = reloadSound;
}
if (useAdvanced)
{
asset.shootingDirection = shootingDirection;
asset.shootingMechanism = shootingMechanism;
asset.projectile = projectile.GetComponent<Projectile>();
asset.fireRate = fireRate;
asset.muzzleVelocity = muzzleVelocity;
asset.range = range;
asset.damage = damage;
}
EditorUtility.SetDirty(asset);
AssetDatabase.SaveAssets();
FPSFrameworkEditor.EnterRenameMode();
Close();
}
private void GenerateAudioProfiles(AudioClip fire, AudioClip reload, string path, FirearmPreset preset)
{
if (autoGenerateAudioProfiles == false) return;
AudioProfile au_f = ScriptableObject.CreateInstance<AudioProfile>();
AudioProfile au_r = ScriptableObject.CreateInstance<AudioProfile>();
AssetDatabase.CreateAsset(au_f, $"{path}/{firearmName} Fire.asset");
AssetDatabase.CreateAsset(au_r, $"{path}/{firearmName} Reload.asset");
au_f.audioClip = fire;
au_r.audioClip = reload;
au_f.dynamicPitch = true;
au_r.dynamicPitch = true;
au_f.pitchOffset = 0.1f;
au_r.pitchOffset = 0.1f;
preset.fireSound = au_f;
preset.reloadSound = au_r;
AssetDatabase.SaveAssets();
}
private static void ApplyFirearmPreset(FirearmPreset firearmPreset)
{
Preset preset = null;
string ar_guid = "64c9b02392daca0498df648bcccdb079";
string sniper_guid = "9fe2cb05b34212942bd255db80b314f8";
string shotgun_guid = "c985f3f4496731d41b02d6ee73020c0f";
string pistol_guid = "6e916598804de1940a08b55f937a6ef6";
string final_guid = null;
switch(currentPreset)
{
case FirearmCreationPreset.AssaultRifle:
final_guid = ar_guid;
break;
case FirearmCreationPreset.Sniper:
final_guid = sniper_guid;
break;
case FirearmCreationPreset.Shotgun:
final_guid = shotgun_guid;
break;
case FirearmCreationPreset.Pistol:
final_guid = pistol_guid;
break;
}
string path = AssetDatabase.GUIDToAssetPath(final_guid);
preset = AssetDatabase.LoadAssetAtPath<Preset>(path);
preset.ApplyTo(firearmPreset);
}
private static void LoadAudioProfiles()
{
string ar_f_guid = "32a2f80fb0c67e64081a8e973e0f22c5";
string ar_r_guid = "cd3d8a42beb20e64d90c2402498f37e3";
string sni_f_guid = "e5e1842243349a545bda2d90cc685395";
string sni_r_guid = "98c362cd6ac2c4e48845533df6f2af88";
string shot_f_guid = "31e52a32b0f9831408c3126de7814af7";
string shot_r_guid = "669d23cb73bae3b49b628eb231fd07c4";
string pi_f_guid = "6fc414a5c7974da40a76e86234b199f4";
string pi_r_guid = "31e2cd0af72bb72479102f6cd5972cfb";
switch (currentPreset)
{
case FirearmCreationPreset.AssaultRifle:
fireSound = FindAudioProfile(ar_f_guid, "Assault Rifle_1 Fire");
reloadSound = FindAudioProfile(ar_r_guid, "Assault Rifle_1 Reload");
break;
case FirearmCreationPreset.Sniper:
fireSound = FindAudioProfile(sni_f_guid, "Sniper_1 Fire");
reloadSound = FindAudioProfile(sni_r_guid, "Sniper_1_1 Reload");
break;
case FirearmCreationPreset.Shotgun:
fireSound = FindAudioProfile(shot_f_guid, "Shotgun_1 Fire");
reloadSound = FindAudioProfile(shot_r_guid, "Shotgun_1_1 Reload");
break;
case FirearmCreationPreset.Pistol:
fireSound = FindAudioProfile(pi_f_guid, "Pistol_1 Fire");
reloadSound = FindAudioProfile(pi_r_guid, "Pistol_1 Reload");
break;
}
}
public static GameObject FindPrefab(string GUID, string name)
{
// Replace this with your prefab's GUID (from its .meta file)
string targetGUID = GUID;
// Convert GUID to an asset path
string assetPath = AssetDatabase.GUIDToAssetPath(targetGUID);
if (!string.IsNullOrEmpty(assetPath))
{
// Load the prefab at the asset path
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
if (prefab != null)
{
return prefab;
}
else
{
Debug.LogWarning("The asset is not a prefab or couldn't be loaded.");
}
}
else
{
Debug.LogError($"Couldn't find '{name}' prefab. Make sure to reimport 'Prefabs' folder from the package manager.");
}
return null;
}
public static AudioProfile FindAudioProfile(string GUID, string name)
{
// Replace this with your prefab's GUID (from its .meta file)
string targetGUID = GUID;
// Convert GUID to an asset path
string assetPath = AssetDatabase.GUIDToAssetPath(targetGUID);
if (!string.IsNullOrEmpty(assetPath))
{
// Load the prefab at the asset path
AudioProfile profile = AssetDatabase.LoadAssetAtPath<AudioProfile>(assetPath);
if (profile != null)
{
return profile;
}
else
{
Debug.LogWarning("The asset is not a prefab or couldn't be loaded.");
}
}
else
{
Debug.LogError($"Couldn't find '{name}' AudioProfile. Make sure to reimport 'Art' folder from the package manager.");
}
return null;
}
}
}