docs
This commit is contained in:
@@ -0,0 +1,735 @@
|
||||
# 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:
|
||||
|
||||
1. **Right-click** in Content Browser
|
||||
2. Navigate to **Ballistics** category
|
||||
3. 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:
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
```cpp
|
||||
// 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
|
||||
```cpp
|
||||
// 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
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
```cpp
|
||||
// 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
|
||||
|
||||
1. **Consistent Naming**: Use clear, descriptive names for ballistic assets
|
||||
2. **Folder Structure**: Organize by caliber, weapon type, or material category
|
||||
3. **Version Control**: Use proper check-in practices for shared assets
|
||||
4. **Documentation**: Add descriptions to asset properties
|
||||
|
||||
### Performance in Editor
|
||||
|
||||
1. **Disable Debug Visualization**: Turn off when not needed
|
||||
2. **Limit Asset Count**: Don't load too many ballistic assets simultaneously
|
||||
3. **Use Asset References**: Prefer soft references in editor tools
|
||||
4. **Profile Editor Tools**: Monitor performance of custom panels
|
||||
|
||||
### Workflow Optimization
|
||||
|
||||
1. **Template Assets**: Create template assets for common configurations
|
||||
2. **Batch Operations**: Use batch tools for large-scale updates
|
||||
3. **Validation Tools**: Implement asset validation for quality assurance
|
||||
4. **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](../tutorials/advanced-concepts) - Complex implementation examples
|
||||
- [API Reference](../api/overview) - Complete function documentation
|
||||
- [Performance Optimization](performance-optimization) - Optimize your ballistic systems
|
||||
|
||||
---
|
||||
|
||||
*For more editor customization examples, see the [UE5 Editor Extension Documentation](https://docs.unrealengine.com/5.0/en-US/editor-extension-development/).*
|
||||
@@ -0,0 +1,463 @@
|
||||
# Migration Guide
|
||||
|
||||
This guide helps you migrate from older versions of EasyBallistics to the latest version (2.83+), including the new Ballistic Impact System and Physical Material integration.
|
||||
|
||||
## Overview
|
||||
|
||||
Version 2.83 introduces significant architectural changes:
|
||||
|
||||
- **New Ballistic Impact System**: Event-driven impact handling
|
||||
- **Physical Material Integration**: Seamless UE5 workflow
|
||||
- **Enhanced Asset Creation**: Streamlined editor tools
|
||||
- **Performance Improvements**: Better pooling and LOD systems
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### 1. Material Response System
|
||||
|
||||
**Old System** (2.8x and earlier):
|
||||
```cpp
|
||||
// Material Response Map used string keys
|
||||
MaterialResponseMap->Map.Add("Steel", SteelResponse);
|
||||
MaterialResponseMap->Map.Add("Wood", WoodResponse);
|
||||
```
|
||||
|
||||
**New System** (2.83+):
|
||||
```cpp
|
||||
// Material Response Map uses Physical Material references
|
||||
MaterialResponseMap->Map.Add(SteelPhysicalMaterial, SteelResponse);
|
||||
MaterialResponseMap->Map.Add(WoodPhysicalMaterial, WoodResponse);
|
||||
```
|
||||
|
||||
**Migration Steps**:
|
||||
1. Convert string keys to Physical Material references
|
||||
2. Assign Physical Materials to all surface meshes
|
||||
3. Update any Blueprint logic that references material names
|
||||
|
||||
### 2. Impact Event System
|
||||
|
||||
**Old System**:
|
||||
```cpp
|
||||
// Old impact event signature
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnOldImpact,
|
||||
FVector, Location,
|
||||
FVector, Normal,
|
||||
FString, MaterialName);
|
||||
```
|
||||
|
||||
**New System**:
|
||||
```cpp
|
||||
// New impact event signature
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FOnBallisticImpact,
|
||||
FVector, ImpactLocation,
|
||||
FVector, ImpactNormal,
|
||||
UPhysicalMaterial*, HitMaterial,
|
||||
float, PenetrationDepth,
|
||||
bool, bDidPenetrate);
|
||||
```
|
||||
|
||||
**Migration Steps**:
|
||||
1. Update event binding signatures
|
||||
2. Replace string material checks with Physical Material references
|
||||
3. Use new penetration depth and success parameters
|
||||
|
||||
### 3. Component Architecture
|
||||
|
||||
**Old System**:
|
||||
```cpp
|
||||
// Impact logic was built into bullet class
|
||||
class AEBBullet : public AActor
|
||||
{
|
||||
// All impact logic here
|
||||
void ProcessImpact(FHitResult Hit);
|
||||
};
|
||||
```
|
||||
|
||||
**New System**:
|
||||
```cpp
|
||||
// Impact logic separated into component
|
||||
class AEBBullet : public AActor
|
||||
{
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UEBBallisticImpactComponent* BallisticImpactComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool UseNewImpactSystem = false; // Migration flag
|
||||
};
|
||||
```
|
||||
|
||||
## Step-by-Step Migration
|
||||
|
||||
### Phase 1: Preparation
|
||||
|
||||
1. **Backup Your Project**
|
||||
- Create a full project backup
|
||||
- Commit all changes to version control
|
||||
- Document current implementation
|
||||
|
||||
2. **Inventory Assets**
|
||||
- List all Material Response Maps
|
||||
- Identify surfaces using ballistic materials
|
||||
- Note custom impact event handlers
|
||||
|
||||
3. **Create Physical Materials**
|
||||
```cpp
|
||||
// Create Physical Materials for each surface type
|
||||
// Steel
|
||||
UPhysicalMaterial* PM_Steel = CreateDefaultSubobject<UPhysicalMaterial>("PM_Steel");
|
||||
|
||||
// Wood
|
||||
UPhysicalMaterial* PM_Wood = CreateDefaultSubobject<UPhysicalMaterial>("PM_Wood");
|
||||
|
||||
// Add ballistic properties (auto-generated based on names)
|
||||
```
|
||||
|
||||
### Phase 2: Asset Migration
|
||||
|
||||
1. **Update Material Response Maps**
|
||||
```cpp
|
||||
// Migration function for Material Response Maps
|
||||
void MigrateMaterialResponseMap(UEBMaterialResponseMap* Map)
|
||||
{
|
||||
// Create new map with Physical Material keys
|
||||
TMap<UPhysicalMaterial*, FEBMaterialResponseMapEntry> NewMap;
|
||||
|
||||
for (const auto& Entry : Map->OldStringMap)
|
||||
{
|
||||
FString MaterialName = Entry.Key;
|
||||
UPhysicalMaterial* PhysMat = FindOrCreatePhysicalMaterial(MaterialName);
|
||||
|
||||
if (PhysMat)
|
||||
{
|
||||
NewMap.Add(PhysMat, Entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
Map->Map = NewMap;
|
||||
Map->OldStringMap.Empty(); // Clear old data
|
||||
}
|
||||
```
|
||||
|
||||
2. **Assign Physical Materials**
|
||||
```cpp
|
||||
// Assign Physical Materials to static meshes
|
||||
void AssignPhysicalMaterials()
|
||||
{
|
||||
TArray<AActor*> StaticMeshActors;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AStaticMeshActor::StaticClass(), StaticMeshActors);
|
||||
|
||||
for (AActor* Actor : StaticMeshActors)
|
||||
{
|
||||
AStaticMeshActor* MeshActor = Cast<AStaticMeshActor>(Actor);
|
||||
UStaticMeshComponent* MeshComp = MeshActor->GetStaticMeshComponent();
|
||||
|
||||
// Auto-assign based on mesh name or material
|
||||
UPhysicalMaterial* PhysMat = DeterminePhysicalMaterial(MeshActor);
|
||||
MeshComp->SetPhysMaterialOverride(PhysMat);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Code Migration
|
||||
|
||||
1. **Update Event Handlers**
|
||||
```cpp
|
||||
// Old event handler
|
||||
UFUNCTION()
|
||||
void HandleOldImpact(FVector Location, FVector Normal, FString MaterialName)
|
||||
{
|
||||
if (MaterialName == "Steel")
|
||||
{
|
||||
SpawnSteelImpactEffect(Location, Normal);
|
||||
}
|
||||
}
|
||||
|
||||
// New event handler
|
||||
UFUNCTION()
|
||||
void HandleNewImpact(FVector ImpactLocation, FVector ImpactNormal,
|
||||
UPhysicalMaterial* HitMaterial, float PenetrationDepth, bool bDidPenetrate)
|
||||
{
|
||||
if (HitMaterial == SteelPhysicalMaterial)
|
||||
{
|
||||
SpawnSteelImpactEffect(ImpactLocation, ImpactNormal);
|
||||
|
||||
// Use new parameters
|
||||
if (bDidPenetrate)
|
||||
{
|
||||
SpawnPenetrationEffect(ImpactLocation, PenetrationDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Enable New Impact System**
|
||||
```cpp
|
||||
// Enable new system on bullets
|
||||
void EnableNewImpactSystem()
|
||||
{
|
||||
// For existing bullets
|
||||
for (TSubclassOf<AEBBullet> BulletClass : AllBulletClasses)
|
||||
{
|
||||
AEBBullet* CDO = BulletClass.GetDefaultObject();
|
||||
CDO->UseNewImpactSystem = true;
|
||||
|
||||
// Ensure component exists
|
||||
if (!CDO->BallisticImpactComponent)
|
||||
{
|
||||
CDO->BallisticImpactComponent = CreateDefaultSubobject<UEBBallisticImpactComponent>(TEXT("BallisticImpactComponent"));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Testing and Validation
|
||||
|
||||
1. **Create Test Scenarios**
|
||||
```cpp
|
||||
// Test impact system
|
||||
void TestImpactSystem()
|
||||
{
|
||||
// Test different materials
|
||||
TestMaterialImpact(SteelPhysicalMaterial);
|
||||
TestMaterialImpact(WoodPhysicalMaterial);
|
||||
TestMaterialImpact(ConcretePhysicalMaterial);
|
||||
|
||||
// Test penetration
|
||||
TestPenetrationDepth();
|
||||
TestRicochetBehavior();
|
||||
}
|
||||
```
|
||||
|
||||
2. **Validate Performance**
|
||||
```cpp
|
||||
// Performance comparison
|
||||
void ValidatePerformance()
|
||||
{
|
||||
float OldSystemTime = BenchmarkOldSystem();
|
||||
float NewSystemTime = BenchmarkNewSystem();
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("Performance Comparison:"));
|
||||
UE_LOG(LogTemp, Warning, TEXT("Old System: %.3f ms"), OldSystemTime);
|
||||
UE_LOG(LogTemp, Warning, TEXT("New System: %.3f ms"), NewSystemTime);
|
||||
UE_LOG(LogTemp, Warning, TEXT("Improvement: %.1f%%"),
|
||||
((OldSystemTime - NewSystemTime) / OldSystemTime) * 100.0f);
|
||||
}
|
||||
```
|
||||
|
||||
## Asset Migration Tools
|
||||
|
||||
### Automated Migration Script
|
||||
|
||||
```cpp
|
||||
// Automated migration utility
|
||||
class FEBMigrationTool
|
||||
{
|
||||
public:
|
||||
static void MigrateProject()
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Starting EasyBallistics migration..."));
|
||||
|
||||
// Step 1: Create Physical Materials
|
||||
CreatePhysicalMaterials();
|
||||
|
||||
// Step 2: Migrate Material Response Maps
|
||||
MigrateAllMaterialResponseMaps();
|
||||
|
||||
// Step 3: Update Bullet Classes
|
||||
UpdateAllBulletClasses();
|
||||
|
||||
// Step 4: Assign Physical Materials to Meshes
|
||||
AssignPhysicalMaterialsToMeshes();
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("Migration completed!"));
|
||||
}
|
||||
|
||||
private:
|
||||
static void CreatePhysicalMaterials()
|
||||
{
|
||||
TArray<FString> CommonMaterials = {
|
||||
"Steel", "Wood", "Concrete", "Glass", "Fabric"
|
||||
};
|
||||
|
||||
for (const FString& MaterialName : CommonMaterials)
|
||||
{
|
||||
CreatePhysicalMaterialAsset(MaterialName);
|
||||
}
|
||||
}
|
||||
|
||||
static void MigrateAllMaterialResponseMaps()
|
||||
{
|
||||
TArray<UEBMaterialResponseMap*> AllMaps;
|
||||
FindAllAssetsOfClass(AllMaps);
|
||||
|
||||
for (UEBMaterialResponseMap* Map : AllMaps)
|
||||
{
|
||||
MigrateMaterialResponseMap(Map);
|
||||
Map->MarkPackageDirty();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Material Name Mapping
|
||||
|
||||
```cpp
|
||||
// Map common material names to Physical Materials
|
||||
class FMaterialNameMapper
|
||||
{
|
||||
public:
|
||||
static UPhysicalMaterial* MapNameToPhysicalMaterial(const FString& MaterialName)
|
||||
{
|
||||
static TMap<FString, FString> NameMapping = {
|
||||
{"Steel", "PM_Steel"},
|
||||
{"Metal", "PM_Steel"},
|
||||
{"Iron", "PM_Steel"},
|
||||
{"Wood", "PM_Wood"},
|
||||
{"Wooden", "PM_Wood"},
|
||||
{"Timber", "PM_Wood"},
|
||||
{"Concrete", "PM_Concrete"},
|
||||
{"Stone", "PM_Concrete"},
|
||||
{"Glass", "PM_Glass"},
|
||||
{"Fabric", "PM_Fabric"},
|
||||
{"Cloth", "PM_Fabric"}
|
||||
};
|
||||
|
||||
FString* MappedName = NameMapping.Find(MaterialName);
|
||||
if (MappedName)
|
||||
{
|
||||
return LoadObject<UPhysicalMaterial>(nullptr, **MappedName);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Blueprint Migration
|
||||
|
||||
### Old Blueprint Pattern
|
||||
|
||||
```blueprint
|
||||
// Old impact event handling
|
||||
Event OnImpact (Location, Normal, MaterialName)
|
||||
→ Branch (MaterialName == "Steel")
|
||||
→ True: Spawn Steel Impact Effect
|
||||
→ False: Branch (MaterialName == "Wood")
|
||||
→ True: Spawn Wood Impact Effect
|
||||
```
|
||||
|
||||
### New Blueprint Pattern
|
||||
|
||||
```blueprint
|
||||
// New impact event handling
|
||||
Event OnBallisticImpact (ImpactLocation, ImpactNormal, HitMaterial, PenetrationDepth, bDidPenetrate)
|
||||
→ Switch on Physical Material (HitMaterial)
|
||||
→ Steel: Spawn Steel Impact Effect
|
||||
→ Wood: Spawn Wood Impact Effect
|
||||
→ Default: Spawn Generic Impact Effect
|
||||
→ Branch (bDidPenetrate)
|
||||
→ True: Spawn Penetration Effect (using PenetrationDepth)
|
||||
```
|
||||
|
||||
## Common Migration Issues
|
||||
|
||||
### Issue 1: Missing Physical Materials
|
||||
|
||||
**Problem**: Material Response Map entries pointing to null Physical Materials
|
||||
|
||||
**Solution**:
|
||||
```cpp
|
||||
// Validate and fix missing materials
|
||||
void FixMissingPhysicalMaterials(UEBMaterialResponseMap* Map)
|
||||
{
|
||||
for (auto& Entry : Map->Map)
|
||||
{
|
||||
if (!Entry.Key)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Found null Physical Material in response map"));
|
||||
// Create default material or remove entry
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Issue 2: Performance Regression
|
||||
|
||||
**Problem**: New system performing slower than old system
|
||||
|
||||
**Solution**:
|
||||
```cpp
|
||||
// Optimize new system
|
||||
void OptimizeNewSystem()
|
||||
{
|
||||
// Enable object pooling
|
||||
Bullet->EnablePooling = true;
|
||||
|
||||
// Optimize impact component
|
||||
Bullet->BallisticImpactComponent->bEnableBallisticCalculations = true;
|
||||
Bullet->BallisticImpactComponent->bUseMathematicalPenetration = false; // For performance
|
||||
}
|
||||
```
|
||||
|
||||
### Issue 3: Event Not Firing
|
||||
|
||||
**Problem**: New impact events not triggering
|
||||
|
||||
**Solution**:
|
||||
```cpp
|
||||
// Ensure proper component setup
|
||||
void FixEventBinding()
|
||||
{
|
||||
// Verify component exists
|
||||
if (!Bullet->BallisticImpactComponent)
|
||||
{
|
||||
Bullet->BallisticImpactComponent = NewObject<UEBBallisticImpactComponent>(Bullet);
|
||||
}
|
||||
|
||||
// Bind events
|
||||
Bullet->BallisticImpactComponent->OnBallisticImpact.AddDynamic(
|
||||
this, &AMyActor::HandleBallisticImpact);
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
### Pre-Migration
|
||||
- [ ] Project backup created
|
||||
- [ ] Asset inventory completed
|
||||
- [ ] Physical Materials created
|
||||
- [ ] Migration tools prepared
|
||||
|
||||
### Migration Process
|
||||
- [ ] Material Response Maps updated
|
||||
- [ ] Physical Materials assigned to meshes
|
||||
- [ ] Event handlers updated
|
||||
- [ ] New impact system enabled
|
||||
- [ ] Performance validated
|
||||
|
||||
### Post-Migration
|
||||
- [ ] All systems tested
|
||||
- [ ] Performance benchmarked
|
||||
- [ ] Documentation updated
|
||||
- [ ] Team trained on new system
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues during migration:
|
||||
|
||||
1. **Check Documentation**: Review the [troubleshooting guide](../troubleshooting)
|
||||
2. **Community Support**: Join our [Discord](https://discord.gg/easyballistics)
|
||||
3. **Professional Support**: Contact support@easyballistics.com
|
||||
4. **GitHub Issues**: Report bugs on [GitHub](https://github.com/your-org/easyballistics/issues)
|
||||
|
||||
## Next Steps
|
||||
|
||||
After successful migration:
|
||||
|
||||
1. **[Performance Optimization](performance-optimization)** - Optimize your updated system
|
||||
2. **[Advanced Features](../core-concepts/overview)** - Explore new capabilities
|
||||
3. **[Best Practices](../advanced/editor-tools)** - Learn optimal workflows
|
||||
|
||||
---
|
||||
|
||||
*Migration complete? Continue with [Performance Optimization](performance-optimization) to get the most out of your updated system.*
|
||||
@@ -0,0 +1,570 @@
|
||||
# Multiplayer Guide
|
||||
|
||||
This guide covers implementing EasyBallistics in multiplayer environments, including server authority, client prediction, and network optimization.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
EasyBallistics uses a hybrid approach for multiplayer:
|
||||
|
||||
- **Server Authority**: All impact calculations and final results
|
||||
- **Client Prediction**: Immediate visual feedback for responsive gameplay
|
||||
- **Reconciliation**: Smooth correction of prediction errors
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as Server
|
||||
participant O as Other Clients
|
||||
|
||||
C->>S: Fire Request (RPC)
|
||||
S->>S: Validate & Spawn Bullet
|
||||
S->>O: Replicate Bullet Spawn
|
||||
C->>C: Predict Trajectory
|
||||
S->>O: Authoritative Impact
|
||||
Note over C,O: Reconcile if needed
|
||||
```
|
||||
|
||||
## Core Networking Components
|
||||
|
||||
### Server Authority
|
||||
|
||||
All authoritative actions require server validation:
|
||||
|
||||
```cpp
|
||||
// Server-side weapon firing
|
||||
UFUNCTION(Server, Reliable, WithValidation)
|
||||
void FireWeapon(FVector AimDirection)
|
||||
{
|
||||
// Validate player can fire
|
||||
if (!CanFire())
|
||||
return;
|
||||
|
||||
// Spawn bullet on server
|
||||
SpawnBulletOnServer(AimDirection);
|
||||
}
|
||||
|
||||
bool FireWeapon_Validate(FVector AimDirection)
|
||||
{
|
||||
// Anti-cheat validation
|
||||
return IsValidDirection(AimDirection) && !IsRateLimited();
|
||||
}
|
||||
```
|
||||
|
||||
### Client Prediction
|
||||
|
||||
Immediate visual feedback without waiting for server:
|
||||
|
||||
```cpp
|
||||
// Client-side prediction
|
||||
void PredictiveFire(FVector AimDirection)
|
||||
{
|
||||
// Immediate visual effects
|
||||
SpawnMuzzleFlash();
|
||||
PlayFireSound();
|
||||
|
||||
// Predict bullet trajectory
|
||||
if (EnableClientPrediction)
|
||||
{
|
||||
PredictBulletPath(AimDirection);
|
||||
}
|
||||
|
||||
// Send to server
|
||||
ServerFire(AimDirection);
|
||||
}
|
||||
```
|
||||
|
||||
## Replication Setup
|
||||
|
||||
### Barrel Component Configuration
|
||||
|
||||
```cpp
|
||||
// In EBBarrel component
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Replication")
|
||||
bool ReplicateVariables = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Replication")
|
||||
bool ReplicateShotFiredEvents = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Replication")
|
||||
bool ClientSideAim = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Replication")
|
||||
float ClientAimUpdateFrequency = 15.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Replication")
|
||||
float ClientAimDistanceLimit = 200.0f;
|
||||
```
|
||||
|
||||
### Bullet Replication
|
||||
|
||||
```cpp
|
||||
// Configure bullet replication
|
||||
Bullet->bReplicates = true;
|
||||
Bullet->SetReplicateMovement(true);
|
||||
Bullet->ReliableReplication = false; // Use unreliable for performance
|
||||
|
||||
// Network optimization
|
||||
Bullet->NetCullDistanceSquared = 10000000.0f; // 1000m
|
||||
Bullet->NetUpdateFrequency = 20.0f; // 20 updates per second
|
||||
```
|
||||
|
||||
## Network Optimization
|
||||
|
||||
### Bandwidth Management
|
||||
|
||||
```cpp
|
||||
// Optimize network traffic
|
||||
void OptimizeNetworkSettings()
|
||||
{
|
||||
// Reduce replication frequency for distant bullets
|
||||
if (DistanceToPlayer > 5000.0f)
|
||||
{
|
||||
Bullet->NetUpdateFrequency = 5.0f;
|
||||
}
|
||||
|
||||
// Use unreliable replication for position updates
|
||||
Bullet->ReliableReplication = false;
|
||||
|
||||
// Cull very distant bullets
|
||||
if (DistanceToPlayer > 10000.0f)
|
||||
{
|
||||
Bullet->NetCullDistanceSquared = 0.0f; // Don't replicate
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Object Pooling for Multiplayer
|
||||
|
||||
```cpp
|
||||
// Enable pooling for better performance
|
||||
Bullet->EnablePooling = true;
|
||||
Bullet->MaxPoolSize = 50; // Per client
|
||||
|
||||
// Server manages pool sizes
|
||||
void ManagePoolSizes()
|
||||
{
|
||||
int32 PlayerCount = GetNumPlayers();
|
||||
int32 OptimalPoolSize = FMath::Min(100, PlayerCount * 20);
|
||||
|
||||
for (auto& Bullet : BulletPool)
|
||||
{
|
||||
Bullet->MaxPoolSize = OptimalPoolSize;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Client-Side Prediction
|
||||
|
||||
### Prediction System
|
||||
|
||||
```cpp
|
||||
// Enable prediction for responsive gameplay
|
||||
void SetupPrediction()
|
||||
{
|
||||
// Enable client prediction
|
||||
Barrel->ClientSideAim = true;
|
||||
Barrel->ClientAimUpdateFrequency = 30.0f;
|
||||
|
||||
// Bullet prediction settings
|
||||
Bullet->EnableClientPrediction = true;
|
||||
Bullet->PredictionTolerance = 5.0f; // 5cm tolerance
|
||||
}
|
||||
```
|
||||
|
||||
### Prediction Validation
|
||||
|
||||
```cpp
|
||||
// Validate client predictions
|
||||
void ValidatePrediction(FVector ClientLocation, FVector ServerLocation)
|
||||
{
|
||||
float Distance = FVector::Dist(ClientLocation, ServerLocation);
|
||||
|
||||
if (Distance > PredictionTolerance)
|
||||
{
|
||||
// Correct client position
|
||||
CorrectClientPosition(ServerLocation);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Lag Compensation
|
||||
|
||||
### Hit Detection
|
||||
|
||||
```cpp
|
||||
// Compensate for network latency
|
||||
void CompensateForLag(APawn* Shooter, FVector HitLocation)
|
||||
{
|
||||
float Ping = GetPlayerPing(Shooter);
|
||||
float CompensationTime = Ping * 0.001f; // Convert to seconds
|
||||
|
||||
// Rewind world state
|
||||
RewindWorldState(CompensationTime);
|
||||
|
||||
// Perform hit detection
|
||||
bool ValidHit = ValidateHit(HitLocation);
|
||||
|
||||
// Restore world state
|
||||
RestoreWorldState();
|
||||
}
|
||||
```
|
||||
|
||||
### Movement Prediction
|
||||
|
||||
```cpp
|
||||
// Predict target movement for hit validation
|
||||
FVector PredictTargetLocation(APawn* Target, float CompensationTime)
|
||||
{
|
||||
FVector CurrentLocation = Target->GetActorLocation();
|
||||
FVector Velocity = Target->GetVelocity();
|
||||
|
||||
return CurrentLocation + (Velocity * CompensationTime);
|
||||
}
|
||||
```
|
||||
|
||||
## Anti-Cheat Measures
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```cpp
|
||||
// Prevent rapid fire exploits
|
||||
class FRateLimiter
|
||||
{
|
||||
private:
|
||||
float LastFireTime;
|
||||
float MinFireInterval;
|
||||
|
||||
public:
|
||||
bool CanFire()
|
||||
{
|
||||
float CurrentTime = GetWorld()->GetTimeSeconds();
|
||||
if (CurrentTime - LastFireTime < MinFireInterval)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LastFireTime = CurrentTime;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Validation Checks
|
||||
|
||||
```cpp
|
||||
// Validate firing parameters
|
||||
bool ValidateFireRequest(FVector AimDirection, float Range)
|
||||
{
|
||||
// Check aim direction validity
|
||||
if (AimDirection.IsNearlyZero() || !AimDirection.IsNormalized())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check range limits
|
||||
if (Range > MaxWeaponRange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check player state
|
||||
if (!IsPlayerAlive() || IsReloading())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### LOD System
|
||||
|
||||
```cpp
|
||||
// Implement Level of Detail for multiplayer
|
||||
void UpdateBulletLOD(AEBBullet* Bullet)
|
||||
{
|
||||
float Distance = GetDistanceToNearestPlayer(Bullet);
|
||||
|
||||
if (Distance < 1000.0f)
|
||||
{
|
||||
// High detail
|
||||
Bullet->MaxTracesPerStep = 8;
|
||||
Bullet->FixedStepSeconds = 0.016f;
|
||||
}
|
||||
else if (Distance < 3000.0f)
|
||||
{
|
||||
// Medium detail
|
||||
Bullet->MaxTracesPerStep = 4;
|
||||
Bullet->FixedStepSeconds = 0.033f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Low detail
|
||||
Bullet->MaxTracesPerStep = 2;
|
||||
Bullet->FixedStepSeconds = 0.066f;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Culling System
|
||||
|
||||
```cpp
|
||||
// Cull bullets based on relevance
|
||||
void CullIrrelevantBullets()
|
||||
{
|
||||
for (auto& Bullet : ActiveBullets)
|
||||
{
|
||||
bool IsRelevant = false;
|
||||
|
||||
// Check if bullet is visible to any player
|
||||
for (auto& Player : Players)
|
||||
{
|
||||
if (IsVisibleTo(Bullet, Player))
|
||||
{
|
||||
IsRelevant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsRelevant)
|
||||
{
|
||||
Bullet->SetNetDormancy(DORM_DormantAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Event Synchronization
|
||||
|
||||
### Impact Events
|
||||
|
||||
```cpp
|
||||
// Synchronize impact events across clients
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void MulticastImpactEvent(FVector Location, FVector Normal,
|
||||
UPhysicalMaterial* Material)
|
||||
{
|
||||
// Spawn impact effects on all clients
|
||||
SpawnImpactEffect(Location, Normal, Material);
|
||||
|
||||
// Play impact sound
|
||||
PlayImpactSound(Location, Material);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Events
|
||||
|
||||
```cpp
|
||||
// Create custom networked events
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnNetworkedImpact,
|
||||
FVector, Location,
|
||||
float, Damage,
|
||||
AActor*, HitActor);
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnNetworkedImpact OnNetworkedImpact;
|
||||
|
||||
// Broadcast to all clients
|
||||
void BroadcastImpactEvent(FVector Location, float Damage, AActor* HitActor)
|
||||
{
|
||||
if (HasAuthority())
|
||||
{
|
||||
OnNetworkedImpact.Broadcast(Location, Damage, HitActor);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dedicated Server Optimization
|
||||
|
||||
### Server-Specific Settings
|
||||
|
||||
```cpp
|
||||
// Configure for dedicated server
|
||||
void ConfigureForDedicatedServer()
|
||||
{
|
||||
if (IsRunningDedicatedServer())
|
||||
{
|
||||
// Disable visual effects
|
||||
Bullet->DebugEnabled = false;
|
||||
Bullet->SpawnMuzzleFlash = false;
|
||||
|
||||
// Optimize simulation
|
||||
Bullet->MaxTracesPerStep = 4;
|
||||
Bullet->FixedStepSeconds = 0.033f;
|
||||
|
||||
// Increase pool sizes
|
||||
Bullet->MaxPoolSize = 200;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Management
|
||||
|
||||
```cpp
|
||||
// Manage server memory usage
|
||||
void ManageServerMemory()
|
||||
{
|
||||
// Garbage collect unused bullets
|
||||
if (UnusedBullets.Num() > MaxUnusedBullets)
|
||||
{
|
||||
int32 ToRemove = UnusedBullets.Num() - MaxUnusedBullets;
|
||||
for (int32 i = 0; i < ToRemove; i++)
|
||||
{
|
||||
UnusedBullets[i]->Destroy();
|
||||
}
|
||||
UnusedBullets.RemoveAt(0, ToRemove);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Weapon System Integration
|
||||
|
||||
```cpp
|
||||
// Integrate with multiplayer weapon system
|
||||
class AMultiplayerWeapon : public AWeapon
|
||||
{
|
||||
UPROPERTY(VisibleAnywhere, Category = "Ballistics")
|
||||
UEBBarrel* BallisticBarrel;
|
||||
|
||||
UFUNCTION(Server, Reliable, WithValidation)
|
||||
void ServerFire(FVector AimDirection)
|
||||
{
|
||||
if (CanFire())
|
||||
{
|
||||
BallisticBarrel->Fire();
|
||||
MulticastFireEffects();
|
||||
}
|
||||
}
|
||||
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void MulticastFireEffects()
|
||||
{
|
||||
// Play effects on all clients
|
||||
SpawnMuzzleFlash();
|
||||
PlayFireSound();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Player State Integration
|
||||
|
||||
```cpp
|
||||
// Integrate with player state for persistence
|
||||
class ABallisticPlayerState : public APlayerState
|
||||
{
|
||||
UPROPERTY(Replicated)
|
||||
int32 ShotsFired;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
int32 ShotsHit;
|
||||
|
||||
UFUNCTION(Server, Reliable)
|
||||
void ServerRecordShot(bool bHit)
|
||||
{
|
||||
ShotsFired++;
|
||||
if (bHit)
|
||||
{
|
||||
ShotsHit++;
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Debugging Multiplayer Issues
|
||||
|
||||
### Network Debugging
|
||||
|
||||
```cpp
|
||||
// Debug network issues
|
||||
void DebugNetworkIssues()
|
||||
{
|
||||
if (GEngine)
|
||||
{
|
||||
// Show network stats
|
||||
GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Yellow,
|
||||
FString::Printf(TEXT("Ping: %.2f ms"), GetPing()));
|
||||
|
||||
// Show bullet count
|
||||
GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Green,
|
||||
FString::Printf(TEXT("Active Bullets: %d"), ActiveBullets.Num()));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Console Commands
|
||||
|
||||
```cpp
|
||||
// Add console commands for debugging
|
||||
UFUNCTION(Exec)
|
||||
void DebugBallistics()
|
||||
{
|
||||
// Toggle debug visualization
|
||||
for (auto& Bullet : ActiveBullets)
|
||||
{
|
||||
Bullet->DebugEnabled = !Bullet->DebugEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void ShowNetworkStats()
|
||||
{
|
||||
// Display network statistics
|
||||
UE_LOG(LogTemp, Warning, TEXT("Network Stats: Packets/sec: %d"),
|
||||
GetNetworkPacketRate());
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Performance Guidelines
|
||||
|
||||
1. **Use Object Pooling**: Always enable pooling for multiplayer
|
||||
2. **Optimize Update Rates**: Reduce frequency for distant bullets
|
||||
3. **Implement LOD**: Scale simulation quality with distance
|
||||
4. **Cull Irrelevant Bullets**: Use network culling for performance
|
||||
|
||||
### Network Guidelines
|
||||
|
||||
1. **Minimize RPCs**: Batch multiple actions when possible
|
||||
2. **Use Unreliable Replication**: For position updates
|
||||
3. **Implement Prediction**: For responsive gameplay
|
||||
4. **Validate Everything**: Server-side validation for security
|
||||
|
||||
### Security Guidelines
|
||||
|
||||
1. **Server Authority**: All important decisions on server
|
||||
2. **Input Validation**: Validate all client inputs
|
||||
3. **Rate Limiting**: Prevent rapid fire exploits
|
||||
4. **Sanity Checks**: Validate physics calculations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Bullets not syncing | Check replication settings |
|
||||
| High latency | Implement client prediction |
|
||||
| Memory leaks | Enable object pooling |
|
||||
| Desync issues | Validate server authority |
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
- Use `stat net` to monitor network usage
|
||||
- Profile with `stat game` for performance bottlenecks
|
||||
- Monitor memory with `stat memory`
|
||||
- Check collision performance with `stat collision`
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Performance Optimization Guide](performance-optimization) - Detailed optimization strategies
|
||||
- [Advanced Tutorials](../tutorials/advanced-concepts) - Complex implementation examples
|
||||
- [API Reference](../api/overview) - Complete function documentation
|
||||
|
||||
---
|
||||
|
||||
*For more advanced networking topics, see the [Advanced Networking Guide](advanced-networking).*
|
||||
@@ -0,0 +1,573 @@
|
||||
# Performance Optimization Guide
|
||||
|
||||
This guide covers comprehensive performance optimization strategies for EasyBallistics, helping you achieve optimal performance in your projects.
|
||||
|
||||
## Performance Overview
|
||||
|
||||
EasyBallistics is designed for high performance, but proper configuration is essential for optimal results. This guide covers:
|
||||
|
||||
- **Memory Management**: Object pooling and garbage collection
|
||||
- **CPU Optimization**: Simulation quality and LOD systems
|
||||
- **Network Optimization**: Bandwidth and replication efficiency
|
||||
- **Platform-Specific Tuning**: Console, mobile, and PC optimizations
|
||||
|
||||
## Object Pooling
|
||||
|
||||
### Why Pooling Matters
|
||||
|
||||
Creating and destroying bullet actors is expensive. Pooling reuses existing actors:
|
||||
|
||||
```cpp
|
||||
// Without pooling: Expensive
|
||||
AEBBullet* Bullet = GetWorld()->SpawnActor<AEBBullet>();
|
||||
Bullet->Destroy(); // Triggers garbage collection
|
||||
|
||||
// With pooling: Efficient
|
||||
AEBBullet* Bullet = GetPooledBullet();
|
||||
Bullet->Deactivate(); // Returns to pool
|
||||
```
|
||||
|
||||
### Configuring Pooling
|
||||
|
||||
```cpp
|
||||
// Enable pooling for high-rate weapons
|
||||
Bullet->EnablePooling = true;
|
||||
Bullet->MaxPoolSize = 100;
|
||||
|
||||
// Configure pool sizes based on weapon type
|
||||
void ConfigurePoolingForWeapon(EWeaponType WeaponType)
|
||||
{
|
||||
switch (WeaponType)
|
||||
{
|
||||
case EWeaponType::Pistol:
|
||||
Bullet->MaxPoolSize = 20;
|
||||
break;
|
||||
case EWeaponType::AssaultRifle:
|
||||
Bullet->MaxPoolSize = 100;
|
||||
break;
|
||||
case EWeaponType::Minigun:
|
||||
Bullet->MaxPoolSize = 500;
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pool Management
|
||||
|
||||
```cpp
|
||||
// Monitor pool efficiency
|
||||
void MonitorPoolEfficiency()
|
||||
{
|
||||
int32 PoolSize = BulletPool.Num();
|
||||
int32 ActiveBullets = GetActiveBulletCount();
|
||||
float PoolUtilization = (float)ActiveBullets / PoolSize;
|
||||
|
||||
// Adjust pool size based on utilization
|
||||
if (PoolUtilization > 0.8f)
|
||||
{
|
||||
// Increase pool size
|
||||
Bullet->MaxPoolSize = FMath::Min(500, Bullet->MaxPoolSize + 50);
|
||||
}
|
||||
else if (PoolUtilization < 0.2f)
|
||||
{
|
||||
// Decrease pool size
|
||||
Bullet->MaxPoolSize = FMath::Max(20, Bullet->MaxPoolSize - 25);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Simulation Quality Scaling
|
||||
|
||||
### Level of Detail (LOD)
|
||||
|
||||
Scale simulation quality based on distance and importance:
|
||||
|
||||
```cpp
|
||||
// Implement LOD system
|
||||
void UpdateBulletLOD(AEBBullet* Bullet)
|
||||
{
|
||||
float Distance = GetDistanceToNearestPlayer(Bullet);
|
||||
bool IsPlayerWeapon = IsPlayerOwnedWeapon(Bullet);
|
||||
|
||||
if (IsPlayerWeapon || Distance < 1000.0f)
|
||||
{
|
||||
// High detail for player weapons and close bullets
|
||||
Bullet->MaxTracesPerStep = 8;
|
||||
Bullet->FixedStepSeconds = 0.016f; // 60 FPS
|
||||
Bullet->TraceComplex = true;
|
||||
}
|
||||
else if (Distance < 3000.0f)
|
||||
{
|
||||
// Medium detail for medium distance
|
||||
Bullet->MaxTracesPerStep = 4;
|
||||
Bullet->FixedStepSeconds = 0.033f; // 30 FPS
|
||||
Bullet->TraceComplex = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Low detail for distant bullets
|
||||
Bullet->MaxTracesPerStep = 2;
|
||||
Bullet->FixedStepSeconds = 0.066f; // 15 FPS
|
||||
Bullet->TraceComplex = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Adaptive Quality
|
||||
|
||||
```cpp
|
||||
// Adjust quality based on performance
|
||||
void AdaptiveQualityControl()
|
||||
{
|
||||
float CurrentFPS = GetCurrentFPS();
|
||||
float TargetFPS = 60.0f;
|
||||
|
||||
if (CurrentFPS < TargetFPS * 0.8f)
|
||||
{
|
||||
// Reduce quality
|
||||
ReduceSimulationQuality();
|
||||
}
|
||||
else if (CurrentFPS > TargetFPS * 1.1f)
|
||||
{
|
||||
// Increase quality
|
||||
IncreaseSimulationQuality();
|
||||
}
|
||||
}
|
||||
|
||||
void ReduceSimulationQuality()
|
||||
{
|
||||
// Reduce trace complexity
|
||||
for (auto& Bullet : ActiveBullets)
|
||||
{
|
||||
Bullet->MaxTracesPerStep = FMath::Max(1, Bullet->MaxTracesPerStep - 1);
|
||||
Bullet->FixedStepSeconds = FMath::Min(0.1f, Bullet->FixedStepSeconds + 0.016f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## CPU Optimization
|
||||
|
||||
### Trace Optimization
|
||||
|
||||
```cpp
|
||||
// Optimize collision traces
|
||||
void OptimizeTraces()
|
||||
{
|
||||
// Use simple collision for distant bullets
|
||||
if (Distance > 2000.0f)
|
||||
{
|
||||
Bullet->TraceComplex = false;
|
||||
}
|
||||
|
||||
// Reduce collision channels
|
||||
Bullet->TraceChannel = ECC_WorldStatic; // Faster than ECC_WorldDynamic
|
||||
|
||||
// Use collision margin to reduce trace frequency
|
||||
Bullet->CollisionMargin = 5.0f; // Skip small gaps
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Threading
|
||||
|
||||
```cpp
|
||||
// Parallelize bullet updates
|
||||
void ParallelBulletUpdate()
|
||||
{
|
||||
// Use async tasks for independent bullets
|
||||
ParallelFor(ActiveBullets.Num(), [&](int32 Index)
|
||||
{
|
||||
AEBBullet* Bullet = ActiveBullets[Index];
|
||||
if (Bullet && Bullet->IsValidLowLevel())
|
||||
{
|
||||
Bullet->UpdatePhysics();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Access Optimization
|
||||
|
||||
```cpp
|
||||
// Structure of Arrays for better cache performance
|
||||
struct FBulletData
|
||||
{
|
||||
TArray<FVector> Positions;
|
||||
TArray<FVector> Velocities;
|
||||
TArray<float> Masses;
|
||||
TArray<float> Diameters;
|
||||
|
||||
void UpdatePhysics(int32 BulletCount, float DeltaTime)
|
||||
{
|
||||
// Vectorized operations
|
||||
for (int32 i = 0; i < BulletCount; i++)
|
||||
{
|
||||
// Cache-friendly sequential access
|
||||
Positions[i] += Velocities[i] * DeltaTime;
|
||||
// Apply drag, gravity, etc.
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Garbage Collection
|
||||
|
||||
```cpp
|
||||
// Minimize garbage collection pressure
|
||||
void MinimizeGarbageCollection()
|
||||
{
|
||||
// Reuse containers instead of creating new ones
|
||||
static TArray<FHitResult> HitResults;
|
||||
HitResults.Reset(); // Clear but keep capacity
|
||||
|
||||
// Use object pooling for frequently created objects
|
||||
static TArray<AEBBullet*> BulletPool;
|
||||
|
||||
// Avoid frequent string operations
|
||||
static FString CachedString;
|
||||
CachedString = FString::Printf(TEXT("Bullets: %d"), BulletCount);
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Profiling
|
||||
|
||||
```cpp
|
||||
// Monitor memory usage
|
||||
void MonitorMemoryUsage()
|
||||
{
|
||||
FGenericPlatformMemory::EMemoryCounterRegion Region =
|
||||
FGenericPlatformMemory::MCR_Physical;
|
||||
|
||||
SIZE_T UsedMemory = FGenericPlatformMemory::GetMemoryUsedFast(Region);
|
||||
|
||||
if (UsedMemory > MaxMemoryThreshold)
|
||||
{
|
||||
// Trigger cleanup
|
||||
TriggerMemoryCleanup();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Platform-Specific Optimization
|
||||
|
||||
### Console Optimization
|
||||
|
||||
```cpp
|
||||
// Configure for console platforms
|
||||
void ConfigureForConsole()
|
||||
{
|
||||
#if PLATFORM_CONSOLE
|
||||
// Reduce pool sizes for limited memory
|
||||
Bullet->MaxPoolSize = 50;
|
||||
|
||||
// Use fixed timestep for consistent performance
|
||||
Bullet->FixedStep = true;
|
||||
Bullet->FixedStepSeconds = 0.033f; // 30 FPS
|
||||
|
||||
// Disable expensive features
|
||||
Bullet->DebugEnabled = false;
|
||||
Bullet->TraceComplex = false;
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
### Mobile Optimization
|
||||
|
||||
```cpp
|
||||
// Configure for mobile platforms
|
||||
void ConfigureForMobile()
|
||||
{
|
||||
#if PLATFORM_MOBILE
|
||||
// Aggressive optimization for mobile
|
||||
Bullet->MaxPoolSize = 20;
|
||||
Bullet->MaxTracesPerStep = 2;
|
||||
Bullet->FixedStepSeconds = 0.066f; // 15 FPS
|
||||
|
||||
// Disable advanced features
|
||||
Bullet->UseMathematicalPhysics = false;
|
||||
Bullet->AtmosphereType = EEBAtmosphereType::AT_Constant;
|
||||
|
||||
// Reduce collision complexity
|
||||
Bullet->TraceComplex = false;
|
||||
Bullet->CollisionMargin = 10.0f;
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
### PC Optimization
|
||||
|
||||
```cpp
|
||||
// Configure for PC platforms
|
||||
void ConfigureForPC()
|
||||
{
|
||||
#if PLATFORM_DESKTOP
|
||||
// Take advantage of more resources
|
||||
Bullet->MaxPoolSize = 200;
|
||||
Bullet->MaxTracesPerStep = 8;
|
||||
|
||||
// Enable advanced features
|
||||
Bullet->UseMathematicalPhysics = true;
|
||||
Bullet->UseNewImpactSystem = true;
|
||||
|
||||
// Adaptive quality based on hardware
|
||||
AdaptToHardwareCapabilities();
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
## Network Optimization
|
||||
|
||||
### Bandwidth Optimization
|
||||
|
||||
```cpp
|
||||
// Optimize network bandwidth
|
||||
void OptimizeNetworkBandwidth()
|
||||
{
|
||||
// Reduce update frequency for distant bullets
|
||||
if (Distance > 5000.0f)
|
||||
{
|
||||
Bullet->NetUpdateFrequency = 5.0f;
|
||||
}
|
||||
|
||||
// Use unreliable replication for position updates
|
||||
Bullet->ReliableReplication = false;
|
||||
|
||||
// Cull very distant bullets
|
||||
if (Distance > 10000.0f)
|
||||
{
|
||||
Bullet->NetCullDistanceSquared = 0.0f;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Replication Efficiency
|
||||
|
||||
```cpp
|
||||
// Efficient replication patterns
|
||||
void OptimizeReplication()
|
||||
{
|
||||
// Batch multiple bullets in single RPC
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void MulticastBulletBatch(const TArray<FBulletData>& BulletData)
|
||||
{
|
||||
for (const auto& Data : BulletData)
|
||||
{
|
||||
SpawnBulletFromData(Data);
|
||||
}
|
||||
}
|
||||
|
||||
// Use delta compression for position updates
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void MulticastBulletPositions(const TArray<FVector_NetQuantize>& Positions)
|
||||
{
|
||||
// Update positions efficiently
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
### Profiling Tools
|
||||
|
||||
```cpp
|
||||
// Built-in profiling
|
||||
void ProfileBallistics()
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_BallisticsUpdate);
|
||||
|
||||
// Profile specific operations
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_BulletPhysics);
|
||||
UpdateBulletPhysics();
|
||||
}
|
||||
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_BulletTraces);
|
||||
PerformBulletTraces();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Performance Metrics
|
||||
|
||||
```cpp
|
||||
// Track performance metrics
|
||||
struct FBallisticsMetrics
|
||||
{
|
||||
float AverageUpdateTime;
|
||||
int32 ActiveBulletCount;
|
||||
float MemoryUsage;
|
||||
float NetworkBandwidth;
|
||||
|
||||
void UpdateMetrics()
|
||||
{
|
||||
// Calculate rolling averages
|
||||
AverageUpdateTime = CalculateAverageUpdateTime();
|
||||
ActiveBulletCount = GetActiveBulletCount();
|
||||
MemoryUsage = GetMemoryUsage();
|
||||
NetworkBandwidth = GetNetworkUsage();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Debug and Visualization
|
||||
|
||||
### Performance Visualization
|
||||
|
||||
```cpp
|
||||
// Visualize performance bottlenecks
|
||||
void VisualizePerformance()
|
||||
{
|
||||
// Color-code bullets by performance cost
|
||||
for (auto& Bullet : ActiveBullets)
|
||||
{
|
||||
float Cost = CalculatePerformanceCost(Bullet);
|
||||
FColor DebugColor = GetPerformanceColor(Cost);
|
||||
|
||||
DrawDebugSphere(GetWorld(), Bullet->GetActorLocation(),
|
||||
10.0f, 12, DebugColor, false, 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
FColor GetPerformanceColor(float Cost)
|
||||
{
|
||||
if (Cost < 0.1f) return FColor::Green; // Low cost
|
||||
if (Cost < 0.5f) return FColor::Yellow; // Medium cost
|
||||
return FColor::Red; // High cost
|
||||
}
|
||||
```
|
||||
|
||||
### Console Commands
|
||||
|
||||
```cpp
|
||||
// Debug console commands
|
||||
UFUNCTION(Exec)
|
||||
void BallisticsStats()
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Active Bullets: %d"), ActiveBullets.Num());
|
||||
UE_LOG(LogTemp, Warning, TEXT("Pool Size: %d"), BulletPool.Num());
|
||||
UE_LOG(LogTemp, Warning, TEXT("Memory Usage: %.2f MB"), GetMemoryUsage());
|
||||
}
|
||||
|
||||
UFUNCTION(Exec)
|
||||
void BallisticsProfile(bool bEnable)
|
||||
{
|
||||
ProfilingEnabled = bEnable;
|
||||
if (bEnable)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Ballistics profiling enabled"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Performance Guidelines
|
||||
|
||||
1. **Always Use Pooling**: Enable object pooling for all bullet types
|
||||
2. **Implement LOD**: Scale quality based on distance and importance
|
||||
3. **Monitor Performance**: Use profiling tools to identify bottlenecks
|
||||
4. **Optimize for Target Platform**: Use platform-specific optimizations
|
||||
5. **Batch Operations**: Group similar operations together
|
||||
|
||||
### Memory Guidelines
|
||||
|
||||
1. **Avoid Frequent Allocation**: Reuse containers and objects
|
||||
2. **Use Object Pooling**: Minimize garbage collection pressure
|
||||
3. **Monitor Memory Usage**: Set up automatic memory monitoring
|
||||
4. **Clean Up Properly**: Ensure bullets are properly deactivated
|
||||
|
||||
### Network Guidelines
|
||||
|
||||
1. **Reduce Update Frequency**: Lower frequency for distant bullets
|
||||
2. **Use Unreliable Replication**: For position updates
|
||||
3. **Implement Culling**: Don't replicate irrelevant bullets
|
||||
4. **Batch RPCs**: Group multiple operations together
|
||||
|
||||
## Common Performance Issues
|
||||
|
||||
### Issue: Low Frame Rate
|
||||
|
||||
**Symptoms**:
|
||||
- Frame rate drops below 30 FPS
|
||||
- Stuttering during intense combat
|
||||
|
||||
**Solutions**:
|
||||
```cpp
|
||||
// Enable object pooling
|
||||
Bullet->EnablePooling = true;
|
||||
|
||||
// Reduce simulation quality
|
||||
Bullet->MaxTracesPerStep = 4;
|
||||
Bullet->FixedStepSeconds = 0.033f;
|
||||
|
||||
// Implement LOD system
|
||||
UpdateBulletLOD(Bullet);
|
||||
```
|
||||
|
||||
### Issue: High Memory Usage
|
||||
|
||||
**Symptoms**:
|
||||
- Memory usage continuously increases
|
||||
- Out of memory crashes
|
||||
|
||||
**Solutions**:
|
||||
```cpp
|
||||
// Proper cleanup
|
||||
void CleanupUnusedBullets()
|
||||
{
|
||||
for (int32 i = ActiveBullets.Num() - 1; i >= 0; i--)
|
||||
{
|
||||
AEBBullet* Bullet = ActiveBullets[i];
|
||||
if (!Bullet->IsValidLowLevel() || Bullet->ShouldBeDestroyed())
|
||||
{
|
||||
Bullet->Deactivate();
|
||||
ActiveBullets.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Issue: Network Lag
|
||||
|
||||
**Symptoms**:
|
||||
- High network bandwidth usage
|
||||
- Desync between clients
|
||||
|
||||
**Solutions**:
|
||||
```cpp
|
||||
// Optimize network settings
|
||||
Bullet->NetUpdateFrequency = 10.0f; // Reduce from 20
|
||||
Bullet->NetCullDistanceSquared = 5000000.0f; // Cull at 500m
|
||||
```
|
||||
|
||||
## Benchmarking Results
|
||||
|
||||
### Performance Targets
|
||||
|
||||
| Platform | Target FPS | Max Bullets | Memory Limit |
|
||||
|----------|------------|-------------|--------------|
|
||||
| PC High-End | 60 FPS | 500+ | 500 MB |
|
||||
| PC Mid-Range | 60 FPS | 200+ | 200 MB |
|
||||
| Console | 30 FPS | 100+ | 100 MB |
|
||||
| Mobile | 30 FPS | 50+ | 50 MB |
|
||||
|
||||
### Optimization Results
|
||||
|
||||
| Optimization | FPS Improvement | Memory Reduction |
|
||||
|--------------|-----------------|------------------|
|
||||
| Object Pooling | +25% | -40% |
|
||||
| LOD System | +15% | -20% |
|
||||
| Trace Optimization | +10% | -5% |
|
||||
| Network Optimization | +5% | -10% |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Advanced Tutorials](../tutorials/advanced-concepts) - Complex implementation examples
|
||||
- [Multiplayer Guide](multiplayer-guide) - Network optimization strategies
|
||||
- [API Reference](../api/overview) - Complete function documentation
|
||||
|
||||
---
|
||||
|
||||
*For platform-specific optimization guides, see the [Platform Guides](../platforms/) section.*
|
||||
@@ -0,0 +1,494 @@
|
||||
# AEBBullet API Reference
|
||||
|
||||
Complete reference for the `AEBBullet` class - the core projectile actor in EasyBallistics.
|
||||
|
||||
## Class Overview
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class EASYBALLISTICS_API AEBBullet : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
```
|
||||
|
||||
The `AEBBullet` class is the primary projectile actor that handles ballistic physics simulation, collision detection, and impact processing.
|
||||
|
||||
## Core Properties
|
||||
|
||||
### State Properties
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `Velocity` | `FVector` | Current velocity vector in cm/s | `(0,0,0)` |
|
||||
| `RandomStream` | `FRandomStream` | Seeded random stream for consistent behavior | - |
|
||||
| `OwnerSafe` | `bool` | Whether bullet should ignore its owner initially | `false` |
|
||||
| `FiringBarrel` | `UEBBarrel*` | Reference to the barrel that fired this bullet | `nullptr` |
|
||||
|
||||
### Debug Properties
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `DebugEnabled` | `bool` | Enable debug visualization | `false` |
|
||||
| `DebugTrailTime` | `float` | Duration to show debug trails (seconds) | `1.0` |
|
||||
| `DebugTrailWidth` | `float` | Width of debug trail lines | `0` |
|
||||
| `DebugTrailColorFast` | `FLinearColor` | Color for high-speed portions of trail | Green |
|
||||
| `DebugTrailColorSlow` | `FLinearColor` | Color for low-speed portions of trail | Red |
|
||||
| `DebugPooling` | `bool` | Debug object pooling behavior | `false` |
|
||||
|
||||
### World Environment
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `Wind` | `FVector` | Wind velocity affecting the bullet | `(0,0,0)` |
|
||||
| `AtmosphereType` | `EEBAtmosphereType` | Atmospheric simulation model | `AT_Constant` |
|
||||
| `SeaLevelAirDensity` | `float` | Air density at sea level (kg/m³) | `1.21` |
|
||||
| `SeaLevelSpeedOfSound` | `float` | Speed of sound at sea level (cm/s) | `34300` |
|
||||
| `WorldScale` | `float` | World scale multiplier | `1.0` |
|
||||
| `SeaLevelAirPressure` | `float` | Atmospheric pressure at sea level (mbar) | `1012.5` |
|
||||
| `SeaLevelAirTemperature` | `float` | Temperature at sea level (°C) | `20.0` |
|
||||
|
||||
### Physics Mode Selection
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `UseMathematicalPhysics` | `bool` | Use mathematical ballistics calculations | `false` |
|
||||
| `BulletPropertiesAsset` | `UEBBulletPropertiesAsset*` | Asset with mathematical bullet properties | `nullptr` |
|
||||
| `MaterialPropertiesAsset` | `UEBMaterialPropertiesAsset*` | Asset with material properties | `nullptr` |
|
||||
|
||||
### Artistic Properties (when `UseMathematicalPhysics = false`)
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `Mass` | `float` | Bullet mass in kg | `0.005` |
|
||||
| `Diameter` | `float` | Bullet diameter in cm | `0.556` |
|
||||
| `FormFactor` | `float` | Form factor for drag calculations | `1.0` |
|
||||
| `MachDragCurve` | `UCurveFloat*` | Drag curve vs Mach number | `nullptr` |
|
||||
|
||||
### Flight Properties
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `MuzzleVelocityMin` | `float` | Minimum muzzle velocity (cm/s) | `100000.0` |
|
||||
| `MuzzleVelocityMax` | `float` | Maximum muzzle velocity (cm/s) | `100000.0` |
|
||||
| `Spread` | `float` | Maximum bullet spread in radians | `0.0` |
|
||||
| `SpreadBias` | `float` | Spread bias for accuracy | `0.0` |
|
||||
|
||||
### Impact System
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `UseNewImpactSystem` | `bool` | Use the new ballistic impact component | `false` |
|
||||
| `BallisticImpactComponent` | `UEBBallisticImpactComponent*` | Impact handling component | Auto-created |
|
||||
| `MaterialResponseMap` | `UEBMaterialResponseMap*` | Material interaction rules | `nullptr` |
|
||||
|
||||
### Collision Settings
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `TraceChannel` | `ECollisionChannel` | Collision channel for traces | `ECC_WorldStatic` |
|
||||
| `TraceComplex` | `bool` | Use complex collision shapes | `false` |
|
||||
| `CollisionMargin` | `float` | Collision detection margin (cm) | `1.0` |
|
||||
| `DespawnVelocity` | `float` | Velocity below which bullet despawns | `100.0` |
|
||||
| `IgnoredActors` | `TArray<AActor*>` | Actors to ignore during collision | Empty |
|
||||
|
||||
### Simulation Settings
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `FixedStep` | `bool` | Use fixed timestep simulation | `false` |
|
||||
| `FixedStepSeconds` | `float` | Fixed timestep duration | `0.1` |
|
||||
| `MaxTracesPerStep` | `int32` | Maximum collision traces per simulation step | `8` |
|
||||
| `DoFirstStepImmediately` | `bool` | Perform first trace immediately on spawn | `true` |
|
||||
|
||||
### Object Pooling
|
||||
|
||||
| Property | Type | Description | Default |
|
||||
|----------|------|-------------|---------|
|
||||
| `EnablePooling` | `bool` | Enable object pooling for this bullet type | `false` |
|
||||
| `MaxPoolSize` | `int32` | Maximum number of bullets in pool | `50` |
|
||||
|
||||
## Static Spawning Functions
|
||||
|
||||
### SpawnWithExactVelocity
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Spawn")
|
||||
static void SpawnWithExactVelocity(
|
||||
TSubclassOf<AEBBullet> BulletClass,
|
||||
AActor* BulletOwner,
|
||||
APawn* BulletInstigator,
|
||||
FVector BulletLocation,
|
||||
FVector BulletVelocity
|
||||
);
|
||||
```
|
||||
|
||||
Spawns a bullet with precise velocity without applying spread or velocity randomization.
|
||||
|
||||
**Parameters:**
|
||||
- `BulletClass`: The bullet class to spawn
|
||||
- `BulletOwner`: Actor that owns the bullet (for collision ignoring)
|
||||
- `BulletInstigator`: Pawn that caused the bullet to be fired (for damage attribution)
|
||||
- `BulletLocation`: World location to spawn the bullet
|
||||
- `BulletVelocity`: Exact velocity vector in cm/s
|
||||
|
||||
### SpawnWithExactVelocityFromBarrel
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Spawn")
|
||||
static void SpawnWithExactVelocityFromBarrel(
|
||||
TSubclassOf<AEBBullet> BulletClass,
|
||||
AActor* BulletOwner,
|
||||
APawn* BulletInstigator,
|
||||
FVector BulletLocation,
|
||||
FVector BulletVelocity,
|
||||
UEBBarrel* SourceBarrel
|
||||
);
|
||||
```
|
||||
|
||||
Spawns a bullet from a specific barrel, inheriting barrel properties and settings.
|
||||
|
||||
### Spawn
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Spawn")
|
||||
static void Spawn(
|
||||
TSubclassOf<AEBBullet> BulletClass,
|
||||
AActor* BulletOwner,
|
||||
APawn* BulletInstigator,
|
||||
FVector BulletLocation,
|
||||
FVector BulletVelocity
|
||||
);
|
||||
```
|
||||
|
||||
Standard bullet spawning with spread and velocity variation applied.
|
||||
|
||||
## Physics Functions
|
||||
|
||||
### UpdateVelocity
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|Flight")
|
||||
FVector UpdateVelocity(
|
||||
UWorld* World,
|
||||
FVector Location,
|
||||
FVector PreviousVelocity,
|
||||
float DeltaTime
|
||||
) const;
|
||||
```
|
||||
|
||||
Updates bullet velocity each simulation step. Override this to implement custom physics.
|
||||
|
||||
**Parameters:**
|
||||
- `World`: World context
|
||||
- `Location`: Current bullet location
|
||||
- `PreviousVelocity`: Velocity from previous frame
|
||||
- `DeltaTime`: Time since last update
|
||||
|
||||
**Returns:** New velocity vector
|
||||
|
||||
### GetWind
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World")
|
||||
FVector GetWind(UWorld* World, FVector Location) const;
|
||||
```
|
||||
|
||||
Gets wind velocity at the specified location. Override for dynamic wind systems.
|
||||
|
||||
### GetAirDensity
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World")
|
||||
float GetAirDensity(UWorld* World, FVector Location) const;
|
||||
```
|
||||
|
||||
Gets air density at the specified location based on altitude and atmosphere type.
|
||||
|
||||
### GetSpeedOfSound
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World")
|
||||
float GetSpeedOfSound(UWorld* World, FVector Location) const;
|
||||
```
|
||||
|
||||
Gets speed of sound at the specified location for Mach number calculations.
|
||||
|
||||
## Mathematical Physics Functions
|
||||
|
||||
### GetEffectiveMass
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Mathematical Physics")
|
||||
float GetEffectiveMass() const;
|
||||
```
|
||||
|
||||
Returns the effective mass of the bullet, considering mathematical properties if enabled.
|
||||
|
||||
### GetEffectiveDiameter
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Mathematical Physics")
|
||||
float GetEffectiveDiameter() const;
|
||||
```
|
||||
|
||||
Returns the effective diameter of the bullet in centimeters.
|
||||
|
||||
### GetEffectiveDragCoefficient
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Mathematical Physics")
|
||||
float GetEffectiveDragCoefficient(float MachNumber) const;
|
||||
```
|
||||
|
||||
Calculates drag coefficient based on Mach number and ballistic coefficient.
|
||||
|
||||
### CalculateMathematicalPenetration
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Mathematical Physics")
|
||||
float CalculateMathematicalPenetration(
|
||||
UPhysicalMaterial* Material,
|
||||
float VelocityMPS,
|
||||
float ImpactAngle
|
||||
) const;
|
||||
```
|
||||
|
||||
Calculates penetration depth using mathematical ballistics formulas.
|
||||
|
||||
### CalculateMathematicalRicochetProbability
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Mathematical Physics")
|
||||
float CalculateMathematicalRicochetProbability(
|
||||
UPhysicalMaterial* Material,
|
||||
float VelocityMPS,
|
||||
float ImpactAngle
|
||||
) const;
|
||||
```
|
||||
|
||||
Calculates ricochet probability using mathematical models.
|
||||
|
||||
## Impact Events
|
||||
|
||||
### OnImpact
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "EBBullet|Impact")
|
||||
void OnImpact(
|
||||
bool Ricochet,
|
||||
bool PassedThrough,
|
||||
FVector Location,
|
||||
FVector IncomingVelocity,
|
||||
FVector Normal,
|
||||
FVector ExitLocation,
|
||||
FVector ExitVelocity,
|
||||
FVector Impulse,
|
||||
float PenetrationDepth,
|
||||
AActor* Actor,
|
||||
USceneComponent* Component,
|
||||
FName BoneName,
|
||||
UPhysicalMaterial* PhysMaterial,
|
||||
FHitResult HitResult
|
||||
);
|
||||
```
|
||||
|
||||
Called when bullet impacts a surface (server authority only).
|
||||
|
||||
### OnNetPredictedImpact
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCosmetic, BlueprintNativeEvent, Category = "EBBullet|Impact")
|
||||
void OnNetPredictedImpact(
|
||||
bool Ricochet,
|
||||
bool PassedThrough,
|
||||
FVector Location,
|
||||
FVector IncomingVelocity,
|
||||
FVector Normal,
|
||||
FVector ExitLocation,
|
||||
FVector ExitVelocity,
|
||||
FVector Impulse,
|
||||
float PenetrationDepth,
|
||||
AActor* Actor,
|
||||
USceneComponent* Component,
|
||||
FName BoneName,
|
||||
UPhysicalMaterial* PhysMaterial,
|
||||
FHitResult HitResult
|
||||
);
|
||||
```
|
||||
|
||||
Called for client-side prediction of impacts (cosmetic only).
|
||||
|
||||
### OnTrace
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "EBBullet|Impact")
|
||||
void OnTrace(FVector StartLocation, FVector EndLocation);
|
||||
```
|
||||
|
||||
Called each simulation step for debugging trajectory visualization.
|
||||
|
||||
## Network Functions
|
||||
|
||||
### VelocityChangeBroadcast
|
||||
|
||||
```cpp
|
||||
UFUNCTION(NetMulticast, Unreliable)
|
||||
void VelocityChangeBroadcast(
|
||||
FVector_NetQuantize NewLocation,
|
||||
FVector NewVelocity
|
||||
);
|
||||
```
|
||||
|
||||
Broadcasts velocity changes to all clients (unreliable for performance).
|
||||
|
||||
### VelocityChangeBroadcastReliable
|
||||
|
||||
```cpp
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void VelocityChangeBroadcastReliable(
|
||||
FVector_NetQuantize NewLocation,
|
||||
FVector NewVelocity
|
||||
);
|
||||
```
|
||||
|
||||
Broadcasts critical velocity changes with reliable delivery.
|
||||
|
||||
### OnTrajectoryUpdateReceived
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "EBBullet|Remote")
|
||||
void OnTrajectoryUpdateReceived(
|
||||
FVector Location,
|
||||
FVector OldVelocity,
|
||||
FVector NewVelocity
|
||||
);
|
||||
```
|
||||
|
||||
Called when trajectory updates are received from server.
|
||||
|
||||
## Pooling Functions
|
||||
|
||||
### Deactivate
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintAuthorityOnly, BlueprintCallable, Category = "EBBullet|Pooling")
|
||||
void Deactivate();
|
||||
```
|
||||
|
||||
Deactivates the bullet and returns it to the object pool.
|
||||
|
||||
### OnDeactivated
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|Activation")
|
||||
void OnDeactivated();
|
||||
```
|
||||
|
||||
Called when bullet is deactivated. Override to implement custom cleanup.
|
||||
|
||||
## Collision Filtering
|
||||
|
||||
### CollisionFilter
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World")
|
||||
bool CollisionFilter(FHitResult HitResult) const;
|
||||
```
|
||||
|
||||
Filters collision hits. Return `false` to ignore the hit.
|
||||
|
||||
**Parameters:**
|
||||
- `HitResult`: The collision result to evaluate
|
||||
|
||||
**Returns:** `true` to process the hit, `false` to ignore it
|
||||
|
||||
## Debug Functions
|
||||
|
||||
### CreateDebugImpactWidget
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Category = "EBBullet|Debug")
|
||||
void CreateDebugImpactWidget(
|
||||
FVector Location,
|
||||
bool bRicochet,
|
||||
bool bPenetration,
|
||||
FVector ImpactVelocity,
|
||||
float PenetrationDepth,
|
||||
UPhysicalMaterial* Material
|
||||
);
|
||||
```
|
||||
|
||||
Creates debug information display at impact location.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Bullet Spawning
|
||||
|
||||
```cpp
|
||||
// Spawn a simple bullet
|
||||
AEBBullet::Spawn(
|
||||
MyBulletClass,
|
||||
MyWeapon,
|
||||
MyPlayer,
|
||||
MuzzleLocation,
|
||||
MuzzleDirection * MuzzleVelocity
|
||||
);
|
||||
```
|
||||
|
||||
### Mathematical Ballistics Setup
|
||||
|
||||
```cpp
|
||||
// Configure for realistic ballistics
|
||||
Bullet->UseMathematicalPhysics = true;
|
||||
Bullet->BulletPropertiesAsset = My556BulletProperties;
|
||||
Bullet->UseNewImpactSystem = true;
|
||||
Bullet->AtmosphereType = EEBAtmosphereType::AT_Earth;
|
||||
```
|
||||
|
||||
### Impact Event Handling
|
||||
|
||||
```cpp
|
||||
// In C++
|
||||
void AMyActor::HandleBulletImpact(/* impact parameters */)
|
||||
{
|
||||
if (bDidPenetrate)
|
||||
{
|
||||
SpawnPenetrationEffect(Location, PenetrationDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnImpactEffect(Location, Normal);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the event
|
||||
Bullet->BallisticImpactComponent->OnBallisticImpact.AddDynamic(
|
||||
this, &AMyActor::HandleBulletImpact);
|
||||
```
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
```cpp
|
||||
// Enable pooling for high-rate fire
|
||||
Bullet->EnablePooling = true;
|
||||
Bullet->MaxPoolSize = 100;
|
||||
|
||||
// Reduce simulation quality for distant bullets
|
||||
if (Distance > 2000.0f)
|
||||
{
|
||||
Bullet->MaxTracesPerStep = 4;
|
||||
Bullet->FixedStepSeconds = 0.033f;
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [UEBBarrel API Reference](barrel-reference) - Weapon barrel component
|
||||
- [UEBBallisticImpactComponent API Reference](impact-component-reference) - Impact handling
|
||||
- [Mathematical Ballistics](../core-concepts/overview#mathematical-vs-artistic-modes) - Physics calculation modes
|
||||
- [Performance Optimization](../advanced/performance-optimization) - Optimization strategies
|
||||
|
||||
---
|
||||
|
||||
*For more detailed examples, see the [Advanced Weapon Systems Tutorial](../tutorials/advanced-weapon-systems).*
|
||||
@@ -357,8 +357,24 @@ When migrating from the old impact system:
|
||||
- Impact events have different parameter signatures
|
||||
- Some artistic properties moved to new component system
|
||||
|
||||
## Detailed API References
|
||||
|
||||
### Core Classes
|
||||
|
||||
- **[AEBBullet](bullet-reference)** - Complete bullet actor reference with all properties and methods
|
||||
- **UEBBarrel** - Weapon barrel component (coming soon)
|
||||
- **UEBBallisticImpactComponent** - Impact handling component (coming soon)
|
||||
- **UEBMathematicalBallistics** - Static calculation functions (coming soon)
|
||||
|
||||
### Asset Classes
|
||||
|
||||
- **UEBBulletPropertiesAsset** - Bullet characteristic definitions (coming soon)
|
||||
- **UEBMaterialResponseMap** - Material interaction rules (coming soon)
|
||||
- **UEBMaterialPropertiesAsset** - Material ballistic properties (coming soon)
|
||||
|
||||
## See Also
|
||||
|
||||
- [Core Concepts](../core-concepts/overview) - System architecture overview
|
||||
- [Quick Start Guide](../getting-started/quick-start) - Create your first weapon
|
||||
- [Advanced Weapon Systems](../tutorials/advanced-weapon-systems) - Complex implementation examples
|
||||
- [Troubleshooting](../troubleshooting) - Common issues and solutions
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
id: gatling-mechanics
|
||||
title: Gatling Mechanics
|
||||
sidebar_label: Gatling Mechanics
|
||||
---
|
||||
|
||||
EasyBallistics supports simulating Gatling-style weapons (miniguns) with spool-up and spool-down behavior. This allows for a dynamic fire rate that changes over time.
|
||||
|
||||
## Enabling Gatling Mode
|
||||
|
||||
To use the Gatling mechanics, you must set the `FireMode` property on the `EBBarrel` component to `Gatling`.
|
||||
|
||||
## Core Properties
|
||||
|
||||
All Gatling-related properties are found on the `EBBarrel` component.
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `GatlingAutoSpool` | `bool` | If true, the barrel will automatically start spooling up when the `Shoot` function is called with `Trigger` set to true, and spool down when called with `false`. If false, you must manually control spooling. |
|
||||
| `GatlingSpoolUpTime` | `float` | The time in seconds it takes for the barrel to ramp up from zero to its maximum fire rate. |
|
||||
| `GatlingSpoolDownTime` | `float` | The time in seconds it takes for the barrel to ramp down from its maximum fire rate to zero after the trigger is released (or `GatlingSpool(false)` is called). |
|
||||
| `FireRateMin` | `float` | The rounds-per-second when the Gatling barrel is at its minimum spool (usually 0). |
|
||||
| `FireRateMax` | `float` | The rounds-per-second when the Gatling barrel has fully spooled up. |
|
||||
|
||||
## Runtime State
|
||||
|
||||
You can monitor the state of the Gatling barrel at runtime with these properties:
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `Spooling` | `bool` | A read-only boolean that is true if the barrel is currently spooling up or down. |
|
||||
| `GatlingPhase` | `float` | A read-only value from 0.0 to 1.0 representing the current spool progress. 0 is fully spooled down, and 1 is fully spooled up. |
|
||||
| `GatlingRPS` | `float` | The current rounds-per-second of the barrel, based on the `GatlingPhase`. This value is interpolated between `FireRateMin` and `FireRateMax`. |
|
||||
|
||||
## Manual Control
|
||||
|
||||
If `GatlingAutoSpool` is disabled, you have direct control over the spooling behavior.
|
||||
|
||||
### `GatlingSpool` (Function)
|
||||
|
||||
This function allows you to manually start and stop the spooling process.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|---|---|---|
|
||||
| `Spool` | `bool` | If `true`, the barrel will begin to spool up. If `false`, it will begin to spool down. |
|
||||
|
||||
**Example Blueprint Usage:**
|
||||
|
||||
You could hook this up to separate key events to allow the player to pre-spool the weapon before firing.
|
||||
|
||||
```blueprint
|
||||
// On "Spool Up" button pressed:
|
||||
MyBarrel->GatlingSpool(true);
|
||||
|
||||
// On "Spool Up" button released:
|
||||
MyBarrel->GatlingSpool(false);
|
||||
```
|
||||
|
||||
While manually spooling, you still need to call the `Shoot` function to actually fire bullets. The rate of fire will depend on the `GatlingRPS` at the moment `Shoot` is called.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
id: trajectory-prediction
|
||||
title: Trajectory Prediction
|
||||
sidebar_label: Trajectory Prediction
|
||||
---
|
||||
|
||||
EasyBallistics provides powerful Blueprint-callable functions for predicting a bullet's trajectory and calculating the required aim to hit a moving target. These are essential for creating smart AI, aim assists, or target lead indicators.
|
||||
|
||||
All prediction functions are located on the `EBBarrel` component.
|
||||
|
||||
## Predicting Bullet Impact
|
||||
|
||||
These functions simulate the full flight of a virtual bullet and report where it will hit.
|
||||
|
||||
### `PredictHit`
|
||||
|
||||
Predicts the trajectory and impact point of a bullet fired from the barrel's current position and orientation.
|
||||
|
||||
**Outputs:**
|
||||
- `Hit` (bool): `true` if an impact is predicted within the `MaxTime`.
|
||||
- `TraceResult` (HitResult): The standard Unreal Engine hit result structure.
|
||||
- `HitLocation` (Vector): The world-space location of the impact.
|
||||
- `HitTime` (float): The predicted time in seconds until impact.
|
||||
- `HitActor` (Actor): The actor that was hit.
|
||||
- `Trajectory` (`TArray<FVector>`): An array of points representing the predicted flight path.
|
||||
|
||||
**Inputs:**
|
||||
- `BulletClass` (`TSubclassOf<AEBBullet>`): The bullet to use for the prediction. The simulation will use this bullet's properties (drag, velocity, etc.).
|
||||
- `IgnoredActors` (`TArray<AActor*>`): A list of actors to ignore during the trace.
|
||||
- `MaxTime` (float): The maximum simulated flight time in seconds.
|
||||
- `Step` (float): The time step for the simulation. Smaller values are more accurate but more expensive.
|
||||
|
||||
### `PredictHitFromLocation`
|
||||
|
||||
This version works identically to `PredictHit`, but allows you to specify a custom starting location and aim direction, rather than using the barrel's transform.
|
||||
|
||||
**Additional Inputs:**
|
||||
- `StartLocation` (Vector): The world-space location to start the prediction from.
|
||||
- `AimDirection` (Vector): The initial direction of fire for the prediction.
|
||||
|
||||
## Calculating Aim Direction (Solving for a Hit)
|
||||
|
||||
These functions are used to calculate the correct direction to fire in order to hit a moving target. This is often called "target leading".
|
||||
|
||||
### `CalculateAimDirection`
|
||||
|
||||
Calculates the aim direction required to hit a target, accounting for bullet drop and target velocity.
|
||||
|
||||
**Outputs:**
|
||||
- `AimDirection` (Vector): The calculated direction you should fire in.
|
||||
- `PredictedTargetLocation` (Vector): The predicted location of the target at the moment of impact.
|
||||
- `PredictedIntersectionLocation` (Vector): The predicted location of the bullet/target intersection.
|
||||
- `PredictedFlightTime` (float): The predicted flight time of the bullet.
|
||||
- `Error` (float): A measure of the final aiming error. Lower is better.
|
||||
|
||||
**Inputs:**
|
||||
- `BulletClass` (`TSubclassOf<AEBBullet>`): The bullet to use for the calculation.
|
||||
- `TargetLocation` (Vector): The current world-space location of the target.
|
||||
- `TargetVelocity` (Vector): The current velocity of the target in cm/s.
|
||||
- `MaxTime`, `Step`, `NumIterations`: Parameters to control the simulation's accuracy and performance. Higher iterations improve the solution.
|
||||
|
||||
### `CalculateAimDirectionFromLocation`
|
||||
|
||||
This version works identically to `CalculateAimDirection`, but allows you to specify a custom starting location for the shot.
|
||||
|
||||
**Additional Inputs:**
|
||||
- `StartLocation` (Vector): The world-space location the shot will be fired from.
|
||||
|
||||
**Example Use Case (AI):**
|
||||
|
||||
An AI character could use `CalculateAimDirection` every tick to get a precise aim vector for the player, then smoothly interpolate its weapon's rotation to match that vector, leading to realistic aiming behavior.
|
||||
@@ -1,157 +1,243 @@
|
||||
# Installation
|
||||
# Installation Guide
|
||||
|
||||
This guide will walk you through installing EasyBallistics in your Unreal Engine project.
|
||||
|
||||
## Prerequisites
|
||||
## System Requirements
|
||||
|
||||
- **Unreal Engine 5.6.0** or later
|
||||
- **Visual Studio 2022** (for C++ projects)
|
||||
- **Git** (for version control)
|
||||
### Minimum Requirements
|
||||
- **Unreal Engine**: 5.6.0 or later
|
||||
- **Operating System**: Windows 10, macOS 10.15, or Ubuntu 18.04
|
||||
- **Memory**: 8GB RAM minimum (16GB recommended)
|
||||
- **Storage**: 500MB available space
|
||||
|
||||
### Supported Platforms
|
||||
- **Development**: Windows, macOS, Linux
|
||||
- **Deployment**: Windows, macOS, Linux, Android, iOS
|
||||
- **Network**: Full multiplayer support
|
||||
|
||||
## Installation Methods
|
||||
|
||||
### Method 1: Marketplace Installation (Recommended)
|
||||
### Method 1: Unreal Engine Marketplace (Recommended)
|
||||
|
||||
1. Open the **Epic Games Launcher**
|
||||
2. Navigate to the **Marketplace**
|
||||
3. Search for "EasyBallistics"
|
||||
4. Click **Add to Project** or **Install to Engine**
|
||||
5. Select your target project
|
||||
6. Click **Install**
|
||||
1. **Open Epic Games Launcher**
|
||||
- Navigate to the Unreal Engine Marketplace
|
||||
- Search for "EasyBallistics"
|
||||
- Click "Add to Project" or "Install to Engine"
|
||||
|
||||
2. **Add to Project**
|
||||
- Open your project in Unreal Engine
|
||||
- Go to **Edit → Plugins**
|
||||
- Find "EasyBallistics" in the **Installed** tab
|
||||
- Check the **Enabled** checkbox
|
||||
- Click **Restart Now**
|
||||
|
||||
### Method 2: Manual Installation
|
||||
|
||||
1. Download the plugin from the marketplace or GitHub
|
||||
2. Extract the plugin to your project's `Plugins` folder:
|
||||
1. **Download Plugin**
|
||||
- Extract the EasyBallistics folder
|
||||
- Copy it to your project's `Plugins` folder
|
||||
- Create the `Plugins` folder if it doesn't exist
|
||||
|
||||
2. **Directory Structure**
|
||||
```
|
||||
YourProject/
|
||||
├── Plugins/
|
||||
│ └── EasyBallistics/
|
||||
│ ├── EasyBallistics.uplugin
|
||||
│ ├── Source/
|
||||
│ ├── Resources/
|
||||
│ └── EasyBallistics.uplugin
|
||||
│ └── ...
|
||||
├── YourProject.uproject
|
||||
└── ...
|
||||
```
|
||||
3. Regenerate project files:
|
||||
|
||||
3. **Enable Plugin**
|
||||
- Right-click your `.uproject` file
|
||||
- Select **Generate Visual Studio project files**
|
||||
- Select "Generate Visual Studio project files"
|
||||
- Open the project in Unreal Engine
|
||||
- Go to **Edit → Plugins**
|
||||
- Enable EasyBallistics in the **Project** tab
|
||||
|
||||
### Method 3: Git Submodule (Advanced)
|
||||
### Method 3: Engine-Level Installation
|
||||
|
||||
```bash
|
||||
# Navigate to your project directory
|
||||
cd YourProject/Plugins
|
||||
1. **Install to Engine**
|
||||
- Copy EasyBallistics to your Engine's plugins folder:
|
||||
```
|
||||
[UE5_Installation]/Engine/Plugins/EasyBallistics/
|
||||
```
|
||||
|
||||
# Add as submodule
|
||||
git submodule add https://github.com/your-org/easyballistics.git EasyBallistics
|
||||
2. **Enable in Projects**
|
||||
- The plugin will be available in all projects
|
||||
- Enable it per-project in **Edit → Plugins**
|
||||
|
||||
# Initialize and update
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
## Verification
|
||||
|
||||
## Enabling the Plugin
|
||||
### Check Plugin Loading
|
||||
|
||||
1. Open your project in Unreal Engine
|
||||
2. Go to **Edit → Plugins**
|
||||
3. Search for "EasyBallistics"
|
||||
4. Check the **Enabled** checkbox
|
||||
5. Click **Restart Now** when prompted
|
||||
1. **Plugin Manager**
|
||||
- Open **Edit → Plugins**
|
||||
- Confirm EasyBallistics shows as "Enabled"
|
||||
- Look for any error messages
|
||||
|
||||

|
||||
2. **Content Browser**
|
||||
- Right-click in Content Browser
|
||||
- Verify "Ballistics" category appears in the context menu
|
||||
- Try creating a new Bullet Properties asset
|
||||
|
||||
## Verifying Installation
|
||||
3. **Blueprint Editor**
|
||||
- Create a new Actor Blueprint
|
||||
- Add an "EB Barrel" component
|
||||
- Verify the component appears and has properties
|
||||
|
||||
### Check Plugin Status
|
||||
### Compilation Test
|
||||
|
||||
1. Open **Edit → Plugins**
|
||||
2. Verify EasyBallistics shows as **Enabled**
|
||||
3. Check for any error messages
|
||||
1. **C++ Projects**
|
||||
- Add EasyBallistics to your module dependencies:
|
||||
```cpp
|
||||
// In YourProject.Build.cs
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"EasyBallistics" // Add this line
|
||||
});
|
||||
```
|
||||
|
||||
### Test Basic Functionality
|
||||
2. **Compile Project**
|
||||
- Build your project in Visual Studio or Rider
|
||||
- Verify no compilation errors
|
||||
|
||||
1. In the Content Browser, right-click
|
||||
2. Look for **Ballistics** category in the asset creation menu
|
||||
3. You should see:
|
||||
- Bullet Properties
|
||||
- Material Response Map
|
||||
## Troubleshooting
|
||||
|
||||

|
||||
### Plugin Not Visible
|
||||
|
||||
### Verify Components
|
||||
**Problem**: EasyBallistics doesn't appear in the Plugin Manager
|
||||
|
||||
1. Create a new Actor Blueprint
|
||||
2. Add Component → Search "Ballistic"
|
||||
3. You should find:
|
||||
- **EB Barrel** (weapon barrel component)
|
||||
- **EB Ballistic Impact Component** (impact handling)
|
||||
**Solutions**:
|
||||
1. Verify file placement in `Plugins/EasyBallistics/`
|
||||
2. Check that `EasyBallistics.uplugin` exists
|
||||
3. Regenerate project files and restart editor
|
||||
4. Check UE5 version compatibility (5.6.0+ required)
|
||||
|
||||
## Project Configuration
|
||||
### Compilation Errors
|
||||
|
||||
### Build Configuration
|
||||
**Problem**: Build errors when enabling the plugin
|
||||
|
||||
Add to your project's `DefaultEngine.ini`:
|
||||
**Solutions**:
|
||||
1. **Missing Dependencies**:
|
||||
```cpp
|
||||
// Add to YourProject.Build.cs
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"EasyBallistics",
|
||||
"PhysicsCore", // Often required
|
||||
"NavigationSystem" // For some features
|
||||
});
|
||||
```
|
||||
|
||||
```ini
|
||||
[/Script/Engine.Engine]
|
||||
+ActiveGameNameRedirects=(OldGameName="EasyBallistics",NewGameName="/Script/EasyBallistics")
|
||||
+ActiveGameNameRedirects=(OldGameName="EasyBallisticsEditor",NewGameName="/Script/EasyBallisticsEditor")
|
||||
```
|
||||
2. **Clean Build**:
|
||||
- Delete `Binaries` and `Intermediate` folders
|
||||
- Regenerate project files
|
||||
- Rebuild solution
|
||||
|
||||
### Module Dependencies
|
||||
3. **Version Mismatch**:
|
||||
- Ensure UE5 version is 5.6.0 or later
|
||||
- Check plugin version compatibility
|
||||
|
||||
If using C++, add to your project's `Build.cs` file:
|
||||
### Content Browser Issues
|
||||
|
||||
**Problem**: "Ballistics" category missing from asset creation menu
|
||||
|
||||
**Solutions**:
|
||||
1. Refresh Content Browser (F5)
|
||||
2. Restart Unreal Engine
|
||||
3. Verify plugin is enabled in Project settings
|
||||
4. Check Editor log for errors
|
||||
|
||||
### Performance Issues
|
||||
|
||||
**Problem**: Frame rate drops after enabling plugin
|
||||
|
||||
**Solutions**:
|
||||
1. **Disable Debug Features**:
|
||||
- Turn off debug visualization in bullet settings
|
||||
- Disable debug impact info on barrels
|
||||
|
||||
2. **Optimize Settings**:
|
||||
- Enable object pooling on bullets
|
||||
- Reduce max traces per step
|
||||
- Use fixed timestep for consistency
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Add Module Dependencies
|
||||
|
||||
For C++ projects, add these to your `.Build.cs` file:
|
||||
|
||||
```cpp
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"EasyBallistics" // Add this line
|
||||
"EasyBallistics",
|
||||
"PhysicsCore",
|
||||
"NavigationSystem"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] {
|
||||
"EasyBallistics"
|
||||
});
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
### Configure Project Settings
|
||||
|
||||
### Common Issues
|
||||
1. **Collision Channels**
|
||||
- Add custom collision channels for bullets
|
||||
- Configure response settings
|
||||
|
||||
#### Plugin Not Appearing
|
||||
- Ensure you're using UE 5.6 or later
|
||||
- Check if the plugin files are in the correct location
|
||||
- Regenerate project files
|
||||
2. **Network Settings**
|
||||
- Adjust replication settings for multiplayer
|
||||
- Configure bandwidth limits
|
||||
|
||||
#### Compilation Errors
|
||||
- Verify Visual Studio 2022 is installed
|
||||
- Check that all dependencies are met
|
||||
- Try a clean rebuild
|
||||
|
||||
#### Missing Components
|
||||
- Restart the editor after enabling the plugin
|
||||
- Clear the derived data cache: **Edit → Developer → Derived Data → Clear**
|
||||
|
||||
### Error Messages
|
||||
|
||||
#### "Module 'EasyBallistics' could not be loaded"
|
||||
1. Check your UE version compatibility
|
||||
2. Verify the plugin is enabled
|
||||
3. Regenerate project files
|
||||
4. Clean and rebuild the project
|
||||
|
||||
#### "Failed to load because module 'EasyBallistics' could not be found"
|
||||
1. Ensure the plugin is in the `Plugins` folder
|
||||
2. Check the `.uplugin` file is valid
|
||||
3. Verify file permissions
|
||||
3. **Performance Settings**
|
||||
- Set appropriate tick rates
|
||||
- Configure object pooling limits
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once installation is complete:
|
||||
After successful installation:
|
||||
|
||||
1. [Quick Start Guide](quick-start) - Create your first ballistic weapon
|
||||
2. [Core Concepts](../core-concepts/overview) - Understand the system architecture
|
||||
3. [API Reference](../api/overview) - Detailed function documentation
|
||||
1. **[Quick Start Guide](quick-start)** - Create your first weapon
|
||||
2. **[Core Concepts](../core-concepts/overview)** - Understand the system
|
||||
3. **[API Reference](../api/overview)** - Detailed function documentation
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you encounter issues during installation:
|
||||
### Support Resources
|
||||
|
||||
- Check the [Troubleshooting](../troubleshooting) guide
|
||||
- Visit our [Discord community](https://discord.gg/easyballistics)
|
||||
- Submit an issue on [GitHub](https://github.com/your-org/easyballistics/issues)
|
||||
- **Discord**: [Join our community](https://discord.gg/easyballistics)
|
||||
- **Email**: support@easyballistics.com
|
||||
- **Forums**: [Unreal Engine Forums](https://forums.unrealengine.com/)
|
||||
- **GitHub**: [Issues and Bug Reports](https://github.com/your-org/easyballistics/issues)
|
||||
|
||||
### Common Installation Issues
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Plugin not loading | Check UE5 version compatibility |
|
||||
| Compilation errors | Add required module dependencies |
|
||||
| Missing asset menu | Refresh Content Browser, restart editor |
|
||||
| Performance drops | Disable debug features, optimize settings |
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
When reporting installation problems, please include:
|
||||
|
||||
- UE5 version and build number
|
||||
- Operating system and version
|
||||
- Plugin version
|
||||
- Error messages and log files
|
||||
- Steps to reproduce the issue
|
||||
|
||||
---
|
||||
|
||||
*Ready to start building ballistic systems? Continue with the [Quick Start Guide](quick-start)!*
|
||||
+11
-3
@@ -65,9 +65,17 @@ graph TD
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. [Installation Guide](getting-started/installation) - Install the plugin
|
||||
2. [Quick Start](getting-started/quick-start) - Create your first weapon
|
||||
3. [Core Concepts](core-concepts/overview) - Understand the system architecture
|
||||
1. **[Installation Guide](getting-started/installation)** - Install and configure the plugin
|
||||
2. **[Quick Start](getting-started/quick-start)** - Create your first weapon in 10 minutes
|
||||
3. **[Core Concepts](core-concepts/overview)** - Understand the system architecture
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
4. **[Advanced Weapon Systems](tutorials/advanced-weapon-systems)** - Smart ammunition and complex firing modes
|
||||
5. **[Multiplayer Guide](advanced/multiplayer-guide)** - Network optimization and server authority
|
||||
6. **[Performance Optimization](advanced/performance-optimization)** - Achieve optimal performance
|
||||
7. **[Editor Tools](advanced/editor-tools)** - Streamlined asset creation workflows
|
||||
8. **[Migration Guide](advanced/migration-guide)** - Upgrade from older versions
|
||||
|
||||
## Support
|
||||
|
||||
|
||||
@@ -380,6 +380,31 @@ DebugTrailColorFast = Green
|
||||
DebugTrailColorSlow = Red
|
||||
```
|
||||
|
||||
### Impact Information Widget
|
||||
|
||||
EasyBallistics includes a widget to display detailed information about bullet impacts in real-time. This is useful for debugging penetration, ricochets, and material responses.
|
||||
|
||||
**How to Enable:**
|
||||
|
||||
1. **Enable on Barrel**: On your `EBBarrel` component, set the `DebugImpactInfo` boolean property to `true`.
|
||||
2. **Create a Widget Blueprint**:
|
||||
* In the Content Browser, click "Add New" -> "User Interface" -> "Widget Blueprint".
|
||||
* In the "Pick Parent Class" dialog, search for and select `EBDebugImpactWidget`.
|
||||
* Name and save your new widget Blueprint. You can customize its appearance here.
|
||||
3. **Assign to Bullet**: On your `EBBullet` asset, find the `DebugWidgetClass` property and assign the Widget Blueprint you just created.
|
||||
|
||||
**Displayed Information:**
|
||||
|
||||
When enabled, a widget will appear at the location of an impact, showing the following:
|
||||
|
||||
* **Impact Type**: Whether the bullet `RICOCHET`, `PENETRATION`, or was `STOPPED`.
|
||||
* **Material**: The name of the `UPhysicalMaterial` that was hit.
|
||||
* **Velocity**: The bullet's velocity at impact, in meters per second (m/s) and kilometers per hour (km/h).
|
||||
* **Mass**: The effective mass of the bullet in kilograms (kg).
|
||||
* **Diameter**: The effective diameter of the bullet in centimeters (cm).
|
||||
* **Energy**: The kinetic energy of the bullet at impact, in Joules (J).
|
||||
* **Penetration**: The depth of penetration in centimeters (cm).
|
||||
|
||||
### Console Commands
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,779 @@
|
||||
# Advanced Weapon Systems Tutorial
|
||||
|
||||
This tutorial demonstrates creating complex weapon systems using EasyBallistics, including smart ammunition, guided projectiles, and advanced firing modes.
|
||||
|
||||
## Smart Ammunition System
|
||||
|
||||
### Heat-Seeking Projectiles
|
||||
|
||||
Create bullets that track heat signatures:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class YOURGAME_API AHeatSeekingBullet : public AEBBullet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AHeatSeekingBullet()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// Heat seeking properties
|
||||
SeekingRange = 2000.0f;
|
||||
SeekingStrength = 0.5f;
|
||||
MaxTurnRate = 45.0f; // degrees per second
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heat Seeking")
|
||||
float SeekingRange = 2000.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heat Seeking")
|
||||
float SeekingStrength = 0.5f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heat Seeking")
|
||||
float MaxTurnRate = 45.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Heat Seeking")
|
||||
TArray<TSubclassOf<AActor>> TargetClasses;
|
||||
|
||||
private:
|
||||
AActor* CurrentTarget = nullptr;
|
||||
float TimeAlive = 0.0f;
|
||||
|
||||
public:
|
||||
virtual void Tick(float DeltaTime) override
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
TimeAlive += DeltaTime;
|
||||
|
||||
// Start seeking after initial acceleration phase
|
||||
if (TimeAlive > 0.1f)
|
||||
{
|
||||
UpdateHeatSeeking(DeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateHeatSeeking(float DeltaTime)
|
||||
{
|
||||
// Find the hottest target within range
|
||||
AActor* BestTarget = FindHottestTarget();
|
||||
|
||||
if (BestTarget)
|
||||
{
|
||||
CurrentTarget = BestTarget;
|
||||
|
||||
// Calculate direction to target
|
||||
FVector ToTarget = (BestTarget->GetActorLocation() - GetActorLocation()).GetSafeNormal();
|
||||
FVector CurrentDirection = Velocity.GetSafeNormal();
|
||||
|
||||
// Calculate turn towards target
|
||||
FVector NewDirection = FMath::VInterpTo(CurrentDirection, ToTarget, DeltaTime,
|
||||
FMath::DegreesToRadians(MaxTurnRate));
|
||||
|
||||
// Apply seeking strength
|
||||
NewDirection = FMath::Lerp(CurrentDirection, NewDirection, SeekingStrength);
|
||||
|
||||
// Update velocity
|
||||
float CurrentSpeed = Velocity.Size();
|
||||
Velocity = NewDirection * CurrentSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
AActor* FindHottestTarget()
|
||||
{
|
||||
TArray<AActor*> NearbyActors;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), APawn::StaticClass(), NearbyActors);
|
||||
|
||||
AActor* BestTarget = nullptr;
|
||||
float BestHeatSignature = 0.0f;
|
||||
float BestDistance = FLT_MAX;
|
||||
|
||||
for (AActor* Actor : NearbyActors)
|
||||
{
|
||||
if (!Actor || Actor == GetOwner())
|
||||
continue;
|
||||
|
||||
float Distance = FVector::Dist(GetActorLocation(), Actor->GetActorLocation());
|
||||
if (Distance > SeekingRange)
|
||||
continue;
|
||||
|
||||
// Calculate heat signature (example implementation)
|
||||
float HeatSignature = CalculateHeatSignature(Actor, Distance);
|
||||
|
||||
if (HeatSignature > BestHeatSignature)
|
||||
{
|
||||
BestTarget = Actor;
|
||||
BestHeatSignature = HeatSignature;
|
||||
BestDistance = Distance;
|
||||
}
|
||||
}
|
||||
|
||||
return BestTarget;
|
||||
}
|
||||
|
||||
float CalculateHeatSignature(AActor* Actor, float Distance)
|
||||
{
|
||||
// Base heat signature
|
||||
float Heat = 1.0f;
|
||||
|
||||
// Check if it's a vehicle (higher heat)
|
||||
if (Actor->IsA<AVehicle>())
|
||||
{
|
||||
Heat *= 2.0f;
|
||||
}
|
||||
|
||||
// Check if engine is running
|
||||
if (UActorComponent* EngineComp = Actor->GetComponentByClass(UEngineComponent::StaticClass()))
|
||||
{
|
||||
Heat *= 1.5f;
|
||||
}
|
||||
|
||||
// Distance falloff
|
||||
Heat *= FMath::Max(0.1f, 1.0f - (Distance / SeekingRange));
|
||||
|
||||
return Heat;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Proximity Explosive Rounds
|
||||
|
||||
Create rounds that detonate near targets:
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class YOURGAME_API AProximityBullet : public AEBBullet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AProximityBullet()
|
||||
{
|
||||
// Proximity settings
|
||||
ProximityRadius = 300.0f;
|
||||
ExplosionRadius = 500.0f;
|
||||
ExplosionDamage = 150.0f;
|
||||
ArmingDistance = 500.0f;
|
||||
|
||||
bIsArmed = false;
|
||||
DistanceTraveled = 0.0f;
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Proximity")
|
||||
float ProximityRadius = 300.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Proximity")
|
||||
float ExplosionRadius = 500.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Proximity")
|
||||
float ExplosionDamage = 150.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Proximity")
|
||||
float ArmingDistance = 500.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Proximity")
|
||||
TSubclassOf<AActor> ExplosionEffect;
|
||||
|
||||
private:
|
||||
bool bIsArmed = false;
|
||||
float DistanceTraveled = 0.0f;
|
||||
FVector LastPosition;
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override
|
||||
{
|
||||
Super::BeginPlay();
|
||||
LastPosition = GetActorLocation();
|
||||
}
|
||||
|
||||
virtual void Tick(float DeltaTime) override
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Track distance traveled
|
||||
FVector CurrentPos = GetActorLocation();
|
||||
DistanceTraveled += FVector::Dist(CurrentPos, LastPosition);
|
||||
LastPosition = CurrentPos;
|
||||
|
||||
// Arm after minimum distance
|
||||
if (!bIsArmed && DistanceTraveled >= ArmingDistance)
|
||||
{
|
||||
bIsArmed = true;
|
||||
OnBulletArmed();
|
||||
}
|
||||
|
||||
// Check proximity if armed
|
||||
if (bIsArmed)
|
||||
{
|
||||
CheckProximity();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void CheckProximity()
|
||||
{
|
||||
TArray<AActor*> NearbyActors;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), APawn::StaticClass(), NearbyActors);
|
||||
|
||||
for (AActor* Actor : NearbyActors)
|
||||
{
|
||||
if (!Actor || Actor == GetOwner())
|
||||
continue;
|
||||
|
||||
float Distance = FVector::Dist(GetActorLocation(), Actor->GetActorLocation());
|
||||
if (Distance <= ProximityRadius)
|
||||
{
|
||||
// Trigger explosion
|
||||
Explode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Explode()
|
||||
{
|
||||
FVector ExplosionLocation = GetActorLocation();
|
||||
|
||||
// Spawn explosion effect
|
||||
if (ExplosionEffect)
|
||||
{
|
||||
GetWorld()->SpawnActor<AActor>(ExplosionEffect, ExplosionLocation, FRotator::ZeroRotator);
|
||||
}
|
||||
|
||||
// Apply radial damage
|
||||
UGameplayStatics::ApplyRadialDamage(
|
||||
GetWorld(),
|
||||
ExplosionDamage,
|
||||
ExplosionLocation,
|
||||
ExplosionRadius,
|
||||
UDamageType::StaticClass(),
|
||||
TArray<AActor*>(), // Ignore list
|
||||
this,
|
||||
GetInstigatorController(),
|
||||
true // Full damage
|
||||
);
|
||||
|
||||
// Trigger explosion event
|
||||
OnExplosion(ExplosionLocation);
|
||||
|
||||
// Destroy bullet
|
||||
Destroy();
|
||||
}
|
||||
|
||||
protected:
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Proximity")
|
||||
void OnBulletArmed();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Proximity")
|
||||
void OnExplosion(FVector Location);
|
||||
};
|
||||
```
|
||||
|
||||
## Advanced Firing Modes
|
||||
|
||||
### Burst Fire with Recoil Control
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class YOURGAME_API UAdvancedBarrel : public UEBBarrel
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UAdvancedBarrel()
|
||||
{
|
||||
// Recoil control settings
|
||||
RecoilAccumulation = 0.0f;
|
||||
RecoilDecayRate = 2.0f;
|
||||
MaxRecoilMultiplier = 3.0f;
|
||||
|
||||
// Burst settings
|
||||
BurstShotDelay = 0.05f;
|
||||
BurstAccuracyPenalty = 0.1f;
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Advanced Firing")
|
||||
float RecoilDecayRate = 2.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Advanced Firing")
|
||||
float MaxRecoilMultiplier = 3.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Advanced Firing")
|
||||
float BurstShotDelay = 0.05f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Advanced Firing")
|
||||
float BurstAccuracyPenalty = 0.1f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Advanced Firing")
|
||||
UCurveFloat* RecoilPattern;
|
||||
|
||||
private:
|
||||
float RecoilAccumulation = 0.0f;
|
||||
int32 ShotsInBurst = 0;
|
||||
|
||||
public:
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction* ThisTickFunction) override
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
// Decay recoil over time
|
||||
if (RecoilAccumulation > 0.0f)
|
||||
{
|
||||
RecoilAccumulation = FMath::Max(0.0f, RecoilAccumulation - (RecoilDecayRate * DeltaTime));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SpawnBullet(AActor* Owner, FVector LocalLocation, FVector LocalAim) override
|
||||
{
|
||||
// Calculate recoil-modified aim direction
|
||||
FVector ModifiedAim = ApplyRecoilToAim(LocalAim);
|
||||
|
||||
// Apply burst accuracy penalty
|
||||
if (ShotsInBurst > 0)
|
||||
{
|
||||
float BurstSpread = BurstAccuracyPenalty * ShotsInBurst;
|
||||
ModifiedAim = ApplySpreadToDirection(ModifiedAim, BurstSpread);
|
||||
}
|
||||
|
||||
// Spawn bullet with modified direction
|
||||
Super::SpawnBullet(Owner, LocalLocation, ModifiedAim);
|
||||
|
||||
// Update recoil accumulation
|
||||
RecoilAccumulation = FMath::Min(MaxRecoilMultiplier, RecoilAccumulation + 1.0f);
|
||||
|
||||
// Update burst counter
|
||||
if (FireMode == EFireMode::FM_Burst)
|
||||
{
|
||||
ShotsInBurst++;
|
||||
if (ShotsInBurst >= BurstCount)
|
||||
{
|
||||
ShotsInBurst = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShotsInBurst = 0;
|
||||
}
|
||||
|
||||
// Apply recoil to weapon
|
||||
ApplyWeaponRecoil();
|
||||
}
|
||||
|
||||
private:
|
||||
FVector ApplyRecoilToAim(FVector BaseAim)
|
||||
{
|
||||
if (RecoilAccumulation <= 0.0f || !RecoilPattern)
|
||||
return BaseAim;
|
||||
|
||||
// Sample recoil pattern
|
||||
float PatternSample = RecoilPattern->GetFloatValue(RecoilAccumulation);
|
||||
|
||||
// Apply random recoil within pattern bounds
|
||||
float RecoilAmount = PatternSample * FMath::RandRange(0.8f, 1.2f);
|
||||
|
||||
// Create recoil offset (upward and slightly random horizontal)
|
||||
FVector RecoilOffset = FVector(
|
||||
FMath::RandRange(-0.3f, 0.3f) * RecoilAmount, // Horizontal
|
||||
0.0f,
|
||||
1.0f * RecoilAmount // Vertical
|
||||
);
|
||||
|
||||
// Apply recoil to aim direction
|
||||
FVector ModifiedAim = BaseAim + RecoilOffset;
|
||||
return ModifiedAim.GetSafeNormal();
|
||||
}
|
||||
|
||||
void ApplyWeaponRecoil()
|
||||
{
|
||||
// Apply physical recoil to weapon/player
|
||||
APawn* OwnerPawn = Cast<APawn>(GetOwner());
|
||||
if (OwnerPawn && OwnerPawn->GetController())
|
||||
{
|
||||
APlayerController* PC = Cast<APlayerController>(OwnerPawn->GetController());
|
||||
if (PC)
|
||||
{
|
||||
// Camera recoil
|
||||
float RecoilMagnitude = RecoilAccumulation * 0.5f;
|
||||
FRotator RecoilRotation(
|
||||
FMath::RandRange(-RecoilMagnitude, RecoilMagnitude * 2.0f), // Pitch (mostly up)
|
||||
FMath::RandRange(-RecoilMagnitude * 0.3f, RecoilMagnitude * 0.3f), // Yaw
|
||||
0.0f // Roll
|
||||
);
|
||||
|
||||
PC->ClientSetCameraShake(nullptr); // Add camera shake here
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Dual-Mode Weapon System
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class YOURGAME_API ADualModeWeapon : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ADualModeWeapon()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// Create components
|
||||
WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
|
||||
RootComponent = WeaponMesh;
|
||||
|
||||
PrimaryBarrel = CreateDefaultSubobject<UEBBarrel>(TEXT("PrimaryBarrel"));
|
||||
SecondaryBarrel = CreateDefaultSubobject<UEBBarrel>(TEXT("SecondaryBarrel"));
|
||||
|
||||
// Default to primary mode
|
||||
CurrentMode = EWeaponMode::Primary;
|
||||
ModeTransitionTime = 1.0f;
|
||||
bIsTransitioning = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UStaticMeshComponent* WeaponMesh;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UEBBarrel* PrimaryBarrel;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UEBBarrel* SecondaryBarrel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Modes")
|
||||
float ModeTransitionTime = 1.0f;
|
||||
|
||||
private:
|
||||
UENUM(BlueprintType)
|
||||
enum class EWeaponMode : uint8
|
||||
{
|
||||
Primary,
|
||||
Secondary,
|
||||
Transitioning
|
||||
};
|
||||
|
||||
EWeaponMode CurrentMode;
|
||||
bool bIsTransitioning;
|
||||
float TransitionProgress;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Weapon")
|
||||
void Fire()
|
||||
{
|
||||
if (bIsTransitioning)
|
||||
return;
|
||||
|
||||
switch (CurrentMode)
|
||||
{
|
||||
case EWeaponMode::Primary:
|
||||
PrimaryBarrel->Shoot(true);
|
||||
break;
|
||||
case EWeaponMode::Secondary:
|
||||
SecondaryBarrel->Shoot(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Weapon")
|
||||
void SwitchMode()
|
||||
{
|
||||
if (bIsTransitioning)
|
||||
return;
|
||||
|
||||
bIsTransitioning = true;
|
||||
TransitionProgress = 0.0f;
|
||||
|
||||
OnModeTransitionStarted();
|
||||
}
|
||||
|
||||
virtual void Tick(float DeltaTime) override
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (bIsTransitioning)
|
||||
{
|
||||
TransitionProgress += DeltaTime / ModeTransitionTime;
|
||||
|
||||
if (TransitionProgress >= 1.0f)
|
||||
{
|
||||
// Complete transition
|
||||
bIsTransitioning = false;
|
||||
CurrentMode = (CurrentMode == EWeaponMode::Primary) ?
|
||||
EWeaponMode::Secondary : EWeaponMode::Primary;
|
||||
|
||||
OnModeTransitionCompleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update transition animation
|
||||
UpdateTransitionAnimation(TransitionProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Weapon Events")
|
||||
void OnModeTransitionStarted();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Weapon Events")
|
||||
void OnModeTransitionCompleted();
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Weapon Events")
|
||||
void UpdateTransitionAnimation(float Progress);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Weapon")
|
||||
bool IsPrimaryMode() const
|
||||
{
|
||||
return CurrentMode == EWeaponMode::Primary;
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Weapon")
|
||||
bool IsSecondaryMode() const
|
||||
{
|
||||
return CurrentMode == EWeaponMode::Secondary;
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Weapon")
|
||||
UEBBarrel* GetActiveBarrel() const
|
||||
{
|
||||
return (CurrentMode == EWeaponMode::Primary) ? PrimaryBarrel : SecondaryBarrel;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Modular Weapon System
|
||||
|
||||
### Attachment System
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class YOURGAME_API UWeaponAttachment : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attachment")
|
||||
FString AttachmentName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attachment")
|
||||
UStaticMesh* AttachmentMesh;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attachment")
|
||||
FName SocketName;
|
||||
|
||||
// Stat modifications
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
|
||||
float DamageMultiplier = 1.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
|
||||
float AccuracyMultiplier = 1.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
|
||||
float RangeMultiplier = 1.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
|
||||
float FireRateMultiplier = 1.0f;
|
||||
|
||||
// Special effects
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects")
|
||||
bool bProvidesMuzzleFlash = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects")
|
||||
bool bProvidesSuppression = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Effects")
|
||||
bool bProvidesScope = false;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Attachment")
|
||||
void OnAttached(AActor* WeaponActor);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Attachment")
|
||||
void OnDetached(AActor* WeaponActor);
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class YOURGAME_API AModularWeapon : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AModularWeapon()
|
||||
{
|
||||
WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
|
||||
RootComponent = WeaponMesh;
|
||||
|
||||
Barrel = CreateDefaultSubobject<UEBBarrel>(TEXT("Barrel"));
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UStaticMeshComponent* WeaponMesh;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UEBBarrel* Barrel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attachments")
|
||||
TArray<UWeaponAttachment*> Attachments;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Attachments")
|
||||
TArray<UStaticMeshComponent*> AttachmentMeshes;
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Attachments")
|
||||
void AttachAccessory(UWeaponAttachment* Attachment)
|
||||
{
|
||||
if (!Attachment)
|
||||
return;
|
||||
|
||||
Attachments.Add(Attachment);
|
||||
|
||||
// Create mesh component for attachment
|
||||
UStaticMeshComponent* AttachmentMesh = NewObject<UStaticMeshComponent>(this);
|
||||
AttachmentMesh->SetStaticMesh(Attachment->AttachmentMesh);
|
||||
AttachmentMesh->AttachToComponent(WeaponMesh,
|
||||
FAttachmentTransformRules::SnapToTargetNotIncludingScale,
|
||||
Attachment->SocketName);
|
||||
AttachmentMeshes.Add(AttachmentMesh);
|
||||
|
||||
// Apply stat modifications
|
||||
ApplyAttachmentModifications();
|
||||
|
||||
// Trigger attachment event
|
||||
Attachment->OnAttached(this);
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Attachments")
|
||||
void DetachAccessory(UWeaponAttachment* Attachment)
|
||||
{
|
||||
int32 Index = Attachments.Find(Attachment);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
Attachments.RemoveAt(Index);
|
||||
|
||||
// Remove mesh component
|
||||
if (AttachmentMeshes.IsValidIndex(Index))
|
||||
{
|
||||
AttachmentMeshes[Index]->DestroyComponent();
|
||||
AttachmentMeshes.RemoveAt(Index);
|
||||
}
|
||||
|
||||
// Reapply all modifications
|
||||
ApplyAttachmentModifications();
|
||||
|
||||
// Trigger detachment event
|
||||
Attachment->OnDetached(this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void ApplyAttachmentModifications()
|
||||
{
|
||||
// Reset to base stats
|
||||
float DamageMultiplier = 1.0f;
|
||||
float AccuracyMultiplier = 1.0f;
|
||||
float RangeMultiplier = 1.0f;
|
||||
float FireRateMultiplier = 1.0f;
|
||||
|
||||
bool bHasSuppressor = false;
|
||||
bool bHasScope = false;
|
||||
|
||||
// Apply all attachment modifications
|
||||
for (UWeaponAttachment* Attachment : Attachments)
|
||||
{
|
||||
if (Attachment)
|
||||
{
|
||||
DamageMultiplier *= Attachment->DamageMultiplier;
|
||||
AccuracyMultiplier *= Attachment->AccuracyMultiplier;
|
||||
RangeMultiplier *= Attachment->RangeMultiplier;
|
||||
FireRateMultiplier *= Attachment->FireRateMultiplier;
|
||||
|
||||
bHasSuppressor = bHasSuppressor || Attachment->bProvidesSuppression;
|
||||
bHasScope = bHasScope || Attachment->bProvidesScope;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply modifications to barrel
|
||||
Barrel->Spread = Barrel->Spread / AccuracyMultiplier;
|
||||
Barrel->FireRateMin = Barrel->FireRateMin * FireRateMultiplier;
|
||||
Barrel->FireRateMax = Barrel->FireRateMax * FireRateMultiplier;
|
||||
|
||||
// TODO: Apply damage and range modifications to bullets
|
||||
// This would require modifying bullet properties at spawn time
|
||||
}
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintPure, Category = "Attachments")
|
||||
bool HasAttachmentOfType(TSubclassOf<UWeaponAttachment> AttachmentClass) const
|
||||
{
|
||||
for (UWeaponAttachment* Attachment : Attachments)
|
||||
{
|
||||
if (Attachment && Attachment->IsA(AttachmentClass))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Attachments")
|
||||
TArray<UWeaponAttachment*> GetAttachmentsOfType(TSubclassOf<UWeaponAttachment> AttachmentClass) const
|
||||
{
|
||||
TArray<UWeaponAttachment*> MatchingAttachments;
|
||||
|
||||
for (UWeaponAttachment* Attachment : Attachments)
|
||||
{
|
||||
if (Attachment && Attachment->IsA(AttachmentClass))
|
||||
{
|
||||
MatchingAttachments.Add(Attachment);
|
||||
}
|
||||
}
|
||||
|
||||
return MatchingAttachments;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
1. **Use Object Pooling**: Always enable pooling for smart ammunition
|
||||
2. **Limit Seeking Frequency**: Don't update seeking every tick for distant projectiles
|
||||
3. **Optimize Target Detection**: Use spatial partitioning for target finding
|
||||
4. **Cache References**: Store frequently accessed components and actors
|
||||
|
||||
### Gameplay Balance
|
||||
|
||||
1. **Arming Distances**: Prevent exploitation with minimum arming distances
|
||||
2. **Proximity Limits**: Set reasonable proximity detection ranges
|
||||
3. **Targeting Restrictions**: Limit what can be targeted by smart ammunition
|
||||
4. **Countermeasures**: Provide ways to defeat smart ammunition
|
||||
|
||||
### Network Considerations
|
||||
|
||||
1. **Server Authority**: Ensure explosive damage is calculated on server
|
||||
2. **Prediction**: Allow visual effects on clients with server validation
|
||||
3. **Bandwidth**: Minimize replication data for smart projectiles
|
||||
4. **Synchronization**: Keep attachment states synchronized
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Performance Optimization](../advanced/performance-optimization) - Optimize complex weapon systems
|
||||
- [Multiplayer Guide](../advanced/multiplayer-guide) - Network these advanced systems
|
||||
- [Editor Tools](../advanced/editor-tools) - Create custom tools for your weapons
|
||||
|
||||
---
|
||||
|
||||
*Ready for more advanced topics? Explore [Multiplayer Implementation](../advanced/multiplayer-guide) next.*
|
||||
@@ -40,7 +40,7 @@ const config = {
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
routeBasePath: '/',
|
||||
routeBasePath: '/docs',
|
||||
sidebarPath: './sidebars.js',
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
@@ -70,6 +70,7 @@ const config = {
|
||||
logo: {
|
||||
alt: 'EasyBallistics Logo',
|
||||
src: 'img/logo.svg',
|
||||
href: '/docs/intro',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
@@ -77,6 +78,7 @@ const config = {
|
||||
sidebarId: 'tutorialSidebar',
|
||||
position: 'left',
|
||||
label: 'Documentation',
|
||||
to: '/docs/intro',
|
||||
},
|
||||
{
|
||||
href: 'https://www.unrealengine.com/marketplace/en-US/product/easyballistics',
|
||||
@@ -98,11 +100,11 @@ const config = {
|
||||
items: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
to: '/getting-started/installation',
|
||||
to: '/docs/getting-started/installation',
|
||||
},
|
||||
{
|
||||
label: 'API Reference',
|
||||
to: '/api/overview',
|
||||
to: '/docs/api/overview',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -29,6 +29,25 @@ const sidebars = {
|
||||
label: 'Core Concepts',
|
||||
items: [
|
||||
'core-concepts/overview',
|
||||
'core-concepts/gatling-mechanics',
|
||||
'core-concepts/trajectory-prediction',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorials',
|
||||
items: [
|
||||
'tutorials/advanced-weapon-systems',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Advanced Guides',
|
||||
items: [
|
||||
'advanced/multiplayer-guide',
|
||||
'advanced/performance-optimization',
|
||||
'advanced/editor-tools',
|
||||
'advanced/migration-guide',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -36,6 +55,7 @@ const sidebars = {
|
||||
label: 'API Reference',
|
||||
items: [
|
||||
'api/overview',
|
||||
'api/bullet-reference',
|
||||
],
|
||||
},
|
||||
'troubleshooting',
|
||||
|
||||
Reference in New Issue
Block a user