探究如何实现AI集群
第一种,自行实现
参考了一下一个unity项目的阵列实现。
https://github.com/Matthew-J-Spencer/Formations
第二种用商城提供的现成的工具
接下来是自己造轮子的时间了。
具体的并没有按这来写,实际的核心代码如下
TArray<FVector> FormationFactory::GetPoints(FVector orignPostion)
{
switch (FormationType)
{
case EFormationType::Circle:return GetCirclePoint(orignPostion);
break;
case EFormationType::Box:return GetBoxPoint(orignPostion);
break;
default:return TArray<FVector>();
break;
}
}
TArray<FVector> FormationFactory::GetBoxPoint(FVector orignPostion)
{
TArray<FVector> pointArray(new FVector(),Box.Count);
FVector fristVector;
int32 width;
int32 high;
//get the first point , then use the point can get other points by calculation
width = Box.width;
high = FMath::CeilToInt(Box.Count/width) ;
fristVector = FVector(orignPostion.X-(width/2)*Distance,orignPostion.Y+(high/2)*Distance,orignPostion.Z);
for (int i=0;i<pointArray.Num();i++)
{
pointArray[i].X = fristVector.X + ((i)%width)*Distance;
pointArray[i].Y = fristVector.Y - ((i)/width)*Distance;
pointArray[i].Z = fristVector.Z;
}
return pointArray;
}
TArray<FVector> FormationFactory::GetCirclePoint(FVector orignPostion)
{
TArray<FVector> pointArray(new FVector(),Circle.Count);
FVector fristVector = orignPostion;
int32 trunsNum = 0;
int32 currntIndex = 0;
int32 currntMaxIndex = 1;
for (int i=0;i<pointArray.Num();i++)
{
/*//first
pointArray[i] = fristVector;
//second
pointArray[i] = fristVector + FVector(0,100,0);
//third
pointArray[i] = fristVector + FVector(0,100,0).RotateAngleAxis(60*PI/180,FVector::UpVector);*/
//normal
currntIndex++;
if(currntIndex>currntMaxIndex)
{
currntIndex = 1;
currntMaxIndex += Circle.Raise;
trunsNum++;
}
pointArray[i] = fristVector + FVector(0,1,0).RotateAngleAxis((float)(currntIndex-1)/currntMaxIndex*360,FVector::UpVector)*Distance*trunsNum;
// UE_LOG(LogTemp,Log,TEXT("当前圈数:%d ,当前圈数中下标:%d,当前圈数最大下标:%d"),trunsNum,currntIndex,currntMaxIndex);
}
return pointArray;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FFormationFactoryTest,"Test.Formation Test",EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FFormationFactoryTest::RunTest(const FString& Parameters)
{
FormationFactory formationFactory;
formationFactory.Box = FBoxType(10,5);
formationFactory.Circle = FCircleType(10,3);
formationFactory.Distance = 100;
formationFactory.FormationType = EFormationType::Box;
void AFormationCreate_Base::CreateBoxFormation(FBoxType Box)
{
Factory.Box = Box;
Factory.Distance = Distance;
Factory.FormationType = EFormationType::Box;
CreateAIs(Box.Count,Factory.GetPoints(GetTargetLocation()));
}
void AFormationCreate_Base::CreateCircleFormation(FCircleType Circle)
{
Factory.Circle = Circle;
Factory.Distance = Distance;
Factory.FormationType = EFormationType::Circle;
CreateAIs(Circle.Count,Factory.GetPoints(GetActorLocation()));
}
void AFormationCreate_Base::updataBox(FBoxType Box)
{
if(AIs.Num() == 0)
{
CreateBoxFormation(Box);
return;;
}
updataAICount(Box.Count);
Factory.Box = Box;
Factory.Distance = Distance;
Factory.FormationType = EFormationType::Box;
updataAis(Factory.GetPoints(GetActorLocation()));
}
void AFormationCreate_Base::updataCircle(FCircleType Circle)
{
if(AIs.Num() == 0)
{
CreateCircleFormation(Circle);
return;;
}
updataAICount(Circle.Count);
Factory.Circle = Circle;
Factory.Distance = Distance;
Factory.FormationType = EFormationType::Circle;
updataAis(Factory.GetPoints(GetActorLocation()));
}
void AFormationCreate_Base::updataAICount(uint32 Count)
{
int32 tempCount = Count;
if((this->AICount) == tempCount)
return;
if((this->AICount) > tempCount)
{
//make the other character be unactive
for(int i = tempCount-1;i<AIs.Num();i++)
{
AIs[i]->SetHidden(true);
AIs[i]->SetActorHiddenInGame(true);
}
}
if((this->AICount) <tempCount)
{
//active some unactive character,and create some character if this characters is less
int newAIs = tempCount-AIs.Num();
for(int i = 0;i<newAIs;i++)
{
CreateAI(FVector::ZeroVector);
}
}
this->AICount = tempCount;
}
void AFormationCreate_Base::CreateAI(FVector item)
{
DrawDebugBox(GetWorld(),item,FVector(5),FColor::Red,false,10,0,0);
item += FVector(0,0,100);
FRotator rotator = GetActorRotation();
ACharacter* AICharacter = (ACharacter*)(GetWorld()->SpawnActor(AI,&item,&rotator)) ;
if (IsValid(AICharacter))
{
AIs.Add(AICharacter);
AICharacter->SetActorLocation(item);
// AICharacter->PossessedBy(GetWorld()->SpawnActor<AAIController>(AI.GetDefaultObject()->AIControllerClass));
UE_LOG(LogTemp,Log,TEXT("已生成"));
}
}
void AFormationCreate_Base::CreateAIs(uint32 Count, const TArray<FVector>& list)
{
for(FVector item:list)
{
CreateAI(item);
}
AICount = Count;
}
void AFormationCreate_Base::updataAis(const TArray<FVector>& list)
{
UE_LOG(LogTemp,Log,TEXT("更新坐标"));
for(int i = 0;i<AICount;i++)
{
//if it postion is defaluet,set new postion,if not then move to new postion
if(AIs[i]->GetActorLocation() == FVector::ZeroVector)
{
AIs[i]->SetActorLocation(list[i]);
}else
{
// AAIController* AIController = Cast<AAIController> (AIs[i]->GetController());
// AIController->MoveTo(list[i]);
AAIController* AICtl = AIs[i]->GetController<AAIController>();
if(IsValid(AICtl))
{
FVector point = FVector(list[i]);
AICtl->MoveToLocation(point,10,false);
}
}
}
}
UFUNCTION(BlueprintCallable,Category="Formation")
void CreateBoxFormation(FBoxType Box);
UFUNCTION(BlueprintCallable,Category="Formation")
void CreateCircleFormation(FCircleType Circle);
UFUNCTION(BlueprintCallable,Category="Formation")
void updataBox(FBoxType Box);
UFUNCTION(BlueprintCallable,Category="Formation")
void updataCircle(FCircleType Circle);
上面是暴露给蓝图的函数,因为具体的阵列效果需要在蓝图里去检查。因此做了基本的暴露
大致思路是先算出来具体的坐标,再再对应坐标上生成AI,当更新阵列时,通过AI的位移方法移动到指定点上。
不足的地方
阵列变化不够自然:解决方法通过一定计算,给每个点匹配最佳的位移坐标
位移之后AI方向不受控制:在AI移动的方法添加方向的计算,以便于在最后的时间内,能够自然的转换到指定方向。
总而言之。需要提升的地方有很多,这里花了两天时间,学习到最后将效果展示出来。