I was wrong. The Reduce keys option both in the track section as well as in the CurveEditor only reduce keys based on their value instead of their time. So this solution does not work. Turns out there is no built-in way to remove duplicate keys.
So I wrote a small CurveEditorFilter class that seems to do the job.
No guarantees that this works flawlessly.
CurveEditorTimeReduceFilter.h
#pragma once
#include "Filters/CurveEditorFilterBase.h"
#include "CurveEditorTimeReduceFilter.generated.h"
UCLASS(DisplayName="Simplify Time")
class UCurveEditorTimeReduceFilter : public UCurveEditorFilterBase
{
GENERATED_BODY()
public:
explicit UCurveEditorTimeReduceFilter();
protected:
UPROPERTY(EditAnywhere, meta=(Units="s"))
double Tolerance;
virtual void ApplyFilter_Impl(TSharedRef<FCurveEditor> InCurveEditor, const TMap<FCurveModelID, FKeyHandleSet>& InKeysToOperateOn, TMap<FCurveModelID, FKeyHandleSet>& OutKeysToSelect) override;
};
CurveEditorTimeReduceFilter.cpp
#include "Curve/Filter/CurveEditorTimeReduceFilter.h"
UCurveEditorTimeReduceFilter::UCurveEditorTimeReduceFilter() : Super(), Tolerance(0.01) {}
void UCurveEditorTimeReduceFilter::ApplyFilter_Impl(const TSharedRef<FCurveEditor> InCurveEditor, const TMap<FCurveModelID, FKeyHandleSet>& InKeysToOperateOn, TMap<FCurveModelID, FKeyHandleSet>& OutKeysToSelect)
{
OutKeysToSelect = InKeysToOperateOn;
for (const auto& Pair : InKeysToOperateOn)
{
const auto CurveId = Pair.Key;
const auto& KeySet = Pair.Value;
auto* Curve = InCurveEditor->FindCurve(CurveId);
if (!Curve || KeySet.Num() <= 1)
{
continue;
}
const TArray<FKeyHandle> Keys(KeySet.AsArray());
TArray<FKeyPosition> Positions;
Positions.SetNum(Keys.Num());
Curve->GetKeyPositions(Keys, Positions);
TArray<FKeyHandle> KeysToRemove;
auto& SelectedKeysInCurve = OutKeysToSelect[CurveId];
auto LastTime = Positions[0].InputValue;
for (int32 i = 1; i < Positions.Num(); i++)
{
const auto CurrentTime = Positions[i].InputValue;
if (FMath::IsNearlyEqual(CurrentTime, LastTime, Tolerance))
{
KeysToRemove.Add(Keys[i]);
SelectedKeysInCurve.Remove(Keys[i], ECurvePointType::Key);
}
else
{
LastTime = CurrentTime;
}
}
if (KeysToRemove.Num() > 0)
{
Curve->Modify();
Curve->RemoveKeys(KeysToRemove);
}
}
}
Drop these into one of your editor modules and make sure to add a dependency on “CurveEditor” in your build file as well.
Then select all the keys you want to de-duplicate in the CurveEditor, go to Filters and select the Simplify Time filter. Adjust tolerance as needed and apply.