Unreal engine c++ write delegate OnScreenshotCaptured - c++

I am new to C++. I've wrote code in C# and PHP.Since I am using Unreal engine I am trying to learn C++. For my project I need to make a screenshot in-game and show it immediately so I want to get it as a texture.
I made a blueprint node which calls this function i've made:
void UMyBlueprintFunctionLibrary::TakeScreenshot()
{
FScreenshotRequest::RequestScreenshot(true);
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Tried to take screenshot");
}
When I hover my mouse above RequestScreenshot I see the following pop-up:
"Screenshot can be read from memory by subscribing to the viewsport OnScreenshopCaptured delegate"
So that is what I try to do but I have no idea how I looked up this:
https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/Engine/UGameViewportClient/OnScreenshotCaptured/
Can someone tell me how to implement this and how you see/know how to implement it?

I have an alternative, no delegate, but FRenderTarget::ReadPixel() to some buffer you allocated, by implementing your own UGameViewportClient (inherit it), and overriding Draw() function.
I'll show the essential codes, but not complete.
void UMyGameViewportClient::Draw(FViewport* Viewport, FCanvas* SceneCanvas)
{
Super::Draw(Viewport, SceneCanvas);
if (any_condition_you_need) {
CaptureFrame();
}
}
void UMyGameViewportClient::CaptureFrame()
{
if (!Viewport) {
return;
}
if (ViewportSize.X == 0 || ViewportSize.Y == 0) {
return;
}
ColorBuffer.Empty(); // Declare this in header as TArray<FColor>
if (!Viewport->ReadPixels(ColorBuffer, FReadSurfaceDataFlags(),
FIntRect(0, 0, ViewportSize.X, ViewportSize.Y)))
{
return;
}
SaveThumbnailImage();
}
void UMyGameViewportClient::SaveThumbnailImage()
{
IImageWrapperModule& wrappermodule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
auto wrapper_ptr = wrappermodule.CreateImageWrapper(EImageFormat::PNG);
for (int i = 0; i < ColorBuffer.Num(); i++)
{
auto ptr = &ColorBuffer[i];
auto r = ptr->R;
auto b = ptr->B;
ptr->R = b;
ptr->B = r;
ptr->A = 255;
} // not necessary, if you like bgra, just change the following function argument to ERGBFormat::BGRA
wrapper_ptr->SetRaw(&ColorBuffer[0], ColorBuffer.Num() * 4,
ViewportSize.X, ViewportSize.Y, ERGBFormat::RGBA, 8);
FFileHelper::SaveArrayToFile(wrapper_ptr->GetCompressed(), *ThumbnailFile);
}

Related

Spawning Actor from array of classes UE5

I'm attempting to use SpawnActor() to spawn random actors from an array of classes. I've spent several years using UE4/UE5 blueprints, but this is my first time working in cpp in a long time. The trouble I'm running into is with the SpawnActor function, maybe I'm hooking up the parameters wrong.
Below is the code for my spawning logic in my cpp file:
void ADS_Generator::SpawnFlora()
{
FHitResult CurrentHit;
FActorSpawnParameters SpawnInfo;
for (int F = 0; F < FloraIterations; ++F)
{
if (SpawnedFlora.Num() <= MaxFlora)
{
CurrentHit = SpawnLineTrace(Vertices[FMath::RandRange(0, Vertices.Num() - 1)]);
if (CurrentHit.bBlockingHit == true)
{
if (FMath::RandRange(1, 100) >= FloraTolerance)
{
AActor* CurrentFlora = GetWorld()->SpawnActor(Flora[0], CurrentHit.ImpactPoint, FVector(0, 0, FMath::RandRange(-180, 180)), SpawnInfo);
SpawnedFlora.Add(CurrentFlora);
}
}
}
}
}
Below is the code for my header file where I declare the array of actor classes:
UPROPERTY(EditAnywhere)
TArray<TSubclassOf<class AActor>> Flora;
UPROPERTY(EditAnywhere)
TArray<TSubclassOf<class AActor>> Fauna;
The specific error I'm getting is this:
"C++ no instance of overloaded function matches the argument list
argument types are: (TSubclassOf<AActor>, FVector_NetQuantize, FVector, FActorSpawnParameters)
object type is: UWorld"
I've tried adding "::StaticClass()" as well as using "->StaticClass" to no avail.
Any thoughts or advice? I definitely need to watch some videos and brush up on my cpp, I'm very rusty.
Note: I'm using Flora[0] as a placeholder instead of randomizing the array index just to test.
Found the issue! The problem was my Rotation was defined as a plain FVector, but it needed to be converted to a rotation and then a quaternion, respectively. Below is the code, but fixed:
void ADS_Generator::SpawnFlora()
{
FHitResult CurrentHit;
FActorSpawnParameters SpawnInfo;
for (int F = 0; F < FloraIterations; ++F)
{
if (SpawnedFlora.Num() <= MaxFlora)
{
CurrentHit = SpawnLineTrace(Vertices[FMath::RandRange(0, Vertices.Num() - 1)]);
if (CurrentHit.bBlockingHit == true)
{
UE_LOG(LogTemp, Warning, TEXT("HIT!"));
if (FMath::RandRange(1, 100) >= FloraTolerance)
{
FTransform SpawnLoc;
SpawnLoc.SetLocation(CurrentHit.ImpactPoint);
SpawnLoc.SetRotation((FVector(0, 0, FMath::RandRange(-180, 180)).Rotation().Quaternion()));
SpawnLoc.SetScale3D(FVector(1, 1, 1));
AActor* CurrentFlora = GetWorld()->SpawnActor<AActor>(Flora[0]->StaticClass(), SpawnLoc, SpawnInfo);
SpawnedFlora.Add(CurrentFlora);
UE_LOG(LogTemp, Warning, TEXT("Spawned Flora!"));
}
}
else if(CurrentHit.bBlockingHit == false)
{
UE_LOG(LogTemp, Warning, TEXT("NO HIT"));
}
}
}
}
TLDR; Locations and Rotations are stored differently and I totally forgot.

Vulkan's VkImageView and VkImage objects causing unhandled excecption within the SwapChain

I'm following vulkan-tutorial and I've successfully rendered a spinning square. I'm at the point in the lessons right before applying textures. Before moving on within the lessons, I've been modularizing the code into a framework of interfaces one piece at a time. I have successfully managed to extract various Vulkan objects out of the main engine class into their own classes. Each of these class objects has an interface with an initialize, create, and cleanup function at a minimum.
I've done this with a Buffer class that is an abstract base class that my IndexBuffer, VertexBuffer, and UniformBuffer all derived from. I've done this with my CommandPool class, SyncObjects(VkSemaphore and VkFence) classes, my Pipelines(only MainGraphicsPipeline for now), and my SwapChain.
With all of these in their own classes, I'm now storing these classes as either shared_ptr<Class> or vector<shared_ptr<Class>> within my classes that have internal instances of these. This is the design flow that I've been staying with.
Everything was working perfectly fine until I started to have VkImageView types contained within their own class. Within my SwapChainclass it contained the private member:
std::vector<VkImageView> imageViews_
and these member functions that work on them:
void SwapChain::cleanupImageViews() {
for (auto imageView : imageViews_) {
vkDestroyImageView(*device_, imageView, nullptr);
}
}
void SwapChain::createImageViews() {
imageViews_.resize(images_.size());
for (size_t i = 0; i < images_.size(); i++) {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = images_[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = imageFormat_;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(*device_, &createInfo, nullptr, &imageViews_[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
}
With my code in this state, everything works fine. I'm able to render a spinning colored square, I can resize the window and close the window with 0 errors from both Visual Studio and from Vulkan Layers.
When I change to this pattern that I've done before:
std::vector<std::shared_ptr<ImageView>> imageViews_;
and my functions become:
void SwapChain::cleanupImageViews() {
for (auto& imageView : imageViews_ ) {
imageView->cleanup();
}
}
void SwapChain::createImageViews() {
imageViews_.resize(images_.size());
for(auto& i : imageViews_) {
i = std::shared_ptr<ImageView>();
i->initialize(device_);
}
for (size_t i = 0; i < images_.size(); i++ ) {
imagesViews_[i]->create(images_[i], imageFormat_);
}
}
It fails when It calls the create() for the ImageViews giving me an unhandled exception: access read/write violation stating that the "this" pointer within my ImageView class is nullptr.
Here is what my ImageView class looks like:
ImageView.h
#pragma once
#include "utility.h"
namespace ForceEngine {
namespace vk {
class ImageView {
private:
VkImageView imageView_;
VkDevice* device_;
VkImage image_;
VkFormat format_;
public:
ImageView() = default;
~ImageView() = default;
void initialize(VkDevice* device);
void create(VkImage image, VkFormat format);
void cleanup();
VkImageView* get() { return &imageView_; }
private:
void createImageView();
};
} // namespace vk
} // namespace ForceEngine
ImageView.cpp
#include "ImageView.h"
namespace ForceEngine {
namespace vk {
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) {
throw std::runtime_error("failed to initialize ImageView: device was nullptr!");
device_ = device;
}
}
void ImageView::create(VkImage image, VkFormat format) {
//if (image == nullptr) throw std::runtime_error("failed to create Image View: image was nullptr!");
//if (format == nullptr) throw std::runtime_error("failed to create Image View: format was nullptr!");
image_ = image; // This is where it is throwing the exception.
format_ = format;
createImageView();
}
void ImageView::createImageView() {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = image_;
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = format_;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(*device_, &createInfo, nullptr, &imageView_) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
void ImageView::cleanup() {
vkDestroyImageView(*device_, imageView_, nullptr);
}
} // namespace vk
} // namespace ForceEngine
Within my class, I've tried having image_ as a pointer and as a non-pointer and as for the signature of create(), I've tried passing the parameters by copy, reference, pointer, const reference, const pointer, etc. and to no avail, none of the above has worked. Everything keeps causing an exception. I don't know what's causing the access violation. For some reason, the ImageView class is not properly allocating memory for the image_ member as it states that this was nullptr for this class or it can't write to memory, etc. Yet, I've followed this same pattern for all of my other Vulkan Objects and did not have this issue until now.
What are the takes on the usages and proper setup of VkImageView and VkImage within the context of the SwapChain? Currently, the VkImages are still stored within the SwapChain as std::vector<VkImage> I can create it successfully within the class directly, but when I try to extract the VkImageView out into its own class object is when I start to run into this problem. I'm learning Vulkan through this tutorial and I'm starting to get a grasp of how it is designed, but I'm still no expert at the API. Right now any help would be appreciated. And, yes I've stepped through the debugger, I've watched my variables, I've watched the call stack, and for the life of me, I'm completely stumped. If you need more information than this please don't hesitate to ask.
User nicol-bolas pointed out in the comment section that I had i = std::shared_ptr<ImageView>() and that I should have had i = std::make_shared<ImageView>() and yes that is correct and that is how my other independent classes are being created. This was an overlooked typo mistake. So I give credit to NicolBolas for that. However, after fixing that bug, it allowed me to find and fix the real bug.
The actual bug that was causing the Unhandled Exception had nothing to do with the ImageView, VkImageView, VkImage objects. The real culprit was within the ImageView class itself within its initialize() method. I was assigning its member device_ within the scope of the if statement, where I was checking to see if the device pointer being passed in was nullptr or not.
I originally had this:
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) { throw std::runtime_error("failed to initialize Image View: device was nullptr!");
device_ = nullptr;
}
}
And the device_ was never being set. It should have been:
void ImageView::initialize(VkDevice* device) {
if (device == nullptr) throw std::runtime_error("failed to initialize Image View: device was nullptr!");
device_ = nullptr;
}
Now the code works again and I have a spinning colored square. The code exits with 0 errors and no messages from Vulkan Layers.
I had simply overlooked the {} within the if statement of the function.

Unable to delete a list element in C++

I am working on a small game and came across a big problem with lists.
Here's my code:
void cCollisionManager::checkCollision(cPlayer * pPlayer, std::list<cAsteroid*> *asteroidList, std::list<cShot*> *ShotList)
{
sf::FloatRect PlayerBox = pPlayer->getSprite()->getGlobalBounds();
for (auto it : *asteroidList) {
for (auto es : *ShotList) {
sf::FloatRect asteroidboundingBox = it->getSprite()->getGlobalBounds();
sf::FloatRect ShotBox = es->getSprite().getGlobalBounds();
if (asteroidboundingBox.intersects(ShotBox)) {
it = asteroidList->erase(it);
*pPlayer->pPunkte += 1;
std::cout << *pPlayer->pPunkte << std::endl;
}
if (asteroidboundingBox.intersects(PlayerBox)) {
if (*pPlayer->phealth >= 0.f)
*pPlayer->phealth -= 0.5f;
}
}
}
}
I used SFML and basically everything works. But if I want to delete the colliding asteroid and the shot, the programs exits with an error. In the if loop I tried to erase the object, but the compiler also gives an error saying that the argument type is not the same as the object type I am giving to it.
EDIT
I had another look at the other question, you recommended to me, but still I haven't found out how to solve that problem. So if I changed my code to a while loop, the game couldn't handle it, because the Collision Manager is actually called in every single Call of the SFML main loop. So it would just get stuck in my collision loop. So I changed my code a bit, but still, things are not working.
Don't modify sequences that are being enumerated with range-for. Use
iterators and the appropriate result of an erase. – WhozCraig
This is actually the answer to it. I did the mistake - using a for loop and not a while loop and so I had some big issues and bad construction ideas for my code - luckily everything now works!
Here is my final code:
auto it = asteroidList->begin();
auto es = ShotList->begin();
while (it != asteroidList->end()) {
sf::FloatRect PlayerBox = pPlayer->getSprite()->getGlobalBounds();
sf::FloatRect asteroidboundingBox = (*it)->getSprite()->getGlobalBounds();
while (es != ShotList->end())
{
sf::FloatRect ShotBox = (*es)->getSprite().getGlobalBounds();
if (asteroidboundingBox.intersects(ShotBox)) {
it = asteroidList->erase(it);
es = ShotList->erase(es);
std::cout << "Asteroid destroyed" << std::endl;
*pPlayer->pPunkte += 1;
std::cout << *pPlayer->pPunkte << std::endl;
}
if (es != ShotList->end())
es++;
}
if (asteroidboundingBox.intersects(PlayerBox))
{
if (*pPlayer->phealth > 3.f) {
*pPlayer->phealth -= 5.f;
it = asteroidList->erase(it);
}
else
*pPlayer->pBStateAlive = false;
}
if (it != asteroidList->end()) {
it++;
es = ShotList->begin();
}
}
}

Header functions

EDIT: by not working I mean that in my main array mA in main doesn't show any change to the elements within the array.
I have been checking my functions as I develop the headers and they have been working perfectly: Until I got to the final header MonitorArray.h.
mA.getScreen(i).checkScreen();
Didn't work and I couldn't work out why. So I created a new function within MonitorArray to do a similar job using the same function, and to my surprise it worked.
mA.pollScreens();
Which uses (Inside MonitorArray.h):
monitorArray[i].checkScreen();
Function getScreen:
ScreenArray MonitorArray::getScreen(int arrayPointer)
{
if (arrayPointer<0 || arrayPointer>=monitors)
{
return false;
}
else
{
return monitorArray[arrayPointer];
}
}
Function checkScreen and addArray:
void ScreenArray::checkScreen()
{
HDC dMonitor;
PixelArray pArray;
int lenX = 0, lenY = 0;
dMonitor = CreateDC(iMonitor.szDevice, iMonitor.szDevice, NULL, NULL);
lenX = (iMonitor.rcWork.right - iMonitor.rcWork.left) - 1;
lenY = (iMonitor.rcWork.bottom - iMonitor.rcWork.top) - 1;
pArray.setColour(0, GetPixel(dMonitor, 0, 0));
pArray...
...
...
addArray(&pArray);
ReleaseDC(NULL, dMonitor);
}
void ScreenArray::addArray(PixelArray* pA)
{
if (previousCheck(*pA))
{
arrayPosition = 0;
screenArray[arrayPosition] = *pA;
arrayPosition++;
}
else
{
screenArray[arrayPosition] = *pA;
arrayPosition++;
}
if (arrayPosition==11)
{
//Run screen saver on monitor
}
}
Why does running the command within the header file through a new function work but running the functions from main not?
Assuming that "didn't work" means "didn't affect the ScreenArray in my MonitorArray", it's because getScreen returns a copy of the array element
ScreenArray MonitorArray::getScreen(int arrayPointer)
while the new member function most likely works with the array directly.
You'll need to return a pointer to the array element instead:
ScreenArray* MonitorArray::getScreen(int arrayPointer)
{
if (arrayPointer<0 || arrayPointer>=monitors)
{
return NULL;
}
else
{
return &monitorArray[arrayPointer];
}
}
(BTW: the implicit conversion from bool to ScreenArray looks very odd.)

Callback function confuses argument?

I have a sfml window container, and it appears to be working, however the glViewPorts are the wrong size, which I assume is because the wrong sf::Window is being passed.
Here is a function which adds to the window: It takes some information about the sfml window.
int WindowContainer::PushBack(WindowData& data)
{
if(data.WindowSettingsOK() && data.VideoModeOK()){
mWindowVector.resize(mWindowVector.size() + 1);
mDisplayFuncVector.resize(mWindowVector.size());
mInputFuncVector.resize(mWindowVector.size());
mWindowVector.at(mWindowVector.size() - 1) = new sf::Window();
mWindowVector.at(mWindowVector.size() - 1)->Create(data.VideoMode(), data.Title(), data.Style(), data.Settings());
mWindowVector.at(mWindowVector.size() - 1)->SetPosition(data.PositionX(), data.PositionY());
mDisplayFuncVector.at(mWindowVector.size() - 1) = nullptr;
mInputFuncVector.at(mWindowVector.size() - 1) = nullptr;
return 0;
}
else{
PrintError(ErrorMessageType::BadSettings);
return 1;
}
}
Alternatively, this function may be called to setup the display and input function callbacks:
int WindowContainer::PushBack(WindowData& data, function_p displayFunc, function_p inputFunc)
{
int return_val = PushBack(data);
mDisplayFuncVector.at(mWindowVector.size() - 1) = displayFunc;
mInputFuncVector.at(mWindowVector.size() - 1) = inputFunc;
return return_val;
}
Then, when the window needs .Display()'ing, this function is called:
void WindowContainer::ProcessDisplay()
{
for(unsigned int i = 0; i < mWindowVector.size(); i ++){
if(mDisplayFuncVector.at(i) != nullptr){
mDisplayFuncVector.at(i)(*mWindowVector.at(i), mClock, (const void*&)mExternalDrawingDataPointer);
}
mWindowVector.at(i)->Display();
}
}
... This is all good, until the result on the screen is that resizing one window affects the viewport of both windows. This suggests that calling the callback function: mDisplayFuncVector.at(i)(*mWindowVector.at(i), mClock, (const void*&)mExternalDrawingDataPointer); gives the argument of *mWindowVector.at(0) each time, instead of each window individually. (As in *mWindowVector.at(i))
Can anyone help with this problem?
The main loop contains this code:
while(container.Access(0)->IsOpened()){
container.ProcessInput();
container.ProcessDisplay();
}
Container.Access(int) is this function:
const sf::Window*& WindowContainer::Access(unsigned int index)
{
if(index > mWindowVector.size()){
PrintError(ErrorMessageType::IndexOutOfRange);
}
else{
return (const sf::Window*&)mWindowVector.at(index);
}
return (const sf::Window*&)mWindowVector.at(0);
}
Thanks again, I'm sure I have made a mistake somewhere but cannot spot it.
I have been thinking about this question and suspect openGL becomes confused with which window is it drawing to if more than one object is pushed back without a call to Display() to sync everything.
I am yet to test this and confirm.
EDIT The window container now works. It has nothing to do with the callback functions argument.