Calculating delta time

Hi, I'm trying to work out how to calculate delta time for a game I'm making on the Arduboy in order to have speed that are consistent, regardless of what is being drawn on the screen. I have previously used pre-defined delta time functions in different languages, but now need to work out how to calculate it manually.

I've declared the variables currentTime, oldTime and deltaTime.

void loop() {
   oldTime = currentTime;
   currentTime = millis();
   deltaTime = currentTime - oldTime;
}

I then multiply my sprite speeds by deltaTime when using them to translate a sprite. However it's not resulting in a speed that's independent of what is drawing on the screen. When I have a background full of tiles, the speed is quick, but when I don't draw a background at all, the sprite speed is really slow.

Am I misunderstanding something here?

Thanks a lot for any help!

I seem to have fixed the problem. My character positions were ints so I changed them to floats, and I changed my time variables from longs to floats too. I'm not sure why this fixed the problem though.

I've declared the variables currentTime, oldTime and deltaTime.

But you thought we didn't need to see that bit of the code?

I changed my time variables from longs to floats too.

Wrong, wrong, wrong.

The timing variables should be unsigned longs as returned by the millis() function

Here's my entire code. Ok I understand that maybe I should keep the time variables as unsigned longs, but why is it that I can't apply my deltaTime to my player character unless I keep everything floats? Is there something going wrong in the conversion?

#include <Arduboy.h>
Arduboy arduboy;

const unsigned char background[] PROGMEM = {0x81, 0x00, 0x12, 0x40, 0x4, 0x11, 0x00, 0x4,};
const unsigned char player[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x34, 0xfc, 0x8f, 0x34, 0x6, 0x36, 0x8e, 0x94, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2, 0x1e, 0xe6, 0xa3, 0xda, 0x83, 0xc4, 0xb8, 0x00, 0x00, 0x00, 0x00,};

bool aBuffer;
bool showBG;
float playerX;
float playerY;
float oldTime = 0;

void setup() {
  arduboy.begin();
  arduboy.clear();
  ResetGame();
}

void loop() {
  float deltaTime = CalculateDeltaTime();
  arduboy.clear();

  // Draw Background.
  if (showBG) {
    for (int i = 0; i < 128; i += 8) {
      for (int j = 0; j < 64; j += 8) {
        arduboy.drawBitmap(i, j, background, 8, 8, WHITE);
      }
    }
  }
  
  // Draw player character.
  arduboy.fillRect(playerX + 4, playerY, 8, 16, BLACK);
  arduboy.drawBitmap(playerX, playerY, player, 16, 16, WHITE);

  if (arduboy.pressed(LEFT_BUTTON))
    playerX -= deltaTime / 4;
  if (arduboy.pressed(RIGHT_BUTTON))
    playerX += deltaTime / 4;
  if (arduboy.pressed(UP_BUTTON))
    playerY -= deltaTime / 4;
  if (arduboy.pressed(DOWN_BUTTON))
    playerY += deltaTime / 4;
  if (arduboy.pressed(A_BUTTON) and !aBuffer) {
    showBG = !showBG;
    aBuffer = true;
  }
  if (arduboy.pressed(A_BUTTON) and arduboy.pressed(B_BUTTON))
    ResetGame();
  if (arduboy.notPressed(A_BUTTON))
    aBuffer = false;
  
  arduboy.display();
}

void ResetGame()
{
  aBuffer = false;
  showBG = true;
  playerX = 5;
  playerY = 10;
  return;
}

float CalculateDeltaTime(){
  float currentTime = millis();
  float deltaTime = currentTime - oldTime;
  oldTime = currentTime;
  return deltaTime;
}

I've modified my code to use unsigned longs for all time variables and calculations, just casting to a float when I apply deltaTime to my character. This all works correctly and I think it solves my error in using floats.

#include <Arduboy.h>
Arduboy arduboy;

const unsigned char background[] PROGMEM = {0x81, 0x00, 0x12, 0x40, 0x4, 0x11, 0x00, 0x4,};
const unsigned char player[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x34, 0xfc, 0x8f, 0x34, 0x6, 0x36, 0x8e, 0x94, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2, 0x1e, 0xe6, 0xa3, 0xda, 0x83, 0xc4, 0xb8, 0x00, 0x00, 0x00, 0x00,};

bool aBuffer;
bool showBG;
float playerX;
float playerY;
unsigned long oldTime = 0;

void setup() {
  arduboy.begin();
  arduboy.clear();
  ResetGame();
}

void loop() {
  unsigned long deltaTime = CalculateDeltaTime();
  arduboy.clear();

  // Draw Background.
  if (showBG) {
    for (int i = 0; i < 128; i += 8) {
      for (int j = 0; j < 64; j += 8) {
        arduboy.drawBitmap(i, j, background, 8, 8, WHITE);
      }
    }
  }
  
  // Draw player character.
  arduboy.fillRect(playerX + 4, playerY, 8, 16, BLACK);
  arduboy.drawBitmap(playerX, playerY, player, 16, 16, WHITE);

  if (arduboy.pressed(LEFT_BUTTON))
    playerX -= (float)deltaTime / 8;
  if (arduboy.pressed(RIGHT_BUTTON))
    playerX += (float)deltaTime / 8;
  if (arduboy.pressed(UP_BUTTON))
    playerY -= (float)deltaTime / 8;
  if (arduboy.pressed(DOWN_BUTTON))
    playerY += (float)deltaTime / 8;
  if (arduboy.pressed(A_BUTTON) and !aBuffer) {
    showBG = !showBG;
    aBuffer = true;
  }
  if (arduboy.pressed(A_BUTTON) and arduboy.pressed(B_BUTTON))
    ResetGame();
  if (arduboy.notPressed(A_BUTTON))
    aBuffer = false;
  
  arduboy.display();
}

void ResetGame()
{
  aBuffer = false;
  showBG = true;
  playerX = 5;
  playerY = 10;
  return;
}

unsigned long CalculateDeltaTime(){
  unsigned long currentTime = millis();
  unsigned long deltaTime = currentTime - oldTime;
  oldTime = currentTime;
  return deltaTime;
}

andylatham82:
just casting to a float

I am surprised that floating point maths is fast enough for a game.

...R

Maybe it isn't, I'm only learning. I'm an animator by profession, not a coder.

On an Arduino floating point maths is very slow as is integer division. They should be avoided if at all possible if performance matters.

If you can organize the maths so that divisions can be by values which are a power of 2 (e.g. 2, 4. 8. 16 etc) you can use the right shift instruction. For example >> 6 shifts the value to the right by 6 places which is equivalent to dividing by 64 (26 = 64) and that instruction is very fast.

...R

Robin2:
On an Arduino floating point maths is very slow as is integer division. They should be avoided if at all possible if performance matters.

If you can organize the maths so that divisions can be by values which are a power of 2 (e.g. 2, 4. 8. 16 etc) you can use the right shift instruction. For example >> 6 shifts the value to the right by 6 places which is equivalent to dividing by 64 (26 = 64) and that instruction is very fast.

...R

Ah, this is something completely new to me. That's a clever way of dividing. Thanks for letting me know about that! So is multiplying a slow process too? Should left-shifting be used instead for that?

I'm glad I've found the Arduboy really as I think it's limitations are going to be quite beneficial for learning.

Robin2:
If you can organize the maths so that divisions can be by values which are a power of 2 (e.g. 2, 4. 8. 16 etc) you can use the right shift instruction. For example >> 6 shifts the value to the right by 6 places which is equivalent to dividing by 64 (26 = 64) and that instruction is very fast.

I hope you don't mind helping a little more? My deltaTime value with the background showing is 12 milliseconds which is 1100 in binary. Shifting to the right by 3 places gives a value of 1 for my rate of movement. However when I don't draw the background, deltaTime goes down to 2 milliseconds, which is just 10 in binary, and this gives 0 when shifting right by 3 places. What do I do in this case?

andylatham82:
deltaTime goes down to 2 milliseconds, which is just 10 in binary, and this gives 0 when shifting right by 3 places. What do I do in this case?

One way to deal with that is to increase all the values by a factor of maybe 210 so that the values remaining after a shift are meaningful. Then, if necessary you can decrease the final value back to its real-world equivalent.

Be aware if you do this that you may need to use larger variable types - an int instead of a byte or a long instead of an int. It may also be wise to have all of the values of the same type even if the size of the number does not warrant it to prevent strange arithmetic errors if the compiler chooses the wrong type. Of course using larger variables uses more memory and I don't know if that might cause you a problem.

...R