Skip to content

Instantly share code, notes, and snippets.

@Firefly74940
Created January 13, 2019 14:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Firefly74940/89cb14fa3c9396451070d2e22232d53a to your computer and use it in GitHub Desktop.
Save Firefly74940/89cb14fa3c9396451070d2e22232d53a to your computer and use it in GitHub Desktop.
Random Sequence Player NoLoop
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "DPGPluginPrivatePCH.h"
#include "AnimNode_WRandomPlayer.h"
#include "AnimationRuntime.h"
#include "Animation/AnimInstanceProxy.h"
FAnimNode_WRandomPlayer::FAnimNode_WRandomPlayer()
: CurrentEntry(INDEX_NONE)
, CurrentDataIndex(0)
{
}
void FAnimNode_WRandomPlayer::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
FAnimNode_Base::Initialize_AnyThread(Context);
EvaluateGraphExposedInputs.Execute(Context);
const int32 NumEntries = Entries.Num();
if(NumEntries == 0)
{
// early out here, no need to do anything at all if we're not playing anything
return;
}
NormalizedPlayChances.Empty(NormalizedPlayChances.Num());
NormalizedPlayChances.AddUninitialized(NumEntries);
// Initialize normalized play chance for each entry and validate entry data
float SumChances = 0.0f;
for(FWRandomPlayerSequenceEntry& Entry : Entries)
{
SumChances += Entry.ChanceToPlay;
}
for(int32 Idx = 0 ; Idx < NumEntries ; ++Idx)
{
NormalizedPlayChances[Idx] = Entries[Idx].ChanceToPlay / SumChances;
}
// Initialize random stream and pick first entry
RandomStream.Initialize(FPlatformTime::Cycles());
CurrentEntry = GetNextEntryIndex();
PlayData.Empty(2);
PlayData.AddDefaulted(2);
FWRandomAnimPlayData& CurrentData = PlayData[CurrentDataIndex];
// Init play data
CurrentData.PlayRate = RandomStream.FRandRange(Entries[CurrentEntry].MinPlayRate, Entries[CurrentEntry].MaxPlayRate);
}
void FAnimNode_WRandomPlayer::Update_AnyThread(const FAnimationUpdateContext& Context)
{
EvaluateGraphExposedInputs.Execute(Context);
if(Entries.Num() == 0)
{
// We don't have any entries, play data will be invalid - early out
return;
}
FWRandomAnimPlayData* CurrentData = &PlayData[CurrentDataIndex];
if(UAnimSequence* CurrentSequence = Entries[CurrentEntry].Sequence)
{
if(FAnimInstanceProxy* AnimProxy = Context.AnimInstanceProxy)
{
FAnimGroupInstance* SyncGroup;
FAnimTickRecord& TickRecord = AnimProxy->CreateUninitializedTickRecord(INDEX_NONE, SyncGroup);
AnimProxy->MakeSequenceTickRecord(TickRecord, Entries[CurrentEntry].Sequence, false, CurrentData->PlayRate, Context.GetFinalBlendWeight(), CurrentData->InternalTimeAccumulator, CurrentData->MarkerTickRecord);
}
}
}
void FAnimNode_WRandomPlayer::Evaluate_AnyThread(FPoseContext& Output)
{
if(Entries.Num() > 0)
{
UAnimSequence* CurrentSequence = Entries[CurrentEntry].Sequence;
if(CurrentSequence)
{
FWRandomAnimPlayData& CurrentData = PlayData[CurrentDataIndex];
{
// Single anim
CurrentSequence->GetAnimationPose(Output.Pose, Output.Curve, FAnimExtractContext(CurrentData.InternalTimeAccumulator, Output.AnimInstanceProxy->ShouldExtractRootMotion()));
}
}
else
{
Output.ResetToRefPose();
}
}
else
{
Output.ResetToRefPose();
}
}
void FAnimNode_WRandomPlayer::GatherDebugData(FNodeDebugData& DebugData)
{
FString DebugLine = DebugData.GetNodeName(this);
DebugData.AddDebugItem(DebugLine, true);
}
int32 FAnimNode_WRandomPlayer::GetNextEntryIndex()
{
if(Entries.Num() > 0)
{
{
float RandomVal = RandomStream.GetFraction();
const int32 NumEntries = Entries.Num();
// Grab the entry index corresponding to the value
for(int32 Idx = 0 ; Idx < NumEntries ; ++Idx)
{
RandomVal -= NormalizedPlayChances[Idx];
if(RandomVal <= 0.0f)
{
return Idx;
}
}
}
}
return INDEX_NONE;
}
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Math/RandomStream.h"
#include "Animation/AnimationAsset.h"
#include "Animation/AnimNodeBase.h"
#include "AnimNode_WRandomPlayer.generated.h"
struct FWRandomAnimPlayData
{
FWRandomAnimPlayData()
: InternalTimeAccumulator(0.0f)
, PlayRate(1.0f)
{
}
// Current time through the sequence
float InternalTimeAccumulator;
// Calculated play rate
float PlayRate;
// Marker tick record for this play through
FMarkerTickRecord MarkerTickRecord;
};
/** The random player node holds a list of sequences and parameter ranges which will be played continuously
* In a random order. If shuffle mode is enabled then each entry will be played once before repeating any
*/
USTRUCT(BlueprintInternalUseOnly)
struct FWRandomPlayerSequenceEntry
{
GENERATED_BODY()
FWRandomPlayerSequenceEntry()
: Sequence(nullptr)
, ChanceToPlay(1.0f)
, MinPlayRate(1.0f)
, MaxPlayRate(1.0f)
{
}
/** Sequence to play when this entry is picked */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
UAnimSequence* Sequence;
/** When not in shuffle mode, this is the chance this entry will play (normalized against all other sample chances) */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
float ChanceToPlay;
/** Minimum playrate for this entry */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings", meta=(UIMin="0", ClampMin="0"))
float MinPlayRate;
/** Maximum playrate for this entry */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings", meta=(UIMin="0", ClampMin="0"))
float MaxPlayRate;
};
USTRUCT(BlueprintInternalUseOnly)
struct DPGPLUGIN_API FAnimNode_WRandomPlayer : public FAnimNode_Base
{
GENERATED_BODY()
FAnimNode_WRandomPlayer();
public:
/** List of sequences to randomly step through */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
TArray<FWRandomPlayerSequenceEntry> Entries;
// FAnimNode_Base interface
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
virtual void Evaluate_AnyThread(FPoseContext& Output) override;
virtual void GatherDebugData(FNodeDebugData& DebugData) override;
// End of FAnimNode_Base interface
private:
int32 GetNextEntryIndex();
// Normalized list of play chances when we aren't using shuffle mode
TArray<float> NormalizedPlayChances;
// The currently playing entry in the entries list
int32 CurrentEntry;
// Index of the 'current' data set in the PlayData array
int32 CurrentDataIndex;
// Play data for the current and next sequence
TArray<FWRandomAnimPlayData> PlayData;
// Random number source
FRandomStream RandomStream;
};
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "DPGPluginPrivatePCH.h"
#include "Animation/AnimInstanceProxy.h"
#include "AnimNode_WRandomPlayerEditor.h"
#ifdef WITH_EDITOR
#include "EditorCategoryUtils.h"
#endif
#define LOCTEXT_NAMESPACE "AnimGraphNode_RandomPlayerNoLoop"
FLinearColor UAnimGraphNode_WRandomPlayer::GetNodeTitleColor() const
{
return FLinearColor(0.10f, 0.60f, 0.12f);
}
FText UAnimGraphNode_WRandomPlayer::GetTooltipText() const
{
return LOCTEXT("NodeToolTip", "Plays sequences picked from a provided list in random orders without looping");
}
FText UAnimGraphNode_WRandomPlayer::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("NodeTitle", "Random Sequence Player no Loop");
}
FText UAnimGraphNode_WRandomPlayer::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Animation);
}
#undef LOCTEXT_NAMESPACE
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Math/RandomStream.h"
#include "Editor/AnimGraph/Classes/AnimGraphNode_Base.h"
#include "src/AnimNode_WRandomPlayer.h"
#include "AnimNode_WRandomPlayerEditor.generated.h"
UCLASS(MinimalAPI)
class UAnimGraphNode_WRandomPlayer : public UAnimGraphNode_Base
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_WRandomPlayer Node;
// UEdGraphNode interface
virtual FLinearColor GetNodeTitleColor() const override;
virtual FText GetTooltipText() const override;
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FText GetMenuCategory() const override;
// End of UEdGraphNode interface
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment