NeilAgg
(NeilAgg)
May 25, 2023, 5:05pm
1
I am trying to write a class to get a list of the sub-directories in a content folder.
I declared this header:
#pragma once
#include "CoreMinimal.h"
#include "Settings/StructViewerSettings.h"
#include "PuzzleCategories.generated.h"
UCLASS()
class SLITHERLINK3D_API UPuzzleCategories : public UStructViewerSettings
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Puzzles")
TArray<FString> getPuzzleCategories();
private:
TArray<FString> puzzleCategories;
};
Here is my code:
#include "PuzzleCategories.h"
struct PuzzlesCategoryVisitor : public IPlatformFile::FDirectoryVisitor {
bool Visit(const TCHAR* file, bool isDirectory) override {
if (isDirectory) {
UE_LOG(LogInit, Warning, TEXT("Found category: %s"), file);
puzzleCategories.Add(file);
}
return true;
}
};
TArray<FString> UPuzzleCategories::getPuzzleCategories()
{
if (puzzleCategories.IsEmpty() ) {
PuzzlesCategoryVisitor categoryVisitor;
FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(
*FPaths::ProjectContentDir().Append(TEXT("Puzzles")),
categoryVisitor);
}
return puzzleCategories;
}
Visual Studio is saying the puzzleCategories is an undeclared identifier in the Visit method.
I must be misunderstand the variable scope. How do I access it?
NeilAgg
(NeilAgg)
May 25, 2023, 5:41pm
2
I commented out the call to add the items to the puzzleCategories array so I can try adding my function to a blueprint.
I added a call to get the puzzle categories from my widget pre-construct event:
I am getting this warning:
Get Puzzle Categories was pruned because its Exec pin is not connected.
That is strange because there is a connection from the pre-construct event. What did I miss?
3dRaven
(3dRaven)
May 25, 2023, 5:54pm
3
Did you add the UnrealEd module to your build file?
UStructViewerSettings needs it to function.
Check the Module section in the documentation.
NeilAgg
(NeilAgg)
May 25, 2023, 6:02pm
4
I will try adding that.
Is there a better base class to use instead of UStructViewerSettings?
I did not know which one to pick since this code does not seem to fall into any of the ones I could find.
3dRaven
(3dRaven)
May 25, 2023, 6:05pm
5
Why do you need to access the Struct Viewer loading and saving?
If you just want to save & load data then a save file would be better.
UnrealEd will probably cause problems when packing the project as it’s editor only.
NeilAgg
(NeilAgg)
May 25, 2023, 6:09pm
6
I don’t. I am probably using the wrong base class.
3dRaven
(3dRaven)
May 25, 2023, 6:11pm
7
If you base your class off of USavegame then you can just use Unreal’s built in save & load mechanics.
NeilAgg
(NeilAgg)
May 25, 2023, 6:13pm
8
I am not trying to use save and load mechanics. I am just trying to declare a class to get a TArray from a list of directories in the Content folder.
NeilAgg
(NeilAgg)
May 25, 2023, 6:16pm
9
I changed my header file to extend from UObject:
#pragma once
#include "CoreMinimal.h"
#include "PuzzleCategories.generated.h"
UCLASS()
class SLITHERLINK3D_API UPuzzleCategories : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Puzzles")
TArray<FString> getPuzzleCategories();
private:
TArray<FString> puzzleCategories;
};
I still can’t access the puzzleCategories array from my cpp code.
NeilAgg
(NeilAgg)
May 25, 2023, 6:25pm
10
When I go to my blueprint, I am now getting a different error:
This blueprint (self) is not a PuzzleCategories, therefore Target must have a connection.
I am not sure what to connect it to. Here is my screen:
3dRaven
(3dRaven)
May 25, 2023, 6:27pm
11
Sorry, missed the first lines of you text and focused on the code.
Here is code from my old image lib. You should be able to filter out the directories only. For now it get specific files in the set directory, can work recursively.
Just push directory names instead of the file names and you should be set.
#include "ImageUtils.h"
#include "HAL/FileManagerGeneric.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/CanvasRenderTarget2D.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/LocalTimestampDirectoryVisitor.h"
#include "Kismet/KismetRenderingLibrary.h"
#include "Engine/Canvas.h"
#include "CanvasTypes.h"
#include "TextureResource.h"
#include "RendererInterface.h"
#include "RHICommandList.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Misc/Char.h"
#include "FileHelpers.h"
FString UImageFunctionLibrary::filterDirectory(FString &path)
{
if (path.Len() > 0) {
path.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
path.ReplaceInline(TEXT("//"), TEXT("/"), ESearchCase::CaseSensitive);
path.RemoveFromStart(TEXT("/"));
path.RemoveFromEnd(TEXT("/"));
FPlatformMisc::NormalizePath(path);
path.RemoveFromEnd(FPaths::GetExtension(path, true));
}
return path;
}
TArray<FString> UImageFunctionLibrary::GetAllFilesInDirectory(const FString directory, const bool fullPath, const FString onlyFilesStartingWith, const FString onlyFilesWithExtension,bool skipRecursionSelf = true)
{
FString directoryCut = directory;
filterDirectory(directoryCut);
// Get all files in directory
TArray<FString> directoriesToSkip;
TArray<FString> directoriesToSkipRecurse;
if (skipRecursionSelf == true) {
directoriesToSkipRecurse.Add(directoryCut);
}
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
FLocalTimestampDirectoryVisitor Visitor(PlatformFile, directoriesToSkip, directoriesToSkipRecurse, false);
PlatformFile.IterateDirectory(*directoryCut, Visitor);
TArray<FString> files;
for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt)
{
const FString filePath = TimestampIt.Key();
const FString fileName = FPaths::GetCleanFilename(filePath);
bool shouldAddFile = true;
// Check if filename starts with required characters
if (!onlyFilesStartingWith.IsEmpty())
{
const FString left = fileName.Left(onlyFilesStartingWith.Len());
if (!(fileName.Left(onlyFilesStartingWith.Len()).Equals(onlyFilesStartingWith)))
shouldAddFile = false;
}
// Check if file extension is required characters
if (!onlyFilesWithExtension.IsEmpty())
{
if (!(FPaths::GetExtension(fileName, false).Equals(onlyFilesWithExtension, ESearchCase::IgnoreCase)))
shouldAddFile = false;
}
// Add full path to results
if (shouldAddFile)
{
files.Add(fullPath ? filePath : fileName);
}
}
return files;
}
NeilAgg
(NeilAgg)
May 25, 2023, 6:55pm
12
I changed my code to this:
// Copyright (c) 2023 Neil Aggarwal, all rights reserved
#include "PuzzleCategories.h"
/* Function to get the puzzle categories */
TArray<FString> UPuzzleCategories::getPuzzleCategories()
{
if (puzzleCategories.IsEmpty() ) {
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
TArray<FString> directoriesToSkip;
TArray<FString> directoriesToSkipRecurse;
FLocalTimestampDirectoryVisitor Visitor(PlatformFile, directoriesToSkip, directoriesToSkipRecurse, true, false);
PlatformFile.IterateDirectory(*FPaths::ProjectContentDir().Append(TEXT("Puzzles")), Visitor);
for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt) {
const FString filePath = TimestampIt.Key();
const FString fileName = FPaths::GetCleanFilename(filePath);
puzzleCategories.Add(fileName);
}
}
return puzzleCategories;
}
And it compiles.
But, I am still getting the error in my blueprint that the get puzzle categories needs a target.
3dRaven
(3dRaven)
May 25, 2023, 7:07pm
13
It works in my project.
NeilAgg:
needs a target.
Your UPuzzleCategories is of type actor or scene component hence the U prefix. You need to pass in the puzzle component that is added to your actor component.
Or just make it an actor component then there will be no target, it’ll take self as a basis.
NeilAgg
(NeilAgg)
May 26, 2023, 3:56am
14
How did you add the Puzzle Categories target showing in the graph?
3dRaven
(3dRaven)
May 26, 2023, 5:57am
15
I made UPuzzleCategories derived from UActorComponent and added it through the add component panel of the actor blueprint. (You can see it in the first screenshot just below defaultSceneRoot of the actor)
Seems like you don’t need a world/object/actor ref so just make your function static so that it can be called anywhere without Target.
The proper base class would be UBlueprintFunctionLibrary, but UObject should work just fine too.
NeilAgg
(NeilAgg)
May 26, 2023, 1:27pm
17
I changed my header to this:
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Misc/LocalTimestampDirectoryVisitor.h"
#include "PuzzleCategories.generated.h"
UCLASS()
class SLITHERLINK3D_API UPuzzleCategories : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "Puzzles")
static TArray<FString> getPuzzleCategories();
private:
static TArray<FString> puzzleCategories;
};
and my cpp file to this:
#include "PuzzleCategories.h"
TArray<FString> UPuzzleCategories::getPuzzleCategories()
{
if (puzzleCategories.IsEmpty() ) {
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
TArray<FString> directoriesToSkip;
TArray<FString> directoriesToSkipRecurse;
FLocalTimestampDirectoryVisitor Visitor(PlatformFile, directoriesToSkip, directoriesToSkipRecurse, true, false);
PlatformFile.IterateDirectory(*FPaths::ProjectContentDir().Append(TEXT("Puzzles")), Visitor);
for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt) {
const FString filePath = TimestampIt.Key();
const FString fileName = FPaths::GetCleanFilename(filePath);
puzzleCategories.Add(fileName);
}
}
return puzzleCategories;
}
Now, when I try to do Build Solution in Visual Studio, I get this error:
2> Creating library C:\Dev\Slitherlink3D\Intermediate\Build\Win64\x64\UnrealEditor\Development\Slitherlink3D\UnrealEditor-Slitherlink3D.suppressed.lib and object C:\Dev\Slitherlink3D\Intermediate\Build\Win64\x64\UnrealEditor\Development\Slitherlink3D\UnrealEditor-Slitherlink3D.suppressed.exp
2>PuzzleCategories.cpp.obj : error LNK2001: unresolved external symbol "private: static class TArray<class FString,class TSizedDefaultAllocator<32> > UPuzzleCategories::puzzleCategories" (?puzzleCategories@UPuzzleCategories@@0V?$TArray@VFString@@V?$TSizedDefaultAllocator@$0CA@@@@@A)
2>C:\Dev\Slitherlink3D\Binaries\Win64\UnrealEditor-Slitherlink3D.dll : fatal error LNK1120: 1 unresolved externals
I am not sure what that means. Any help?
3dRaven
(3dRaven)
May 26, 2023, 3:31pm
18
You need to init the static TArray in a blueprint library.
I’ll look through my old scripts for an example. I need to refresh my memory
ok here is the proper init for the static array in the cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PuzzleCategories.h"
TArray<FString> UPuzzleCategories::puzzleCategories = TArray<FString>();
TArray<FString> UPuzzleCategories::getPuzzleCategories()
{
if (puzzleCategories.IsEmpty()) {
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
TArray<FString> directoriesToSkip;
TArray<FString> directoriesToSkipRecurse;
FLocalTimestampDirectoryVisitor Visitor(PlatformFile, directoriesToSkip, directoriesToSkipRecurse, true, false);
PlatformFile.IterateDirectory(*FPaths::ProjectContentDir().Append(TEXT("Puzzles")), Visitor);
for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt) {
const FString filePath = TimestampIt.Key();
const FString fileName = FPaths::GetCleanFilename(filePath);
puzzleCategories.Add(fileName);
}
}
return puzzleCategories;
}
The init is not in any function because there is no CDO, begin play etc because the class is based on static functions just
TArray<FString> UPuzzleCategories::puzzleCategories = TArray<FString>();
system
(system)
Closed
June 26, 2023, 1:31am
20
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.