More Boids - Predators preview
- Details
- Category: Site
Still working on refactoring the boids code a little and testing the extensibility by implementing some 'predator' boid types. Here's a little preview until I get the article written. You can of course check out the latest code on Github in the meantime.
{youtube}Gm72CF5ZWJY{/youtube}
{fcomment}
Unrealengine 4 'Boids' Implementation in C++ - PT4 - Refactoring!
- Details
- Category: Articles
So we have a working boids implementation in UnrealEngine, this is fine and all, but when we come to look at expanding the behaviours of our boids...we're going to start hitting a wall.
In the original implementation, all boids use the same hard-coded algorithms, and will always behave identically. I embarked on a refactoring of the code with the following aims:
- Allow boid behaviour algorithms to be added and removed dynamically at runtime.
- Allow boids to maintain their own state and therefore behave independent of other boids.
The core of this meant refactoring the boid behaviours out into their own class, BoidAlgo. Here's the header :
class BoidAlgo
{
public:
BoidAlgo(int _algo_type, float _weight);
virtual ~BoidAlgo();
// Some consts for readability
static const int COHERENCE = 0;
static const int ALIGNMENT = 1;
static const int AVOIDANCE = 2;
static const int BOUNDS = 3;
// The boid algorithm declarations
static FVector func_coherence(const ABoid& _currentBoid, const boid_config& _config, const float& _weight);
static FVector func_alignment(const ABoid& _currentBoid, const boid_config& _config, const float& _weight);
static FVector func_avoidance(const ABoid& _currentBoid, const boid_config& _config, const float& _weight);
static FVector func_bounds(const ABoid& _currentBoid, const boid_config& _config, const float& _weight);
bool setAlgo(int _algoEnum);
// Function pointer used to define which algorithm is to be used in this instance
FVector(*calc_func) (const ABoid& _currentBoid, const boid_config& _config, const float& _weight);
// weighting to apply to this algorithm
float weight;
};
Each boid behaviour is now defined as a static function within BoidAlgo. Each instance of BoidAlgo only contain the weight to apply to the algorithm, and a function pointer *calc_func to the appropriate static function. When we create a BoidAlgo we pass the algo type and weight to the constructor and hey presto, one very lightweight algorithm class. I did initially go the usual inheritance route of having an abstract BoidAlgo class and concrete implementations of each algorithm, but decided that this approach is cleaner, less files, and just as easy to maintain.
The setup and configuration of the boids by the BoidManager now looks like this :
// Called when the game starts or when spawned
void ABoidManager::BeginPlay()
{
Super::BeginPlay();
World = GetWorld();
FVector thisSpawnPos;
// Populate the configuration struct with values from the editor
// ready to pass to the boids
config.allBoids = &boids;
config.avoidBoidRange = AvoidBoidRange;
config.avoidBoundsStrength = AvoidBoundsStrength;
config.velocityMax = VelocityMax;
config.cohesionWeight = CohesionWeight;
config.avoidWeight = AvoidWeight;
config.alignWeight = AlignWeight;
config.spawnBoundsStart = SpawnBoundsStart;
config.spawnBoundsEnd = SpawnBoundsEnd;
config.interactionRange = InteractionRange;
for (int32 i = 0; i < NumberOfBoids; i++)
{
// Pick a random point in the defined bounds to spawn
thisSpawnPos.X = FMath::RandRange(SpawnBoundsStart.X, SpawnBoundsEnd.X);
thisSpawnPos.Y = FMath::RandRange(SpawnBoundsStart.Y, SpawnBoundsEnd.Y);
thisSpawnPos.Z = FMath::RandRange(SpawnBoundsStart.Z, SpawnBoundsEnd.Z);
// Spawn the boid into the world, keep the reference
ABoid* spawnedBoid = World->SpawnActor<ABoid>(ABoid::StaticClass(), thisSpawnPos, FRotator(0.0f));
// Stash the reference, key to this implementation!
boids.Add(spawnedBoid);
// Setup the vector of algorithms to give to this boid
// Note we're giving each boid it's own instance so they can change independently
TArray<BoidAlgo> _algos;
BoidAlgo algo_coherence(BoidAlgo::COHERENCE, config.cohesionWeight);
BoidAlgo algo_alignment(BoidAlgo::ALIGNMENT, config.alignWeight);
BoidAlgo algo_avoidance(BoidAlgo::AVOIDANCE, config.avoidWeight);
BoidAlgo algo_bounds(BoidAlgo::BOUNDS, 1.0f);
_algos.Add(algo_coherence);
_algos.Add(algo_alignment);
_algos.Add(algo_avoidance);
_algos.Add(algo_bounds);
// Pass the configuration and starting algorithms to the boid
spawnedBoid->setup(&config, _algos);
}
}
Pretty similar to before, but now we are creating a vector of BoidAlgo instances for each Boid. This might seem wasteful, but remember, each instance only contains a float and a function pointer.
Inside the Boid class, we now handle all the behaviours with just a few lines of code :
for (auto& _algo : algos)
{
newVelocity += _algo.calc_func((*this), (*config), _algo.weight);
}
Just loop through the algos we have and apply the result to our new velocity.
Although the boids behave exactly as they did previously, we now have a much more robust codebase to start adding advanced behaviours. We can change the algo weightings on each boid individually, and even add and remove algorithms on the fly....perhaps we will be adding an algorithm for terrain avoidance or predator evasion? Now it's as easy as writing the static function in BoidAlgo and adding it to the Boid's list of algos. Sorted :)
As before, all the code is available here on github (note I've moved to a new repo with this refactoring).
New Articles Coming
- Details
- Category: Site
Had a busy few weeks, but I have been beavering away at the UnrealEngine code. I have a fairly major refactoring underway to make the code more extensible and re-usable. Should be up soon....on hold at the moment as I'm just preparing for a big desktop upgrade which is going to make UE work much slicker and enable me to stream without my CPU having a heart attack. Back soon!
{fcomment}
First UnrealEngine Articles Up!
- Details
- Category: Site
After finding a somewhat embarrassing bit of broken back-end configuration that was messing up my menus (now fixed), I've also got my first UnrealEngine articles uploaded! I could do with streamlining them a bit which I'll probably fit in some time this week, but here they are!
I have a short series of articles describing a basic implemantion of the Boids flocking simulation in UE4.
Part 1 covers the principles of the boids algorithms, and the basic setup of the Boid class.
Part 2 covers the header of the BoidManager class and the UPROPERTY values availablet to tweak in the editor
Part 3 covers the meat and gravy, the implementation of the boids algorithms in the BoidManager cpp.
Once I get a decent frame capture application sorted out, I'll get some video of the feature up, it looks pretty cool for such a simple set of algorithms :)
Next up I plan on implementing collision avoidance so the flock will flow around obstacles.
{fcomment}
Page 19 of 25