Animation Node, Entire Source for a Turn In Place Node - Epic Wiki
# Animation Node, Entire Source for a Turn In Place Node
# Contents
# Overview
Dear Community,
Here is my entire code for a Turn In Place animation node!
This node detects when there is little to no velocity, but the character is changing directions constantly.
This code shows you my method of accessing the player character from within the animation node code.
# Animation USTRUCT
# .h
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. #pragma once #include "AnimNode_VictoryTurnInPlace.generated.h" USTRUCT() struct FAnimNode_VictoryTurnInPlace : public FAnimNode_Base { GENERATED_USTRUCT_BODY() /** Base Pose*/ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links) FPoseLink BasePose; /** Turning In Place! */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links) FPoseLink TurnPose; /** How Quickly to Blend In/Out of Turn Pose */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links, meta=(PinShownByDefault)) float TurnBlendDuration; /** What Amount of Turn Per Tick Qualifies for Maximum Turn Blending? Anything less per tick will result in slower Turn Blending. Result: If player turns slowly, the turn blend blends in slowly, and ramps up smoothly to max turn blend as player turns faster. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links, meta=(PinShownByDefault)) float TurnSpeedModifierMAX; /** The Lower This Number The Faster The Turn In Place Anim Will Activate */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links, meta=(PinShownByDefault)) float TurnSensitivity; /** The Lower This Number The Faster The Turn In Place Anim Will Activate */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links, meta=(PinShownByDefault) ) float MoveSensitivity; /** Seeing this in the log can help you decided what TurnSpeedModifierMAX to use */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Logs) float ShowTurnRotationChangePerTick; // FAnimNode_Base interface public: // FAnimNode_Base interface virtual void Initialize(const FAnimationInitializeContext& Context) OVERRIDE; virtual void Update(const FAnimationUpdateContext & Context) OVERRIDE; virtual void Evaluate(FPoseContext& Output) OVERRIDE; // End of FAnimNode_Base interface //~~~ Constructor ~~~ public: FAnimNode_VictoryTurnInPlace(); //Functions protected: void DetermineUseTurnPose(); void UpdateBlendAlpha(); protected: //Our very own Blend node, yay! (makes this all super clear) FAnimationNode_TwoWayBlend OurVeryOwnBlend; AActor * OwningActor; FVector PrevLoc; FVector CurLoc; float PrevYaw; float CurYaw; float TurnAmountThisTick; bool WorldIsGame; //~~~ Blending ~~~ float BlendDurationMult; //blend slower if moving slower float InternalBlendDuration; //divided the input by 100 just cause it looks better that way float BlendAlpha; bool BlendingIntoTurnPose; //false = blending out of FVector2D RangeIn; FVector2D RangeOut; };
# .CPP
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. #include "VictoryGame.h" //#include "AnimationRuntime.h" FAnimNode_VictoryTurnInPlace::FAnimNode_VictoryTurnInPlace() : FAnimNode_Base() , TurnBlendDuration(4.f) , TurnSpeedModifierMAX(4.333) , TurnSensitivity(0.777f) , MoveSensitivity(25.f) { WorldIsGame = false; BlendDurationMult = 1; InternalBlendDuration = TurnBlendDuration / 100; RangeIn = FVector2D(0, TurnSpeedModifierMAX); RangeOut = FVector2D(0, 1); ShowTurnRotationChangePerTick = false; } void FAnimNode_VictoryTurnInPlace::Initialize(const FAnimationInitializeContext & Context) { //Init the Inputs BasePose.Initialize(Context); TurnPose.Initialize(Context); //Get the Actor Owner OwningActor = Context.AnimInstance-> GetSkelMeshComponent()->GetOwner(); //Editor or Game? UWorld * TheWorld = Context.AnimInstance->GetWorld(); if (!TheWorld) return; //~~~~~~~~~~~~~~~~ WorldIsGame = (TheWorld->WorldType == EWorldType::Game); //~~~ //~~~ Init the Blend ~~~ OurVeryOwnBlend.A = BasePose; OurVeryOwnBlend.B = TurnPose; OurVeryOwnBlend.Initialize(Context); } void FAnimNode_VictoryTurnInPlace::DetermineUseTurnPose() { //Delta time //Context.GetDeltaTime(); //Get Current CurYaw = OwningActor->GetActorRotation().Yaw; CurLoc = OwningActor->GetActorLocation(); //~~~ Choose Turn Pose or Base Pose ~~~ //Yaw Delta Amount TurnAmountThisTick = FMath::Abs(CurYaw - PrevYaw); if (TurnAmountThisTick < TurnSensitivity) { BlendingIntoTurnPose = false; } //Turning Amount is Sufficient and Movement is slow enough else if(FVector::DistSquared(CurLoc, PrevLoc) < MoveSensitivity) { BlendingIntoTurnPose = true; } //~~~ Save Previous ~~~ PrevYaw = CurYaw; PrevLoc = CurLoc; //Log the Change in Rotation Per Tick if(ShowTurnRotationChangePerTick) UE_LOG(LogAnimation, Warning, TEXT("turn difference per tick, %f"), TurnAmountThisTick); //~~~ Calc Blend Mult ~~~ //In case this gets modified during game time RangeIn.Y = TurnSpeedModifierMAX; //Mapped Range BlendDurationMult = FMath::GetMappedRangeValue(RangeIn, RangeOut, TurnAmountThisTick); } void FAnimNode_VictoryTurnInPlace::UpdateBlendAlpha() { if (BlendingIntoTurnPose) { if (BlendAlpha >= 1) BlendAlpha = 1; else BlendAlpha += InternalBlendDuration * BlendDurationMult; //modify blend-in by speed of turning } //Blending out else { if (BlendAlpha <= 0) BlendAlpha = 0; else BlendAlpha -= InternalBlendDuration; } } void FAnimNode_VictoryTurnInPlace::Update(const FAnimationUpdateContext & Context) { //EDITOR //Editor mode? just use the base pose if (!WorldIsGame) { BlendAlpha = 0; } //GAME //Actually in Game so the Owner Instance Should Exist else { //Try Again if not found if (!OwningActor) OwningActor = Context.AnimInstance->GetSkelMeshComponent()->GetOwner(); //Not found if (!OwningActor) { UE_LOG(LogAnimation, Warning, TEXT("FAnimNode_VictoryTurnInPlace::Update() Owning Actor was not found")); return; //~~~~~~~~~~~~~~~~~~~ } //~~~ Determine Use Turn Pose ~~~ DetermineUseTurnPose(); //~~~ Calc Blend Alpha ~~~ UpdateBlendAlpha(); } //~~~ Do Updates ~~~ //At end of Blend, only evaluate 1, save resources //************************************************************************** // FPoseLinkBase::Update Active Pose - this is what makes the glowing line thing happen and animations loop //************************************************************************** if (BlendAlpha >= 1) TurnPose.Update(Context); else if (BlendAlpha <= 0) BasePose.Update(Context); //Currently Blending else { //Blend node below handles this now //BasePose.Update(Context); //TurnPose.Update(Context); //~~~ Update the Blend ~~~ OurVeryOwnBlend.Alpha = BlendAlpha; OurVeryOwnBlend.Update(Context); } //*************************************** // Evaluate Graph, see AnimNode_Base, AnimNodeBase.h EvaluateGraphExposedInputs.Execute(Context); //*************************************** } void FAnimNode_VictoryTurnInPlace::Evaluate(FPoseContext & Output) { //~~~ Fully In Base Pose ~~~ if(BlendAlpha <= 0) BasePose.Evaluate(Output); //~~~ Fully In Turn Pose ~~~ else if (BlendAlpha >= 1) TurnPose.Evaluate(Output); //~~~ Currently Blending ~~~ else { OurVeryOwnBlend.Evaluate(Output); } }
# World Is Game
Please note the use of WorldIsGame.
In the editor, there is no instanced version of the Character, so I do not run that part of the code.
Here is how you can determine if your node is running in the Editor preview or in the actual game!
WorldIsGame = (TheWorld->WorldType == EWorldType::Game);
# Animation Graph Node
# .H
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. #pragma once #include "AnimGraphDefinitions.h" #include "Kismet2/BlueprintEditorUtils.h" #include "AnimGraphNode_VictoryTurnInPlace.generated.h" //Whole point of this is to be wrapper for node struct // so it depends on it, and that node must compile first // for type to be recognized UCLASS(MinimalAPI, dependson=FAnimNode_VictoryTurnInPlace) class UAnimGraphNode_VictoryTurnInPlace : public UAnimGraphNode_Base { GENERATED_UCLASS_BODY() UPROPERTY(EditAnywhere, Category=Settings) FAnimNode_VictoryTurnInPlace Node; public: // UEdGraphNode interface virtual FString GetNodeTitle(ENodeTitleType::Type TitleType) const OVERRIDE; virtual FLinearColor GetNodeTitleColor() const OVERRIDE; virtual FString GetNodeCategory() const OVERRIDE; // End of UEdGraphNode interface protected: // UAnimGraphNode_SkeletalControlBase interface virtual FString GetControllerDescription() const; // End of UAnimGraphNode_SkeletalControlBase interface };
# .CPP
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. #include "VictoryGame.h" ///////////////////////////////////////////////////// // UAnimGraphNode_VictoryTurnInPlace UAnimGraphNode_VictoryTurnInPlace::UAnimGraphNode_VictoryTurnInPlace(const FPostConstructInitializeProperties& PCIP) : Super(PCIP) { } //Title Color! FLinearColor UAnimGraphNode_VictoryTurnInPlace::GetNodeTitleColor() const { return FLinearColor(0,12,12,1); } //Node Category FString UAnimGraphNode_VictoryTurnInPlace::GetNodeCategory() const { return FString("Victory Anim Nodes"); } FString UAnimGraphNode_VictoryTurnInPlace::GetControllerDescription() const { return TEXT("~~~ Victory Turn In Place ~~~"); } FString UAnimGraphNode_VictoryTurnInPlace::GetNodeTitle(ENodeTitleType::Type TitleType) const { FString Result = *GetControllerDescription(); Result += (TitleType == ENodeTitleType::ListView) ? TEXT("") : TEXT("\n"); return Result; }
Enjoy!
In UE 4.7 you need adequate include headers for FAnim* classes :
- include "Runtime/Engine/Classes/Animation/AnimNodeBase.h"
- include "Runtime/Engine/Classes/Animation/InputScaleBias.h"
- include "Runtime/Engine/Classes/Animation/AnimNode_TwoWayBlend.h"
Retrieved from "https://wiki.unrealengine.com/index.php?title=Animation_Node,_Entire_Source_for_a_Turn_In_Place_Node&oldid=12295"