第一种,自行实现
参考了一下一个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移动的方法添加方向的计算,以便于在最后的时间内,能够自然的转换到指定方向。
总而言之。需要提升的地方有很多,这里花了两天时间,学习到最后将效果展示出来。