Thanks a lot! Your code work quite well after I add it to my system, but only one problem is still bothering me:
in my system, I got a csv file including crowd’s trajectoy, at each timeslot, I wany to replay the characters’ [location, rotation] and their animation(walk, a animation sequence file in my content dir).
I’ve done it by spawn the characters in the same timeslot(like timeslot i), and use function Character->GetMesh()->PlayAnimation(Anim, true); to play the animation.
But after I add your code, all work well except the characters in the pics saved do not play animation.
Could you help me with it?Thanks and I know I am being stupid, sorry for taking your time.
after adding code:(Pic takes when animation is not playing)
what I want(pic that I take before):(Pic takes when animation is playing)
My code:
Header file:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CrowdSpawnSystem.h"
#include "MyCameraActor.h"
#include "Engine/TextureRenderTarget2D.h"
#include "CaptureActor.h"
#include "ReplayCrowdSystem.generated.h"
UCLASS()
class POPULATIONSYSTEMFULLPACK_API AReplayCrowdSystem : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AReplayCrowdSystem();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//Crowd Trajectory Array
TArray<FCharacterTrajectoryInfo> CrowdTrajectoryArray{};
//Default FileName to read, Default Dir is FPaths::ProjectContentDir()
FString DefaultFileName = FPaths::ProjectContentDir() + "Trajectory.csv";
//Read a Trajectory File to Memory, returns the max_timeslot of the file
// !!!!!make SURE that the CSV file's structure is Right:
// "%d,%d,%s,%f,%s\n"
// ped.timeslot, ped.ID, *ped.Location.ToString(), ped.Rotation.Yaw, *ped.BP_Name!!!!!
int ReadTrajectoryFile(const FString& FilePath);
//maximum of timeslots
int max_timeslot = 0;
//place the characters of an exact timeslot into the scene, return next startIndex to search(for continuously replay)
//!!!!!Ensure that the trajectory file is ordered by timeslot
//timeslot: the timeslot to replay;
//startIndex: from which Index Of The Array(Row Of CSV File) to search, set to 0 as default
int ReplayTimeslot(int timeslot, int startIndex = 0);
//Blueprint to use
TSubclassOf<AActor> characterBPToSpawn;
//Animation
UAnimSequence* Anim;
//Character Array in a timeslot
TArray<ACharacter*> CharactersInTimeSlot;
//Replay All Timeslots from 1 to max_timeslot
FTimerHandle Timer;
void ReplayAllTimeslots();
int current_timeslot=1;
bool CaptureMode = false;
TArray<FVector> CameraLocations;
TArray<AMyCameraActor*> Cameras; //Cameras to capture crowd
//for CameraToUse to take Screenshot
void TakeScreenshot(int CameraIndex);
//
//ACaptureActor* CaptureActor1;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
class USceneCaptureComponent2D* CamCapture;
UPROPERTY()
USceneComponent* Root;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UTextureRenderTarget2D* RT;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FVector2D RTSize = FVector2D(4096, 2304);
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TEnumAsByte<ETextureRenderTargetFormat> TargetFormat;
UFUNCTION()
void CompleteImage();
private:
class UCameraComponent* Cam;
void InitCapture();
int32 NumberOfImagesSaved;
};
CPP:
#include "ReplayCrowdSystem.h"
//#include "Components/SceneCaptureComponent2D.h"
//#include "Camera/CameraActor.h"
//#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "HighResScreenshot.h"
#include "MyCameraActor.h"
#include "MyBlueprintFunctionLibrary.h"
#include "ImageUtils.h"
#include "Misc/FileHelper.h"
#include "Engine/LocalPlayer.h"
#include "Engine/GameViewportClient.h"
#include "Camera/CameraComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Kismet/KismetRenderingLibrary.h"
#include "Camera/CameraActor.h"
#include "Camera/CameraComponent.h"
#include "Runtime/ImageWriteQueue/Public/ImageWriteBlueprintLibrary.h"
// Sets default values
AReplayCrowdSystem::AReplayCrowdSystem()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//TODO; Load BPs of characters
FString CharacterBlueprintPath = TEXT("/Script/Engine.Blueprint'/Game/CrowdSpawnSystem/Character_BPs/ThirdPersonCharacter_5.ThirdPersonCharacter_5_C'");
UClass* CharacterBlueprintClass = LoadClass<ACharacter>(nullptr, *CharacterBlueprintPath);
if (CharacterBlueprintClass == nullptr) {
UE_LOG(LogTemp, Warning, TEXT("Failed to load asset: %s"), *CharacterBlueprintPath);
}
else {
UE_LOG(LogTemp, Warning, TEXT("Succeeded to load asset: %s"), *CharacterBlueprintPath);
characterBPToSpawn = CharacterBlueprintClass;
}
//Load animation
FString AnimSeqPath = TEXT("/Script/Engine.AnimSequence'/Game/CrowdSpawnSystem/Animations/ThirdPersonWalk_1.ThirdPersonWalk_1'");
static ConstructorHelpers::FObjectFinder<UAnimSequence> AnimSeqFinder(TEXT("/Script/Engine.AnimSequence'/Game/CrowdSpawnSystem/Animations/ThirdPersonWalk_1.ThirdPersonWalk_1'"));
Anim = AnimSeqFinder.Object;
//initialize capture
Root = CreateDefaultSubobject<USceneComponent>("Root");
SetRootComponent(Root);
Cam = CreateDefaultSubobject<UCameraComponent>("Cam");
Cam->SetupAttachment(Root);
CamCapture = CreateDefaultSubobject<USceneCaptureComponent2D>("Cam Capture");
CamCapture->SetupAttachment(Root);
CamCapture->bCaptureOnMovement = false;
CamCapture->bCaptureEveryFrame = false;
}
// Called when the game starts or when spawned
void AReplayCrowdSystem::BeginPlay()
{
Super::BeginPlay();
//read trajectory file
max_timeslot = ReadTrajectoryFile(DefaultFileName);
UE_LOG(LogTemp, Warning, TEXT("max_timeslot:%d"), max_timeslot);
//init camera class
//FString CameraBlueprintPath = TEXT("/Script/Engine.Blueprint'/Game/CrowdSpawnSystem/4KCamera1.4KCamera1_C'");
FString CameraBlueprintPath = TEXT("/Script/Engine.Blueprint'/Game/CrowdSpawnSystem/Cameras/CameraCapture.CameraCapture_C'");
UClass* CameraBlueprintClass = LoadClass<ACameraActor>(nullptr, *CameraBlueprintPath);
//init camera locations
CameraLocations = { {2590.0,4190.0,5000.0} , { 1500.0,1500.0,5000.0 }, { 1500.0,4000.0,5000.0 }, { 1500.0,6500.0,5000.0 }, { 3500.0,1500.0,5000.0 },
{3500.0,4000.0,5000.0},{3500.0,6500.0,5000.0} };
//spawn cameras
FActorSpawnParameters spawnParams1;
spawnParams1.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
for (const FVector& location : CameraLocations) {
AMyCameraActor* CameraToUse1 = GetWorld()->SpawnActor<AMyCameraActor>(CameraBlueprintClass, location, { -90.0,0.0,0.0 }, spawnParams1);
Cameras.Add(CameraToUse1);
}
//TakeScreenshot(CameraToUse1);
//CaptureActor1 = GetWorld()->SpawnActor<ACaptureActor>(ACaptureActor::StaticClass(), GetActorTransform(), spawnParams1);
//replay trajectory and take pics
GetWorld()->GetTimerManager().SetTimer(Timer, this, &AReplayCrowdSystem::ReplayAllTimeslots, 1.0, true, 0.1f);
//for (int i = 1; i <= max_timeslot; i++) {
// /*for (int j = 0; j < CharactersInTimeSlot.Num(); j++) {
// CharactersInTimeSlot[j]->Destroy();
// }*/
// CharactersInTimeSlot.Empty();
// int next = ReplayTimeslot(i);
// UE_LOG(LogTemp, Warning, TEXT("next_timeslot:%d"), next);
//}
//InitCapture();
}
// Called every frame
void AReplayCrowdSystem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AReplayCrowdSystem::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
int AReplayCrowdSystem::ReadTrajectoryFile(const FString& FilePath)
{
int max_temp = 0;
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FilePath)) {
TArray<FString> CSVRows;
FFileHelper::LoadFileToStringArray(CSVRows, *FilePath);
for (const FString& CSVRow : CSVRows)
{
TArray<FString> CSVColumns;
CSVRow.ParseIntoArray(CSVColumns, TEXT(","), true);
// add trajectory to CrowdTrajectoryArray
if (CSVColumns.Num() == 6)//
{
FCharacterTrajectoryInfo NewStruct;
// Parse and assign values from the CSV columns to FCharacterTrajectoryInfo struct
NewStruct.timeslot = FCString::Atoi(*CSVColumns[0]);
NewStruct.ID = FCString::Atoi(*CSVColumns[1]);
NewStruct.Location.InitFromString(*CSVColumns[2]);
NewStruct.Rotation.InitFromString("P=0.0 Y=" + CSVColumns[3] + " R=0.0");
NewStruct.VelocityValue = FCString::Atof(*CSVColumns[4]);
NewStruct.BP_Name = *CSVColumns[5];
// Add the struct to the array
CrowdTrajectoryArray.Add(NewStruct);
}
//find maximum value of timeslot
//ensure that timeslot is at column[0]!!!!!
int timeslot_temp = FCString::Atoi(*CSVColumns[0]);
max_temp = FMath::Max(max_temp, timeslot_temp);
}
}
else {
UE_LOG(LogTemp, Warning, TEXT("File doesn't exist!!!"));
}
return max_temp;
}
int AReplayCrowdSystem::ReplayTimeslot(int timeslot, int startIndex)
{
int NextTimeslot_StartIndex = startIndex;
FActorSpawnParameters spawnParams;
spawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
for (int i = startIndex; i < CrowdTrajectoryArray.Num(); i++) {
if (CrowdTrajectoryArray[i].timeslot == timeslot) {
//replay
//characterBPToSpawn = characterBPToSpawn;//TODO
//UE_LOG(LogTemp, Warning, TEXT("Rotation: %s"), *CrowdTrajectoryArray[i].Rotation.ToString());
ACharacter* Character = GetWorld()->SpawnActor<ACharacter>(characterBPToSpawn, CrowdTrajectoryArray[i].Location, CrowdTrajectoryArray[i].Rotation, spawnParams);
if (Character) {
CharactersInTimeSlot.Add(Character);
if (Character->GetMesh()) {
Character->GetMesh()->PlayAnimation(Anim, true);
//Character->GetMesh()->SetAnimInstanceClass(nullptr);
}
}
NextTimeslot_StartIndex = i + 1;
}
//TODO if(CrowdTrajectoryArray[i].timeslot != timeslot)
}
return NextTimeslot_StartIndex;
}
void AReplayCrowdSystem::ReplayAllTimeslots()
{
for (int j = 0; j < CharactersInTimeSlot.Num(); j++) {
CharactersInTimeSlot[j]->Destroy();
}
CharactersInTimeSlot.Empty();
int next = ReplayTimeslot(current_timeslot);
//take pics
NumberOfImagesSaved = 0;
for (int i = 0; i < Cameras.Num();i++) {
if(Cameras[i])
TakeScreenshot(i);
}
/*for (int i = 0; i < CameraLocations.Num(); i++) {
CaptureActor1->TakeScreenshot(i, CameraLocations[i], current_timeslot);
}*/
/*if (Cameras[0])
TakeScreenshot(Cameras[0]);*/
UE_LOG(LogTemp, Warning, TEXT("next_timeslot:%d"), next);
current_timeslot++;
if (current_timeslot > max_timeslot) {
GetWorld()->GetTimerManager().ClearTimer(Timer);
}
}
void AReplayCrowdSystem::TakeScreenshot(int CameraIndex)
{
//if (Cameras[CameraIndex])
//{
// FString FilePath = FPaths::ProjectContentDir() + TEXT("/Pic")+ FString::Printf(TEXT("%d"), CameraIndex)+ TEXT("/CapturedImage")+
// FString::Printf(TEXT("%d"), current_timeslot) + TEXT(".png");
// Cameras[CameraIndex]->SavePath = FilePath;
// UE_LOG(LogTemp, Warning, TEXT("camera location:%s"), *Cameras[CameraIndex]->GetActorLocation().ToString());
// Cameras[CameraIndex]->SavePic();
// if(CameraIndex>0)
// UE_LOG(LogTemp, Warning, TEXT("!!!IS EQUAL:%s"), (Cameras[CameraIndex]->rt1== Cameras[CameraIndex-1]->rt1)? TEXT("true") : TEXT("false"));
// //APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
// //AActor* PreviousViewTarget = PlayerController->GetViewTarget();
// //// 切换视角到摄像机
// //PlayerController->SetViewTarget(CameraToUse);
// //// 设置截图分辨率
// ////PlayerController->GetLocalPlayer()->ViewportClient->Viewport->SetViewportSize(FIntPoint(Width, Height));
// //// 执行高分辨率截图
// //PlayerController->GetLocalPlayer()->ViewportClient->Viewport->TakeHighResScreenShot();
// //PlayerController->SetViewTarget(PreviousViewTarget);
//}
if (Cameras[CameraIndex])
{
//InitCapture();
//FString FilePath = FPaths::ProjectContentDir() + RelativeDirectory;
//FString FileName = Prefix + FString::FromInt(CameraIndex) + ".png";
FString FilePath = FPaths::ProjectContentDir() + TEXT("/Pic/Pic") + FString::Printf(TEXT("%d"), CameraIndex) + TEXT("/CapturedImage") +
FString::Printf(TEXT("%d"), current_timeslot) + TEXT(".png");
//UE_LOG(LogTemp, Warning, TEXT("camera location:%s"), *Cameras[CameraIndex]->GetActorLocation().ToString());
if (CamCapture)
{
UE_LOG(LogTemp, Warning, TEXT("camera location:%s"), *Cameras[CameraIndex]->GetActorLocation().ToString());
SetActorLocation(Cameras[CameraIndex]->GetActorLocation());
SetActorRotation(Cameras[CameraIndex]->GetActorRotation());
FMinimalViewInfo viewInfo;
Cam->GetCameraView(0, viewInfo);
CamCapture->SetCameraView(viewInfo);
RT->RenderTargetFormat = TargetFormat;
CamCapture->bAlwaysPersistRenderingState = true;
CamCapture->CaptureSource = SCS_FinalColorLDR;
CamCapture->TextureTarget = RT;
CamCapture->SetCameraView(viewInfo);
CamCapture->CaptureScene();
RT->UpdateResourceImmediate(false);
//UKismetRenderingLibrary::ExportRenderTarget(this, RT, FilePath, FileName);
FImageWriteOptions Options;
Options.bAsync = true;//false;
Options.bOverwriteFile = true;
Options.Format = EDesiredImageFormat::PNG;
Options.OnComplete.BindUFunction(this, "CompleteImage");
Options.CompressionQuality = 0;
//UImageWriteBlueprintLibrary::ExportToDisk(RT, FilePath + "/" + FileName, Options);
UImageWriteBlueprintLibrary::ExportToDisk(RT, FilePath, Options);
RT->UpdateResourceImmediate(true);
}
}
}
void AReplayCrowdSystem::CompleteImage()
{
NumberOfImagesSaved++;
/*if (Debug == true) {
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Cyan, "Saved [" + FString::FromInt(NumberOfImagesSaved) + "]");
}
if (NumberOfImagesSaved == Cameras.Num()) {
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Green, "All Saved");
}
}
}*/
}
void AReplayCrowdSystem::InitCapture() {
if (CamCapture == nullptr) {
CamCapture = NewObject<USceneCaptureComponent2D>(this);
CamCapture->RegisterComponent();
CamCapture->bCaptureOnMovement = false;
CamCapture->bCaptureEveryFrame = false;
CamCapture->DetailMode = EDetailMode::DM_Epic;
}
if (RT == nullptr) {
RT = UKismetRenderingLibrary::CreateRenderTarget2D(GetWorld(), RTSize.X, RTSize.Y);
}
}
ReplayCrowdSystem.zip (5.1 KB)
Trajectory.csv (1.8 MB)