-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBodySimulator.cpp
More file actions
92 lines (79 loc) · 3.41 KB
/
BodySimulator.cpp
File metadata and controls
92 lines (79 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Fill out your copyright notice in the Description page of Project Settings.
#include "BodySimulator.h"
#include "Runtime/Core/Public/Async/ParallelFor.h"
// Sets default values
ABodySimulator::ABodySimulator()
{
// 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;
PrimaryActorTick.bStartWithTickEnabled = true;
PrimaryActorTick.TickInterval = 0.0167f;
PrimaryActorTick.TickGroup = TG_DuringPhysics;
InstancedMesh = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("InstancedMesh"));
SetRootComponent(InstancedMesh);
static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("StaticMesh'/Game/SM_Circle'"));
InstancedMesh->SetStaticMesh(MeshAsset.Object);
}
// Called when the game starts or when spawned
void ABodySimulator::BeginPlay()
{
Super::BeginPlay();
InitBodies();
}
// Called every frame
void ABodySimulator::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (bIsGravityEnabled)
GravityStep(DeltaTime);
UpdatePositionStep(DeltaTime);
}
void ABodySimulator::InitBodies()
{
Bodies.SetNumUninitialized(BodyNum);
Transforms.SetNumUninitialized(BodyNum);
for (int32 Index = 0; Index < BodyNum; ++Index) {
FVector2D RandomPosition(FMath::RandPointInCircle(PlacementRadius));
const float RadialSpeedFactor = PlacementRadius / RandomPosition.Size();
FVector2D RandomVelocity {FMath::FRandRange(BaseInitialVelocity - 100.0f, BaseInitialVelocity + 100.0f) / RadialSpeedFactor, 0};
RandomVelocity = RandomVelocity.GetRotated(90.0f + FMath::RadiansToDegrees(FMath::Atan2(RandomPosition.Y, RandomPosition.X)));
const float Mass = FMath::FRandRange(MinMass, MaxMass);
const float MeshScale = FMath::Sqrt(Mass) * BodyDisplayScale;
const FTransform MeshTransform (
FRotator(),
TranslationFrom2DCoordinates(RandomPosition),
FVector(MeshScale, MeshScale, 1.0f)
);
Transforms[Index] = MeshTransform;
Bodies[Index] = FEntity{RandomPosition, RandomVelocity, Mass, Index};
}
InstancedMesh->AddInstances(Transforms, false);
}
void ABodySimulator::GravityStep(float DeltaTime)
{
ParallelFor(Bodies.Num(), [&] (int32 Index) {
FVector2D Acceleration(0.0f, 0.0f);
for (const FEntity& AffectingBody: Bodies) {
if (AffectingBody.Index == Bodies[Index].Index) continue; // exclude self
float Distance = FVector2D::Distance(Bodies[Index].Position, AffectingBody.Position);
Distance = FMath::Max(Distance, MinimumGravityDistance); // avoids division by zero
Acceleration += AffectingBody.Mass / Distance * G / Distance * (AffectingBody.Position - Bodies[Index].Position) / Distance;
}
Bodies[Index].Velocity += Acceleration * DeltaTime ;
});
}
void ABodySimulator::UpdatePositionStep(float DeltaTime)
{
const FVector2D HalfWorld(WorldWidth * 0.5f, WorldHeight * 0.5f);
for (FEntity& Body: Bodies) {
Body.Position += Body.Velocity * DeltaTime;
Body.Position.X = FMath::Wrap(Body.Position.X, -HalfWorld.X, HalfWorld.X);
Body.Position.Y = FMath::Wrap(Body.Position.Y, -HalfWorld.Y, HalfWorld.Y);
Transforms[Body.Index].SetTranslation(TranslationFrom2DCoordinates(Body.Position));
}
InstancedMesh->BatchUpdateInstancesTransforms(0, Transforms, false, true);
}
FVector ABodySimulator::TranslationFrom2DCoordinates(const FVector2D& XYCoordinates)
{
return FVector(XYCoordinates.Y, XYCoordinates.X, 0.0f);
}