{"id":83,"date":"2021-09-09T23:07:39","date_gmt":"2021-09-09T15:07:39","guid":{"rendered":"http:\/\/www.jinhaihan.top\/WordPress\/?p=83"},"modified":"2021-10-07T17:44:12","modified_gmt":"2021-10-07T09:44:12","slug":"%e4%b8%80%e7%a7%8d%e7%ae%80%e5%8d%95%e7%9a%84cai-task%e5%ae%9e%e7%8e%b0","status":"publish","type":"post","link":"https:\/\/www.jinhaihan.top\/WordPress\/%e4%b8%80%e7%a7%8d%e7%ae%80%e5%8d%95%e7%9a%84cai-task%e5%ae%9e%e7%8e%b0\/","title":{"rendered":"\u4e00\u79cd\u7b80\u5355\u7684C++AI Task\u5b9e\u73b0"},"content":{"rendered":"<p>\u7ecf\u8fc7\u8003\u8651\uff0c\u51b3\u5b9a\u4f7f\u7528C++\u5199AI\u7c7b\u4ee5\u63d0\u5347\u6027\u80fd\u3002<br \/>\n\u4f46\u5bf9\u4e8eC++\u7684AI Task\u7c7b\u6ca1\u6709\u7ecf\u9a8c\uff0c\u4e5f\u6ca1\u6709\u4e00\u4e9b\u4f18\u79c0\u7684\u6837\u4f8b\u5b66\u4e60\u53c2\u8003\uff0c\u4e8e\u662f\u60f3\u5230\u53c2\u8003\u5f15\u64ce\u5185\u7684\u5b9e\u73b0\u3002<\/p>\n<p>\u53c2\u8003\u84dd\u56fe\u7c7bUBTTask_BlueprintBase\u7684\u5b9e\u73b0\u65b9\u5f0f\uff0c\u53ef\u4ee5\u65b0\u5efaC++\u7c7b\u5e76\u590d\u5236\u5185\u5bb9\u3002<\/p>\n<p>\u6ce8\u610f\uff0c\u6b64\u5904\u5e94\u5f53\u6ce8\u91ca\u6216\u5220\u9664<code>FBTNodeBPImplementationHelper::AISpecific<\/code>\uff0c\u6b64\u4e3a\u5e38\u91cf\u4f1a\u5f71\u54cd\u8fd0\u884c\u3002<\/p>\n<p>\u590d\u5236\u4e4b\u540e\uff0c\u53ef\u4ee5\u5c06\u6b64\u4f5c\u4e3aC++\u7684AI\u57fa\u7c7b\u3002\u5728\u5b50\u7c7b\u7684\u6784\u9020\u51fd\u6570\u4e2d\u542f\u7528\u76f8\u5e94\u7684\u53d8\u91cf\uff0c\u5373\u53ef\u542f\u7528\u5bf9\u5e94\u7684\u51fd\u6570\u56de\u8c03\u3002<\/p>\n<p>\u4e0b\u9762\u662f\u4e2a\u5b50\u7c7b\u4f8b\u5b50\uff08\u57fa\u7c7b\u4ee3\u7801\u653e\u5728\u6700\u540e\u9762\uff09\uff1a<\/p>\n<pre><code class=\"language-cpp\">\/\/.CPP File\n\/\/\u6784\u9020\u5668\u4e2d\u542f\u7528ReceiveExecuteAI\u3001Tick\u7684\u56de\u8c03\uff1a\nUBTTask_Follow::UBTTask_Follow(const FObjectInitializer&amp; ObjectInitializer) : Super(ObjectInitializer)\n{\n    ReceiveExecuteImplementations = 1;\n    ReceiveTickImplementations = 1;\n    bNotifyTick =1;\n}\n\n\/\/\u5b9e\u73b0\u56de\u8c03\uff1a\nvoid UBTTask_Follow::ReceiveExecuteAI(AAIController* OwnerController, APawn* ControlledPawn)\n{\n}\n\nvoid UBTTask_Follow::ReceiveTickAI(AAIController* OwnerController, APawn* ControlledPawn, float DeltaSeconds)\n{\n}<\/code><\/pre>\n<pre><code class=\"language-cpp\">\/\/.h File\nUCLASS()\nclass PROJECTSPARK_API UBTTask_Follow : public UTaskNodeBase\n{\n    GENERATED_BODY()\n    UBTTask_Follow(const FObjectInitializer&amp; ObjectInitializer);\n\n    public:\n\n    protected:\n    virtual void ReceiveExecuteAI(AAIController* OwnerController, APawn* ControlledPawn) override;\n    virtual void ReceiveTickAI(AAIController* OwnerController, APawn* ControlledPawn, float DeltaSeconds) override;\n};\n<\/code><\/pre>\n<hr \/>\n<p>\u4e0b\u9762\u662f\u57fa\u7c7b\u5b9e\u73b0\uff1a<\/p>\n<pre><code class=\"language-cpp\">\/\/.h File\n\/\/ Fill out your copyright notice in the Description page of Project Settings.\n\n#pragma once\n\n#include &quot;CoreMinimal.h&quot;\n#include &quot;UObject\/ObjectMacros.h&quot;\n#include &quot;BehaviorTree\/BTTaskNode.h&quot;\n#include &quot;TaskNodeBase.generated.h&quot;\n\nUCLASS()\nclass PROJECTSPARK_API UTaskNodeBase : public UBTTaskNode\n{\n    GENERATED_BODY()\n    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory) override;\n    virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory) override;\n\n    virtual FString GetStaticDescription() const override;\n    virtual void DescribeRuntimeValues(const UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray&lt;FString&gt;&amp; Values) const override;\n    virtual void OnInstanceDestroyed(UBehaviorTreeComponent&amp; OwnerComp) override;\n    virtual void OnTaskFinished(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult) override;\n    virtual void InitializeFromAsset(UBehaviorTree&amp; Asset) override;\n\n    virtual void SetOwner(AActor* ActorOwner) override;\n\n#if WITH_EDITOR\n    virtual bool UsesBlueprint() const override;\n#endif\n\npublic:\n    UTaskNodeBase(const FObjectInitializer&amp; ObjectInitializer);\n\nprotected:\n    \/** Cached AIController owner of BehaviorTreeComponent. *\/\n    UPROPERTY(Transient)\n    TObjectPtr&lt;AAIController&gt; AIOwner;\n\n    \/** Cached actor owner of BehaviorTreeComponent. *\/\n    UPROPERTY(Transient)\n    TObjectPtr&lt;AActor&gt; ActorOwner;\n\n    \/** If any of the Tick functions is implemented, how often should they be ticked.\n     *  Values &lt; 0 mean &#039;every tick&#039;. *\/\n    UPROPERTY(EditAnywhere, Category = Task)\n    FIntervalCountdown TickInterval;\n\n    \/** temporary variable for ReceiveExecute(Abort)-FinishExecute(Abort) chain *\/\n    mutable TEnumAsByte&lt;EBTNodeResult::Type&gt; CurrentCallResult;\n\n    \/** properties that should be copied *\/\n    TArray&lt;FProperty*&gt; PropertyData;\n\n#if WITH_EDITORONLY_DATA\n    UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Description)\n    FString CustomDescription;\n#endif \/\/ WITH_EDITORONLY_DATA\n\n    \/** show detailed information about properties *\/\n    UPROPERTY(EditInstanceOnly, Category=Description)\n    uint32 bShowPropertyDetails : 1;\n\n    \/** set if ReceiveTick is implemented by blueprint *\/\n    uint32 ReceiveTickImplementations : 2;\n\n    \/** set if ReceiveExecute is implemented by blueprint *\/\n    uint32 ReceiveExecuteImplementations : 2;\n\n    \/** set if ReceiveAbort is implemented by blueprint *\/\n    uint32 ReceiveAbortImplementations : 2;\n\n    \/** set when task enters Aborting state *\/\n    uint32 bIsAborting : 1;\n\n    \/** if set, execution is inside blueprint&#039;s ReceiveExecute(Abort) event\n      * FinishExecute(Abort) function should store their result in CurrentCallResult variable *\/\n    mutable uint32 bStoreFinishResult : 1;\n\n    \/** entry point, task will stay active until FinishExecute is called.\n     *  @Note that if both generic and AI event versions are implemented only the more \n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    UFUNCTION(BlueprintImplementableEvent)\n    void ReceiveExecute(AActor* OwnerActor);\n\n    \/** if blueprint graph contains this event, task will stay active until FinishAbort is called\n     *  @Note that if both generic and AI event versions are implemented only the more\n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    UFUNCTION(BlueprintImplementableEvent)\n    void ReceiveAbort(AActor* OwnerActor);\n\n    \/** tick function\n     *  @Note that if both generic and AI event versions are implemented only the more\n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    UFUNCTION(BlueprintImplementableEvent)\n    void ReceiveTick(AActor* OwnerActor, float DeltaSeconds);\n\n    \/** Alternative AI version of ReceiveExecute\n    *   @see ReceiveExecute for more details\n     *  @Note that if both generic and AI event versions are implemented only the more\n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    virtual void ReceiveExecuteAI(AAIController* OwnerController, APawn* ControlledPawn);\n\n    \/** Alternative AI version of ReceiveAbort\n     *  @see ReceiveAbort for more details\n     *  @Note that if both generic and AI event versions are implemented only the more\n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    UFUNCTION(BlueprintImplementableEvent, Category = AI)\n    void ReceiveAbortAI(AAIController* OwnerController, APawn* ControlledPawn);\n\n    \/** Alternative AI version of tick function.\n     *  @see ReceiveTick for more details\n     *  @Note that if both generic and AI event versions are implemented only the more\n     *  suitable one will be called, meaning the AI version if called for AI, generic one otherwise *\/\n    virtual void ReceiveTickAI(AAIController* OwnerController, APawn* ControlledPawn, float DeltaSeconds);\n\n    \/** finishes task execution with Success or Fail result *\/\n    UFUNCTION(BlueprintCallable, Category=&quot;AI|BehaviorTree&quot;)\n    void FinishExecute(bool bSuccess);\n\n    \/** aborts task execution *\/\n    UFUNCTION(BlueprintCallable, Category=&quot;AI|BehaviorTree&quot;)\n    void FinishAbort();\n\n    \/** task execution will be finished (with result &#039;Success&#039;) after receiving specified message *\/\n    UFUNCTION(BlueprintCallable, Category=&quot;AI|BehaviorTree&quot;)\n    void SetFinishOnMessage(FName MessageName);\n\n    \/** task execution will be finished (with result &#039;Success&#039;) after receiving specified message with indicated ID *\/\n    UFUNCTION(BlueprintCallable, Category=&quot;AI|BehaviorTree&quot;)\n    void SetFinishOnMessageWithId(FName MessageName, int32 RequestID = -1);\n\n    \/** check if task is currently being executed *\/\n    UFUNCTION(BlueprintCallable, Category=&quot;AI|BehaviorTree&quot;)\n    bool IsTaskExecuting() const;\n\n    \/** check if task is currently being aborted *\/\n    UFUNCTION(BlueprintCallable, Category = &quot;AI|BehaviorTree&quot;)\n    bool IsTaskAborting() const;\n\n    \/** ticks this task *\/\n    virtual void TickTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;\n};<\/code><\/pre>\n<pre><code class=\"language-cpp\">\/\/.cpp File \n\/\/ Fill out your copyright notice in the Description page of Project Settings.\n\n#include &quot;AIModule\/TaskNodeBase.h&quot;\n#include &quot;AIController.h&quot;\n#include &quot;BehaviorTree\/BTFunctionLibrary.h&quot;\n#include &quot;BlueprintNodeHelpers.h&quot;\n#include &quot;BehaviorTree\/BehaviorTree.h&quot;\n\nUTaskNodeBase::UTaskNodeBase(const FObjectInitializer&amp; ObjectInitializer) : Super(ObjectInitializer)\n{\n    UClass* StopAtClass = UTaskNodeBase::StaticClass();\n    ReceiveTickImplementations = FBTNodeBPImplementationHelper::CheckEventImplementationVersion(TEXT(&quot;ReceiveTick&quot;), TEXT(&quot;ReceiveTickAI&quot;), *this, *StopAtClass);\n    ReceiveExecuteImplementations = FBTNodeBPImplementationHelper::CheckEventImplementationVersion(TEXT(&quot;ReceiveExecute&quot;), TEXT(&quot;ReceiveExecuteAI&quot;), *this, *StopAtClass);\n    ReceiveAbortImplementations = FBTNodeBPImplementationHelper::CheckEventImplementationVersion(TEXT(&quot;ReceiveAbort&quot;), TEXT(&quot;ReceiveAbortAI&quot;), *this, *StopAtClass);\n\n    bNotifyTick = ReceiveTickImplementations != FBTNodeBPImplementationHelper::NoImplementation;\n    bNotifyTaskFinished = true;\n    bShowPropertyDetails = true;\n    bIsAborting = false;\n\n    \/\/ all blueprint based nodes must create instances\n    bCreateNodeInstance = true;\n\n    if (HasAnyFlags(RF_ClassDefaultObject))\n    {\n        BlueprintNodeHelpers::CollectPropertyData(this, StopAtClass, PropertyData);\n    }\n}\n\nvoid UTaskNodeBase::InitializeFromAsset(UBehaviorTree&amp; Asset)\n{\n    Super::InitializeFromAsset(Asset);\n    if (Asset.BlackboardAsset)\n    {\n        BlueprintNodeHelpers::ResolveBlackboardSelectors(*this, *StaticClass(), *Asset.BlackboardAsset);\n    }\n}\n\nvoid UTaskNodeBase::SetOwner(AActor* InActorOwner) \n{ \n    ActorOwner = InActorOwner;\n    AIOwner = Cast&lt;AAIController&gt;(InActorOwner);\n}\n\nEBTNodeResult::Type UTaskNodeBase::ExecuteTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory)\n{\n    \/\/ fail when task doesn&#039;t react to execution (start or tick)\n    CurrentCallResult = (ReceiveExecuteImplementations != 0 || ReceiveTickImplementations != 0) ? EBTNodeResult::InProgress : EBTNodeResult::Failed;\n    bIsAborting = false;\n\n    if (ReceiveExecuteImplementations != FBTNodeBPImplementationHelper::NoImplementation)\n    {\n        bStoreFinishResult = true;\n\n        if (AIOwner != nullptr &amp;&amp; (ReceiveExecuteImplementations\/*&amp; FBTNodeBPImplementationHelper::AISpecific*\/))\n        {\n            ReceiveExecuteAI(AIOwner, AIOwner-&gt;GetPawn());\n        }\n        else if (ReceiveExecuteImplementations &amp; FBTNodeBPImplementationHelper::Generic)\n        {\n            ReceiveExecute(ActorOwner);\n        }\n\n        bStoreFinishResult = false;\n    }\n\n    return CurrentCallResult;\n}\n\nEBTNodeResult::Type UTaskNodeBase::AbortTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory)\n{\n    \/\/ force dropping all pending latent actions associated with this blueprint\n    \/\/ we can&#039;t have those resuming activity when node is\/was aborted\n    BlueprintNodeHelpers::AbortLatentActions(OwnerComp, *this);\n\n    CurrentCallResult = ReceiveAbortImplementations != 0 ? EBTNodeResult::InProgress : EBTNodeResult::Aborted;\n    bIsAborting = true;\n\n    if (ReceiveAbortImplementations != FBTNodeBPImplementationHelper::NoImplementation)\n    {\n        bStoreFinishResult = true;\n\n        if (AIOwner != nullptr &amp;&amp; (ReceiveAbortImplementations &amp; FBTNodeBPImplementationHelper::AISpecific))\n        {\n            ReceiveAbortAI(AIOwner, AIOwner-&gt;GetPawn());\n        }\n        else if (ReceiveAbortImplementations &amp; FBTNodeBPImplementationHelper::Generic)\n        {\n            ReceiveAbort(ActorOwner);\n        }\n\n        bStoreFinishResult = false;\n    }\n\n    return CurrentCallResult;\n}\n\nvoid UTaskNodeBase::TickTask(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, float DeltaSeconds)\n{\n    if (TickInterval.Tick(DeltaSeconds))\n    {\n        DeltaSeconds = TickInterval.GetElapsedTimeWithFallback(DeltaSeconds);\n\n        if (AIOwner != nullptr &amp;&amp; (ReceiveTickImplementations \/*&amp; FBTNodeBPImplementationHelper::AISpecific*\/))\n        {\n            ReceiveTickAI(AIOwner, AIOwner-&gt;GetPawn(), DeltaSeconds);\n        }\n        else if (ReceiveTickImplementations &amp; FBTNodeBPImplementationHelper::Generic)\n        {\n            ReceiveTick(ActorOwner, DeltaSeconds);\n        }\n\n        TickInterval.Reset();\n    }\n}\n\nvoid UTaskNodeBase::OnTaskFinished(UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult)\n{\n    Super::OnTaskFinished(OwnerComp, NodeMemory, TaskResult);\n\n    TickInterval.Set(0); \/\/ so that we tick as soon as enabled back\n\n    if (TaskResult != EBTNodeResult::InProgress)\n    {\n        BlueprintNodeHelpers::AbortLatentActions(OwnerComp, *this);\n    }\n}\n\nvoid UTaskNodeBase::FinishExecute(bool bSuccess)\n{\n    UBehaviorTreeComponent* OwnerComp = Cast&lt;UBehaviorTreeComponent&gt;(GetOuter());\n    EBTNodeResult::Type NodeResult(bSuccess ? EBTNodeResult::Succeeded : EBTNodeResult::Failed);\n\n    if (bStoreFinishResult)\n    {\n        CurrentCallResult = NodeResult;\n    }\n    else if (OwnerComp &amp;&amp; !bIsAborting)\n    {\n        FinishLatentTask(*OwnerComp, NodeResult);\n    }\n}\n\nvoid UTaskNodeBase::FinishAbort()\n{\n    UBehaviorTreeComponent* OwnerComp = Cast&lt;UBehaviorTreeComponent&gt;(GetOuter());\n    EBTNodeResult::Type NodeResult(EBTNodeResult::Aborted);\n\n    if (bStoreFinishResult)\n    {\n        CurrentCallResult = NodeResult;\n    }\n    else if (OwnerComp &amp;&amp; bIsAborting)\n    {\n        FinishLatentAbort(*OwnerComp);\n    }\n}\n\nbool UTaskNodeBase::IsTaskExecuting() const\n{\n    UBehaviorTreeComponent* OwnerComp = Cast&lt;UBehaviorTreeComponent&gt;(GetOuter());\n    EBTTaskStatus::Type TaskStatus = OwnerComp-&gt;GetTaskStatus(this);\n\n    return (TaskStatus == EBTTaskStatus::Active);\n}\n\nbool UTaskNodeBase::IsTaskAborting() const\n{\n    \/\/ use already cached data\n    return bIsAborting;\n}\n\nvoid UTaskNodeBase::SetFinishOnMessage(FName MessageName)\n{\n    UBehaviorTreeComponent* OwnerComp = Cast&lt;UBehaviorTreeComponent&gt;(GetOuter());\n    if (OwnerComp)\n    {\n        OwnerComp-&gt;RegisterMessageObserver(this, MessageName);\n    }\n}\n\nvoid UTaskNodeBase::SetFinishOnMessageWithId(FName MessageName, int32 RequestID)\n{\n    UBehaviorTreeComponent* OwnerComp = Cast&lt;UBehaviorTreeComponent&gt;(GetOuter());\n    if (OwnerComp)\n    {\n        OwnerComp-&gt;RegisterMessageObserver(this, MessageName, RequestID);\n    }\n}\n\nFString UTaskNodeBase::GetStaticDescription() const\n{\n    FString ReturnDesc =\n#if WITH_EDITORONLY_DATA\n        CustomDescription.Len() ? CustomDescription :\n#endif \/\/ WITH_EDITORONLY_DATA\n        Super::GetStaticDescription();\n\n    UTaskNodeBase* CDO = (UTaskNodeBase*)(GetClass()-&gt;GetDefaultObject());\n    if (bShowPropertyDetails &amp;&amp; CDO)\n    {\n        UClass* StopAtClass = UTaskNodeBase::StaticClass();\n        FString PropertyDesc = BlueprintNodeHelpers::CollectPropertyDescription(this, StopAtClass, CDO-&gt;PropertyData);\n        if (PropertyDesc.Len())\n        {\n            ReturnDesc += TEXT(&quot;:\\n\\n&quot;);\n            ReturnDesc += PropertyDesc;\n        }\n    }\n\n    return ReturnDesc;\n}\n\nvoid UTaskNodeBase::DescribeRuntimeValues(const UBehaviorTreeComponent&amp; OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray&lt;FString&gt;&amp; Values) const\n{\n    UTaskNodeBase* CDO = (UTaskNodeBase*)(GetClass()-&gt;GetDefaultObject());\n    if (CDO &amp;&amp; CDO-&gt;PropertyData.Num())\n    {\n        BlueprintNodeHelpers::DescribeRuntimeValues(this, CDO-&gt;PropertyData, Values);\n    }\n}\n\nvoid UTaskNodeBase::OnInstanceDestroyed(UBehaviorTreeComponent&amp; OwnerComp)\n{\n    \/\/ force dropping all pending latent actions associated with this blueprint\n    BlueprintNodeHelpers::AbortLatentActions(OwnerComp, *this);\n}\n\n#if WITH_EDITOR\n\nbool UTaskNodeBase::UsesBlueprint() const\n{\n    return true;\n}\n\n#endif \/\/ WITH_EDITOR\n\nvoid UTaskNodeBase::ReceiveExecuteAI(AAIController* OwnerController, APawn* ControlledPawn)\n{\n}\n\nvoid UTaskNodeBase::ReceiveTickAI(AAIController* OwnerController, APawn* ControlledPawn, float DeltaSeconds)\n{\n}\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u7ecf\u8fc7\u8003\u8651\uff0c\u51b3\u5b9a\u4f7f\u7528C++\u5199AI\u7c7b\u4ee5\u63d0\u5347\u6027&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":135,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[13],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/650966ae313a1fc4fe606ca7b66840cb.jpg?fit=1680%2C1050&ssl=1","jetpack-related-posts":[{"id":162,"url":"https:\/\/www.jinhaihan.top\/WordPress\/c-%e7%94%9f%e6%88%90%e8%93%9d%e5%9b%be%e7%b1%bb%e5%af%b9%e8%b1%a1\/","url_meta":{"origin":83,"position":0},"title":"C++ \u751f\u6210\u84dd\u56fe\u7c7b\u5bf9\u8c61","author":"Kim","date":"2022\u5e743\u67082\u65e5","format":false,"excerpt":"\u83b7\u53d6\u84dd\u56fe\u8d44\u6e90\u8def\u5f84 \u5728UE\u4e2d\u53f3\u952e\u8d44\u6e90\uff0c\u9009\u62e9Copy Reference\u5373\u53ef\uff0c\u540e\u7eed\u7528\u5230\u7684\u8def\u5f84\u3001Name\u90fd\u2026","rel":"","context":"\u5728\u201c\u535a\u5ba2\u201d\u4e2d","block_context":{"text":"\u535a\u5ba2","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2022\/03\/R.jpg?fit=1200%2C750&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2022\/03\/R.jpg?fit=1200%2C750&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2022\/03\/R.jpg?fit=1200%2C750&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2022\/03\/R.jpg?fit=1200%2C750&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":241,"url":"https:\/\/www.jinhaihan.top\/WordPress\/%e5%9f%ba%e4%ba%8ekotlin%e5%92%8c%e6%90%ba%e7%a8%8b%e7%9a%84%e4%b8%80%e7%a7%8dretrofit%e5%b0%81%e8%a3%85\/","url_meta":{"origin":83,"position":1},"title":"\u57fa\u4e8eKotlin\u548c\u643a\u7a0b\u7684\u4e00\u79cdRetrofit\u5c01\u88c5","author":"Kim","date":"2023\u5e742\u670819\u65e5","format":false,"excerpt":"\u4e00\u3001\u5c01\u88c5\u6b65\u9aa4 \u76f4\u63a5\u4e0a\u4ee3\u7801\uff1a \u9996\u5148\u5f15\u5165\u76f8\u5173\u4f9d\u8d56\uff1a api 'com.squareup.ret\u2026","rel":"","context":"\u5728\u201cAndroid\u201d\u4e2d","block_context":{"text":"Android","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/android\/"},"img":{"alt_text":"Free network image","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2023\/02\/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvcHgxMDg5NTEyLWltYWdlLWt3dnkzcXdyLmpwZw.jpg?fit=1200%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2023\/02\/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvcHgxMDg5NTEyLWltYWdlLWt3dnkzcXdyLmpwZw.jpg?fit=1200%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2023\/02\/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvcHgxMDg5NTEyLWltYWdlLWt3dnkzcXdyLmpwZw.jpg?fit=1200%2C800&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2023\/02\/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvcHgxMDg5NTEyLWltYWdlLWt3dnkzcXdyLmpwZw.jpg?fit=1200%2C800&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":37,"url":"https:\/\/www.jinhaihan.top\/WordPress\/databinding%e8%87%aa%e5%ae%9a%e4%b9%89%e6%96%b9%e6%b3%95%e3%80%81%e5%8f%82%e6%95%b0%e7%bb%91%e5%ae%9a%ef%bc%88bindingadapter%ef%bc%89\/","url_meta":{"origin":83,"position":2},"title":"DataBinding\u81ea\u5b9a\u4e49\u65b9\u6cd5\u3001\u53c2\u6570\u7ed1\u5b9a\uff08BindingAdapter\uff09","author":"Kim","date":"2020\u5e7411\u670830\u65e5","format":false,"excerpt":"\u53c2\u8003\u6587\u6863\uff1a https:\/\/blog.csdn.net\/guiying712\/article\/det\u2026","rel":"","context":"\u5728\u201cAndroid\u201d\u4e2d","block_context":{"text":"Android","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/android\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/9d44955fb48b1ee67f28e63a99bbd4a2.jpg?fit=1200%2C675&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/9d44955fb48b1ee67f28e63a99bbd4a2.jpg?fit=1200%2C675&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/9d44955fb48b1ee67f28e63a99bbd4a2.jpg?fit=1200%2C675&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/9d44955fb48b1ee67f28e63a99bbd4a2.jpg?fit=1200%2C675&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":64,"url":"https:\/\/www.jinhaihan.top\/WordPress\/64\/","url_meta":{"origin":83,"position":3},"title":"\u865a\u5e7b4\u81ea\u5b9a\u4e49webbrowser\uff0c\u89e3\u9664\u5e27\u7387\u9501\u5b9a","author":"Kim","date":"2021\u5e744\u670830\u65e5","format":false,"excerpt":"\u6e90\u7801\u5206\u6790 Webrowser\u662f\u865a\u5e7b\u5b98\u65b9\u7684webUi\u63d2\u4ef6\uff0c\u4f46\u63d2\u4ef6\u672c\u8eab\u9ed8\u8ba4\u88ab\u9501\u4e8624FPS\uff0c\u5bfc\u81f4\u90e8\u5206\u52a8\u753b\u2026","rel":"","context":"\u5728\u201c\u865a\u5e7b4\/5\u201d\u4e2d","block_context":{"text":"\u865a\u5e7b4\/5","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/unreal-engine\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/338856.jpg?fit=1200%2C750&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/338856.jpg?fit=1200%2C750&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/338856.jpg?fit=1200%2C750&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/338856.jpg?fit=1200%2C750&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":87,"url":"https:\/\/www.jinhaihan.top\/WordPress\/cai%e4%b8%ad%e5%88%87%e6%8d%a2blackboard%e7%9a%84%e5%80%bc\/","url_meta":{"origin":83,"position":4},"title":"C++AI\u4e2d\u5207\u6362BlackBoard\u7684\u503c","author":"Kim","date":"2021\u5e749\u67089\u65e5","format":false,"excerpt":"\u9996\u5148\u5b9a\u4e49\u4e00\u4e2aFBlackboardKeySelector UPROPERTY(EditAnywher\u2026","rel":"","context":"\u5728\u201c\u865a\u5e7b4\/5\u201d\u4e2d","block_context":{"text":"\u865a\u5e7b4\/5","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/unreal-engine\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/888563.png?fit=1200%2C675&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/888563.png?fit=1200%2C675&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/888563.png?fit=1200%2C675&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/888563.png?fit=1200%2C675&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":186,"url":"https:\/\/www.jinhaihan.top\/WordPress\/maven%e6%8f%92%e4%bb%b6-%e5%92%8c-maven-publish-%e6%8f%92%e4%bb%b6%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95-md\/","url_meta":{"origin":83,"position":5},"title":"maven\u63d2\u4ef6 \u548c maven-publish \u63d2\u4ef6\u4f7f\u7528\u65b9\u6cd5.md","author":"Kim","date":"2022\u5e744\u670818\u65e5","format":false,"excerpt":"\u622a\u53d6\u8f6c\u8f7d\u81ea\u7f51\u7edc\uff0c\u65b9\u4fbf\u8bb0\u5f55\u77e5\u8bc6\uff0c\u975e\u539f\u521b \u4e00\u3001Maven\u63d2\u4ef6\u7684\u4f7f\u7528\u65b9\u6cd5 maven \u63d2\u4ef6\u662f Gradle\u2026","rel":"","context":"\u5728\u201cAndroid\u201d\u4e2d","block_context":{"text":"Android","link":"https:\/\/www.jinhaihan.top\/WordPress\/category\/bolg\/android\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/4869_pc.jpg?fit=1200%2C675&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/4869_pc.jpg?fit=1200%2C675&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/4869_pc.jpg?fit=1200%2C675&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/www.jinhaihan.top\/WordPress\/wp-content\/uploads\/2021\/10\/4869_pc.jpg?fit=1200%2C675&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/posts\/83"}],"collection":[{"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/comments?post=83"}],"version-history":[{"count":1,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/posts\/83\/revisions"}],"predecessor-version":[{"id":84,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/posts\/83\/revisions\/84"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/media\/135"}],"wp:attachment":[{"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/media?parent=83"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/categories?post=83"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jinhaihan.top\/WordPress\/wp-json\/wp\/v2\/tags?post=83"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}