Implementing blinking light in C++

Hi,

I’m trying to learn C++ API, and to do that I tried to implement blinking light in C++. I did that first in Blueprints following this tutorial, and it works great: https://www.youtube.com/watch?v=pE88f3QRp0A.

Unfortunately when I’m trying to replicate this logic in C++ my light blinks way too fast, and I don’t understand why. I prepared a recording from my test level with two lights:

  • blue light is implemented in C++, you can see it blinks very fast
  • red light is implemented in blueprint using logic from tutorial linked above.

You can find recording here (uploading to youtube because new users cannot add attachments): https://www.youtube.com/watch?v=DhvG0jZDnTE

In above example both lights have same parameters:

  • min intensity: 0.2f
  • max intensity 0.8f
  • pulse rate: 5.0f

Source code of C++ component with logic:

// Fill out your copyright notice in the Description page of Project Settings.


#include "BlinkerCpp.h"
#include "Components/PointLightComponent.h"

UBlinkerCpp::UBlinkerCpp()
{
	PrimaryComponentTick.bCanEverTick = true;
	m_ProgressTime = 0.0f;
	m_ProgressAlpha = 0.0f;
}

void UBlinkerCpp::BeginPlay()
{
	Super::BeginPlay();

	if (UPointLightComponent* light = GetOwner()->FindComponentByClass<UPointLightComponent>())
	{
		light->SetIntensity(5.0f);
	}
}

void UBlinkerCpp::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	m_ProgressTime = FMath::Modulo(m_ProgressTime + DeltaTime, m_PulseRate);
	m_ProgressAlpha = m_ProgressTime / m_PulseRate;
	float multiply = m_ProgressAlpha * 360.0f;
	float sinResult = FMath::Sin(multiply);
	float result = FMath::GetMappedRangeValueClamped(FVector2f(-1.0f, 1.0f), FVector2f(m_MinLightIntensity, m_MaxLightIntensity), sinResult);

	if (UPointLightComponent* light = GetOwner()->FindComponentByClass<UPointLightComponent>())
	{
		light->SetIntensity(result);
	}
}

I’m not sure where did I make a mistake. Could someone please help me out find the difference between C++ logic and blueprint logic?

You were pretty close :slight_smile:

You may need to adjust the max value depending on the units you measure the intensity in.

Add include to the cpp file

#include "Kismet/KismetMathLibrary.h"


void UBlinkerCpp::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	UKismetMathLibrary::FMod(m_ProgressTime + DeltaTime, m_PulseRate, m_ProgressTime);	
	m_ProgressAlpha = m_ProgressTime / m_PulseRate;
	float multiply = m_ProgressAlpha * 360.0f;
	float sinResult = UKismetMathLibrary::DegSin(multiply);
	
	float result = FMath::GetMappedRangeValueClamped(FVector2f(-1.0f, 1.0f), FVector2f(m_MinLightIntensity, m_MaxLightIntensity), sinResult);

	if (UPointLightComponent* light = GetOwner()->FindComponentByClass<UPointLightComponent>())
	{
		light->SetIntensity(result);
	}
}

It works, thank you very much! So the implementation of math functions in Kismet is slightly different, then the ones in FMath?

Also, side question, is this actually best way of doing this calculation? I’m talking about the solution shown in this blueprint tutorial I linked in first post. I’m probably wrong (because I’m terrible when it comes to math), but I feel there should be simpler solution?

There is no FMath::Modulo. or any function in the entire engine base called Modulo at least in the newer versions of the engine.
I’m guessing the main sin function might take in a value based on radians while your function as per the the youtube tutorial you can see that the creator uses sind as in sinus function that takes in degrees.

The base function FMath::Sin uses the generic platform implementation that most likely takes in radians. Hence the wrong outcome.

As for other options it’s tough.

You could have a curve with values (would be more flexible than sin) that you could read the value at a specific time and maybe try linking it to a timer function, but it still would have to update often to seem smooth. It also gives you a timer handle in return that you can use to pause / play & even cancel the timer.

Hmm, interesting, I have it on 5.4.4 inside “Runtime/Core/Public/Math/UnrealMathUtility.h”. I tested your solution using FMath::Modulo instead of UKismetMathLibrary::FMod, and it still works, so I guess the important bit is usage of UKismetMathLibrary::DegSin instead of FMath::Sin, just as you described yourself.

Also thank you for ideas for different implementation :slight_smile:

i have another question, it’s related to this one, so I’m not sure if I should post it in here or start a new thread. I’ll try here, please let me know if I should move it to new thread. Question: I noticed that the speed of blinking slows down when current intensity is close to min and max values, and speeds up in the middle. While it looks smooth, I was trying to change it so that speed remains the same. I assume this is related to the fact that I’m using sin function, and I should be using lerp, but simply replacing call to UKismetMathLibrary::DegSin with UKismetMathLibrary::Lerp produces weird results. I assume it’s just not that simple. Could you please help me out figuring out how to make that change?

Most likely the linear fade should be able to be done via the timer + curve with linear curve points. I’ll try to implement it.

in header add properties

	UPROPERTY(EditAnywhere, Category = Curve)
	UCurveFloat* Curve;
	
	float CurveLength = 0;

	FTimerHandle TimerHandle;

	float elapsed = 0;

	void UpdateLight();

in cpp


// Called when the game starts
void UBlinkerCpp::BeginPlay()
{
	Super::BeginPlay();

	if (UPointLightComponent* light = GetOwner()->FindComponentByClass<UPointLightComponent>())
	{
		light->SetIntensity(5.0f);
	}
	GetWorld()->GetTimerManager().SetTimer(TimerHandle,this, &UBlinkerCpp::UpdateLight, 0.1f, true);	
	double minTime;
	double maxTime;
	Curve->GetTimeRange(minTime, maxTime);
	CurveLength = maxTime;

}


void UBlinkerCpp::UpdateLight()
{
	if (Curve != nullptr) {
		float value = Curve->GetFloatValue(elapsed);
		elapsed++;
		if (elapsed >= CurveLength) {
			elapsed = 0;
		}		
		if (UPointLightComponent* light = GetOwner()->FindComponentByClass<UPointLightComponent>())
		{
			light->SetIntensity(value);
		}
	}
}

The curve float takes over the characteristics

Example:

curve:
obraz

You can also add round points to slow down fade and add a change of speed for the pulse.

Not sure if timers internally take into account delta time.

With the timer the light will update in this case ever 0,1 second, instead of every frame.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.