22 KiB
22 KiB
Editor Tools Guide
This guide covers the specialized editor tools and workflows provided by EasyBallistics for creating and managing ballistic assets.
Asset Creation System
Ballistics Category
EasyBallistics adds a dedicated "Ballistics" category to the Content Browser asset creation menu:
- Right-click in Content Browser
- Navigate to Ballistics category
- Choose from available asset types:
- Bullet Properties - Define projectile characteristics
- Material Response Map - Configure surface interactions
- Mathematical Material Properties - Advanced material definitions
Asset Factories
The plugin includes specialized asset factories for streamlined creation:
// Bullet Properties Factory
class FEBBulletPropertiesFactory : public UFactory
{
public:
FEBBulletPropertiesFactory()
{
SupportedClass = UEBBulletPropertiesAsset::StaticClass();
bCreateNew = true;
bEditAfterNew = true;
}
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent,
FName Name, EObjectFlags Flags,
UObject* Context,
FFeedbackContext* Warn) override
{
return NewObject<UEBBulletPropertiesAsset>(InParent, Class, Name, Flags);
}
};
Physical Material Integration
Enhanced Material Editor
EasyBallistics extends the Physical Material editor with ballistic properties:
Auto-Configuration
- Smart Detection: Automatically configures properties based on material names
- Common Presets: Steel, wood, concrete, fabric presets
- One-Click Setup: Generate appropriate ballistic properties instantly
Custom Properties Panel
// Custom details panel for Physical Materials
class FEBPhysicalMaterialCustomization : public IDetailCustomization
{
public:
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override
{
// Add ballistic properties section
IDetailCategoryBuilder& BallisticsCategory =
DetailBuilder.EditCategory("Ballistics");
// Add custom property widgets
BallisticsCategory.AddCustomRow(LOCTEXT("CreateBallistics", "Create Ballistics"))
.WholeRowContent()
[
SNew(SButton)
.Text(LOCTEXT("CreateNew", "Create New Ballistic Properties"))
.OnClicked(this, &FEBPhysicalMaterialCustomization::OnCreateNewClicked)
];
}
};
Material Property Creation
Automatic Property Generation
// Auto-generate properties based on material name
UEBMaterialPropertiesAsset* AutoGenerateProperties(UPhysicalMaterial* PhysMat)
{
FString MaterialName = PhysMat->GetName().ToLower();
if (MaterialName.Contains("steel") || MaterialName.Contains("metal"))
{
return CreateSteelProperties();
}
else if (MaterialName.Contains("wood"))
{
return CreateWoodProperties();
}
else if (MaterialName.Contains("concrete"))
{
return CreateConcreteProperties();
}
return CreateDefaultProperties();
}
Property Templates
// Steel template
FMathematicalMaterialProperties CreateSteelProperties()
{
FMathematicalMaterialProperties Props;
Props.DensityGPerCm3 = 7.85f;
Props.MaterialHardness = 200.0f;
Props.TensileStrengthMPa = 400.0f;
Props.BallisticLimitVelocity = 800.0f;
return Props;
}
// Wood template
FMathematicalMaterialProperties CreateWoodProperties()
{
FMathematicalMaterialProperties Props;
Props.DensityGPerCm3 = 0.6f;
Props.MaterialHardness = 20.0f;
Props.TensileStrengthMPa = 50.0f;
Props.BallisticLimitVelocity = 300.0f;
return Props;
}
Asset Type Actions
Bullet Properties Actions
class FEBBulletPropertiesActions : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override
{
return NSLOCTEXT("AssetTypeActions", "EBBulletProperties", "Bullet Properties");
}
virtual FColor GetTypeColor() const override
{
return FColor(255, 196, 128); // Orange theme
}
virtual UClass* GetSupportedClass() const override
{
return UEBBulletPropertiesAsset::StaticClass();
}
virtual uint32 GetCategories() override
{
return EAssetTypeCategories::Gameplay;
}
virtual void GetActions(const TArray<UObject*>& InObjects,
FMenuBuilder& MenuBuilder) override
{
MenuBuilder.AddMenuEntry(
LOCTEXT("BulletProperties_CreateMaterial", "Create Material Response Map"),
LOCTEXT("BulletProperties_CreateMaterialTooltip", "Creates a new Material Response Map for this bullet type"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(this, &FEBBulletPropertiesActions::ExecuteCreateMaterialMap, InObjects))
);
}
};
Material Response Map Actions
class FEBMaterialResponseMapActions : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override
{
return NSLOCTEXT("AssetTypeActions", "EBMaterialResponseMap", "Material Response Map");
}
virtual void GetActions(const TArray<UObject*>& InObjects,
FMenuBuilder& MenuBuilder) override
{
MenuBuilder.AddMenuEntry(
LOCTEXT("MaterialMap_PopulateCommon", "Populate Common Materials"),
LOCTEXT("MaterialMap_PopulateCommonTooltip", "Adds entries for common materials"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateSP(this, &FEBMaterialResponseMapActions::ExecutePopulateCommon, InObjects))
);
}
};
Custom Property Editors
Bullet Properties Editor
// Custom property widget for ballistic coefficient
class SEBBallisticCoefficientWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SEBBallisticCoefficientWidget) {}
SLATE_ATTRIBUTE(float, G1Value)
SLATE_ATTRIBUTE(float, G7Value)
SLATE_ATTRIBUTE(bool, UseG7)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs)
{
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(LOCTEXT("G1BC", "G1 BC:"))
]
+ SHorizontalBox::Slot()
[
SNew(SSpinBox<float>)
.Value(InArgs._G1Value)
.MinValue(0.001f)
.MaxValue(2.0f)
.Delta(0.001f)
]
]
// Add G7 section...
];
}
};
Material Response Map Editor
// Custom editor for material response entries
class FEBMaterialResponseMapDetails : public IDetailCustomization
{
public:
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override
{
TSharedRef<IPropertyHandle> MapProperty =
DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UEBMaterialResponseMap, Map));
IDetailCategoryBuilder& MapCategory =
DetailBuilder.EditCategory("Material Responses");
// Custom array widget with material picker
MapCategory.AddCustomRow(LOCTEXT("MaterialEntries", "Material Entries"))
.WholeRowContent()
[
CreateMaterialMapWidget(MapProperty)
];
}
private:
TSharedRef<SWidget> CreateMaterialMapWidget(TSharedRef<IPropertyHandle> PropertyHandle)
{
return SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
// Material picker + response settings
CreateEntryWidget()
];
}
};
Debug Visualization Tools
Editor Debug Panel
// Custom editor panel for ballistics debugging
class SEBDebugPanel : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SEBDebugPanel) {}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs)
{
ChildSlot
[
SNew(SVerticalBox)
// Bullet visualization controls
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SCheckBox)
.Content()
[
SNew(STextBlock)
.Text(LOCTEXT("ShowTrajectories", "Show Bullet Trajectories"))
]
.OnCheckStateChanged(this, &SEBDebugPanel::OnShowTrajectoriesChanged)
]
// Performance metrics
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(this, &SEBDebugPanel::GetPerformanceText)
]
];
}
private:
FText GetPerformanceText() const
{
int32 ActiveBullets = GetActiveBulletCount();
float MemoryUsage = GetMemoryUsage();
return FText::Format(
LOCTEXT("PerformanceStats", "Active Bullets: {0}\nMemory: {1} MB"),
ActiveBullets,
MemoryUsage
);
}
};
Viewport Debug Rendering
// Custom viewport client for ballistics debugging
class FEBDebugViewportClient : public FEditorViewportClient
{
public:
virtual void Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) override
{
FEditorViewportClient::Draw(View, PDI);
if (bShowBulletTrajectories)
{
DrawBulletTrajectories(PDI);
}
if (bShowImpactAnalysis)
{
DrawImpactAnalysis(PDI);
}
}
private:
void DrawBulletTrajectories(FPrimitiveDrawInterface* PDI)
{
for (const auto& Bullet : GetActiveBullets())
{
TArray<FVector> Trajectory = PredictTrajectory(Bullet);
for (int32 i = 0; i < Trajectory.Num() - 1; i++)
{
PDI->DrawLine(Trajectory[i], Trajectory[i + 1],
FColor::Green, SDPG_World, 2.0f);
}
}
}
};
Asset Browser Integration
Thumbnail Renderer
// Custom thumbnail renderer for bullet properties
class UEBBulletPropertiesThumbnailRenderer : public UDefaultSizedThumbnailRenderer
{
public:
virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height,
FRenderTarget* RenderTarget, FCanvas* Canvas, bool bAdditionalViewFamily) override
{
UEBBulletPropertiesAsset* BulletProps = Cast<UEBBulletPropertiesAsset>(Object);
if (!BulletProps)
return;
// Draw bullet icon/diagram
DrawBulletThumbnail(Canvas, BulletProps, X, Y, Width, Height);
}
private:
void DrawBulletThumbnail(FCanvas* Canvas, UEBBulletPropertiesAsset* Props,
int32 X, int32 Y, uint32 Width, uint32 Height)
{
// Draw bullet shape based on properties
FCanvasBoxItem BulletBox(FVector2D(X + Width * 0.2f, Y + Height * 0.3f),
FVector2D(Width * 0.6f, Height * 0.4f));
BulletBox.SetColor(FColor::Brass);
Canvas->DrawItem(BulletBox);
// Add caliber text
FString CaliberText = FString::Printf(TEXT("%.3f\""), Props->DiameterInches);
FCanvasTextItem TextItem(FVector2D(X + 5, Y + Height - 15),
FText::FromString(CaliberText),
GEngine->GetSmallFont(), FColor::White);
Canvas->DrawItem(TextItem);
}
};
Asset Details Customization
// Enhanced details panel for bullet properties
class FEBBulletPropertiesCustomization : public IDetailCustomization
{
public:
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override
{
// Add ballistic calculator section
IDetailCategoryBuilder& CalculatorCategory =
DetailBuilder.EditCategory("Ballistic Calculator");
CalculatorCategory.AddCustomRow(LOCTEXT("Calculator", "Calculator"))
.WholeRowContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
CreateBallisticCalculator()
]
];
}
private:
TSharedRef<SWidget> CreateBallisticCalculator()
{
return SNew(SExpandableArea)
.AreaTitle(LOCTEXT("BallisticCalc", "Ballistic Calculator"))
.InitiallyCollapsed(true)
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(LOCTEXT("SectionalDensity", "Sectional Density:"))
]
+ SHorizontalBox::Slot()
[
SNew(STextBlock)
.Text(this, &FEBBulletPropertiesCustomization::GetSectionalDensityText)
]
]
// Add more calculated values...
];
}
};
Workflow Tools
Batch Asset Operations
// Batch operations for ballistic assets
class FEBAssetBatchOperations
{
public:
// Update all bullet properties with new mathematical model
static void UpdateAllBulletProperties()
{
TArray<UEBBulletPropertiesAsset*> AllBulletAssets;
FindAllAssetsOfClass(AllBulletAssets);
for (auto* Asset : AllBulletAssets)
{
UpdateBulletPropertiesFormulas(Asset);
Asset->MarkPackageDirty();
}
}
// Validate all material response maps
static void ValidateAllMaterialMaps()
{
TArray<UEBMaterialResponseMap*> AllMaps;
FindAllAssetsOfClass(AllMaps);
for (auto* Map : AllMaps)
{
ValidateMaterialMap(Map);
}
}
private:
template<typename T>
static void FindAllAssetsOfClass(TArray<T*>& OutAssets)
{
FAssetRegistryModule& AssetRegistryModule =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByClass(T::StaticClass()->GetFName(), AssetData);
for (const auto& Asset : AssetData)
{
if (T* LoadedAsset = Cast<T>(Asset.GetAsset()))
{
OutAssets.Add(LoadedAsset);
}
}
}
};
Import/Export Tools
// CSV import/export for bullet properties
class FEBDataImportExport
{
public:
// Export bullet properties to CSV
static void ExportBulletPropertiesToCSV(const TArray<UEBBulletPropertiesAsset*>& Assets,
const FString& FilePath)
{
FString CSV = TEXT("Name,GrainWeight,DiameterInches,LengthInches,BCG1,BCG7\n");
for (const auto* Asset : Assets)
{
CSV += FString::Printf(TEXT("%s,%.1f,%.3f,%.3f,%.3f,%.3f\n"),
*Asset->GetName(),
Asset->GrainWeight,
Asset->DiameterInches,
Asset->LengthInches,
Asset->BallisticCoefficientG1,
Asset->BallisticCoefficientG7
);
}
FFileHelper::SaveStringToFile(CSV, *FilePath);
}
// Import bullet properties from CSV
static TArray<UEBBulletPropertiesAsset*> ImportBulletPropertiesFromCSV(const FString& FilePath)
{
TArray<UEBBulletPropertiesAsset*> CreatedAssets;
FString FileContent;
if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
{
return CreatedAssets;
}
TArray<FString> Lines;
FileContent.ParseIntoArrayLines(Lines);
// Skip header line
for (int32 i = 1; i < Lines.Num(); i++)
{
UEBBulletPropertiesAsset* Asset = CreateAssetFromCSVLine(Lines[i]);
if (Asset)
{
CreatedAssets.Add(Asset);
}
}
return CreatedAssets;
}
};
Content Browser Extensions
Custom Content Browser Columns
// Add custom columns for ballistic assets
class FEBContentBrowserExtensions
{
public:
static void RegisterColumns()
{
FContentBrowserModule& ContentBrowserModule =
FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
// Add caliber column for bullet properties
ContentBrowserModule.GetAllAssetViewContextMenuExtenders().Add(
FContentBrowserMenuExtender_SelectedAssets::CreateStatic(
&FEBContentBrowserExtensions::OnExtendContentBrowserAssetSelectionMenu));
}
private:
static TSharedRef<FExtender> OnExtendContentBrowserAssetSelectionMenu(
const TArray<FAssetData>& SelectedAssets)
{
TSharedRef<FExtender> Extender = MakeShared<FExtender>();
if (SelectedAssets.Num() > 0 &&
SelectedAssets[0].AssetClass == UEBBulletPropertiesAsset::StaticClass()->GetFName())
{
Extender->AddMenuExtension(
"CommonAssetActions",
EExtensionHook::After,
nullptr,
FMenuExtensionDelegate::CreateStatic(&FEBContentBrowserExtensions::CreateBulletPropertiesMenu)
);
}
return Extender;
}
static void CreateBulletPropertiesMenu(FMenuBuilder& MenuBuilder)
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ExportToCSV", "Export to CSV"),
LOCTEXT("ExportToCSVTooltip", "Export bullet properties to CSV file"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateStatic(&FEBContentBrowserExtensions::ExportToCSV))
);
}
};
Editor Preferences
Plugin Settings
// Settings panel for EasyBallistics editor tools
UCLASS(config=EditorPerProjectUserSettings)
class EASYBALLISTICSEDITOR_API UEBEditorSettings : public UObject
{
GENERATED_BODY()
public:
UEBEditorSettings()
{
bShowDebugVisualization = true;
bAutoGenerateMaterialProperties = true;
DefaultBulletMaterial = EBulletMaterial::CopperJacket;
}
// Debug visualization options
UPROPERTY(EditAnywhere, config, Category = "Debug",
meta = (ToolTip = "Show trajectory visualization in viewport"))
bool bShowDebugVisualization;
UPROPERTY(EditAnywhere, config, Category = "Debug")
FColor TrajectoryColor = FColor::Green;
UPROPERTY(EditAnywhere, config, Category = "Debug")
float TrajectoryThickness = 2.0f;
// Asset creation defaults
UPROPERTY(EditAnywhere, config, Category = "Asset Creation")
bool bAutoGenerateMaterialProperties;
UPROPERTY(EditAnywhere, config, Category = "Asset Creation")
EBulletMaterial DefaultBulletMaterial;
UPROPERTY(EditAnywhere, config, Category = "Asset Creation")
EBulletType DefaultBulletType;
};
Settings Registration
// Register settings with editor
void FEasyBallisticsEditorModule::RegisterSettings()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->RegisterSettings("Project", "Plugins", "EasyBallistics",
LOCTEXT("RuntimeSettingsName", "EasyBallistics"),
LOCTEXT("RuntimeSettingsDescription", "Configure EasyBallistics plugin"),
GetMutableDefault<UEBEditorSettings>()
);
}
}
Best Practices
Asset Organization
- Consistent Naming: Use clear, descriptive names for ballistic assets
- Folder Structure: Organize by caliber, weapon type, or material category
- Version Control: Use proper check-in practices for shared assets
- Documentation: Add descriptions to asset properties
Performance in Editor
- Disable Debug Visualization: Turn off when not needed
- Limit Asset Count: Don't load too many ballistic assets simultaneously
- Use Asset References: Prefer soft references in editor tools
- Profile Editor Tools: Monitor performance of custom panels
Workflow Optimization
- Template Assets: Create template assets for common configurations
- Batch Operations: Use batch tools for large-scale updates
- Validation Tools: Implement asset validation for quality assurance
- Import/Export: Use CSV tools for external data integration
Troubleshooting Editor Issues
Common Problems
| Issue | Solution |
|---|---|
| Custom panels not appearing | Check module dependencies and registration |
| Asset thumbnails not showing | Verify thumbnail renderer registration |
| Properties not saving | Ensure MarkPackageDirty() is called |
| Performance issues | Disable debug features, optimize custom widgets |
Debug Tools
- Use Output Log to monitor editor messages
- Enable Developer Tools for additional debugging
- Check Asset Registry for asset loading issues
- Profile with Unreal Insights for performance analysis
Next Steps
- Advanced Tutorials - Complex implementation examples
- API Reference - Complete function documentation
- Performance Optimization - Optimize your ballistic systems
For more editor customization examples, see the UE5 Editor Extension Documentation.