Initial Commit

This commit is contained in:
j.foucher 2026-03-13 15:35:51 +01:00
commit 9c4061da5b
47 changed files with 6293 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,92 @@
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Engine/Maps/Templates/OpenWorld
[/Script/Engine.RendererSettings]
r.AllowStaticLighting=False
r.GenerateMeshDistanceFields=True
r.DynamicGlobalIlluminationMethod=1
r.ReflectionMethod=1
r.SkinCache.CompileShaders=True
r.RayTracing=True
r.Shadow.Virtual.Enable=1
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
-D3D12TargetedShaderFormats=PCD3D_SM5
+D3D12TargetedShaderFormats=PCD3D_SM6
-D3D11TargetedShaderFormats=PCD3D_SM5
+D3D11TargetedShaderFormats=PCD3D_SM5
Compiler=Default
AudioSampleRate=48000
AudioCallbackBufferFrameSize=1024
AudioNumBuffersToEnqueue=1
AudioMaxChannels=0
AudioNumSourceWorkers=4
SpatializationPlugin=
SourceDataOverridePlugin=
ReverbPlugin=
OcclusionPlugin=
CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0)
CacheSizeKB=65536
MaxChunkSizeOverrideKB=0
bResampleForDevice=False
MaxSampleRate=48000.000000
HighSampleRate=32000.000000
MedSampleRate=24000.000000
LowSampleRate=12000.000000
MinSampleRate=8000.000000
CompressionQualityModifier=1.000000
AutoStreamingThreshold=0.000000
SoundCueCookQualityIndex=-1
[/Script/LinuxTargetPlatform.LinuxTargetSettings]
-TargetedRHIs=SF_VULKAN_SM5
+TargetedRHIs=SF_VULKAN_SM6
[/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass=Desktop
AppliedTargetedHardwareClass=Desktop
DefaultGraphicsPerformance=Maximum
AppliedDefaultGraphicsPerformance=Maximum
[/Script/WorldPartitionEditor.WorldPartitionEditorSettings]
CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet'
[/Script/Engine.UserInterfaceSettings]
bAuthorizeAutomaticWidgetVariableCreation=False
FontDPIPreset=Standard
FontDPI=72
[/Script/Engine.Engine]
+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/PS_Ballistics")
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/PS_Ballistics")
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
bEnablePlugin=True
bAllowNetworkConnection=True
SecurityToken=488367AA4E45F3B86D51FD85BEDFD355
bIncludeInShipping=False
bAllowExternalStartInShipping=False
bCompileAFSProject=False
bUseCompression=False
bLogFiles=False
bReportStats=False
ConnectionType=USBOnly
bUseManualIPAddress=False
ManualIPAddress=

View File

@ -0,0 +1,3 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=256345A449041A43ADB1968A8E9E7C8D

View File

@ -0,0 +1,84 @@
[/Script/Engine.InputSettings]
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
bAltEnterTogglesFullscreen=True
bF11TogglesFullscreen=True
bUseMouseForTouch=False
bEnableMouseSmoothing=True
bEnableFOVScaling=True
bCaptureMouseOnLaunch=True
bEnableLegacyInputScales=True
bEnableMotionControls=True
bFilterInputByPlatformUser=False
bShouldFlushPressedKeysOnViewportFocusLost=True
bAlwaysShowTouchInterface=False
bShowConsoleOnFourFingerTap=True
bEnableGestureRecognizer=False
bUseAutocorrect=False
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
DefaultViewportMouseLockMode=LockOnCapture
FOVScale=0.011110
DoubleClickTime=0.200000
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
-ConsoleKeys=Tilde
+ConsoleKeys=Tilde

1796
Unreal/PS_Ballistics.sln Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"FileVersion": 3,
"EngineAssociation": "5.5",
"Category": "",
"Description": "",
"Modules": [
{
"Name": "PS_Ballistics",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "ModelingToolsEditorMode",
"Enabled": true,
"TargetAllowList": [
"Editor"
]
}
]
}

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"EasyBallistics": "UnrealEditor-EasyBallistics.dylib"
}
}

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"EasyBallistics": "UnrealEditor-EasyBallistics-Win64-DebugGame.dll"
}
}

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"EasyBallistics": "UnrealEditor-EasyBallistics.dll"
}
}

View File

@ -0,0 +1,29 @@
{
"FileVersion": 3,
"Version": 0,
"VersionName": "2.82",
"FriendlyName": "EasyBallistics",
"Description": "",
"Category": "Gameplay",
"CreatedBy": "Mookie",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/bbecde0f66914263b57fd2af5a0c7ffe",
"SupportURL": "",
"EngineVersion": "5.5.0",
"CanContainContent": false,
"Installed": true,
"Modules": [
{
"Name": "EasyBallistics",
"Type": "Runtime",
"LoadingPhase": "Default",
"PlatformAllowList": [
"Win64",
"Linux",
"Mac",
"Android"
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,38 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class EasyBallistics : ModuleRules
{
public EasyBallistics(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"PhysicsCore"
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}

View File

@ -0,0 +1,32 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBullet.h"
FVector AEBBullet::UpdateVelocity_Implementation(UWorld* World, FVector Location, FVector PreviousVelocity, float DeltaTime) const {
FVector NewVelocity = PreviousVelocity;
//airDensity
float air;
float speedOfSound;
air = GetAirDensity(World, Location);
speedOfSound = GetSpeedOfSound(World, Location);
//gravity
if (!OverrideGravity) {
NewVelocity += FVector(0, 0, World->GetGravityZ())*DeltaTime;
}
else {
NewVelocity += Gravity*DeltaTime;
};
//drag
FVector relVel = (NewVelocity - GetWind(World, Location));
float speed = relVel.Size();
float mach = speed / speedOfSound;
float profile = FMath::Pow(Diameter / 200.0f, 2.0f)*3.141592f;
float drag = GetCurveValue(MachDragCurve, mach, 0.25f)*FMath::Pow(speed / 100.0f, 2.0f)*profile*air*FormFactor*50.0f;
NewVelocity -= relVel.GetSafeNormal() * drag / Mass * DeltaTime / WorldScale;
return NewVelocity;
}

View File

@ -0,0 +1,118 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBarrel.h"
#include "Net/UnrealNetwork.h"
#define REPOWNERONLY false
void UEBBarrel::ShotFiredMulticast_Implementation() {
ShotFired.Broadcast();
}
void UEBBarrel::SpawnBulletEventMulticast_Implementation(FVector Start, FVector Velocity) {
SpawnBulletEvent.Broadcast(Start, Velocity);
}
void UEBBarrel::Shoot(bool Trigger, int nextFireID) {
if (ClientSideAim && GetOwner()->GetRemoteRole() == ROLE_Authority && Trigger) {
Aim = GetComponentTransform().GetUnitAxis(EAxis::X);
Location = GetComponentTransform().GetLocation();
nextFireEventID = nextFireID;
ShootRepCSA(Trigger, UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(), Location), Aim, nextFireID);
}
else {
nextFireEventID = nextFireID;
ShootRep(Trigger, nextFireID);
}
}
void UEBBarrel::ShootRep_Implementation(bool Trigger, int nextFireID) {
if (Trigger) {
if (FireMode == EFireMode::FM_Burst || FireMode == EFireMode::FM_InterBurst) {
BurstRemaining = BurstCount;
};
Shooting = true;
}
else {
//burst cannot be interrupted
if (FireMode != EFireMode::FM_Burst || BurstRemaining<=0) {
Shooting = false;
}
}
}
bool UEBBarrel::ShootRep_Validate(bool Trigger, int nextFireID) {
return true;
}
void UEBBarrel::ShootRepCSA_Implementation(bool Trigger, FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim, int nextFireID) {
Location = UGameplayStatics::RebaseZeroOriginOntoLocal(GetWorld(), NewLocation);
Aim = NewAim;
RemoteAimReceived = true;
if (Trigger) {
if (FireMode == EFireMode::FM_Burst || FireMode == EFireMode::FM_InterBurst) {
BurstRemaining = BurstCount;
};
Shooting = true;
}
else {
//burst cannot be interrupted
if (FireMode != EFireMode::FM_Burst || BurstRemaining <= 0) {
Shooting = false;
}
}
}
bool UEBBarrel::ShootRepCSA_Validate(bool Trigger, FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim, int nextFireID) {
return true;
}
void UEBBarrel::GatlingSpool_Implementation(bool Spool) {
Spooling = Spool;
}
bool UEBBarrel::GatlingSpool_Validate(bool Spool) {
return true;
}
void UEBBarrel::SwitchFireMode_Implementation(EFireMode NewFireMode) {
FireMode = NewFireMode;
}
bool UEBBarrel::SwitchFireMode_Validate(EFireMode NewFireMode) {
return true;
}
void UEBBarrel::ClientAim_Implementation(FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim) {
Location = UGameplayStatics::RebaseZeroOriginOntoLocal(GetWorld(),NewLocation);
Aim = NewAim;
RemoteAimReceived = true;
}
bool UEBBarrel::ClientAim_Validate(FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim) {
return true;
}
void UEBBarrel::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
#if REPOWNERONLY
DOREPLIFETIME_CONDITION(UEBBarrel, FireMode, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, CycleAmmoCount, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, CycleAmmoPos, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, Ammo, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, ChamberedBullet, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, Shooting, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, ShootingBlocked, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(UEBBarrel, Spooling, COND_OwnerOnly);
#else
DOREPLIFETIME(UEBBarrel, FireMode);
DOREPLIFETIME(UEBBarrel, CycleAmmoCount);
DOREPLIFETIME(UEBBarrel, CycleAmmoPos);
DOREPLIFETIME(UEBBarrel, Ammo);
DOREPLIFETIME(UEBBarrel, ChamberedBullet);
DOREPLIFETIME(UEBBarrel, Shooting);
DOREPLIFETIME(UEBBarrel, ShootingBlocked);
DOREPLIFETIME(UEBBarrel, Spooling);
#endif
}

View File

@ -0,0 +1,66 @@
// Copyright 2019 Mookie. All Rights Reserved.
#if WITH_EDITOR
#include "EBBarrel.h"
#include "PrimitiveSceneProxy.h"
FPrimitiveSceneProxy* UEBBarrel::CreateSceneProxy() {
{
class FBarrelProxy : public FPrimitiveSceneProxy
{
public:
FBarrelProxy(UEBBarrel* InComponent) : FPrimitiveSceneProxy(InComponent)
{
bWillEverBeLit = false;
Component = InComponent;
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_BarrelSceneProxy_GetDynamicMeshElements);
const FMatrix& Transform = GetLocalToWorld();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap && ((1 << ViewIndex)!=0))
{
const FSceneView* View = Views[ViewIndex];
const FLinearColor DrawColor = GetViewSelectionColor(FColor::Green, *View, IsSelected(), IsHovered(), true, IsIndividuallySelected());
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
DrawDirectionalArrow(PDI, Transform, DrawColor, Component->DebugArrowSize, Component->DebugArrowSize*0.1f, 16, Component->DebugArrowSize*0.01f);
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
const bool bProxyVisible = IsSelected();
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = (IsShown(View));
Result.bDynamicRelevance = true;
Result.bShadowRelevance = false;
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
return Result;
}
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
virtual SIZE_T GetTypeHash() const override { return 0; }
private:
UEBBarrel* Component;
};
return new FBarrelProxy(this);
}
};
FBoxSphereBounds UEBBarrel::CalcBounds(const FTransform& LocalToWorld) const
{
float SphereRadius = DebugArrowSize;
return FBoxSphereBounds(FVector::ZeroVector, FVector(SphereRadius), SphereRadius).TransformBy(LocalToWorld);
}
#endif

View File

@ -0,0 +1,136 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBullet.h"
#include "EBMaterialResponseMap.h"
#include "Net/UnrealNetwork.h"
void AEBBullet::VelocityChangeBroadcast_Implementation(FVector_NetQuantize NewLocation, FVector NewVelocity) {
if (!HasAuthority()) {
FVector RebasedLocation = UGameplayStatics::RebaseZeroOriginOntoLocal(GetWorld(), NewLocation);
OnTrajectoryUpdateReceived(RebasedLocation, Velocity, NewVelocity);
SetActorLocation(RebasedLocation);
Velocity = NewVelocity;
CanRetrace = false;
}
}
void AEBBullet::VelocityChangeBroadcastReliable_Implementation(FVector_NetQuantize NewLocation, FVector NewVelocity) {
if (!HasAuthority()) {
FVector RebasedLocation = UGameplayStatics::RebaseZeroOriginOntoLocal(GetWorld(), NewLocation);
OnTrajectoryUpdateReceived(RebasedLocation, Velocity, NewVelocity);
SetActorLocation(RebasedLocation);
Velocity = NewVelocity;
CanRetrace = false;
}
}
void AEBBullet::SpawnWithExactVelocity(TSubclassOf<class AEBBullet> BulletClass, AActor* BulletOwner, APawn* BulletInstigator, FVector BulletLocation, FVector BulletVelocity, int nextEventFireID) {
if (BulletClass != nullptr && BulletOwner != nullptr) {
FActorSpawnParameters spawnParams;
spawnParams.Owner = BulletOwner;
spawnParams.Instigator = BulletInstigator;
AEBBullet* Default = Cast<AEBBullet>(BulletClass->GetDefaultObject());
Default->fireEventID = nextEventFireID;
FTransform Transform;
Transform.SetLocation(BulletLocation);
Transform.SetScale3D(Default->GetActorScale());
if (Default->RotateActor) {
FRotator Rotation = UKismetMathLibrary::MakeRotFromX(BulletVelocity);
if (Default->RotateRandomRoll) Rotation.Add(0, 0, Default->RandomStream.FRandRange(-180.0f, 180.0f));
Transform.SetRotation(Rotation.Quaternion());
}
else {
Transform.SetRotation(FQuat(1, 0, 0, 1));
}
if (!Default->Shotgun) {
AEBBullet* bullet = SpawnOrReactivate(BulletOwner->GetWorld(), BulletClass, Transform, BulletVelocity, BulletOwner, BulletInstigator, nextEventFireID);
}
else {
for (int i = 0; i < Default->ShotCount; i++) {
float Vel = BulletVelocity.Size()*Default->RandomStream.FRandRange(1.0 - Default->ShotVelocitySpread, 1.0 + Default->ShotVelocitySpread);
FVector SubmunitionVelocity = Default->RandomStream.VRandCone(BulletVelocity, Default->ShotSpread)*Vel;
AEBBullet* bullet = SpawnOrReactivate(BulletOwner->GetWorld(), BulletClass, Transform, SubmunitionVelocity, BulletOwner, BulletInstigator, nextEventFireID);
}
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("Cannot spawn bullet - invalid class or owner"));
}
}
void AEBBullet::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(AEBBullet, Velocity, COND_InitialOnly);
DOREPLIFETIME_CONDITION(AEBBullet, RandomStream, COND_InitialOnly);
}
//alternative spawn
void AEBBullet::Spawn(TSubclassOf<class AEBBullet> BulletClass, AActor* BulletOwner, APawn* BulletInstigator, FVector BulletLocation, FVector BulletVelocity, int eventFireID) {
if (BulletClass != nullptr && BulletOwner != nullptr) {
FActorSpawnParameters spawnParams;
spawnParams.Owner = BulletOwner;
spawnParams.Instigator = BulletInstigator;
AEBBullet* Default = Cast<AEBBullet>(BulletClass->GetDefaultObject());
Default->fireEventID = eventFireID;
FTransform Transform;
Transform.SetLocation(BulletLocation);
Transform.SetScale3D(Default->GetActorScale());
if (Default->RotateActor) {
if (Default->RotateActor) {
FRotator Rotation = UKismetMathLibrary::MakeRotFromX(BulletVelocity);
if (Default->RotateRandomRoll) Rotation.Add(0, 0, Default->RandomStream.FRandRange(-180.0f, 180.0f));
Transform.SetRotation(Rotation.Quaternion());
}
}
else {
Transform.SetRotation(FQuat(1, 0, 0, 1));
}
//init velocity
float TotalSpread = Default->Spread;
if (Default->SpreadBias > 0.0f){
float SpreadMult = FMath::Pow(FMath::FRand(), Default->SpreadBias);
TotalSpread *= SpreadMult;
}
FVector BulletVelocityNew = Default->RandomStream.VRandCone(BulletVelocity, TotalSpread)*BulletVelocity.Size();
float VelocityMP = FMath::Lerp(Default->MuzzleVelocityMin, Default->MuzzleVelocityMax, Default->RandomStream.FRand());
BulletVelocityNew = BulletVelocityNew * VelocityMP;
if (!Default->Shotgun) {
AEBBullet* bullet = SpawnOrReactivate(BulletOwner->GetWorld(), BulletClass, Transform, BulletVelocityNew, BulletOwner, BulletInstigator, eventFireID);
}
else {
for (int i = 0; i < Default->ShotCount; i++) {
float Vel = BulletVelocityNew.Size()*Default->RandomStream.FRandRange(1.0 - Default->ShotVelocitySpread, 1.0 + Default->ShotVelocitySpread);
FVector SubmunitionVelocity = Default->RandomStream.VRandCone(BulletVelocityNew, Default->ShotSpread)*Vel;
AEBBullet* bullet = SpawnOrReactivate(BulletOwner->GetWorld(), BulletClass, Transform, SubmunitionVelocity, BulletOwner, BulletInstigator, eventFireID);
}
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("Cannot spawn bullet - invalid class or owner"));
}
}
void AEBBullet::OnImpact_Implementation(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, int FireEventID){
return;
};
void AEBBullet::OnNetPredictedImpact_Implementation(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, int FireEventID) {
return;
};
void AEBBullet::OnDeactivated_Implementation() {
return;
};

View File

@ -0,0 +1,4 @@
// Copyright 2020 Mookie. All Rights Reserved.
#include "EBBullet.h"

View File

@ -0,0 +1,65 @@
#include "EBBarrel.h"
#include "EBBullet.h"
void UEBBarrel::CalculateAimDirection(TSubclassOf<class AEBBullet> BulletClass, FVector TargetLocation, FVector TargetVelocity, FVector& AimDirection, FVector& PredictedTargetLocation, FVector& PredictedIntersectionLocation, float& PredictedFlightTime, float& Error, float MaxTime, float Step, int NumIterations) const {
FVector StartLocation = GetComponentLocation();
CalculateAimDirectionFromLocation(BulletClass, StartLocation, TargetLocation, TargetVelocity, AimDirection, PredictedTargetLocation, PredictedIntersectionLocation, PredictedFlightTime, Error, MaxTime, Step, NumIterations);
}
void UEBBarrel::CalculateAimDirectionFromLocation(TSubclassOf<class AEBBullet> BulletClass, FVector StartLocation, FVector TargetLocation, FVector TargetVelocity, FVector& AimDirection, FVector& PredictedTargetLocation, FVector& PredictedIntersectionLocation, float& PredictedFlightTime, float& Error, float MaxTime, float Step, int NumIterations) const {
if (!BulletClass->IsValidLowLevel()) {
UE_LOG(LogTemp, Warning, TEXT("CalculateAimDirection - invalid bullet class"));
return;
}
AEBBullet* bullet = Cast<AEBBullet>(BulletClass->GetDefaultObject());
FVector AddVelocity = AdditionalVelocity;
UPrimitiveComponent* parent = Cast<UPrimitiveComponent>(GetAttachParent());
if (parent != nullptr) {
if (parent->IsSimulatingPhysics()) {
AddVelocity += parent->GetPhysicsLinearVelocityAtPoint(StartLocation) * InheritVelocity;
}
}
FVector InitialAimDirection = (TargetLocation - StartLocation).GetSafeNormal(); //initial prediction
AimDirection = InitialAimDirection;
FVector PreviousAimDirection = AimDirection;
for (int Iteration = 0; Iteration < NumIterations; Iteration++) {
FVector CurrentBulletLocation = StartLocation;
FVector Velocity = (AimDirection * (FMath::Lerp(MuzzleVelocityMultiplierMin, MuzzleVelocityMultiplierMax, 0.5) * FMath::Lerp(bullet->MuzzleVelocityMin, bullet->MuzzleVelocityMax, 0.5))) + AddVelocity;
bool hit = 0;
for (float time = 0; time <= MaxTime; time += Step) {
FVector PreviousVelocity = Velocity;
Velocity = bullet->UpdateVelocity(GetWorld(), CurrentBulletLocation, Velocity, Step);
FVector TraceVector = ((((PreviousVelocity + Velocity) * 0.5) - TargetVelocity) * Step);
FVector TraceEndLocation = CurrentBulletLocation + TraceVector;
FVector IntersectionPoint;
hit = FMath::SegmentPlaneIntersection(CurrentBulletLocation - TraceVector, TraceEndLocation, FPlane(TargetLocation, InitialAimDirection), IntersectionPoint); //actual hit test
if (hit) {
PredictedIntersectionLocation = IntersectionPoint;
FQuat AimCorrection = FQuat::FindBetween((IntersectionPoint - StartLocation), (TargetLocation - StartLocation));
AimDirection = AimCorrection.RotateVector(AimDirection).GetSafeNormal();
Error = (IntersectionPoint - TargetLocation).Size();
float AdditionalFlightTime = (FVector(CurrentBulletLocation - IntersectionPoint).Size() / TraceVector.Size()) * Step;
PredictedFlightTime = time + AdditionalFlightTime;
PredictedTargetLocation = TargetLocation + TargetVelocity * AdditionalFlightTime;
break;
}
//no hit, keep going
CurrentBulletLocation = TraceEndLocation;
}
if (!hit) {
Error = 99999999999999999.0f;
return; //no solution
}
}
}

View File

@ -0,0 +1,25 @@
#include "EBBullet.h"
bool AEBBullet::CollisionFilter_Implementation(FHitResult HitResult) const{
return true;
};
FHitResult AEBBullet::FilterHits(TArray<FHitResult> Results, bool &hit) const{
TArray<FHitResult> OutResults;
for (FHitResult Result : Results) {
if (Result.bBlockingHit) {
hit = true;
return Result;
}else{
if (CollisionFilter(Result)) {
hit = true;
return Result;
}
}
}
hit = false;
return FHitResult(); //blank
};

View File

@ -0,0 +1,303 @@
// Copyright 2018 Mookie. All Rights Reserved.
#include "EBBarrel.h"
UEBBarrel::UEBBarrel() {
PrimaryComponentTick.bCanEverTick = true;
bHiddenInGame = true;
bAutoActivate = true;
SetIsReplicatedByDefault(ReplicateVariables);
RandomStream.GenerateNewSeed();
GatlingRPS = FireRateMin;
}
void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (AntiRecoil)
{
if (previousAim.Num() > AntiRecoilPrevCount)
{
previousAim.RemoveAt(0);
}
if (previousLocation.Num() > AntiRecoilPrevCount)
{
previousLocation.RemoveAt(0);
}
}
if (ClientSideAim){
if (GetOwner()->GetRemoteRole()==ROLE_Authority){
TimeSinceAimUpdate += DeltaTime;
if (TimeSinceAimUpdate >= 1.0f / ClientAimUpdateFrequency) {
if (AntiRecoil)
{
// ForAntiRecoil
previousAim.Add(GetComponentTransform().GetUnitAxis(EAxis::X));
previousLocation.Add(GetComponentTransform().GetLocation());
Aim = previousAim[0];
Location = previousLocation[0];
}
else
{
Aim = GetComponentTransform().GetUnitAxis(EAxis::X);
Location = GetComponentTransform().GetLocation();
}
ClientAim(UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(),Location), Aim);
TimeSinceAimUpdate = FMath::Fmod(TimeSinceAimUpdate, 1.0f / ClientAimUpdateFrequency);
};
}else{
if (!RemoteAimReceived) {
if (AntiRecoil)
{
// ForAntiRecoil
previousAim.Add(GetComponentTransform().GetUnitAxis(EAxis::X));
previousLocation.Add(GetComponentTransform().GetLocation());
Aim = previousAim[0];
Location = previousLocation[0];
}
else
{
Aim = GetComponentTransform().GetUnitAxis(EAxis::X);
Location = GetComponentTransform().GetLocation();
}
}
else {
FVector LocOffset = (Location - GetComponentLocation());
if (LocOffset.Size() > ClientAimDistanceLimit) {
//lag or cheater???
Location = GetComponentLocation() + LocOffset.GetSafeNormal()*ClientAimDistanceLimit;
}
}
}
}
else {
if (AntiRecoil)
{
// ForAntiRecoil
previousAim.Add(GetComponentTransform().GetUnitAxis(EAxis::X));
previousLocation.Add(GetComponentTransform().GetLocation());
Aim = previousAim[0];
Location = previousLocation[0];
}
else
{
Aim = GetComponentTransform().GetUnitAxis(EAxis::X);
Location = GetComponentTransform().GetLocation();
}
}
//Only server can tick
if (GetOwner()->GetLocalRole() == ROLE_Authority){
float RemainingDelta;
if (FireMode == EFireMode::FM_Gatling) {
if (Spooling || (GatlingAutoSpool && Shooting)) {
GatlingRPS = FMath::Lerp(GatlingRPS, FireRateMax, FMath::Min(GatlingSpoolUpTime*DeltaTime, 1.0f));
}
else {
GatlingRPS = FMath::Lerp(GatlingRPS, FireRateMin, FMath::Min(GatlingSpoolUpTime*DeltaTime, 1.0f));
}
GatlingPhase += GatlingRPS*DeltaTime;
for (int i = 1; i <= GatlingPhase; i++) {
if (Cooldown <= 0.0f && LoadNext) {
NextBullet();
}
if (Shooting && ChamberedBullet != nullptr && (!ShootingBlocked)) {
SpawnBullet(GetOwner(), Location, Aim, nextFireEventID);
}
}
GatlingPhase = FMath::Fmod(GatlingPhase, 1.0f);
}
else {
RemainingDelta = DeltaTime;
do {
float step = FMath::Min(Cooldown, RemainingDelta);
Cooldown -= step;
RemainingDelta -= step;
if (Cooldown <= 0.0f && LoadNext) {
NextBullet();
}
//shoot when ready
if (Shooting && ChamberedBullet != nullptr && (!ShootingBlocked)) {
if (BurstRemaining > 0 || (FireMode != EFireMode::FM_Burst && FireMode != EFireMode::FM_InterBurst)) {
SpawnBullet(GetOwner(), Location, Aim, nextFireEventID);
}
else {
Shooting = false;
}
}
} while (RemainingDelta > 0 && Cooldown > 0);
}
}
}
void UEBBarrel::NextBullet() {
if (ChamberedBullet == nullptr) {
if (Ammo.Num() > 0 && (CycleAmmoCount > 0 || CycleAmmoUnlimited || (!CycleAmmo))) {
//cycle ammo
if (CycleAmmo) {
if (CycleAmmoPos >= Ammo.Num()) { CycleAmmoPos = 0; }
ChamberedBullet = Ammo[CycleAmmoPos];
CycleAmmoPos++;
if (!CycleAmmoUnlimited) {
CycleAmmoCount--;
}
}
else {
ChamberedBullet = Ammo[0];
Ammo.RemoveAt(0, 1, EAllowShrinking::Yes);
}
ReadyToShoot.Broadcast();
}
else {
AmmoDepleted.Broadcast();
}
}
}
void UEBBarrel::SpawnBullet(AActor* Owner, FVector InLocation, FVector InAim, int fireEventID) {
TSubclassOf<class AEBBullet> BulletClass = ChamberedBullet;
if (BulletClass != nullptr) {
FVector OutLocation;
FVector OutAim;
InitialBulletTransform(InLocation, InAim, OutLocation, OutAim);
AEBBullet* Default = Cast<AEBBullet>(BulletClass->GetDefaultObject());
float BulletSpread = Default->Spread;
if (Default->SpreadBias > 0.0f) {
float SpreadMult = FMath::Pow(FMath::FRand(), Default->SpreadBias);
BulletSpread *= SpreadMult;
}
float BarrelSpread = Spread;
if (SpreadBias > 0.0f) {
float SpreadMult = FMath::Pow(FMath::FRand(), SpreadBias);
BarrelSpread *= SpreadMult;
}
float TotalSpread = BulletSpread+BarrelSpread;
OutAim = RandomStream.VRandCone(OutAim,TotalSpread);
float BulletVelocity = FMath::Lerp(MuzzleVelocityMultiplierMin* Default->MuzzleVelocityMin, MuzzleVelocityMultiplierMax*Default->MuzzleVelocityMax, RandomStream.FRand());
FVector Velocity = OutAim*BulletVelocity;
//get parent physics body
UPrimitiveComponent* parent = Cast<UPrimitiveComponent>(GetAttachParent());
Velocity += AdditionalVelocity;
if (parent != nullptr) {
if (parent->IsSimulatingPhysics()) {
Velocity += parent->GetPhysicsLinearVelocityAtPoint(OutLocation)*InheritVelocity;
}
if (Default->Shotgun) {
ApplyRecoil(parent, OutLocation, -Velocity*Default->Mass*RecoilMultiplier*Default->ShotCount);
}
else{
ApplyRecoil(parent, OutLocation, -Velocity*Default->Mass*RecoilMultiplier);
}
}
BeforeShotFired.Broadcast();
#ifdef WITH_EDITOR
if (shotTrace) {
DrawDebugLine(GetWorld(), OutLocation, Velocity, FColor(1, 0, 0, 1), false, 3, 0, 0);
};
#endif
if (ReplicateShotFiredEvents) {
SpawnBulletEventMulticast(OutLocation, Velocity);
}
else {
SpawnBulletEvent.Broadcast(OutLocation, Velocity);
}
AEBBullet::SpawnWithExactVelocity(BulletClass, Owner, Owner->GetInstigator(), OutLocation, Velocity, fireEventID);
//spend ammo
ChamberedBullet = nullptr;
if (FireMode != EFireMode::FM_Gatling) {
Cooldown = 1.0f / FMath::Lerp(FireRateMin, FireRateMax, RandomStream.FRand());
}
//fire modes
switch (FireMode) {
case EFireMode::FM_Auto:
LoadNext = true;
break;
case EFireMode::FM_Burst:
LoadNext = true;
break;
case EFireMode::FM_InterBurst:
LoadNext = true;
break;
case EFireMode::FM_Semiauto:
Shooting = false;
LoadNext = true;
break;
case EFireMode::FM_Manual:
Shooting = false;
LoadNext = false;
break;
case EFireMode::FM_Slamfire:
LoadNext = false;
break;
case EFireMode::FM_Gatling:
LoadNext = true;
break;
};
if (BurstRemaining > 0) {
BurstRemaining--;
}
else {
if (FireMode == EFireMode::FM_Burst || FireMode == EFireMode::FM_InterBurst) {
Cooldown = FMath::Max(Cooldown, BurstCooldown);
}
}
if (ReplicateShotFiredEvents) {
ShotFiredMulticast();
}
else {
ShotFired.Broadcast();
}
}
}
void UEBBarrel::InitialBulletTransform_Implementation(FVector InLocation, FVector InDirection, FVector& OutLocation, FVector& OutDirection) {
OutLocation = InLocation;
OutDirection = InDirection;
}
void UEBBarrel::ApplyRecoil_Implementation(UPrimitiveComponent* Component, FVector InLocation, FVector Impulse){
if (Component->IsSimulatingPhysics()) {
Component->AddImpulseAtLocation(Impulse, InLocation);
}
}

View File

@ -0,0 +1,141 @@
// Copyright 2018 Mookie. All Rights Reserved.
#include "EBBullet.h"
// Sets default values
AEBBullet::AEBBullet() {
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
SetTickGroup(ETickingGroup::TG_PrePhysics);
}
// Called when the game starts or when spawned
void AEBBullet::BeginPlay() {
SetActorEnableCollision(AllowComponentCollisions);
if(!IsRecycled){
Super::BeginPlay();
IsRecycled = true;
}
else{
ReceiveBeginPlay();
}
if (SafeLaunch) {
OwnerSafe = true;
}
if (DoFirstStepImmediately) {
float DeltaTime = GetWorld()->GetDeltaSeconds();
if (RandomFirstStepDelta) {
DeltaTime *= RandomStream.FRand();
};
if (FixedStep) {
Step(FixedStepSeconds);
}
else {
Step(DeltaTime);
}
}
}
// Called every frame
void AEBBullet::Tick(float DeltaTime) {
Super::Tick(DeltaTime);
if (FixedStep) {
AccumulatedDelta += DeltaTime;
while (AccumulatedDelta >= FixedStepSeconds) {
Step(FixedStepSeconds);
AccumulatedDelta -= FixedStepSeconds;
}
}
else {
Step(DeltaTime);
}
}
void AEBBullet::Step(float DeltaTime) {
FVector start = GetActorLocation();
bool sendUpdate = false;
if (Retrace && CanRetrace) {
//time travel
float remainingTime = LastTraceDelta;
int remainingSteps = MaxTracesPerStep;
FVector PreviousVelocity = LastTracePrevVelocity;
SetActorLocation(LastTraceStart);
Velocity = LastTraceVelocity;
do {
if (RetraceOnAnotherChannel) {
remainingTime = Trace(GetActorLocation(),
PreviousVelocity,
remainingTime,
RetraceChannel);
}
else {
remainingTime = Trace(GetActorLocation(),
PreviousVelocity,
remainingTime,
TraceChannel);
}
PreviousVelocity = Velocity;
remainingSteps -= 1;
if (remainingTime > 0.0f) { sendUpdate = true; };
} while (remainingTime > 0.0f && remainingSteps > 0);
}
CanRetrace = false;
FVector PreviousVelocity = Velocity;
Velocity = UpdateVelocity(GetWorld(), GetActorLocation(), Velocity, DeltaTime);
//trace
float remainingTime = DeltaTime;
int remainingSteps = MaxTracesPerStep;
do {
remainingTime = Trace(GetActorLocation(),
PreviousVelocity,
remainingTime,
TraceChannel
);
PreviousVelocity = Velocity;
remainingSteps -= 1;
if (remainingTime > 0.0f) { sendUpdate = true; };
} while (remainingTime > 0.0f && remainingSteps > 0);
if (sendUpdate) {
if (ReliableReplication) {
VelocityChangeBroadcastReliable(UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(),GetActorLocation()), Velocity);
}
else {
VelocityChangeBroadcast(UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(), GetActorLocation()), Velocity);
}
}
if(SafeDelay <= 0.0f){
OwnerSafe = false;
}
else {
SafeDelay -= DeltaTime;
}
if (RotateActor) {
FRotator NewRot = UKismetMathLibrary::MakeRotFromX(Velocity);
NewRot.Roll = GetActorRotation().Roll;
SetActorRotation(NewRot);
}
}
float AEBBullet::GetCurveValue(const UCurveFloat* curve, float in, float deflt) const {
if (curve == nullptr) return deflt;
return curve->GetFloatValue(in);
}
void AEBBullet::ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) {
Super::ApplyWorldOffset(InOffset, bWorldShift);
LastTraceStart += InOffset;
}

View File

@ -0,0 +1,21 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// Copyright 2016 Mookie. All Rights Reserved.
#include "EasyBallistics.h"
#define LOCTEXT_NAMESPACE "FEasyBallisticsModule"
void FEasyBallisticsModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FEasyBallisticsModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FEasyBallisticsModule, EasyBallistics)

View File

@ -0,0 +1,58 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBullet.h"
FVector AEBBullet::GetWind_Implementation(UWorld* World, FVector Location) const{
return Wind;
}
float AEBBullet::GetAirDensity_Implementation(UWorld* World, FVector Location) const{
switch (AtmosphereType) {
case (EEBAtmosphereType::AT_Curve): {
float airmp = SeaLevelAirDensity / GetCurveValue(AirDensityCurve, 0, SeaLevelAirDensity);
return GetCurveValue(AirDensityCurve, GetAltitude(World, Location) / WorldScale, SeaLevelAirDensity)* airmp;
}
case (EEBAtmosphereType::AT_Earth): {
return GetAltitudeDensity(GetAltitude(World, Location) / WorldScale / 100.0f);
}
default:{
return SeaLevelAirDensity;
}
}
}
float AEBBullet::GetSpeedOfSound_Implementation(UWorld* World, FVector Location) const{
if (!SpeedOfSoundVariesWithAltitude) {
return SeaLevelSpeedOfSound * WorldScale;
}
float Altitude = GetAltitude(World, Location);
float soundvmp = SeaLevelSpeedOfSound / GetCurveValue(SpeedOfSoundCurve, 0, SeaLevelSpeedOfSound);
return GetCurveValue(SpeedOfSoundCurve, Altitude, SeaLevelSpeedOfSound)*WorldScale*soundvmp;
}
float AEBBullet::GetAltitudePressure(float AltitudeMeter) const {
return FMath::Max(SeaLevelAirPressure * FMath::Pow((1 - (0.0000225577 * AltitudeMeter)), 5.25588), 0.0f);
}
float AEBBullet::GetAltitudeTemperature(float AltitudeMeter) const {
return SeaLevelAirTemperature - (TemperatureLapseRate * FMath::Min(AltitudeMeter, TropopauseAltitude));
}
float AEBBullet::GetAltitudeDensity(float AltitudeMeter) const {
float Temperature = GetAltitudeTemperature(AltitudeMeter);
float Pressure = GetAltitudePressure(AltitudeMeter);
return Pressure * 100.0f / ((Temperature + 273.15) * SpecificGasConstant);
}
float AEBBullet::GetAltitude(UWorld* World, FVector Location) const{
FVector DistanceFromOrigin = (Location - WorldCenterLocation + FVector(World->OriginLocation));
if (SphericalAltitude)
{
return (DistanceFromOrigin.Size() - SeaLevelRadius);
}
else {
return DistanceFromOrigin.Z;
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2020 Mookie. All Rights Reserved.
#include "EBBullet.h"
float AEBBullet::PenetrationTrace(FVector StartLocation, FVector EndLocation, TWeakObjectPtr<UPrimitiveComponent, FWeakObjectPtr> Component, EPenTraceType PenTraceType, TEnumAsByte<ECollisionChannel> CollisionChannel, FVector &ExitLocation, FVector &ExitNormal) {
FCollisionQueryParams QueryParams;
QueryParams.bTraceComplex = TraceComplex;
QueryParams.bFindInitialOverlaps = true;
FHitResult Result;
switch (PenTraceType) {
case(EPenTraceType::PT_BackTrace): {
bool Hit = GetWorld()->LineTraceSingleByChannel(Result, EndLocation, StartLocation, CollisionChannel, QueryParams);
if (!Hit) return 1.0f;
ExitNormal = Result.Normal;
ExitLocation = Result.Location;
return (1.0f - Result.Time);
}
case(EPenTraceType::PT_ByComponent): {
bool Hit = Component->LineTraceComponent(Result, EndLocation, StartLocation, QueryParams);
if (!Hit) return 1.0f;
ExitNormal = Result.Normal;
ExitLocation = Result.Location;
return (1.0f - Result.Time);
}
case(EPenTraceType::PT_TwoSidedGeometry): {
bool Hit = GetWorld()->LineTraceSingleByChannel(Result, StartLocation, EndLocation, CollisionChannel, QueryParams);
if (!Hit) return 1.0f;
ExitLocation = Result.Location;
ExitNormal = -Result.Normal;
return Result.Time;
}
default:
return 1.0f;
}
}

View File

@ -0,0 +1,169 @@
#include "EBBullet.h"
void AEBBullet::Deactivate() {
//server only
if (!HasAuthority()) { return; }
OnDeactivated();
this->DeactivateToPool();
DeactivationBroadcast();
}
AEBBullet* AEBBullet::GetFromPool(UWorld* World, UClass* BulletClass) {
AEBBullet* Pool = Cast<AEBBullet>(BulletClass->GetDefaultObject());
if (Pool) {
//find first of correct class;
bool CleanupRequired=false;
int32 FoundIndex = Pool->Pooled.IndexOfByPredicate(
[&](auto InItem) {
if (InItem.IsValid() && InItem->GetWorld() == World) {
return true;
}
else {
CleanupRequired = true;
return false;
}
});
TWeakObjectPtr<AEBBullet> Found = nullptr;
if (FoundIndex != INDEX_NONE) {
Found = Pool->Pooled[FoundIndex];
Pool->Pooled.RemoveAtSwap(FoundIndex,EAllowShrinking::Yes);
}
if (CleanupRequired) {
#ifdef WITH_EDITOR
if (Pool->DebugPooling) {
GEngine->AddOnScreenDebugMessage(2, 2, FColor::White, TEXT("Invalid reference in pool, cleaning up"));
}
#endif
Pool->Pooled.RemoveAll([&](auto InItem) {
if (InItem.IsValid() && InItem->GetWorld() == World) {
return false;
}
else {
return true;
}
});
}
return(Found.Get());
}
else {
return nullptr;
}
}
AEBBullet* AEBBullet::SpawnOrReactivate(UWorld* World, TSubclassOf<class AEBBullet> BulletClass, const FTransform& Transform, FVector BulletVelocity, AActor* BulletOwner, APawn* BulletInstigator, int nextEventFireID) {
AEBBullet* bullet;
AEBBullet* Recycled = GetFromPool(World, BulletClass);
if (Recycled) {
AEBBullet* Default = Cast<AEBBullet>(BulletClass->GetDefaultObject());
Recycled->Reset();
Recycled->fireEventID = nextEventFireID;
Recycled->SetOwner(BulletOwner);
Recycled->SetInstigator(BulletInstigator);
Recycled->SetActorTransform(Transform,false,nullptr,ETeleportType::TeleportPhysics);
Recycled->Velocity = BulletVelocity;
Recycled->SetActorHiddenInGame(Default->IsHidden());
Recycled->SetActorTickEnabled(true);
Recycled->CanRetrace = false;
Recycled->IgnoredActors = Default->IgnoredActors;
Recycled->SafeDelay = Default->SafeDelay;
Recycled->SetLifeSpan(Default->InitialLifeSpan);
Recycled->FinishSpawning(Transform);
//if (!Recycled->HasActorBegunPlay()){ Recycled->BeginPlay(); }
//Recycled->ReactivationBroadcast(UGameplayStatics::RebaseLocalOriginOntoZero(Recycled->GetWorld(), Transform.GetLocation()), BulletVelocity, BulletOwner, BulletInstigator);
#ifdef WITH_EDITOR
if (Recycled->DebugPooling) {
GEngine->AddOnScreenDebugMessage(0, 2, FColor::Green, TEXT("Recycling pooled bullet"));
}
#endif
return Recycled;
}
else {
bullet = Cast<AEBBullet>(World->SpawnActorDeferred<AEBBullet>(BulletClass, Transform, BulletOwner, BulletInstigator));
bullet->RandomStream.GenerateNewSeed();
bullet->Velocity = BulletVelocity;
bullet->fireEventID = nextEventFireID;
bullet->FinishSpawning(Transform);
//UGameplayStatics::FinishSpawningActor(bullet, Transform);
#ifdef WITH_EDITOR
if (bullet->DebugPooling) {
GEngine->AddOnScreenDebugMessage(0, 2, FColor::Orange, TEXT("Spawning new bullet"));
}
#endif
return bullet;
}
}
void AEBBullet::FinishSpawning(FTransform Transform) {
if(IsRecycled){
if (!HasActorBegunPlay()){
BeginPlay();
}
ReactivationBroadcast(UGameplayStatics::RebaseLocalOriginOntoZero(this->GetWorld(), Transform.GetLocation()), this->Velocity, this->GetOwner(), this->GetInstigator(), fireEventID);
}else{
UGameplayStatics::FinishSpawningActor(this, Transform);
}
}
void AEBBullet::ReactivationBroadcast_Implementation(FVector_NetQuantize NewLocation, FVector NewVelocity, AActor* BulletOwner, APawn* BulletInstigator, int nextEventFireID) {
if (!HasAuthority()) {
AEBBullet* Default = Cast<AEBBullet>(this->StaticClass()->GetDefaultObject());
SetOwner(BulletOwner);
SetInstigator(BulletInstigator);
fireEventID = nextEventFireID;
SetActorLocation(UGameplayStatics::RebaseZeroOriginOntoLocal(GetWorld(), NewLocation));
Velocity = NewVelocity;
CanRetrace = false;
SetActorHiddenInGame(Default->IsHidden());
SetActorTickEnabled(true);
SafeDelay = Default->SafeDelay;
OwnerSafe = Default->SafeLaunch;
BeginPlay();
}
}
void AEBBullet::DeactivationBroadcast_Implementation() {
if (!HasAuthority()) {
OnDeactivated();
this->DeactivateToPool();
}
}
void AEBBullet::LifeSpanExpired() {
Deactivate();
}
void AEBBullet::DeactivateToPool() {
AEBBullet* Pool = Cast<AEBBullet>(GetClass()->GetDefaultObject());
if (Pool && EnablePooling) {
SetActorHiddenInGame(true);
SetActorTickEnabled(false);
Pool->Pooled.Add(this);
EndPlay(EEndPlayReason::RemovedFromWorld);
if (Pool->Pooled.Num() > MaxPoolSize) {
AEBBullet* Oldest = (Pool->Pooled[0].Get());
Pool->Pooled.RemoveAtSwap(0,EAllowShrinking::Yes);
if (Oldest) { Oldest->Destroy(); }
}
#ifdef WITH_EDITOR
if (DebugPooling) {
GEngine->AddOnScreenDebugMessage(2, 2, FColor::White, FString("Bullet pooled: ") + FString::FromInt(Pool->Pooled.Num()));
}
#endif
}
else {
Destroy();
}
}

View File

@ -0,0 +1,74 @@
// Copyright 2020 Mookie. All Rights Reserved.
#include "EBBarrel.h"
#include "EBBullet.h"
void UEBBarrel::PredictHit(bool& Hit, FHitResult& HitResult, FVector& HitLocation, float& HitTime, AActor*& HitActor, TArray<FVector>& Trajectory, TSubclassOf<class AEBBullet> BulletClass, TArray<AActor*> IgnoredActors, float MaxTime, float Step) const {
FVector StartLocation = GetComponentLocation();
FVector AimDirection = GetComponentQuat().GetForwardVector();
PredictHitFromLocation(Hit, HitResult, HitLocation, HitTime, HitActor, Trajectory, BulletClass, StartLocation, AimDirection, IgnoredActors, MaxTime, Step);
}
void UEBBarrel::PredictHitFromLocation(bool &Hit, FHitResult& HitResult, FVector& HitLocation, float& HitTime, AActor*& HitActor, TArray<FVector>& Trajectory, TSubclassOf<class AEBBullet> BulletClass, FVector StartLocation, FVector AimDirection, TArray<AActor*> IgnoredActors, float MaxTime, float Step) const{
if (!BulletClass->IsValidLowLevel()) {
UE_LOG(LogTemp, Warning, TEXT("PredictHit - invalid bullet class"));
return;
}
float Time = 0;
Trajectory = TArray<FVector>();
FVector CurrentLocation = StartLocation;
AEBBullet* Bullet = Cast<AEBBullet>(BulletClass->GetDefaultObject());
FVector Velocity = AimDirection.GetSafeNormal()*(FMath::Lerp(MuzzleVelocityMultiplierMin, MuzzleVelocityMultiplierMax, 0.5)*FMath::Lerp(Bullet->MuzzleVelocityMin, Bullet->MuzzleVelocityMax, 0.5));
UPrimitiveComponent* Parent = Cast<UPrimitiveComponent>(GetAttachParent());
Velocity += AdditionalVelocity;
if (Parent != nullptr) {
if (Parent->IsSimulatingPhysics()) {
Velocity += Parent->GetPhysicsLinearVelocityAtPoint(CurrentLocation)*InheritVelocity;
}
}
while (Time < MaxTime) {
FVector PreviousVelocity = Velocity;
Velocity = Bullet->UpdateVelocity(GetWorld(), CurrentLocation, Velocity, Step);
Hit = UEBBarrel::PredictTrace(GetWorld(), Bullet, CurrentLocation, CurrentLocation + FMath::Lerp(PreviousVelocity, Velocity, 0.5f)*Step, HitResult, IgnoredActors);
if (Hit) {
Trajectory.Add(HitResult.Location);
HitTime = Time+(HitResult.Time*Step);
HitActor = HitResult.GetActor();
HitLocation = HitResult.Location;
return;
}
else {
Trajectory.Add(CurrentLocation);
CurrentLocation += FMath::Lerp(PreviousVelocity, Velocity, 0.5f)*Step;
Time += Step;
}
}
Hit = false;
HitTime = MaxTime;
HitLocation = CurrentLocation;
HitActor = nullptr;
}
bool UEBBarrel::PredictTrace(UWorld* World, AEBBullet* Bullet, FVector Start, FVector End, FHitResult &HitResult, TArray<AActor*> IgnoredActors) const {
FCollisionResponseParams ResponseParams;
FCollisionQueryParams QueryParams;
QueryParams.bTraceComplex = Bullet->TraceComplex;
QueryParams.bReturnPhysicalMaterial = true;
if (Bullet->SafeLaunch) {
QueryParams.AddIgnoredActor(GetOwner());
}
QueryParams.AddIgnoredActors(IgnoredActors);
return World->LineTraceSingleByChannel(HitResult, Start, End, Bullet->TraceChannel, QueryParams, ResponseParams);
}

View File

@ -0,0 +1,74 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBarrel.h"
#include "EBBullet.h"
TArray<TSubclassOf<class AEBBullet>> UEBBarrel::GetAmmo(bool CountChambered) const {
if (!CountChambered || ChamberedBullet == nullptr) {
return Ammo;
}
else {
TArray<TSubclassOf<class AEBBullet>> RetAmmo;
RetAmmo.Add(ChamberedBullet);
RetAmmo.Append(Ammo);
return RetAmmo;
};
};
int UEBBarrel::GetAmmoCount(bool CountChambered) const {
int remainingAmmo;
if (CycleAmmo) {
remainingAmmo = CycleAmmoCount;
}
else {
remainingAmmo = Ammo.Num();
};
if (CountChambered) {
if (ChamberedBullet != nullptr) {
remainingAmmo++;
};
};
return remainingAmmo;
};
void UEBBarrel::SetAmmo(int Count, bool UnloadChambered, bool CancelShooting, bool ManualCharge, const TArray<TSubclassOf<class AEBBullet>>& NewAmmo) {
Ammo = NewAmmo;
CycleAmmoCount = Count;
if (UnloadChambered) {
ChamberedBullet = nullptr;
};
if (CancelShooting) {
BurstRemaining = 0;
Shooting = false;
};
if (ManualCharge) {
LoadNext = false;
};
};
void UEBBarrel::Charge_Implementation() {
LoadNext = true;
};
bool UEBBarrel::Charge_Validate() {
return true;
};
void UEBBarrel::UnloadChambered_Implementation(bool ManualCharge) {
ChamberedBullet = nullptr;
if (ManualCharge) {
LoadNext = false;
};
};
bool UEBBarrel::UnloadChambered_Validate(bool ManualCharge) {
return true;
};

View File

@ -0,0 +1,28 @@
// Copyright 2020 Mookie. All Rights Reserved.
#include "EBBullet.h"
TArray<AActor*>AEBBullet::GetSafeLaunchIgnoredActors(AActor* BulletOwner) const{
TArray<AActor*> Results = SafeLaunchIgnoredActors;
Results.Add(BulletOwner);
if (SafeLaunchIgnoreAttachParent && BulletOwner) {
AActor* AttachedRoot = BulletOwner;
while (true) { //find attachment root
AActor* AttachedTo;
AttachedTo = AttachedRoot->GetAttachParentActor();
if (AttachedTo) {
Results.Add(AttachedTo);
AttachedRoot = AttachedTo;
}
else break;
}
Results.Add(AttachedRoot);
if (SafeLaunchIgnoreAllAttached) Results.Append(GetAttachedActorsRecursive(AttachedRoot));
}
return Results;
}

View File

@ -0,0 +1,216 @@
// Copyright 2016 Mookie. All Rights Reserved.
#include "EBBullet.h"
float AEBBullet::Trace(FVector start, FVector PreviousVelocity, float delta, TEnumAsByte<ECollisionChannel> CollisionChannel) {
bool Hit;
FHitResult HitResult;
TArray<FHitResult> Results;
FCollisionResponseParams ResponseParameters;
FCollisionQueryParams CollisionParameters;
CollisionParameters.bTraceComplex = TraceComplex;
CollisionParameters.bReturnPhysicalMaterial = true;
CollisionParameters.AddIgnoredActor(this);
CollisionParameters.AddIgnoredActors(IgnoredActors);
CollisionParameters.bReturnFaceIndex = true;
if (OwnerSafe) {
CollisionParameters.AddIgnoredActors(GetSafeLaunchIgnoredActors(GetOwner()));
}
FVector TraceDistance = (PreviousVelocity + Velocity)*0.5*delta;
GetWorld()->LineTraceMultiByChannel(Results, start, start + TraceDistance, CollisionChannel, CollisionParameters, ResponseParameters);
if (Results.Num() > 0) {
HitResult = FilterHits(Results, Hit);
}
else { Hit = false; }
if (Hit) {
//Reduce velocity
Velocity = FMath::Lerp(PreviousVelocity, Velocity, HitResult.Time);
bool Ricochet = false;
bool Penetration = false;
FVector exitLoc;
FVector exitNormal;
FVector NewVelocity = Velocity;
//material mods
bool neverPenetrate = false;
bool neverRicochet = false;
float penDepthMultiplier = 1.0f;
float penNormalization = PenetrationNormalization;
float penNormalizationGrazing = PenetrationNormalizationGrazing;
float penEnterSpread = PenetrationEntryAngleSpread;
float penExitSpread = PenetrationExitAngleSpread;
float ricProbMultiplier = 1.0f;
float ricRestitution = RicochetRestitution;
float ricFriction = RicochetFriction;
float ricSpread = RicochetSpread;
EPenTraceType PenTraceType = DefaultPenTraceType;
UPhysicalMaterial* PhysMaterial = HitResult.PhysMaterial.Get();
if (PhysMaterial) {
//material response modifiers
if (MaterialResponseMap != nullptr) {
FEBMaterialResponseMapEntry* ResponseEntry = MaterialResponseMap->Map.Find(PhysMaterial);
if (ResponseEntry != nullptr) {
neverPenetrate = ResponseEntry->NeverPenetrate;
neverRicochet = ResponseEntry->NeverRicochet;
PenTraceType = ResponseEntry->PenTraceType;
penDepthMultiplier = ResponseEntry->PenetrationDepthMultiplier;
penNormalization = PenetrationNormalization + ResponseEntry->PenetrationNormalization;
penNormalizationGrazing = PenetrationNormalizationGrazing + ResponseEntry->PenetrationNormalizationGrazing;
penEnterSpread = PenetrationEntryAngleSpread + ResponseEntry->PenetrationEntryAngleSpread;
penExitSpread = PenetrationExitAngleSpread + ResponseEntry->PenetrationExitAngleSpread;
ricProbMultiplier = ResponseEntry->RicochetProbabilityMultiplier;
ricRestitution = FMath::Lerp(RicochetRestitution, ResponseEntry->RicochetRestitution, ResponseEntry->RicochetRestitutionInfluence);
ricFriction = FMath::Lerp(RicochetFriction, ResponseEntry->RicochetFriction, ResponseEntry->RicochetFrictionInfluence);
ricSpread = RicochetSpread + ResponseEntry->RicochetSpread;
}
}
if (MaterialDensityControlsPenetrationDepth) {
penDepthMultiplier /= PhysMaterial->Density;
}
if (MaterialRestitutionControlsRicochet) {
RicochetRestitution *= PhysMaterial->Restitution;
}
}
float dot = FVector::DotProduct(Velocity.GetSafeNormal(), HitResult.Normal) + 1.0f;
FVector cross = FVector::CrossProduct(Velocity.GetSafeNormal(), HitResult.Normal);
FVector flat = HitResult.Normal.RotateAngleAxis(-90.0f, cross);
#ifdef WITH_EDITOR
if (DebugEnabled) {
FColor DebugColor = FColor::MakeRedToGreenColorFromScalar(Velocity.Size() / MuzzleVelocityMax);
DrawDebugLine(GetWorld(), start, HitResult.Location, DebugColor, false, DebugTrailTime, 0, DebugTrailWidth);
};
#endif
float GrazingAngle = FMath::Pow(dot, GrazingAngleExponent);
FVector PenetrationVector = RandomStream.VRandCone(Velocity, penEnterSpread);
PenetrationVector = FMath::Lerp(PenetrationVector, -HitResult.Normal, FMath::Lerp(penNormalization, penNormalizationGrazing, GrazingAngle));
float PenetrationDistance = FMath::Lerp(MinPenetration, MaxPenetration, RandomStream.FRand()) * FMath::Pow((Velocity.Size() / ((MuzzleVelocityMin + MuzzleVelocityMax) * 0.5f)), 2.0f) * penDepthMultiplier;
float PenetrationDepth = -FVector::DotProduct(PenetrationVector, HitResult.Normal) * PenetrationDistance;
float BlockTIme = 1.0f;
if (PenetrationDistance > 0.0f) {
if (!neverPenetrate) {
BlockTIme = PenetrationTrace(HitResult.Location - (HitResult.Normal * CollisionMargin), HitResult.Location + PenetrationVector * PenetrationDistance, HitResult.Component, PenTraceType, CollisionChannel, exitLoc, exitNormal);
}
}
if (BlockTIme >= 0.999999f) {
//no pen
SetActorLocation(HitResult.Location + HitResult.Normal * CollisionMargin);
float ricThreshold = 1.0f;
if (SpeedControlsRicochetProbability) { ricThreshold *= Velocity.Size() / MuzzleVelocityMax; };
if (!neverRicochet && RandomStream.FRand() * ricThreshold < FMath::Lerp(RicochetProbability * ricProbMultiplier, RicochetProbabilityGrazing * ricProbMultiplier, GrazingAngle)) {
//bounce
FVector bounceAngle = flat * dot * (1.0f - ricFriction);
bounceAngle += HitResult.Normal * (1.0f - dot) * ricRestitution;
bounceAngle = RandomStream.VRandCone(bounceAngle, ricSpread) * bounceAngle.Size();
NewVelocity = bounceAngle * Velocity.Size();
Ricochet = true;
OwnerSafe = false;
}
else {
//stopped
NewVelocity = FVector(0, 0, 0);
}
}
else {
//penetration
float RemainingEnergy = FMath::Pow(1.0f - BlockTIme, 2.0f);
SetActorLocation(exitLoc + exitNormal * CollisionMargin);
NewVelocity = RandomStream.VRandCone(PenetrationVector, penExitSpread * (1.0f - RemainingEnergy));
NewVelocity = FMath::Lerp(NewVelocity, Velocity.GetSafeNormal(), RemainingEnergy);
NewVelocity *= RemainingEnergy * Velocity.Size();
Penetration = true;
OwnerSafe = false;
}
//response
FVector Impulse = (Velocity - NewVelocity) * Mass * ImpulseMultiplier;
if (AddImpulse && HitResult.Component->IsSimulatingPhysics()) {
HitResult.Component->AddImpulseAtLocation(Impulse, HitResult.Location, HitResult.BoneName);
}
//impact actual
if (HasAuthority()) {
OnImpact(Ricochet, Penetration, HitResult.Location, Velocity, HitResult.Normal, GetActorLocation(), NewVelocity, Impulse, PenetrationDepth, HitResult.GetActor(), HitResult.Component.Get(), HitResult.BoneName, PhysMaterial, HitResult, fireEventID);
}
else {
OnNetPredictedImpact(Ricochet, Penetration, HitResult.Location, Velocity, HitResult.Normal, GetActorLocation(), NewVelocity, Impulse, PenetrationDepth, HitResult.GetActor(), HitResult.Component.Get(), HitResult.BoneName, PhysMaterial, HitResult, fireEventID);
}
Velocity = NewVelocity;
if ((Velocity.Size() < DespawnVelocity) || (!Ricochet && !Penetration && (DespawnVelocity>0.0f))){
Deactivate();
}
CanRetrace = false;
}
else {
//prepare for time travel
if (Retrace) {
CanRetrace = true;
LastTraceStart = start;
LastTraceDelta = delta;
LastTracePrevVelocity = PreviousVelocity;
LastTraceVelocity = Velocity;
}
SetActorLocation(start + TraceDistance);
HitResult.Time = 1.0f;
OnTrace(start, GetActorLocation());
#ifdef WITH_EDITOR
if (DebugEnabled) {
FLinearColor Color = GetDebugColor(Velocity.Size() / ((MuzzleVelocityMin + MuzzleVelocityMax)*0.5f));
DrawDebugLine(GetWorld(), start, start + TraceDistance, Color.ToFColor(true), false, DebugTrailTime, 0, 0);
}
}
#endif
return delta*(1.0f - HitResult.Time);
}
TArray<AActor*> AEBBullet::GetAttachedActorsRecursive(AActor* Actor, uint16 Depth, TArray<AActor*> VisitedActors) const {
//TODO: limit depth
TArray<AActor*> Attached;
Actor->GetAttachedActors(Attached);
TArray<AActor*> AttachedRecursive;
for (AActor* ActorRecursive : Attached) { // Skip already visited actors to avoid infinite recursion
if (!VisitedActors.Contains(ActorRecursive)) {
VisitedActors.Add(ActorRecursive);
AttachedRecursive += GetAttachedActorsRecursive(ActorRecursive, Depth+1, VisitedActors);
VisitedActors.Remove(ActorRecursive); // Remove from visited actors to allow other branches to visit it
}
}
Attached += AttachedRecursive;
return Attached;
}

View File

@ -0,0 +1,154 @@
// Copyright 2020 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "Kismet/GameplayStatics.h"
#include "EBBullet.h"
#include "EBBarrel.generated.h"
UENUM(BlueprintType)
enum class EFireMode : uint8
{
FM_Auto UMETA(DisplayName = "Full Auto"),
FM_Semiauto UMETA(DisplayName = "Semiauto"),
FM_Burst UMETA(DisplayName = "Burst"),
FM_InterBurst UMETA(DisplayName = "Interruptible Burst"),
FM_Manual UMETA(DisplayName = "Manual"),
FM_Slamfire UMETA(DisplayName = "Slam Fire"),
FM_Gatling UMETA(DisplayName = "Gatling")
};
UCLASS(Blueprintable, ClassGroup = (Custom), hidecategories = (Object, LOD, Physics, Lighting, TextureStreaming, Collision, HLOD, Mobile, VirtualTexture, ComponentReplication), editinlinenew, meta = (BlueprintSpawnableComponent))
class EASYBALLISTICS_API UEBBarrel : public UPrimitiveComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UEBBarrel();
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "PROSERVE") int nextFireEventID = 100.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") bool shotTrace = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") float DebugArrowSize = 100.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil") bool AntiRecoil = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil") int AntiRecoilPrevCount = 5;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Velocity", meta = (ToolTip = "Bullet inherits barrel velocity, only works with physics enabled or with additional velocity set")) float InheritVelocity = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Velocity", meta = (ToolTip = "Amount of recoil applied to the barrel, only works with physics enabled")) float RecoilMultiplier = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Velocity", meta = (ToolTip = "Additional velocity, for use with InheritVelocity")) FVector AdditionalVelocity = FVector(0,0,0);
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Additional maximum spread, in radians, applied on top of bullet spread", ClampMin = "0")) float Spread=0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Additional Spread bias, higher is more accurate on average", ClampMin = "0")) float SpreadBias = 0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Minimum of random multiplier applied to bullet muzzle velocity")) float MuzzleVelocityMultiplierMin = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Maximum of random multiplier applied to bullet muzzle velocity")) float MuzzleVelocityMultiplierMax = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Minimum fire rate, rounds per second")) float FireRateMin = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Maximum fire rate, rounds per second, set to same number as FireRateMin to disable randomization")) float FireRateMax = 1.0f;
UPROPERTY(Replicated, BlueprintReadWrite, EditAnywhere, Category = "Weapon") EFireMode FireMode = EFireMode::FM_Auto;
UPROPERTY(Replicated, BlueprintReadWrite, EditAnywhere, Category = "Weapon") bool ShootingBlocked;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Number of rounds auto fired in burst mode")) int BurstCount = 3;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Minimum time between bursts")) float BurstCooldown = 0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon", meta = (ToolTip = "Automatically spin up gatling when trigger is being held down")) bool GatlingAutoSpool = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon") float GatlingSpoolUpTime = 1.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Weapon") float GatlingSpoolDownTime = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Weapon") float GatlingPhase = 0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Ammo") bool CycleAmmo = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Ammo", meta = (EditCondition = "CycleAmmo")) bool CycleAmmoUnlimited = true;
UPROPERTY(Replicated, BlueprintReadWrite, EditAnywhere, Category = "Ammo") TArray<TSubclassOf<class AEBBullet>> Ammo;
UPROPERTY(Replicated, BlueprintReadWrite, EditAnywhere, Category = "Ammo", meta = (EditCondition = "CycleAmmo")) int CycleAmmoCount;
UPROPERTY(Replicated, BlueprintReadWrite, EditAnywhere, Category = "Ammo", meta = (EditCondition = "CycleAmmo")) int CycleAmmoPos;
UPROPERTY(Replicated, BlueprintReadWrite, Category = "WeaponState") TSubclassOf<class AEBBullet> ChamberedBullet;
UPROPERTY(Replicated, BlueprintReadWrite, Category = "WeaponState") bool Shooting;
UPROPERTY(Replicated, BlueprintReadWrite, Category = "WeaponState") bool Spooling = false;
UPROPERTY(BlueprintReadWrite, Category = "Weapon") float GatlingRPS = 0.0f;
UPROPERTY(BlueprintReadWrite, Category = "WeaponState") bool LoadNext=true;
UPROPERTY(BlueprintReadWrite, Category = "WeaponState") float Cooldown;
UPROPERTY(BlueprintReadWrite, Category = "WeaponState") int BurstRemaining;
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;
FRandomStream RandomStream;
UFUNCTION() void NextBullet();
UFUNCTION(BlueprintPure, Category = "Ammo") int GetAmmoCount(bool CountChambered) const;
UFUNCTION(BlueprintPure, Category = "Ammo") TArray<TSubclassOf<class AEBBullet>> GetAmmo(bool CountChambered) const;
UFUNCTION(BlueprintAuthorityOnly, BlueprintCallable, Category = "Ammo") void SetAmmo(int count, bool UnloadChambered, bool CancelShooting, bool ManualCharge, const TArray<TSubclassOf<class AEBBullet>>& NewAmmo);
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Ammo") void Charge();
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Ammo") void UnloadChambered(bool ManualCharge);
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Shooting") void SwitchFireMode(EFireMode NewFireMode);
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Shooting") void GatlingSpool(bool Spool);
UFUNCTION(BlueprintCallable, Category = "Shooting") void Shoot(bool Trigger, int nextEventFire);
UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "IgnoredActors"), Category = "Prediction") void PredictHit(bool& Hit, FHitResult& TraceResult, FVector& HitLocation, float& HitTime, AActor*& HitActor, TArray<FVector>& Trajectory, TSubclassOf<class AEBBullet> BulletClass, TArray<AActor*>IgnoredActors, float MaxTime = 10.0f, float Step = 0.1f) const;
UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "IgnoredActors"), Category = "Prediction") void PredictHitFromLocation(bool &Hit, FHitResult& TraceResult, FVector& HitLocation, float& HitTime, AActor*& HitActor, TArray<FVector>& Trajectory, TSubclassOf<class AEBBullet> BulletClass, FVector StartLocation, FVector AimDirection, TArray<AActor*>IgnoredActors, float MaxTime = 10.0f, float Step = 0.1f) const;
UFUNCTION(BlueprintCallable, Category = "Prediction") void CalculateAimDirection(TSubclassOf<class AEBBullet> BulletClass, FVector TargetLocation, FVector TargetVelocity, FVector& AimDirection, FVector& PredictedTargetLocation, FVector& PredictedIntersectionLocation, float& PredictedFlightTime, float& Error, float MaxTime = 10.0f, float Step = 0.1f, int NumIterations = 4) const;
UFUNCTION(BlueprintCallable, Category = "Prediction") void CalculateAimDirectionFromLocation(TSubclassOf<class AEBBullet> BulletClass, FVector StartLocation, FVector TargetLocation, FVector TargetVelocity, FVector& AimDirection, FVector& PredictedTargetLocation, FVector& PredictedIntersectionLocation, float& PredictedFlightTime, float& Error, float MaxTime = 10.0f, float Step=0.1f, int NumIterations = 4) const;
UFUNCTION(BlueprintNativeEvent, Category = "Events") void InitialBulletTransform(FVector InLocation, FVector InDirection, FVector& OutLocation, FVector& OutDirection);
UFUNCTION(BlueprintNativeEvent, Category = "Events") void ApplyRecoil(UPrimitiveComponent* Component, FVector InLocation, FVector Impulse);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBeforeShotFired);
UPROPERTY(BlueprintAssignable, Category = "Events")
FBeforeShotFired BeforeShotFired;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FSpawnBulletEvent, FVector, Start, FVector, Velocity);
UPROPERTY(BlueprintAssignable, Category = "Events")
FSpawnBulletEvent SpawnBulletEvent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FShotFired);
UPROPERTY(BlueprintAssignable, Category = "Events")
FShotFired ShotFired;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAmmoDepleted);
UPROPERTY(BlueprintAssignable, Category = "Events")
FAmmoDepleted AmmoDepleted;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FReadyToShoot);
UPROPERTY(BlueprintAssignable, Category = "Events")
FReadyToShoot ReadyToShoot;
#if WITH_EDITOR
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
virtual bool IsZeroExtent() const override { return false; };
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
#endif
private:
void SpawnBullet(AActor* Owner, FVector LocalLocation, FVector LocalAim, int fireEventID);
UFUNCTION(Server, Unreliable, WithValidation) void ClientAim(FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim);
UFUNCTION(Server, Reliable, WithValidation) void ShootRep(bool Trigger, int nextFireID);
UFUNCTION(Server, Reliable, WithValidation) void ShootRepCSA(bool Trigger, FVector_NetQuantize NewLocation, FVector_NetQuantizeNormal NewAim, int nextFireID);
UFUNCTION(NetMulticast, Reliable)
void ShotFiredMulticast();
UFUNCTION(NetMulticast, Reliable)
void SpawnBulletEventMulticast(FVector Start, FVector Velocity);
FVector Aim;
FVector Location;
TArray<FVector> previousAim;
TArray<FVector> previousLocation;
bool RemoteAimReceived;
float TimeSinceAimUpdate;
bool PredictTrace(UWorld* World, AEBBullet* Bullet, FVector Start, FVector End, FHitResult &HitResult, TArray<AActor*> IgnoredActors) const;
};

View File

@ -0,0 +1,227 @@
// Copyright 2016 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Curves/CurveFloat.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "DrawDebugHelpers.h"
#include "Components/PrimitiveComponent.h"
#include "EBMaterialResponseMap.h"
#include "EBBullet.generated.h"
UENUM(BlueprintType)
enum class EEBAtmosphereType : uint8
{
AT_Constant UMETA(DisplayName = "Constant"),
AT_Curve UMETA(DisplayName = "Density Curve"),
AT_Earth UMETA(DisplayName = "Earth/IGL")
};
UCLASS(Blueprintable, BlueprintType)
class EASYBALLISTICS_API AEBBullet : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AEBBullet();
UPROPERTY(Replicated, BlueprintReadWrite, Category = "State") FVector Velocity;
UPROPERTY(Replicated, BlueprintReadWrite, Category = "State") FRandomStream RandomStream;
UPROPERTY(BlueprintReadWrite, Category = "State") bool OwnerSafe=false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "PROSERVE") int fireEventID;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") bool DebugEnabled;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") float DebugTrailTime=1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") float DebugTrailWidth=0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") FLinearColor DebugTrailColorFast = FLinearColor(0, 1, 0, 1);
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") FLinearColor DebugTrailColorSlow = FLinearColor(1, 0, 0, 1);
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Debug") bool DebugPooling;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") FVector Wind;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Select atmosphere model")) EEBAtmosphereType AtmosphereType;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World", meta = (ToolTip = "Air Density at sea level - in KG/m^3", ClampMin = "0")) float SeaLevelAirDensity = 1.21;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World", meta = (ToolTip = "in cm/s", ClampMin = "0")) float SeaLevelSpeedOfSound = 34300;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World", meta = (ToolTip = "Used for Density Curve atmosphere model")) UCurveFloat* AirDensityCurve;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") bool SpeedOfSoundVariesWithAltitude = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") UCurveFloat* SpeedOfSoundCurve;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") float WorldScale = 1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Atmosphere pressure at 0,0,0 - in millibars", ClampMin = "0")) float SeaLevelAirPressure = 1012.5f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Atmosphere Temperature at 0,0,0 - in degrees C")) float SeaLevelAirTemperature = 20.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Temperature Decrease With Altitude, degrees per meter")) float TemperatureLapseRate = 0.00649f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Altitude at which temperature stops decreasing, in meters")) float TropopauseAltitude = 11000.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Specific Gas Constant, dry air = 287.058", ClampMin = "0")) float SpecificGasConstant = 287.058;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "World Origin Location")) FVector WorldCenterLocation = FVector(0, 0, 0);
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Use spherical planet model to get altitude")) bool SphericalAltitude = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, SaveGame, Category = "World", meta = (ToolTip = "Planet radius, in Unreal units", EditCondition = "SphericalAltitude", ClampMin = "0")) float SeaLevelRadius = 637100000.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") bool OverrideGravity = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "World") FVector Gravity = FVector(0,0,-980);
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Safe launch") bool SafeLaunch = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Safe launch", Meta = (EditCondition = "SafeLaunch")) bool SafeLaunchIgnoreAttachParent = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Safe launch", Meta = (EditCondition = "SafeLaunchIgnoreAttachParent")) bool SafeLaunchIgnoreAllAttached = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Safe launch", Meta = (EditCondition = "SafeLaunch", ClampMin = "0")) float SafeDelay = 1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Safe launch", Meta = (EditCondition = "SafeLaunch")) TArray<AActor*> SafeLaunchIgnoredActors;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Shotgun") bool Shotgun=false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Shotgun", meta = (EditCondition = "Shotgun")) int ShotCount=10;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Shotgun", meta = (EditCondition = "Shotgun")) float ShotSpread=0.01;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Shotgun", meta = (EditCondition = "Shotgun")) float ShotVelocitySpread = 0.01;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") float MuzzleVelocityMin = 100000.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") float MuzzleVelocityMax = 100000.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight", meta = (ToolTip = "Maximum bullet spread, in radians", ClampMin = "0")) float Spread = 0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight", meta = (ToolTip = "Spread bias, higher is more accurate on average", ClampMin = "0")) float SpreadBias = 0.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") float Mass = 0.005;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") float Diameter = 0.556;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") float FormFactor = 1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Flight") UCurveFloat* MachDragCurve;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float GrazingAngleExponent = 2.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float MinPenetration = 10.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float MaxPenetration = 20.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float PenetrationNormalization = 0.5;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float PenetrationNormalizationGrazing = 0.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float PenetrationEntryAngleSpread = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float PenetrationExitAngleSpread = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float RicochetProbability = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float RicochetProbabilityGrazing = 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float RicochetRestitution = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float RicochetFriction = 0.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float RicochetSpread = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") bool SpeedControlsRicochetProbability = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") bool AddImpulse = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") float ImpulseMultiplier = 1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") EPenTraceType DefaultPenTraceType = EPenTraceType::PT_BackTrace;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") UEBMaterialResponseMap* MaterialResponseMap;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") bool MaterialDensityControlsPenetrationDepth = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Impact") bool MaterialRestitutionControlsRicochet = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Replication") bool ReliableReplication = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision", meta = (ToolTip = "Allow components to collide, intended for use with trigger volumes. Do not use for actual collisions.")) bool AllowComponentCollisions = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision") TEnumAsByte<ECollisionChannel> TraceChannel;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision") bool TraceComplex;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision") float CollisionMargin=1.0;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision", meta = (ToolTip = "Bullets with lower velocity will automatically despawn on impact, never despawn if set to zero or negative")) float DespawnVelocity=100.0f;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Collision") TArray<AActor*> IgnoredActors;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Simulation", meta = (ToolTip = "Spawned bullet performs first trace immediately, instead of waiting for next simulation step")) bool DoFirstStepImmediately = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Simulation", meta = (EditCondition = "DoFirstStepImmediately")) bool RandomFirstStepDelta = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Simulation") bool FixedStep = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Simulation", meta = (EditCondition = "FixedStep", ClampMin = "0")) float FixedStepSeconds = 0.1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Simulation") int MaxTracesPerStep = 8;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Retrace") bool Retrace = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Retrace") bool RetraceOnAnotherChannel = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Retrace", meta=(EditCondition="RetraceOnAnotherChannel")) TEnumAsByte<ECollisionChannel> RetraceChannel;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Rotation") bool RotateActor = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Rotation") bool RotateRandomRoll = true;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Pooling") bool EnablePooling = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Pooling", meta = (EditCondition = "EnablePooling")) int MaxPoolSize = 50;
//rebase
virtual void ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) override;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick(float DeltaSeconds) override;
virtual void LifeSpanExpired() override;
UFUNCTION(BlueprintCallable, Category = "EBBullet|Spawn")
static void SpawnWithExactVelocity(TSubclassOf<class AEBBullet> BulletClass, AActor* BulletOwner, APawn* BulletInstigator, FVector BulletLocation, FVector BulletVelocity, int EventFireID);
UFUNCTION(BlueprintCallable, Category = "EBBullet|Spawn")
static void Spawn(TSubclassOf<class AEBBullet> BulletClass, AActor* BulletOwner, APawn* BulletInstigator, FVector BulletLocation, FVector BulletVelocity, int EventFireID);
UFUNCTION(NetMulticast, Unreliable)
void VelocityChangeBroadcast(FVector_NetQuantize NewLocation, FVector NewVelocity);
UFUNCTION(NetMulticast, Reliable)
void VelocityChangeBroadcastReliable(FVector_NetQuantize NewLocation, FVector NewVelocity);
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, int EventFireID);
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, int EventFireID);
UFUNCTION(BlueprintImplementableEvent, Category = "EBBullet|Impact")
void OnTrace(FVector StartLocation, FVector EndLocation);
UFUNCTION(BlueprintImplementableEvent, Category = "EBBullet|Remote")
void OnTrajectoryUpdateReceived(FVector Location, FVector OldVelocity, FVector NewVelocity);
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|Activation")
void OnDeactivated();
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|Flight")FVector UpdateVelocity(UWorld* World, FVector Location, FVector PreviousVelocity, float DeltaTime) const;
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World") FVector GetWind(UWorld* World, FVector Location) const;
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World") float GetAirDensity(UWorld* World, FVector Location) const;
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World") float GetSpeedOfSound(UWorld* World, FVector Location) const;
UFUNCTION(BlueprintNativeEvent, Category = "EBBullet|World") bool CollisionFilter(FHitResult HitResult) const;
//pooling
UFUNCTION(BlueprintAuthorityOnly, BlueprintCallable, Category = "EBBullet|Pooling")void Deactivate();
UFUNCTION(NetMulticast, Reliable)
void ReactivationBroadcast(FVector_NetQuantize NewLocation, FVector NewVelocity, AActor* BulletOwner, APawn* BulletInstigator, int nextFireEventID);
UFUNCTION(NetMulticast, Reliable)
void DeactivationBroadcast();
private:
UPROPERTY() TArray<TWeakObjectPtr<AEBBullet>> Pooled;
static AEBBullet* GetFromPool(UWorld* World, UClass* BulletClass);
static AEBBullet* SpawnOrReactivate(UWorld* World, TSubclassOf<class AEBBullet> BulletClass, const FTransform& Transform, FVector BulletVelocity, AActor* BulletOwner, APawn* BulletInstigator, int nextFireEventID);
void DeactivateToPool();
void FinishSpawning(FTransform Transform);
void Step(float DeltaTime);
float Trace(FVector start, FVector PreviousVelocity, float delta, TEnumAsByte<ECollisionChannel> channel);
TArray<AActor*> GetAttachedActorsRecursive(AActor* Actor, uint16 Depth = 0, TArray<AActor*> VisitedActors = TArray<AActor*>()) const;
float PenetrationTrace(FVector start, FVector end, TWeakObjectPtr<UPrimitiveComponent,FWeakObjectPtr> comp, EPenTraceType penType, TEnumAsByte<ECollisionChannel> channel, FVector &exitLoc, FVector &exitNormal);
float GetCurveValue(const UCurveFloat* curve, float in, float deflt) const;
float AccumulatedDelta;
bool CanRetrace = false;
FVector LastTraceStart;
float LastTraceDelta;
FVector LastTraceVelocity;
FVector LastTracePrevVelocity;
bool IsRecycled;
FHitResult FilterHits(TArray<FHitResult> Results, bool &hit) const;
TArray<AActor*>GetSafeLaunchIgnoredActors(AActor* Owner) const;
float GetAltitude(UWorld* World, FVector Location) const;
float GetAltitudePressure(float AltitudeMeter) const;
float GetAltitudeTemperature(float AltitudeMeter) const;
float GetAltitudeDensity(float AltitudeMeter) const;
#ifdef WITH_EDITOR
FLinearColor GetDebugColor(float In) const{
return FMath::Lerp(DebugTrailColorSlow, DebugTrailColorFast, In);
}
#endif
};

View File

@ -0,0 +1,45 @@
// Copyright 2016 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "EBMaterialResponseMap.generated.h"
UENUM(BlueprintType)
enum class EPenTraceType : uint8
{
PT_BackTrace UMETA(DisplayName = "Back Trace"),
PT_ByComponent UMETA(DisplayName = "By Component"),
PT_TwoSidedGeometry UMETA(DisplayName = "Double Sided Geometry"),
};
USTRUCT(BlueprintType)
struct FEBMaterialResponseMapEntry {
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, Category = "Material") EPenTraceType PenTraceType = EPenTraceType::PT_BackTrace;
UPROPERTY(EditAnywhere, Category = "Material") bool NeverPenetrate = false;
UPROPERTY(EditAnywhere, Category = "Material") float PenetrationDepthMultiplier = 1.0f;
UPROPERTY(EditAnywhere, Category = "Material") float PenetrationNormalization = 0.0f;
UPROPERTY(EditAnywhere, Category = "Material") float PenetrationNormalizationGrazing = 0.0f;
UPROPERTY(EditAnywhere, Category = "Material") float PenetrationEntryAngleSpread = 0.0f;
UPROPERTY(EditAnywhere, Category = "Material") float PenetrationExitAngleSpread = 0.0;
UPROPERTY(EditAnywhere, Category = "Material") bool NeverRicochet = false;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetProbabilityMultiplier = 1.0f;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetRestitution = 0.5f;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetRestitutionInfluence = 0.0f;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetFriction = 0.5f;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetFrictionInfluence = 0.0f;
UPROPERTY(EditAnywhere, Category = "Material") float RicochetSpread = 0.0f;
};
UCLASS(BlueprintType)
class UEBMaterialResponseMap : public UDataAsset{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Responses") TMap<UPhysicalMaterial*, FEBMaterialResponseMapEntry> Map;
};

View File

@ -0,0 +1,17 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// Copyright 2018 Mookie. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"
#include "Modules/ModuleManager.h"
class FEasyBallisticsModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class PS_BallisticsTarget : TargetRules
{
public PS_BallisticsTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_5;
ExtraModuleNames.Add("PS_Ballistics");
}
}

View File

@ -0,0 +1,23 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class PS_Ballistics : ModuleRules
{
public PS_Ballistics(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}

View File

@ -0,0 +1,6 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PS_Ballistics.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, PS_Ballistics, "PS_Ballistics" );

View File

@ -0,0 +1,6 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"

View File

@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.Collections.Generic;
public class PS_BallisticsEditorTarget : TargetRules
{
public PS_BallisticsEditorTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_5;
ExtraModuleNames.Add("PS_Ballistics");
}
}

335
Unreal/UpgradeLog.htm Normal file

File diff suppressed because one or more lines are too long