UPDATES
Minimap Update 8/2/2015
DOWNLOADS
DESCRIPTION
Hi guys,
time ago i discovered this usefull Easy Offscreen Indicator Blueprint and i decided to implement it in my project to make a (very very simple) Metal Gear Solid 5 style enemy marker, now my system is at a good point and i want to share it with you.
It’s a very simple stuff and has some bugs (specially when offscreen marker go at bottom of the screen), nothing to do with AAA style games but is the first complex work i achieved alone and i’m proud of it
Any help and suggestions to improve the system are very appreciated! Especially for the C++ part because (as i said) is not mine.
Here a short video on what i’m talking about (sorry for the horrid grammar error on the walls, i fixed it).
In the example project you will find some NPC and Actors already marked to show you the various kind of marker, and one NPC not marked, to discover what kind of marker the NPC has you have to “scan” it, to do it look at it and press and hold Q.
With E you can spawn custom checkpoints, at the moment the spawn location not work very well (if you look at the top of the wall and you press E the CP will be spawned too high and you cant reach it) because my next step is to create a minimap and spawn CP from minimap.
M.O.S. work with both Third Person and First Person Player Characters.
M.O.S. core it’s composed by:
[spoiler]
-
1x Blueprint Function Library C++ Class
-
HUDBlueprintLibrary Thank to jeff_lamarche
-
1x Blueprint Component
-
BP_MarkerComponent
-
1x Blueprint Interface
-
BP_MOS
-
1x Blueprint Widget
-
BP_MarkerWidget
-
2x Enumeration
-
EMarkerCheckpointState
-
EMarkerState
-
4x Texture
-
BlankCheckpoint64
-
BlankMarker64
-
BlankVehicleMarker64
-
BlankWeaponMarker64
-
5x Material
-
PP_OutlineCustomDepthOcclusion Thank to
-
M_BlankCheckpoint64
-
M_BlankMarker64
-
M_BlankVehicleMarker64
-
M_BlankWeaponMarker64
-
8x Material Instance
-
PP_OutlineCustomDepthOcclusion_Inst Thank to
-
M_CustomCheckpoint_Inst
-
M_EnemyMarker_Inst
-
M_FriendlyMarker_Inst
-
M_MissionCheckpoint_Inst
-
M_NeutralMarker_Inst
-
M_VehicleMarker_Inst
-
M_WeaponMarker_Inst
[/spoiler]
How to use M.O.S.?
Use M.O.S it’s very simple
IMPORTANT STEP
Create a Blueprint Function Library C++ class and copy and paste this code from Easy Offscreen Indicator Blueprint (or copy and paste from the example project file), remember to change names with yours.
(Obvious don’t do it into the example project, it has it).
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HUDBlueprintLibrary.generated.h"
/**
*
*/
UCLASS()
class MOSEXAMPLE_API UHUDBlueprintLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/**
* Converts a world location to screen position for HUD drawing. This differs from the results of FSceneView::WorldToScreen in that it returns a position along the edge of the screen for offscreen locations
*
* @param InLocation - The world space location to be converted to screen space
* @param EdgePercent - How close to the edge of the screen, 1.0 = at edge, 0.0 = at center of screen. .9 or .95 is usually desirable
* @outparam OutScreenPosition - the screen coordinates for HUD drawing
* @outparam OutRotationAngleDegrees - The angle to rotate a hud element if you want it pointing toward the offscreen indicator, 0° if onscreen
* @outparam bIsOnScreen - True if the specified location is in the camera view (may be obstructed)
*/
UFUNCTION(BlueprintPure, meta = (WorldContext = "WorldContextObject", CallableWithoutWorldContext), Category = "HUD|Util")
static void FindScreenEdgeLocationForWorldLocation(UObject* WorldContextObject, const FVector& InLocation, const float EdgePercent, FVector2D& OutScreenPosition, float& OutRotationAngleDegrees, bool &bIsOnScreen);
};
.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MOSExample.h"
#include "HUDBlueprintLibrary.h"
void UHUDBlueprintLibrary::FindScreenEdgeLocationForWorldLocation(UObject* WorldContextObject, const FVector& InLocation, const float EdgePercent, FVector2D& OutScreenPosition, float& OutRotationAngleDegrees, bool &bIsOnScreen)
{
bIsOnScreen = false;
OutRotationAngleDegrees = 0.f;
if (!GEngine) return;
const FVector2D ViewportSize = FVector2D(GEngine->GameViewport->Viewport->GetSizeXY());
const FVector2D ViewportCenter = FVector2D(ViewportSize.X / 2, ViewportSize.Y / 2);
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject);
if (!World) return;
APlayerController* PlayerController = (WorldContextObject ? UGameplayStatics::GetPlayerController(WorldContextObject, 0) : NULL);
if (!PlayerController) return;
ACharacter* PlayerCharacter = Cast<ACharacter>(PlayerController->GetPawn());
if (!PlayerCharacter) return;
FVector Forward = PlayerCharacter->GetActorForwardVector();
FVector Offset = (InLocation - PlayerCharacter->GetActorLocation()).GetSafeNormal();
float DotProduct = FVector::DotProduct(Forward, Offset);
bool bLocationIsBehindCamera = (DotProduct < 0);
if (bLocationIsBehindCamera)
{
// For behind the camera situation, we cheat a little to put the
// marker at the bottom of the screen so that it moves smoothly
// as you turn around. Could stand some refinement, but results
// are decent enough for most purposes.
FVector DiffVector = InLocation - PlayerCharacter->GetActorLocation();
FVector Inverted = DiffVector * -1.f;
FVector NewInLocation = PlayerCharacter->GetActorLocation() * Inverted;
NewInLocation.Z -= 5000;
PlayerController->ProjectWorldLocationToScreen(NewInLocation, OutScreenPosition);
OutScreenPosition.Y = (EdgePercent * ViewportCenter.X) * 2.f;
OutScreenPosition.X = -ViewportCenter.X - OutScreenPosition.X;
}
PlayerController->ProjectWorldLocationToScreen(InLocation, OutScreenPosition);//*ScreenPosition);
// Check to see if it's on screen. If it is, ProjectWorldLocationToScreen is all we need, return it.
if (OutScreenPosition.X >= 0.f && OutScreenPosition.X <= ViewportSize.X
&& OutScreenPosition.Y >= 0.f && OutScreenPosition.Y <= ViewportSize.Y)
{
bIsOnScreen = true;
return;
}
OutScreenPosition -= ViewportCenter;
float AngleRadians = FMath::Atan2(OutScreenPosition.Y, OutScreenPosition.X);
AngleRadians -= FMath::DegreesToRadians(90.f);
OutRotationAngleDegrees = FMath::RadiansToDegrees(AngleRadians) + 180.f;
float Cos = cosf(AngleRadians);
float Sin = -sinf(AngleRadians);
OutScreenPosition = FVector2D(ViewportCenter.X + (Sin * 180.f), ViewportCenter.Y + Cos * 180.f);
float m = Cos / Sin;
FVector2D ScreenBounds = ViewportCenter * EdgePercent;
if (Cos > 0)
{
OutScreenPosition = FVector2D(ScreenBounds.Y / m, ScreenBounds.Y);
}
else
{
OutScreenPosition = FVector2D(-ScreenBounds.Y / m, -ScreenBounds.Y);
}
if (OutScreenPosition.X > ScreenBounds.X)
{
OutScreenPosition = FVector2D(ScreenBounds.X, ScreenBounds.X*m);
}
else if (OutScreenPosition.X < -ScreenBounds.X)
{
OutScreenPosition = FVector2D(-ScreenBounds.X, -ScreenBounds.X*m);
}
OutScreenPosition += ViewportCenter;
}
Step 1
Open the Blueprint where you want to use M.O.S. (for example your NPC Blueprint), and in Class Settings add the BP_MOS interface
[spoiler]
[/spoiler]
Step 2
Add the BP_MarkerComponent
[spoiler]
[/spoiler]
Step 3
Implement the BP_MOS interface into your Blueprint Event Graph and call the BP_MarkerComponent OnMark event.
[spoiler]
[/spoiler]
Step 4
In the BP_MarkerComponent details tweak the M.O.S. properties as you want
[spoiler]
[/spoiler]
TIPS: set “MaxDistance” as a negative number to make your marker “persistent” on distance.
Step 5
Send a BP_MOS interface OnMark message where you want, in my example i sent the message to the actor retrieved from a LineTraceByChannel hit into BP_MyPlayerController event graph.
[spoiler]
[/spoiler]
Step 6
Setup a Post Process volume into your level, in details set it “Unbound” and under “Post Process Settings -> Misc -> Blendables” add one element and use the PP_OutlineCustomDepthOcclusion_Inst
[spoiler]
[/spoiler]