Udostępnij za pośrednictwem


Bizzy Bees XNA to DirectX/DirectXTK – Part 7

This is part of a blog series… if you came here directly you might want to read the introduction first.

 

The final step to make the game playable is to add some user interaction.

Capturing the users input

In DirectX the user input is event based so what we need to do is to add a method to the BizzyBeesGame class called HandleInput and call this from BizzyBees::OnPointerPressed

1. Add a public HandleInput method to BizzyBeesGame

     void HandleInput(Windows::Foundation::Point position);

2. Call the BizzyBeesGame::HandleInput method from BizzyBees::OnPointerPressed

 void BizzyBees::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
{
    // Insert your code here.
    m_renderer->HandleInput(args->CurrentPoint->Position);
}

In the HandleInput method we will check to see if we are clicking in the beePicker area or in one of the columns and handle the input accordingly so we also need to add two more methods to BizzyBeesGame called HandleFlowerSelection and HandleBeeSelection

3. Add private methods for handling bee and flower selections to the BizzyBeesGame class

     void HandleFlowerSelection(float x, float y);
    void HandleBeeSelection(float x);

Handing bee selection

We are only going to worry about handling flower selections if there is a be selected already since the order of the game goes like this… a. select a bee, b. select a column c. if the selected bee matches the bottom flower in that column we have a match. 

4. In order to do this we’ll add a new private member variable to the BeePicker class called selectedBee.

     shared_ptr<Bee>                    selectedBee;

5. Reset the selectedBee in BeePicker::ResetBeePicker method so that no bee is selected at the start of the game

     selectedBee = nullptr;

6. In the BizzyBeeGame::HandleInput method, check if we are in the bee picker area (anywhere between Y=700 and the bottom of the screen) and if so handle bee selection, if not handle flower selection (we’ll check later to make sure we’re touching inside a column area)

 void BizzyBeeGame::HandleInput(Windows::Foundation::Point position){
    if (position.Y > 700)
        HandleBeeSelection(position.X);
    else
        HandleFlowerSelection(position.X, position.Y);
}

7. Add a new public bool IsSelected field in the Bee class which we’ll use to draw selected bees slightly differently, and initialize it to false in the Bee constructor

 Bee::Bee(int color) : Color(color), IsSelected(false){}

8. In the BeePicker class add a new method SelectBeeAtPosition(float x) , in this method we will deselect any previously selected bee and select the bee at the given x position.

 void BeePicker::SelectBeeAtPosition(float x)
{
    //reset any previously selected bee
    if (selectedBee){
        selectedBee->IsSelected = false;
        selectedBee = nullptr;
    }

    //select the new bee
    auto beeIndex = (int)((x - beeStartX) / beeDeltaX);
    if (beeIndex < 0) beeIndex = 0;
    if (beeIndex > 4) beeIndex = 4;
    bees[beeIndex]->IsSelected = true;
    selectedBee = bees[beeIndex];
}

9. Handing the bee selection will be as simple as calling this method from BizzyBeeGame::HandleBeeSelection

 void BizzyBeeGame::HandleBeeSelection(float x){
    beePicker->SelectBeeAtPosition(x);
}

10. In the BeePicker::Draw method we will add a few lines to draw the selected bee with a DimGray light so that we get a visual cue when we select a bee

 void BeePicker::Draw(shared_ptr<SpriteBatch> spriteBatch, ComPtr<ID3D11ShaderResourceView> texture){
    for (int i = 0; i < 5; i++){
        RECT sourceRect = { bees[i]->Color * 91, 0, (bees[i]->Color + 1) * 91, 91 };
        Vector2 position = Vector2(beeStartX + i*beeDeltaX, beeStartY);
        if (bees[i]->IsSelected)
            spriteBatch->Draw(texture.Get(), position, &sourceRect, Colors::DimGray);
        else
            spriteBatch->Draw(texture.Get(), position, &sourceRect, Colors::White);
    }
}

11. For now, leave the BizzyBeeGame::HandleFlowerSelection empty and run the app to make sure everything works as it should with the bee selection

image

Handle flower selection

OK, great, now we can select bees.  Handling the flower selection will be a bit trickier though.  These are the steps we’ll need to take

  • check that we have a selected bee
  • check that we are in a column
  • get the bottom flower of that column
  • if the bottom flower is a) a rainbow flower or b) a flower with the same color as the bee we have a match and if so
  • … remove the bottom flower
  • … replace the selected bee (making sure we have at least one possible match left so that we don’t get stuck without matches) – Note: ideally we should make this algorithm better but I’ll leave that up to the reader.
  • … if the flower was a rainbow flower, increase the score

1. Implement the BizzyBeeGame::HandleFlowerSelection capturing all of the above

 void BizzyBeeGame::HandleFlowerSelection(float x, float y){
    
    //if we havent selected a bee, we have nothing to do
    if (beePicker->selectedBee == nullptr)
        return;

    const int RAINBOWCOLOR = 6;

    //only proceed if we are in the column area
    if (x > 10 && x < 470 && y > 100 && y < 700)
    {
        //get the selected column
        auto selectedColumnIndex = (int)((x - 10) / 92);    
        auto selectedColumn = columns[selectedColumnIndex];

        //get the selected flower
        auto selectedFlower = selectedColumn->GetBottomFlower();

        //check if we have a match or if the flower is rainbow
        if (selectedFlower != nullptr && (selectedFlower->Color == RAINBOWCOLOR || selectedFlower->Color == beePicker->selectedBee->Color)){
            //remove the bottom flower
            selectedColumn->RemoveBottomFlower();

            //replace the bee - making sure we have a match by selecting a random bottom flower 
            auto randomMatchingColor = columns[rand() % 5]->GetBottomFlower()->Color;
            beePicker->RemoveAndReplaceSelectedBee(randomMatchingColor);

            //check if we should add points
            if (selectedFlower->Color == RAINBOWCOLOR)
            {
                score++;
            }
        }
    }
}

OK so now we have to implement a few of the supporting methods so let’s add definitions in the h files for the following

  • public shared_ptr<Flower> Column::GetBottomFlower()
  • public void Column::RemoveBottomFlower()
  • public void BeePicker::RemoveAndReplaceSelectedBee(int color)
  • and a private field in BizzyBeeGame called int score;

2. The bottom flower is the flower at position 0 in the flowers vector so Column::GetBottomFlower and Column::RemoveBottomFlower are pretty straight forward

 shared_ptr<Flower> Column::GetBottomFlower(){
    if (flowers.size() > 0)
        return flowers[0];
    else
        return nullptr;
}

void Column::RemoveBottomFlower(){
    if (flowers.size() > 0)
        flowers.erase(begin(flowers));
}

3. In the BeePicker::RemoveAndReplaceSelectedBee we don’t actually remove anything, we just replace it but it has the same effect

 void BeePicker::RemoveAndReplaceSelectedBee(int color){
    
    //Deselect the selected bee
    selectedBee->IsSelected = false;
    
    //set the new bee color - 
    //if the flower suggested was rainbow just randomize, anything will match
    if (color > numBeeColors)
        selectedBee->Color = rand() % (numBeeColors + 1);
    else
        selectedBee->Color = color;

    //clear the selectedBee pointer
    selectedBee = nullptr;
}

4. We need to do something about the score, right now, we get points but noone is really there to see it:).  In the BizzyBeeGame::DrawHUD method add the following line to print the score

     DrawString(score.ToString()->Data(), Vector2(127, 45), Colors::Yellow);

5. For good measure we’ll also set the score = 0 in the BizzyBeeGame::ResetGame method so that we’ll start with a fresh slate.  It won’t matter now but will be good later

image

And with that my friends, we actually have a fully functioning game.  In the final article we’ll add on a few nice to haves to make it even better.