Реализуйте пул объектов для снарядов, чтобы избежать затрат на спавн при каждом выстреле
Реализуйте пул снарядов, чтобы выстрел переиспользовал заранее созданных акторов вместо вызова SpawnActor на каждый выстрел. Prewarm создаёт набор один раз; Acquire выдаёт свободный деактивированный снаряд и заново его активирует.
Ограничения:
SpawnActorвызывается только вPrewarm; путь стрельбы не должен спавнить илиDestroy.- Простаивающие акторы пула деактивированы (без тика, без коллизии, скрыты).
- Сбрасывайте состояние снаряда при переиспользовании; верните
nullptr, если пул пуст.
void UProjectilePool::Prewarm(UWorld* World, TSubclassOf<AProjectile> Class, int32 Count)
{
// ваш код здесь
}
AProjectile* UProjectilePool::Acquire(const FVector& Loc, const FVector& Dir)
{
// ваш код здесь
}
Допишите реализацию.
Заранее создайте фиксированный набор акторов-снарядов один раз, держите их деактивированными и выдавайте свободный при выстреле вместо вызова SpawnActor. Возвращайте отработавшие снаряды в пул деактивацией, а не вызовом Destroy.
- ✗Вызывать
SpawnActor/Destroyвнутри пула, сводя на нет весь его смысл - ✗Оставлять
Tickи коллизию включёнными на простаивающих пулированных акторах - ✗Не сбрасывать состояние снаряда (скорость, урон, владельца) при переиспользовании
- →Как обработать опустошение пула при непрерывной стрельбе?
- →Почему возвращённый снаряд должен сбросить скорость и состояние коллизии?
Идея
Спавн и уничтожение акторов стоят дорого: аллокация, регистрация компонентов и нагрузка на сборщик мусора. Пул создаёт снаряды один раз и переиспользует их.
UCLASS()
class AProjectile : public AActor
{
GENERATED_BODY()
public:
// Включить снаряд: вернуть в игру в заданной точке
void Activate(const FVector& Location, const FVector& Direction);
// Выключить снаряд: вернуть в пул
void Deactivate();
};
void AProjectile::Activate(const FVector& Location, const FVector& Direction)
{
SetActorLocationAndRotation(Location, Direction.Rotation());
SetActorHiddenInGame(false);
SetActorEnableCollision(true);
SetActorTickEnabled(true);
if (UProjectileMovementComponent* Move = FindComponentByClass<UProjectileMovementComponent>())
{
Move->Velocity = Direction * Speed; // сброс состояния при переиспользовании
Move->Activate();
}
}
void AProjectile::Deactivate()
{
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
SetActorTickEnabled(false); // простаивающий снаряд не тикает
}
Пул:
UCLASS()
class UProjectilePool : public UObject
{
GENERATED_BODY()
UPROPERTY() TArray<AProjectile*> Pool;
public:
void Prewarm(UWorld* World, TSubclassOf<AProjectile> Class, int32 Count)
{
for (int32 i = 0; i < Count; ++i)
{
AProjectile* P = World->SpawnActor<AProjectile>(Class); // спавн один раз
P->Deactivate();
Pool.Add(P);
}
}
AProjectile* Acquire(const FVector& Loc, const FVector& Dir)
{
for (AProjectile* P : Pool)
{
if (P->IsHidden()) // свободный экземпляр
{
P->Activate(Loc, Dir);
return P;
}
}
return nullptr; // пул пуст — растить или пропустить выстрел
}
};
При попадании или таймауте снаряд вызывает Deactivate() вместо Destroy() — SpawnActor в горячем пути стрельбы больше не вызывается.