Something is wrong with my collisions

So in my game, I have a point struct full of different coords for food sprites, as well as the cursor. I made hitboxes for all of them, but when they collide, the code inside the if-collide-condition doesn’t activate. Am I doing something wrong maybe?

When you define the player Rect, you do it once and it never gets updated.

038    Rect Player (playerX, playerY, 5, 5);

Later when you move the player, the playerX and playerY values may change but the original rect you defined does not.

Define a new function that returns the player’s rectangle, like this:

Rect getPlayerRect() {

    Rect playerRect;
    playerRect.x = playerX;
    playerRect.y = playerY;
    playerRect.width = 5;
    playerRect.height = 5;

    return playerRect;
}

Then just before testing the collisions:

void workScreen() {

   ...

    Rect playerRect = getPlayerRect();

    if (arduboy.collide(playerRect, FriedChicken)) {
        money = money + 1000;
    }

Finally remove the ‘static’ Player rectangle at line 038.

2 Likes

Thanks! One more thing, is there a way I can assign more members to the arduboy.collide function? For example, I would like it so that the player must collide with, lettuce, onion, tomato, patty and buns, then receive the money, instead of just touching one thing (ie friedchicken).

Another thing to look at:

You have images named friedchicken, lettuce and so forth.

If you put the images them into a

const uint8_t * const foodImages[] PROGMEM = { friedchicken, lettuce, onion ..  and so on };

Then you can change this:

void drawWorkFood() {
  Sprites::drawOverwrite(foodOptions[0].x, foodOptions[0].y, friedchicken, 0);
  Sprites::drawOverwrite(foodOptions[1].x, foodOptions[1].y, lettuce, 0);
  Sprites::drawOverwrite(foodOptions[2].x, foodOptions[2].y, onion, 0);
  Sprites::drawOverwrite(foodOptions[3].x, foodOptions[3].y, buns, 0);
  Sprites::drawOverwrite(foodOptions[4].x, foodOptions[4].y, patty, 0);
  Sprites::drawOverwrite(foodOptions[5].x, foodOptions[5].y, soda, 0);
  Sprites::drawOverwrite(foodOptions[6].x, foodOptions[6].y, fries, 0);
  Sprites::drawOverwrite(foodOptions[7].x, foodOptions[7].y, pizza, 0);
  Sprites::drawOverwrite(foodOptions[8].x, foodOptions[8].y, tomato, 0);
}

to

void drawWorkFood() {
  for (uint8_t i = 0; i < 9; i++) {
      Sprites::drawOverwrite(foodOptions[i].x, foodOptions[i].y, foodImages[i], 0);
  }
}

In this order?

No, the order can be any.

OK. You could do it a number of ways.

Simple

Create a new array called something like bool touchedFood[9]; and as each item is touched then you set the corresponding flag to true. At the end of your collision detection, you can then loop through the elements and look to see which have been set. If enough then award money …

Better

Or … using structs.

enum class FoodType : uint8_t {
  FriedChicken,
  Lettuce,
  ... ,
  None
};

struct FoodItem {

  int16_t x;
  int16_t y;
  bool touched = false;
  FoodType foodType = FoodType::None;

  Rect getRect() {

    Rect foodRect;
    foodRect.x = this->x;
    foodRect.y = this->y;
    foodRect.width = 8;
    foodRect.height = 8;

    return foodRect;
  }

}

Then in your initialisation

const uint8_t NoOfItems = 9;
FoodItem foodItems[NoOfItems];

foodItems[0].x = 2;    
foodItems[0].y = 32;    
foodItems[0].foodType = FoodType::FriedChicken;    

And in your collision:

void workScreen() {

  ...

  Rect playerRect = getPlayerRect();

  for (uint8_t i = 0; i < NoOfItems; i++) {
  
    Rect foodRect = foodItems[i].getRect();

    if (arduboy.collide(playerRect, foodRect)) {
        foodItems[i].touched = true;
    }

  }

Then you can loop through the food items to see what has been touched …

  uint8_t itemsTouched = 0;

  for (uint8_t i = 0; i < NoOfItems; i++) {
  
    if (foodItems[i].touched) {
      itemsTouched++;
    }

  }

  if (itemsTouced > ??) {
    award money
  }

To render the items …


  for (uint8_t i = 0; i < NoOfItems; i++) {
  
    Sprites::drawOverwrite(foodItems[i].x, foodItems[i].y, static_cast<uint8_t>(foodItems[i].foodType) );

  }

This second approach looks like more code, but if you were to change the number of food items from 9 to 12 it would handle it with no code change. Additionally, you are not repeating the code for 9, 12 or 15 items.

2 Likes

I tried the simple way, and I coded it so that there is 4 items (starting from zero, so 5 items), and every time customer order is 5, if the player collides and presses A with any burger ingredient, then a specific string in the food touched array will be turned true, and if all strings are true (ie, you touched all burger ingredients), then 150 will be added to your money counter, and customer order will be random. However, it doesn’t work, though it should. Here’s my code currently.

EDIT: I figured out why it isn’t working, in the if statement that checks if all bools are true, I did

if (touchedFood[0] == true && touchedFood[1] == true and so on) {
money += 50
touchedFood[0] = false;
touchedFood[1] = false;
and so on
}

So basically, whenever any of the bools becomes true, it instantly activates the false-code inside the if statement, so is there a way I can check to see if all the values of touchedFood, without doing it like the above code? Something maybe like “if (touchedFood == true)” or something that can check if ALL the values of touchedFood are true.

Actually, you’re using &&s, so it should only activate when all are true.

Your problem is that you’re declaring touchedFood inside of workScreen, which means it’s a local variable, not a global variable. That also means it’s reset to the value you declare it with, which in this case is false for everything.

Variables declared inside functions cease to exist when the function ends. For a variable to exist between function calls you have to make them global, which means declaring them at the ‘top level’ (i.e. the same level as your functions).

bool touchedFood[4] {false, false, false, false};

void workScreen() {

Once touchedFood is global you can do:

bool isAllFoodTouched()
{
	for(uint8_t index = 0; index < 4; ++index)
		if(!touchedFood[index])
			return false;
	
	return true;
}

Which loops through all the values of touchedFood, and if any one of them is false, it returns false. If none are found to be false then it returns true, because if none are false then they must logically all be true.

There is a way that would work for all arrays of bool, e.g. you could do areAllTrue(touchedFood), but that would require some fairly advanced ‘magic’. I can provide such a function if you won’t be put off by not understanding how it works.

There’s also a way to make a function that will get the size of an array, but it involves the same advanced ‘magic’.


By the way, doing == true is a complete waste of time.
If x is a bool then x == true will always give the same result as just x.
E.g. sfxToggle has the same value as sfxToggle == true, so if (arduboy.justPressed(A_BUTTON) && sfxToggle) would behave the same as if (arduboy.justPressed(A_BUTTON) && sfxToggle == true).

== false is similarly redundant, the ! operator is a better option.

1 Like