Porting Weapon Rotation Leading from UDK/UE3

Hi All,

I’m trying to port the following UTWeapon code to UE4, but I’m a bit stuck on two areas, and was wondering if anyone has some insights on how to solve this:

The two areas I’m not sure of are as follows:

  1. Equivalent of Bitwise operations on Rotators in Unrealscript vs. UE4?
  2. Equivalent of the Rotator operator “ClockWiseFrom” in UE4?

Some help would be greatly appreciated.

Thanks!

Some more details below:

Unrealscript:

This part is called from Weapon.SetPosition()



SpecRotation.Pitch = LagRot(SpecRotation.Pitch & 65535, LastRotation.Pitch & 65535, MaxPitchLag, 1);




simulated function int LagRot(int NewValue, int LastValue, float MaxDiff, int Index)
{
	local int RotDiff;
	local float LeadMag, DeltaTime;

	if ( NewValue ClockWiseFrom LastValue )
	{
		if ( LastValue > NewValue )
		{
			LastValue -= 65536;
		}
	}
	else
	{
		if ( NewValue > LastValue )
		{
			NewValue -= 65536;
		}
	}

	DeltaTime = WorldInfo.TimeSeconds - LastRotUpdate;
	RotDiff = NewValue - LastValue;
	if ( (RotDiff == 0) || (OldRotDiff[Index] == 0) )
	{
		LeadMag = ShouldLagRot() ? OldLeadMag[Index] : 0.0;
		if ( (RotDiff == 0) && (OldRotDiff[Index] == 0) )
		{
			OldMaxDiff[Index] = 0;
		}
	}
	else if ( (RotDiff > 0) == (OldRotDiff[Index] > 0) )
	{
		if (ShouldLagRot())
		{
			MaxDiff = FMin(1, Abs(RotDiff)/(12000*DeltaTime)) * MaxDiff;
			if ( OldMaxDiff[Index] != 0 )
				MaxDiff = FMax(OldMaxDiff[Index], MaxDiff);

			OldMaxDiff[Index] = MaxDiff;
			LeadMag = (NewValue > LastValue) ? -1* MaxDiff : MaxDiff;
		}
		else
		{
			LeadMag = 0;
		}
		if ( DeltaTime < 1/RotChgSpeed )
		{
			LeadMag = (1.0 - RotChgSpeed*DeltaTime)*OldLeadMag[Index] + RotChgSpeed*DeltaTime*LeadMag;
		}
		else
		{
			LeadMag = 0;
		}
	}
	else
	{
		LeadMag = 0;
		OldMaxDiff[Index] = 0;
		if ( DeltaTime < 1/ReturnChgSpeed )
		{
			LeadMag = (1 - ReturnChgSpeed*DeltaTime)*OldLeadMag[Index] + ReturnChgSpeed*DeltaTime*LeadMag;
		}
	}
	OldLeadMag[Index] = LeadMag;
	OldRotDiff[Index] = RotDiff;

	return NewValue + LeadMag;
}



C++ I have so far:

In my weapon’s SetPosition equivalent:



void APWNWeapon::UpdateDisplay(float deltaTime, FRotator newRotation, FVector newLocation)
{
        ...
        // Convert floats to ints and do bitwise operation.
        FinalRotation.Yaw = LagRot((float)((int32)newRotation.Yaw & (int32)360), (float)((int32)newRotation.Yaw & (int32)360), MaxYawLag, 0);
        ...
}




float APWNWeapon::LagRot(float NewValue, float LastValue, float MaxDiff, int32 Index)
{
	float RotDiff;
	float LeadMag, DeltaTime;

	// Remove "Rotator" values
	NewValue /= 65535.0f;
	LastValue /= 65535.0f;	

        // UE4 equivalent to "ClockWiseFrom"?
	**if (NewValue ClockWiseFrom LastValue)**
	{
		if (LastValue > NewValue)
		{
			LastValue -= 360.0f;
		}
		else if (NewValue > LastValue)
		{
			NewValue -= 360.0f;
		}
	//}	

	DeltaTime = GetWorld()->TimeSeconds - LastRotUpdate;
	RotDiff = NewValue - LastValue;

	if ((RotDiff == 0) || (OldRotDiff[Index] == 0))
	{
		LeadMag = ShouldLagRot ? OldLeadMag[Index] : 0.0;
		if ((RotDiff == 0) && (OldRotDiff[Index] == 0))
		{
			OldMaxDiff[Index] = 0;
		}
	}
	else if ((RotDiff > 0.0f) == (OldRotDiff[Index] > 0))
	{
		if (ShouldLagRot)
		{
			// TODO:

			MaxDiff = fminf(1, abs(RotDiff) / ((12000.0f / 65535.0f) * DeltaTime)) * MaxDiff;
			if (OldMaxDiff[Index] != 0.0f)
				MaxDiff = fmaxf(OldMaxDiff[Index], MaxDiff);

			OldMaxDiff[Index] = MaxDiff;
			LeadMag = (NewValue > LastValue) ? -1.0f * MaxDiff : MaxDiff;
		}
		else
		{
			LeadMag = 0.0f;
		}
		if (DeltaTime < 1.0f / RotChgSpeed)
		{
			LeadMag = (1.0f - RotChgSpeed * DeltaTime) * OldLeadMag[Index] + RotChgSpeed * DeltaTime * LeadMag;
		}
		else
		{
			LeadMag = 0;
		}
	}
	else
	{
		LeadMag = 0;
		OldMaxDiff[Index] = 0;
		if (DeltaTime < 1 / ReturnChgSpeed)
		{
			LeadMag = (1 - ReturnChgSpeed * DeltaTime) * OldLeadMag[Index] + ReturnChgSpeed * DeltaTime * LeadMag;
		}
	}
	OldLeadMag[Index] = LeadMag;
	OldRotDiff[Index] = RotDiff;

	return NewValue + LeadMag;
}


You probably don’t need to do any of this.

Try this in Tick, it’ll interpolate a rotator to a desired target at X speed. Higher is faster obviously. 0.25 - 2.0 is probably a good bet.


MyWeaponAngle (stored FRotator) = FMath::RInterpTo(MyWeaponAngle,TargetAngle,DeltaTime,speed);

I think I will still need to cater for the fact that rotations can be passed in as either negative or positive numbers, e.g.

Yaw = -30.0f is the same as Yaw = 330.0f.

In any case, thanks for the suggestion - I’ll give that a try over the weekend and report back.

Cheers!

I’ve had a look at this, but unfortunately this alone won’t suffice. The biggest and most complex chunk of work is actually calculating what the target angle should be.
After you calculate that, the InterpMethod will do just fine though.

Necrobump!

Finally managed to figure this one out - got the missing piece of the puzzle from Unreal Tournament 4 Source.

I’m not sure why, but it seems the code they’re using do not rotate back to a zero offset when the mouse is not moving so I added a little part to do that.
Also, they’re using just the offset part and applying that to the mesh’s relative rotation, whereas I’m setting my whole weapon’s rotation with the full amount.
In any case, source code for anyone interested:

Header:



        /*************************/
	/*** Rotation Lag/Lead ***/
	/*************************/
	// Taken from Unreal Tournament code
	/** Previous frame's weapon rotation */
	UPROPERTY()
	FRotator LastRotation;

	/** Saved values used for lagging weapon rotation */	
	float	OldRotDiff[2];	
	float	OldLeadMag[2];	
	float	OldMaxDiff[2];

	//65536
	/** How fast Weapon Rotation offsets */	
	float RotChgSpeed = 2.0f;

	/** How fast Weapon Rotation returns */	
	float ReturnChgSpeed = 2.0f;

	float GoToZeroSpeed = 15.0f;

	/** Max Weapon Rotation Yaw offset */	
	float MaxYawLag = -7.5f;

	/** Max Weapon Rotation Pitch offset */	
	float MaxPitchLag = -6.0f;

	/** @return whether the weapon's rotation is allowed to lag behind the holder's rotation */
	virtual bool ShouldLagRot();

	/** Lag a component of weapon rotation behind player's rotation. */
	virtual float LagWeaponRotation(float NewValue, float LastValue, float DeltaTime, float MaxDiff, int Index);


Cpp:



// Called from either Tick or my Character's CalcCamera function
void APWNWeapon::UpdateLocRot(FVector newLoc, FRotator newRot, float deltaTime)
{
	FRotator finalRot = newRot;

	// Rotation Leading/Lag
	finalRot.Yaw = LagWeaponRotation(newRot.Yaw, LastRotation.Yaw, deltaTime, MaxYawLag, 0);
	finalRot.Pitch = LagWeaponRotation(newRot.Pitch, LastRotation.Pitch, deltaTime, MaxPitchLag, 1);
	finalRot.Roll = newRot.Roll;	

	// Set our loc	
	LastRotation = newRot;
	this->SetActorLocation(newLoc);
	this->SetActorRotation(finalRot);
}

bool APWNWeapon::ShouldLagRot()
{
	return true;
}

float APWNWeapon::LagWeaponRotation(float NewValue, float LastValue, float DeltaTime, float MaxDiff, int Index)
{
	// check if NewValue is clockwise from LastValue
	NewValue = FMath::UnwindDegrees(NewValue);
	LastValue = FMath::UnwindDegrees(LastValue);

	float LeadMag = 0.f;
	float RotDiff = NewValue - LastValue;
	if ((RotDiff == 0.f) || (OldRotDiff[Index] == 0.f))
	{
		LeadMag = ShouldLagRot() ? OldLeadMag[Index] : 0.f;
		if ((RotDiff == 0.f) && (OldRotDiff[Index] == 0.f))
		{
			OldMaxDiff[Index] = 0.f;
		}

                // Interp back to zero 
		const float returnDelta = GoToZeroSpeed * DeltaTime;

		if (LeadMag > 0.0f)
		{
			LeadMag -= returnDelta;
			LeadMag = FMath::Max(0.0f, LeadMag);
		}
		else if (LeadMag < 0.0f)
		{
			LeadMag += returnDelta;
			LeadMag = FMath::Min(0.0f, LeadMag);
		}
	}
	else if ((RotDiff > 0.f) == (OldRotDiff[Index] > 0.f))
	{
		if (ShouldLagRot())
		{
			MaxDiff = FMath::Min(1.f, FMath::Abs(RotDiff) / (66.f * DeltaTime)) * MaxDiff;
			if (OldMaxDiff[Index] != 0.f)
			{
				MaxDiff = FMath::Max(OldMaxDiff[Index], MaxDiff);
			}

			OldMaxDiff[Index] = MaxDiff;
			LeadMag = (NewValue > LastValue) ? -1.f * MaxDiff : MaxDiff;
		}
		LeadMag = (DeltaTime < 1.f / RotChgSpeed)
			? LeadMag = (1.f - RotChgSpeed * DeltaTime) * OldLeadMag[Index] + RotChgSpeed * DeltaTime * LeadMag
			: LeadMag = 0.f;
	}
	else
	{
		OldMaxDiff[Index] = 0.f;
		if (DeltaTime < 1.f / ReturnChgSpeed)
		{
			LeadMag = (1.f - ReturnChgSpeed * DeltaTime) * OldLeadMag[Index] + ReturnChgSpeed * DeltaTime * LeadMag;
		}
	}

	OldLeadMag[Index] = LeadMag;
	OldRotDiff[Index] = RotDiff;

	return NewValue + LeadMag;
}


Small update - I’ve tweaked this a bit to also do some location offset based on the lag/lead and I think the results are pretty good.
If anyone’s interested, here’s the updated version of the UpdateLocRot function:



void APWNWeapon::UpdateLocRot(FVector newLoc, FRotator newRot, float deltaTime)
{
	FRotator finalRot = newRot;

	// Rotation Leading/Lag
	float lagDiff;	
	finalRot.Pitch = LagWeaponRotation(newRot.Pitch, LastRotation.Pitch, deltaTime, MaxPitchLag, 1, lagDiff);
	finalRot.Yaw = LagWeaponRotation(newRot.Yaw, LastRotation.Yaw, deltaTime, MaxYawLag, 0, lagDiff);
	finalRot.Roll = newRot.Roll;

	// Location offset
	float locDiff = lagDiff / MaxYawLag;
	locDiff *= MaxLocLagX;

	//UPWNGameGlobals::Log(TEXT("YAW"), lagDiff, true);
	ArmMesh->SetRelativeLocation(FVector(ArmMeshOffset.X, ArmMeshOffset.Y + locDiff, ArmMeshOffset.Z), false);

	// Set our loc	
	LastRotation = newRot;
	this->SetActorLocation(newLoc);
	this->SetActorRotation(finalRot);
	//ArmMesh->SetRelativeRotation(finalRot);
}


P.S. you will also have to add the following variable declaration to your header:



/** Max Location Lag - X */
float MaxLocLagX = 5.0f;


I see you also have an extra argument in the LagWeaponRotation function, could you show me the updated LagWeaponRotation as well? :slight_smile: