Bonus: Understanding Helper Functions
Welcome to the bonus tutorial! In this part, we'll dive deep into the helper functions used in the Adventure game. Helper functions are one of the most important concepts in programming, so let's explore them in detail.
What are Helper Functions?
Helper functions are small, reusable pieces of code that perform specific tasks. They help you:
- Organize your code - Break complex tasks into smaller, manageable pieces
- Reuse code - Write once, use many times
- Make code easier to read - Give meaningful names to complex operations
- Debug more easily - Test and fix problems in isolated functions
- Maintain code - Changes to one function don't affect the rest of your program
The Helper Functions in Our Adventure Game
Let's examine each helper function in the Adventure game and understand how they work.
1. waitForInput() - The Input Handler
ButtonEvent waitForInput() {
ButtonEvent pressed = Kywy::Events::KywyEvents::INPUT;
// Check initial state of buttons
bool initialLeftPressed = engine.input.dPadLeftPressed;
bool initialRightPressed = engine.input.dPadRightPressed;
// Track previous state to make sure a button was not already held down
bool wasLeftPressed = initialLeftPressed;
bool wasRightPressed = initialRightPressed;
while (pressed == Kywy::Events::KywyEvents::INPUT) {
bool leftPressed = engine.input.dPadLeftPressed;
bool rightPressed = engine.input.dPadRightPressed;
// Detect button press: current is pressed AND previous was not pressed
if (leftPressed && !wasLeftPressed) {
pressed = Kywy::Events::KywyEvents::D_PAD_LEFT_PRESSED;
} else if (rightPressed && !wasRightPressed) {
pressed = Kywy::Events::KywyEvents::D_PAD_RIGHT_PRESSED;
}
wasLeftPressed = leftPressed;
wasRightPressed = rightPressed;
engine.display.update();
delay(10);
}
return pressed;
}
What it does: - Waits for the player to press either the left or right d-pad button - Uses edge detection to only register when a button is first pressed (not held down) - Returns which button was pressed
Why it's a helper function: - Input handling logic is complex and reusable - Keeps the main game loop clean and focused on game logic - Handles the tricky edge detection automatically
Key concepts: - Edge detection: Only triggers when button state changes from not pressed to pressed - State tracking: Remembers previous button states - Return values: Functions can send information back to where they were called
2. drawInstructionText() - The UI Drawer
void drawInstructionText() {
// Right text
engine.display.getTextSize("Go Right", textWidth, textHeight);
engine.display.fillRectangle(centerX + 5, centerY - 25, textWidth + 10, textHeight + 10, Display::Object2DOptions().color(0xFF));
engine.display.drawRectangle(centerX + 5, centerY - 25, textWidth + 10, textHeight + 10, Display::Object2DOptions().color(0x00));
engine.display.drawText(centerX + 10, centerY - 20, "Go Right");
// Left text
engine.display.getTextSize("Go Left", textWidth, textHeight);
engine.display.fillRectangle(centerX - 65, centerY - 25, 60, 20, Display::Object2DOptions().color(0xFF));
engine.display.drawRectangle(centerX - 65, centerY - 25, 60, 20, Display::Object2DOptions().color(0x00));
engine.display.drawText(centerX - 60, centerY - 20, "Go Left");
}
What it does: - Draws the "Go Left" and "Go Right" instruction boxes on screen - Creates white backgrounds with black borders for the text - Positions the text boxes in the center of the screen
Why it's a helper function: - Drawing UI elements requires many steps - Used on multiple screens, so it's reused - Separates UI concerns from game logic
Key concepts: - Text measurement: Getting the size of text before drawing - Rectangle drawing: Creating backgrounds and borders - Positioning: Calculating where to place elements on screen
3. drawBottomText() - The Story Teller
void drawBottomText(const char* text) {
engine.display.getTextSize(text, textWidth, textHeight);
engine.display.fillRectangle(0, KYWY_DISPLAY_HEIGHT - textHeight - 10, KYWY_DISPLAY_WIDTH, textHeight + 10, Display::Object2DOptions().color(0xFF));
engine.display.drawRectangle(0, KYWY_DISPLAY_HEIGHT - textHeight - 10, KYWY_DISPLAY_WIDTH, textHeight + 10, Display::Object2DOptions().color(0x00));
engine.display.drawText(5, KYWY_DISPLAY_HEIGHT - textHeight - 5, text);
}
What it does: - Draws story text in a box at the bottom of the screen - Automatically sizes the box based on the text content - Creates a white background with black border
Why it's a helper function: - Story text appears on multiple screens - Handles text layout automatically - Makes it easy to add narrative elements
Key concepts:
- Parameters: Functions can receive information (the text parameter)
- Dynamic sizing: Box size adjusts to fit the content
- Screen coordinates: Using display dimensions for positioning
How Helper Functions Work Together
In the main game loop, these helper functions work together seamlessly:
void loop() {
// Show start screen
engine.display.clear();
engine.display.drawBitmap(0, 0, KYWY_DISPLAY_WIDTH, KYWY_DISPLAY_HEIGHT, startScreen);
drawInstructionText(); // Helper function draws UI
ButtonEvent choice = waitForInput(); // Helper function gets input
if (choice == Kywy::Events::KywyEvents::D_PAD_LEFT_PRESSED) {
engine.display.clear();
engine.display.drawBitmap(0, 0, KYWY_DISPLAY_WIDTH, KYWY_DISPLAY_HEIGHT, leftScreen);
drawBottomText("You found a reward!"); // Helper function shows story
}
// ... more game logic
}
Notice how the main loop focuses on what the game should do, while the helper functions handle how to do it.
Creating Your Own Helper Functions
Here are some tips for creating effective helper functions:
1. Single Responsibility
Each function should do one thing well:
// Good: One clear purpose
void drawPlayer(int x, int y) {
// Draw player sprite
}
// Bad: Multiple responsibilities
void handleEverything() {
// Draw player, handle input, update score, play sound...
}
2. Descriptive Names
Choose names that clearly describe what the function does:
// Good names
void calculatePlayerDamage(int damage);
void renderHealthBar();
void playVictorySound();
// Bad names
void func1();
void doStuff();
void helper();
3. Parameters and Return Values
Use parameters to receive information and return values to send results:
// Function that receives information and returns a result
int calculateScore(int points, int multiplier) {
return points * multiplier;
}
// Function that receives information but doesn't return anything
void displayMessage(const char* message, int x, int y) {
// Display the message at the given position
}
4. Keep Them Short
Aim for functions that fit on your screen. If a function gets too long, break it into smaller helper functions.
Common Helper Function Patterns
Input Handlers
bool isButtonJustPressed(bool currentState, bool& previousState) {
bool justPressed = currentState && !previousState;
previousState = currentState;
return justPressed;
}
Drawing Functions
void drawCenteredText(const char* text, int y) {
uint16_t textWidth, textHeight;
engine.display.getTextSize(text, textWidth, textHeight);
int x = (KYWY_DISPLAY_WIDTH - textWidth) / 2;
engine.display.drawText(x, y, text);
}
Math Utilities
int clamp(int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
Practice Exercise
Try creating a helper function for the Adventure game:
- Create a function called
showGameOverScreen()that displays a game over message - Create a function called
resetGame()that resets the score and health - Modify the main loop to use these new helper functions
Summary
Helper functions are the building blocks of clean, maintainable code. They:
- Make your code more organized and readable
- Allow code reuse
- Make debugging easier
- Help you think about problems in smaller pieces
Remember: If you find yourself writing the same code multiple times, or if a function is getting too long and complex, it's time to create a helper function!
In the next tutorial, we'll explore more advanced game mechanics. Keep practicing with helper functions - they're essential for any serious programming project!