Counting points constantly, without framerate dependency


I’m making a simple snowboard game, THPS/Ollie Ollie style, and implemented two ways of getting points.

  1. By spinning character in the air

  2. By grinding on rails

Here’s my implementation of those mechanics:

Scoring for spins:

   void Update()

       private void ScoreForSpins()
        if (!triggerEffects.gameOver && !triggerEffects.grinding)
            zAxis = (int)transform.eulerAngles.z;

            if (zAxis >= expectedzValue - 5 && zAxis <= expectedzValue + 5)
                scoreForSpins += 50;
                if (expectedzValue == 180)
                    expectedzValue = 0;
                    expectedzValue = 180;

Scoring by grinding

    public IEnumerator ScoreGrinding()
        while (triggerEffects.grinding)
            yield return new WaitForSeconds(0.02f);
            grindScore += 2;

Generally this works fine, however my issue is, that those solutions are framerate dependant.
On my PC it works as intended, however on my girlfriend’s slower laptop,
counting score for spinning character often misses the
zAxis >= expectedzValue - 5 && zAxis <= expectedzValue + 5 range,
and the 50+ score isn’t added

I think it’s because of lower framerate, sometimes the frames when this would be checked in Update() are being skipped.

For grinding there’s a separate issue.
On PC running the game with lower framerate, the
yield return new WaitForSeconds(0.02f);
is checked less ofter, because the time between frames is higher then 0.02f, which means getting less points for the same time of grinding on the rail
Of course that is a problem, because every player should be able to get the same amount of points for the grind, no matter their framerate

For the second issue propably setting the WaitForSeconds to higher value, like 0.1f, would solve it, however I would really like to stick to the frantic counting system ala THPS.

Is there any smarter solutions for those issues?


I would suggest using FixedUpdate in place of Update as FixedUpdate isn’t framerate dependant and see if that works for you

Unfortunately, putting it in FixedUpdate makes it even worse, 50 points that should be added for 180 rotation misses even more often then in Update
(I suspect that while FixedUpdate is frame independent, it may actually run less frequently then Update(), and I need thick check to be performed all the time).

Yeah, there’s no denying it. Both of these are thoroughly framerate-dependent in different manners. But, you’re aware of it, so let’s just jump right into this.

First, let’s look at rotation:

Rather than strictly relying on your rotation in the form of EulerAngles, count up your rotation yourself, then translate that result back into rotation as desired.

// Example: Rotation input ranges from [-1 to 1] as a 
// clockwise/counterclockwise rotation
if(!Mathf.Approximately(rotationInput, 0.0f)) // If not zero
	// currentRotation should be zeroed out while on the ground
	// rotationSpeed would be degrees per second rotation
	float frameRotation = rotationInput * rotationSpeed * Time.deltaTime;
	currentRotation += frameRotation ;
	transform.rotation = Quaternion.AngleAxis(frameRotation, Vector3.forward) * transform.rotation;
	// example: rotationThreshold is 180-degree rotations to gain points
	if(Mathf.Abs(currentRotation) <= -rotationThreshold)
		currentRotation -= rotationThreshold * Mathf.Sign(currentRotation);
		activeScore += 50; // using your 50-point example for spins

Now, for grinding, you should probably give score based on distance traveled. In theory, if you were moving an inch per hour, should you still get the same score as you would if you traveled across the entire rail?

Ideally, the score for the current grind should also be stored as a float to ensure that it can be incremented fractionally, rather than only as integer increments. It can just be added to the main score in integer increments, however.

grindScore += Mathf.Abs(distanceTraveledThisFrame);
score += Mathf.FloorToInt(grindScore);
grindScore = Mathf.Repeat(grindScore, 1.0f); // Remove whole number value