Showing posts with label AI. Show all posts
Showing posts with label AI. Show all posts

Monday, September 9, 2019

Overriding OnPossess() with Unreal Engine 4.23.0

Recently Possess() has been deprecated from UE4, and when writing classes based on AAIController you have to use the function OnPossess().  When using the former vs the latter, you will have compilation problems, and potential breaks in the Artificial Intelligence for your various Pawns, and Blueprints, Blackboard, etc.


Above: AI Book I am currently reading. Quite insightful.


If you are using Blueprints, it's a matter of changing them. However you are using C++ code you must first do this in the header file (.h) of your class inheriting from AAIController:



 private: 

    UPROPERTY(transient)
    class UBlackboardComponent *BlackboardComp;
    
    UPROPERTY(transient)
    class UBehaviorTreeComponent *BehaviorComp;
   
 public:

   virtual void OnPossess(APawn *InPawn) override
  
    bool isDead = false;
    uint8 EnemyKeyID;
    uint8 PatrolKeyID;
    uint8 EnemyModeBBKeyID;
    uint8 isDeadBBKeyID;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MoveEnemy")


Then in the implementation file (.cpp) file you implement like this:



 void AEnemyAI::OnPossess(APawn *InPawn) {
    Super::OnPossess(InPawn);
    AEnemyCharacter *Char = Cast<AEnemyCharacter>(InPawn);
    
    if (Char && Char->Enemy) {
        BlackboardComp->InitializeBlackboard(*Char->Enemy->BlackboardAsset);
        EnemyKeyID = BlackboardComp->GetKeyID("Target");
        PatrolKeyID = BlackboardComp->GetKeyID("PatrolTarget");
        EnemyModeBBKeyID = BlackboardComp->GetKeyID("EnemyModeBB");
        isDeadBBKeyID = BlackboardComp->GetKeyID("isEnemyDeadBB");
        tChar = Char;
        BehaviorComp->StartTree(*Char->Enemy);
    }
 }

Monday, September 19, 2016

Get an ENUM value from an Enemy AI Behaviour Tree in Unreal Engine 4.13.0 using C++

Often we intermingle C++ and Blueprints, and need for the two to communicate. With Behavior Trees, using ENUMs is an everyday occurrence and on occasion we need to access said values in C++ Code. Below a Service I created called Check For Player which overrides base UBTService, uses the TickNode to check for the existence of a Player that the EnemyAI sees.

See below:


void UBTService_CheckForPlayer::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
AEnemyAI *EnemyPC = Cast<AEnemyAI>(OwnerComp.GetAIOwner());

if ( EnemyPC) {
        AJumperCharacter *Enemy = Cast<AJumperCharacter>(GetWorld()->GetFirstPlayerController()->GetPawn());
        if (!Enemy) {
            return;
        } else if (Enemy && EnemyPC->tChar->EnemyLife>0) {
                OwnerComp.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>(EnemyPC->PatrolKeyID,EnemyPC);
                OwnerComp.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>(EnemyPC->EnemyKeyID,Enemy);
        }
        uint8 EnemyMode = OwnerComp.GetBlackboardComponent()->GetValueAsEnum("EnemyModeBB");
FString str = FString::FromInt(EnemyMode);
EnemyPC->tChar->SetMode(EnemyMode);
/*
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, str);
*/
}

}
My EnemyCharacter.cpp referenced above has different modes. Since I get an unsigned int8 from GetValueAsEnum("EnemyModeBB"), which is my enum variable in my BehaviourTree , I need to translate the uint8 to an ENUM on my EnemyCharacter class. This isn't necessary and you can case it out but since EnemyCharacter is my main class with all of the EnemyProperties, I want to send the information back.  See below:

void AEnemyCharacter::SetMode(uint8 m) {
    if (Mode!=EnemyMode::Hit)
        switch (m) {
            case 0:SetMode(EnemyMode::Attack);  break;
            case 1:SetMode(EnemyMode::Dead);  break;
            case 2:SetMode(EnemyMode::Flee);  break;
            case 3:SetMode(EnemyMode::Hit);  break;
            case 4:SetMode(EnemyMode::Idle);  break;
            case 5:SetMode(EnemyMode::Patrol);  break;
            case 6:SetMode(EnemyMode::Pursue);  break;
            case 7:SetMode(EnemyMode::Talk);  break;
            case 8:SetMode(EnemyMode::Taunt);  break;
            default:SetMode(EnemyMode::Pursue);  break;
break;
        }

}


Here is the definition of EnemyMode in EnemyCharacter.cpp class:

UENUM(BlueprintType)
enum class EnemyMode : uint8
{
    Attack      UMETA(DisplayName="Attack"),
    Dead        UMETA(DisplayName="Dead"),
    Flee        UMETA(DisplayName="Flee"),
    Hit         UMETA(DisplayName="Hit"),
    Idle        UMETA(DisplayName="Idle"),
    Patrol      UMETA(DisplayName="Patrol"),
    Pursue      UMETA(DisplayName="Pursue"),
    Talk        UMETA(DisplayName="Talk"),
    Taunt       UMETA(DisplayName="Taunt")

};

Also, here is a screenshot for the ENUM:


The variable definition in my BehaviourTree BlackBoard (zombieBB):


Thursday, January 16, 2014

Unity3D C# Enemy Find nearest Player Script

In this script which you attach to the Enemy Objects, you may find the nearest object Tagged "Player". The Enemy faces the Player, and chases within a proximity, with a given frequency. See below:

/// <summary>
/// AI character controller.
/// Just A basic AI Character controller
/// will looking for a Target and moving to and Attacking
/// </summary>

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(CharacterSystem))]

public class AICharacterController : MonoBehaviour {

    public GameObject ObjectTarget;
    public string TargetTag = "Player";
    private CharacterSystem character;
    private int aiTime = 0;
    private float scanFrequency = 1.0f;
    private int aiState = 0;

    void Start () {
        character = gameObject.GetComponent<CharacterSystem>();
        InvokeRepeating ("ScanForTarget",0,scanFrequency);
    }
   
    void ScanForTarget() {
        ObjectTarget = GetNearestTaggedObject();
    }

    public GameObject GetNearestTaggedObject () {
        var nearestDistanceSqr = Mathf.Infinity;
        GameObject nearestObj = null;

        foreach (var obj in GameObject.FindGameObjectsWithTag(TargetTag)) {

            var objectPos = obj.transform.position;
            var distanceSqr = (objectPos - transform.position).sqrMagnitude;
           
            if (distanceSqr < nearestDistanceSqr) {
                nearestObj = obj;
                nearestDistanceSqr = distanceSqr;
            }
        }
        return nearestObj;
    }

    public float DistanceSquaredTo(GameObject source, GameObject target) {
        return Vector3.SqrMagnitude(source.transform.position - target.transform.position);
    }

    void Update () {
            //if (GameObject.Find("CharacterDakota").GetComponent<CharacterStatus>().HP > 0) {
           
        if (GameObject.Find("GameManager").GetComponent<GameManager>().Playing) {

            var direction = Vector3.zero;
            if(aiTime<=0){
                aiState = Random.Range(0,4);
                aiTime = Random.Range(10,100);
            }else{
                aiTime--;
            }
            if(ObjectTarget){
                //ObjectTarget = GameObject.FindGameObjectWithTag(TargetTag);   
                float distance = Vector3.Distance(ObjectTarget.transform.position,this.gameObject.transform.position);
               
                if(distance<=2){
                    transform.LookAt(ObjectTarget.transform.position);
                    if(aiTime<=0){
                        if(aiState == 1){
                            character.Attack();
                        }
                    }
                }else{
                    if(aiState == 1){
                        transform.LookAt(ObjectTarget.transform.position);
                        direction = this.transform.forward;
                        direction.Normalize();
                        character.Move(direction);
                    }
                }
               
            }else{
                ScanForTarget();
            }
        }
    }
}

Generating "Always On Top" NSWindow in macOS across all detected displays

Also: Using UIKit & Cocoa Frameworks using Objective-C In m acOS or OS X , written in either Objective-C or Swift  Langues, you m...