[HELP] AI Perception sight

Hi,

I’m trying to do a kind of metal gear game, so I made a military base and put some barriers crouching to hide from the patrol, I made two guards moving on a route, and testing if barriers block vision from them i got that if I stand behind the wall they can’t see me… I attach a screenshot I want that they can see my head because i think that right now they only can detect the body as a sphere

Thanks!

1 Like

Didnt find any solution yet :frowning: I let you a video to show you the problem

I posted this on the ue4 forum and someone said this to me:

Yeah, they can only see the origin of the sphere, which is usually in the middle of the body.
You can add another collider components to the head, give it a collision tag(I’m bad with names), and have the AI be able to see that.

@sampattuzzi can you help me? :frowning:

Seems like they are saying that the sphere for visibility defaults to the centre of the body.

The part of the code you are using is called the UAIPerceptionComponent. If you dig into the configuration of the senses, UAISenseConfig leads to its child UAISenseConfig_Sight leads to the “implementation” of the sense UAISense_Sight. Here you can see a property called DefaultSightCollisionChannel.

I think you need to find out what that channel is, then add a collider for the head, and have it active on that channel.

I hope that gives you a starting point. Keep me up-to-date if you need more help.

Hi Sam!

Thank you! I did one sphere collision on the character’s head, but I don’t know what channel I have to put, I let you an screenshot, I tried to put all on block (also overlap and ignore (?)) on the collision in the right section but it didn’t work :confused:

Sometimes, Unreal wants the colliders at the root. Try that.

If that fails, see if you can read the code of the sight class to get any clues from it.

Ok… what it means exactly that wants the colliders at the root?

In the ue4 forum someone said this also:

Shrink the body collider so that the body collider stops are the shoulders, and the head collider starts from the neck up.
You will have to reposition the mesh so it fits the collider size.

But I think that this answer is not what i want? :confused: I’m getting a lot of trouble with that, I think it should be more easy :confused:

Thanks Sam

I wouldn’t assume it should be easy, this is Unreal :wink:

By at the root I mean it should be the root component in the blueprint and the mesh should be a child to it. Sounds like that’s what the folk on the forum are saying too.

Sam!

Yep, it’s unreal but I want to say that the collision system should be more friendly :frowning:

Anyway the capsule has to be the root and you can’t put the sphere collision parenting the capsule, I don’t really know what to do :confused: do you know some direct way to talk with the epic guys? xD

Thanks!

@sampattuzzi

ok Sam, I think I got something…

from here https://answers.unrealengine.com/questions/421735/ai-perception-height.html I went here: https://answers.unrealengine.com/questions/229043/ai-perception-only-can-detect-middle-of-player.html

and searching I got this also: https://answers.unrealengine.com/questions/320679/ue410-ai-perception-occlusion-based-perception.html

So, In my ThirdPersonCharacter (that i posses to play) thats my code:

my .h file

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Perception/AISightTargetInterface.h"
#include "GameFramework/Character.h"
#include "SnuffCharacter.generated.h"

UCLASS(config=Game)
class ASnuffCharacter : public ACharacter, public IAISightTargetInterface
{
	GENERATED_BODY()

	/** Camera boom positioning the camera behind the character */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	class USpringArmComponent* CameraBoom;

	/** Follow camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	class UCameraComponent* FollowCamera;
public:
	ASnuffCharacter();

virtual bool CanBeSeenFrom(const FVector& ObserverLocation, FVector& OutSeenLocation, int32& NumberOfLoSChecksPerformed, float& OutSightStrength,     const AActor* IgnoreActor = NULL) const;

	/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
	float BaseTurnRate;

	/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
	float BaseLookUpRate;

protected:

	/** Resets HMD orientation in VR. */
	void OnResetVR();

	/** Called for forwards/backward input */
	void MoveForward(float Value);

	/** Called for side to side input */
	void MoveRight(float Value);

	/** 
	 * Called via input to turn at a given rate. 
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void TurnAtRate(float Rate);

	/**
	 * Called via input to turn look up/down at a given rate. 
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void LookUpAtRate(float Rate);

	/** Handler for when a touch input begins. */
	void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);

	/** Handler for when a touch input stops. */
	void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);

protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	// End of APawn interface

public:
	/** Returns CameraBoom subobject **/
	FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
	/** Returns FollowCamera subobject **/
	FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};

and thats my .cpp:

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "Snuff.h"
#include "Kismet/HeadMountedDisplayFunctionLibrary.h"
#include "SnuffCharacter.h"

//////////////////////////////////////////////////////////////////////////
// ASnuffCharacter

ASnuffCharacter::ASnuffCharacter()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

	// set our turn rates for input
	BaseTurnRate = 45.f;
	BaseLookUpRate = 45.f;

	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...	
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
	GetCharacterMovement()->JumpZVelocity = 600.f;
	GetCharacterMovement()->AirControl = 0.2f;

	// Create a camera boom (pulls in towards the player if there is a collision)
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character	
	CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller

	// Create a follow camera
	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
	FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm

	// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
	// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void ASnuffCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
	// Set up gameplay key bindings
	check(PlayerInputComponent);
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

	PlayerInputComponent->BindAxis("MoveForward", this, &ASnuffCharacter::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &ASnuffCharacter::MoveRight);

	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
	// "turn" handles devices that provide an absolute delta, such as a mouse.
	// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
	PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	PlayerInputComponent->BindAxis("TurnRate", this, &ASnuffCharacter::TurnAtRate);
	PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("LookUpRate", this, &ASnuffCharacter::LookUpAtRate);

	// handle touch devices
	PlayerInputComponent->BindTouch(IE_Pressed, this, &ASnuffCharacter::TouchStarted);
	PlayerInputComponent->BindTouch(IE_Released, this, &ASnuffCharacter::TouchStopped);

	// VR headset functionality
	PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &ASnuffCharacter::OnResetVR);
}


void ASnuffCharacter::OnResetVR()
{
	UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}

void ASnuffCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
		Jump();
}

void ASnuffCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
		StopJumping();
}

void ASnuffCharacter::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void ASnuffCharacter::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void ASnuffCharacter::MoveForward(float Value)
{
	if ((Controller != NULL) && (Value != 0.0f))
	{
		// find out which way is forward
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, Value);
	}
}

void ASnuffCharacter::MoveRight(float Value)
{
	if ( (Controller != NULL) && (Value != 0.0f) )
	{
		// find out which way is right
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);
	
		// get right vector 
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		// add movement in that direction
		AddMovementInput(Direction, Value);
	}
}

bool ASnuffCharacter::CanBeSeenFrom(const FVector& ObserverLocation, FVector& OutSeenLocation, int32& NumberOfLoSChecksPerformed, float& OutSightStrength, const AActor* IgnoreActor) const
{
	UE_LOG(LogTemp, Warning, TEXT("Flag"));

	static const FName NAME_AILineOfSight = FName(TEXT("TestPawnLineOfSight"));

	FHitResult HitResult;
	const bool bHit = GetWorld()->LineTraceSingleByObjectType(HitResult, ObserverLocation, GetActorLocation()
		, FCollisionObjectQueryParams(ECC_TO_BITFIELD(ECC_WorldStatic) | ECC_TO_BITFIELD(ECC_WorldDynamic)) // << Changed this line
		, FCollisionQueryParams(NAME_AILineOfSight, true, IgnoreActor));

	NumberOfLoSChecksPerformed++;

	// Add any other checks you want to perform here
	// ...

	if (bHit == false || (HitResult.Actor.IsValid() && HitResult.Actor->IsOwnedBy(this)))
	{
		OutSeenLocation = GetActorLocation();
		OutSightStrength = 1;
		return true;
	}

	OutSightStrength = 0;
	return false;
}

It compiles and get the log, so what I would like is to put some sockets of the skeleton and get true or false if the NPC can see any of this points, can you help me?

Thanks :frowning:

1 Like

@sampattuzzi solution founded! :smiley:

that’s how finally I implemented the method, advices to improve the code will be wellcomed :smiley:

bool ASnuffCharacter::CanBeSeenFrom(const FVector& ObserverLocation, FVector& OutSeenLocation, int32& NumberOfLoSChecksPerformed, float& OutSightStrength, const AActor* IgnoreActor) const
{
	static const FName NAME_AILineOfSight = FName(TEXT("TestPawnLineOfSight"));

	FHitResult HitResult;
	const bool bHit = GetWorld()->LineTraceSingleByObjectType(HitResult, ObserverLocation, GetActorLocation()
		, FCollisionObjectQueryParams(ECC_TO_BITFIELD(ECC_WorldStatic) | ECC_TO_BITFIELD(ECC_WorldDynamic)) // << Changed this line
		, FCollisionQueryParams(NAME_AILineOfSight, true, IgnoreActor));

	NumberOfLoSChecksPerformed++;

	//TODO now only head check, in future improve it
	FVector socketLocation = GetMesh()->GetSocketLocation("head");

	const bool bHitSocket = GetWorld()->LineTraceSingleByObjectType(HitResult, ObserverLocation, socketLocation
		, FCollisionObjectQueryParams(ECC_TO_BITFIELD(ECC_WorldStatic) | ECC_TO_BITFIELD(ECC_WorldDynamic)) // << Changed this line
		, FCollisionQueryParams(NAME_AILineOfSight, true, IgnoreActor));

	if (bHitSocket == false) {
		OutSeenLocation = GetActorLocation();
		OutSightStrength = 1;

		UE_LOG(LogTemp, Error, TEXT("Hit head"));

		return true;
	}

	if (bHit == false || (HitResult.Actor.IsValid() && HitResult.Actor->IsOwnedBy(this)))
	{
		OutSeenLocation = GetActorLocation();
		OutSightStrength = 1;

		UE_LOG(LogTemp, Error, TEXT("True"));

		return true;
	}

	UE_LOG(LogTemp, Error, TEXT("False"));
	OutSightStrength = 0;
	return false;
}

Awesome detective work! The code looks really good and clear to me. Are you getting the AI headshotting you all the time now? :smiley: :smiley: :smiley:

Yep, but the default behaviour still working too, I think maybe we should do a foeach with the getallsocketsname (or something like that) but for me right know it’s ok.

Another doubt, I was looking buy this: https://www.unrealengine.com/marketplace/phoenyx-anim-pack but i want the animations without the hands in weapon pose, i’ts hard to do it myself?

edit:

if i put this:

if (bHitSocket == false) {
	OutSeenLocation = socketLocation;
	OutSightStrength = 1;
	
	return true;
}

that’s what i got:

There’s likely a way to blend animations from a certain bone. I’m just not too sure how right now.

Would you be interested in documenting your findings in a blog post for us?

Yeah! Would be a great honor to me! :slight_smile:

I have let @Lucy_Becker know you are interested. She should be able to give you an account on the blog and any other details you need.

1 Like

Hi Carlos,

Great to have you on board the blog :slight_smile:

Which is the best email address to use when I set you up?

All the best,
Lucy
Part of the GameDev.tv team

@Vimes all live on the blog now, great job :slight_smile:

https://blog.gamedev.tv/ai-sight-perception-to-custom-points/

1 Like

Privacy & Terms