Download

M.O.S. - Markable Objects System - NO LONGER AVAILABLE

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 :slight_smile:

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).
https://youtube.com/watch?v=1vTlH7QmEhs

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 Tom Looman

  • M_BlankCheckpoint64

  • M_BlankMarker64

  • M_BlankVehicleMarker64

  • M_BlankWeaponMarker64

  • 8x Material Instance

  • PP_OutlineCustomDepthOcclusion_Inst Thank to Tom Looman

  • 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]
vrbrMzl.png
[/spoiler]
Step 2

Add the BP_MarkerComponent

[spoiler]
PAijcEa.png
[/spoiler]
Step 3

Implement the BP_MOS interface into your Blueprint Event Graph and call the BP_MarkerComponent OnMark event.

[spoiler]
NBHsMQ5.png
[/spoiler]

Step 4

In the BP_MarkerComponent details tweak the M.O.S. properties as you want

[spoiler]
jLWRjsl.png
[/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]
zwmOCC8.png
[/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]
v6db863.png
[/spoiler]

Reserved for FAQ

Well this is super neat, good work :smiley:

Cool stuff, ty for your work.
OMG this forum is the hell for my freetime.
So many interesting things around to test out and play with.

Thats amazing man, Great job!

hahaha +1

Well done :slight_smile:

Thank you guys :slight_smile:

I have a question for you, one user on Reddit have mention this memory leak problem into the code

I managed a little bit the original code, do you think now is better?



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;	

}


Nice work, Thanks for sharing mate!

Thanks for sharing. What engine version do I need to be on? I tried opening in 4.8.1 and everything error’d out

I used the 4.8.3, try with it and if you still have errors tell me which for reference.

Kind of sucks how stock ue4 is limited to one blendable at a time. Also how did you have to set some setting to get the blendable to work when the entire actor is covered up. I can’t remember the name, but it makes shadows all funky. I tried getting it to work in my project, but in order to get the x-ray type outline to work at least 90% of the time, I had to set it pretty high.

Is something i have to figure out, at the moment my system is very basic, the Markable Component fire a line trace from the owner actor to the player and start from the bottom of actor location

pCRIpyy.png

rise up the Z value could be a good temporary solution but if you are using the Third Person Character and the linetrace have a hit with a wall but your camera is rotated “behind” the wall you still see the Outline PP, a good solution could be change the end of the trace to the camera location instead of the player, when i will finish the minimap and his integration with MOS for the spawn checkpoint system i will go deep on this.

How does this stop occlusion culling? :confused:

Mmm, probably i don’t understand what you mean :smiley:

Sorry, not a good explanation of what I was talking about, and it looks like I changed how I was going to phrase the sentence part way through. Lol

When I used that x-ray type blendable that you are using on that character model to see him through walls a while ago, I had trouble getting the actor to stay on screen. When I partially block him from my camera sight, I can see the portion of him that is blocked by a wall or something, but the second he is entirely blocked, he disappeared.
My guess was that occlusion culling kept him from being rendered and thus had nothing to outline. I tried setting the bounds scale setting, and that worked some of the time but would still culled at certain angles. I had to crank it as high as 13. This was on 4.7 though.

This is the one I am taking about http://www.tomlooman.com/ue4-evolves-outline-post-effect/
It looks like you had it working fine in yours.

Don’t worry, not your fault, english isn’t my language and sometime i have hard time understand :slight_smile:

About your problem i don’t know how to help you, i’m not so skilled in Materials topic yet and i’m using the Tom’s outline effect “As-Is”, i only tweaked the color in material instance.

Remanaged for 4.9

Example Project Engine Version 4.9

Working on a clickable minimap where you will be able to spawn checkpoints, minimap now run well and i will integrate it into M.O.S.

https://www.youtube.com/watch?v=N6vh5IG-ais

Oh man this is really great!
And that minimap function :open_mouth: just wow, waiting for it!
thank you very much for sharing ^^

Minimap is basically ready now but i want to improve it before release the example project, i’m looking for the ability to resize and move it at runtime, meanwhile a new video about my progress :slight_smile:

https://youtube.com/watch?v=okC0WPWGuBY