Learning shuffling hiding and revealing in Find Me using Dart
Find Me is a focus based game where players track a hidden ball while cups move around the screen.
The core gameplay depends on three important systems.
The first system is shuffling.
The second system is hiding.
The third system is revealing.
These mechanics may look simple from the outside, but creating them properly in Dart requires careful planning.
You need smooth movement, clean state management, proper timing, and a reliable way to track the hidden object during every shuffle round.
In this tutorial you will learn how to create the full gameplay logic behind Find Me.
You will understand how cups move across the screen, how the hidden object stays attached to the correct cup, and how to reveal the final result when the player taps a cup.
This tutorial focuses on clean Dart logic that can later be connected to Flame or Flutter widgets.
The goal is to help you understand the thinking process behind arcade style memory games.
Understanding the basic game structure
Before building the shuffle system, you need a structure that represents cups in the game.
Every cup should know its position, its index, and whether it currently contains the hidden ball.
A simple model makes the entire system easier to manage.
The game becomes more stable because all important data stays organized in one place.
class CupData {
int index;
double x;
double y;
bool hasBall;
CupData({
required this.index,
required this.x,
required this.y,
this.hasBall = false,
});
}
This structure creates a reusable cup object.
Every cup has an index for identification and coordinates for positioning on the screen.
The hasBall variable is extremely important because it tracks the hidden object during shuffling.
Even when cups move rapidly, the ball stays connected to the correct cup.
Creating the initial cups
The next step is creating three cups at the start of the game.
Normally the center cup contains the ball first because players need a fair chance to track movement.
List<CupData> cups = [
CupData(index: 0, x: 100, y: 300),
CupData(index: 1, x: 250, y: 300, hasBall: true),
CupData(index: 2, x: 400, y: 300),
];
Here three cups are placed in a straight horizontal line.
The second cup contains the hidden ball.
At this stage the game is calm and readable.
Players clearly understand where the ball begins before the shuffle animation starts.
Learning how hiding works
Hiding is one of the most important visual moments in Find Me.
Players first see the ball and then the cups move downward to cover it.
This action creates anticipation and prepares players for the tracking challenge.
In Dart the hiding process usually changes a visibility state.
bool ballVisible = true;
void hideBall() {
ballVisible = false;
}
When the hideBall function runs, the ball disappears visually from the screen.
However the actual ball data still exists inside the correct cup.
This separation is very important in game development.
Visual hiding should never destroy gameplay data.
Preparing the shuffle system
The shuffle system is the heart of the game.
Players spend most of their time watching cups swap positions.
A good shuffle system needs three things.
Smooth movement
Random swapping
Correct ball tracking
The simplest way to shuffle cups is by swapping their positions.
void swapCupPositions(CupData first, CupData second) {
double tempX = first.x;
double tempY = first.y;
first.x = second.x;
first.y = second.y;
second.x = tempX;
second.y = tempY;
}
This method exchanges coordinates between two cups.
The cups visually move into each other’s position.
Notice that the hasBall value is never changed.
The cup itself carries the ball data while moving around the screen.
Creating random shuffle patterns
If cups always move in the same order, players quickly memorize patterns.
Random movement keeps gameplay challenging and replayable.
Dart provides a Random class that helps generate unpredictable shuffle behavior.
import 'dart:math';
Random random = Random();
void randomShuffle() {
int firstIndex = random.nextInt(cups.length);
int secondIndex = random.nextInt(cups.length);
while (firstIndex == secondIndex) {
secondIndex = random.nextInt(cups.length);
}
swapCupPositions(
cups[firstIndex],
cups[secondIndex],
);
}
This logic selects two random cups and swaps their positions.
The while loop prevents the same cup from being selected twice.
Randomness creates excitement because players cannot predict the next movement.
Making shuffle movement feel smooth
Instant teleportation looks unnatural in arcade games.
Good shuffle movement should glide smoothly across the screen.
You can achieve this using interpolation.
double lerp(double start, double end, double progress) {
return start + (end - start) * progress;
}
Interpolation slowly moves values between a start position and an end position.
This technique is widely used in games because it creates fluid motion.
void animateCup(
CupData cup,
double targetX,
double targetY,
double progress,
) {
cup.x = lerp(cup.x, targetX, progress);
cup.y = lerp(cup.y, targetY, progress);
}
Here the cup gradually approaches the target position.
The movement becomes visually satisfying instead of jumping instantly.
Controlling shuffle speed
Difficulty progression is important in Find Me.
Early rounds should feel easy while later rounds become intense.
You can control this by adjusting shuffle duration.
double shuffleSpeed = 1.0;
void increaseDifficulty() {
shuffleSpeed *= 0.9;
}
Every round slightly reduces shuffle timing.
This creates faster animations and increases player pressure.
Small speed adjustments work better than massive jumps because progression feels natural.
Tracking the hidden ball correctly
One common beginner mistake is moving the ball separately from the cups.
This often creates bugs where the wrong cup reveals the ball.
A safer approach is attaching the ball directly to the cup data.
CupData? findBallCup() {
for (final cup in cups) {
if (cup.hasBall) {
return cup;
}
}
return null;
}
This method searches through every cup and returns the one containing the ball.
Since the ball belongs to the cup itself, the system remains reliable during every shuffle.
Learning reveal mechanics
Revealing is the final reward moment of every round.
After the player selects a cup, the game must show whether the guess was correct.
A reveal animation creates suspense and improves satisfaction.
void revealCup(CupData selectedCup) {
if (selectedCup.hasBall) {
print('Correct choice');
} else {
print('Wrong choice');
}
}
This simple logic checks if the chosen cup contains the ball.
In a real game you would trigger animations, sounds, score updates, and visual effects here.
Adding score progression
Score systems encourage replayability and make players feel rewarded for accuracy.
Faster decisions should produce higher rewards.
int score = 0;
void addScore(int round) {
score += round * 10;
}
This structure increases rewards as rounds become more difficult.
A progression system keeps players motivated to continue improving.
Preventing input during shuffle
Players should not tap cups while movement is still happening.
Doing so can create broken game states.
A simple boolean lock solves this problem.
bool isShuffling = false;
void startShuffle() {
isShuffling = true;
}
void endShuffle() {
isShuffling = false;
}
During shuffle the game ignores player input.
Once movement finishes, tapping becomes active again.
This small system improves stability and prevents accidental interactions.
Handling player taps
The game must detect which cup the player selected after shuffling stops.
void onCupTap(CupData tappedCup) {
if (isShuffling) {
return;
}
revealCup(tappedCup);
}
This keeps the interaction clean and prevents invalid gameplay states.
Proper input handling is essential for responsive arcade games.
Creating multiple shuffle rounds
Real gameplay needs several swaps before players can make a decision.
Multiple random swaps create confusion and challenge memory tracking.
Future<void> performShuffleRounds() async {
startShuffle();
for (int i = 0; i < 5; i++) {
randomShuffle();
await Future.delayed(
Duration(milliseconds: 600),
);
}
endShuffle();
}
This creates five shuffle actions with small delays between each movement.
Delays are important because players need time to visually process motion.
Improving replay value
Strong replay value keeps players returning to the game repeatedly.
You can improve replayability using random shuffle patterns, increasing speed, and score competition.
Small visual changes also help the game feel fresh during long sessions.
Even simple arcade games become addictive when progression feels rewarding.
Understanding why these mechanics matter
Shuffling creates tension.
Hiding creates mystery.
Revealing creates emotional payoff.
These three mechanics work together to create the full Find Me experience.
Without proper hiding the game feels predictable.
Without smooth shuffling the game feels cheap.
Without satisfying reveals the game feels unrewarding.
Understanding this relationship helps developers build better arcade gameplay systems.
Expanding the game further
Once the basic mechanics work properly, you can expand the game with advanced features.
You can add more cups for harder gameplay.
You can create curved shuffle paths instead of straight movement.
You can add sound effects during swaps.
You can introduce combo scoring systems for perfect streaks.
Advanced visual polish transforms a simple prototype into a complete arcade experience.
Final thoughts
Learning shuffling hiding and revealing in Find Me teaches important game development concepts.
You learn animation logic, state management, player interaction, randomness, timing, and progression systems.
Even though the game looks simple from the outside, its mechanics require thoughtful design to feel smooth and enjoyable.
By building these systems in Dart you develop a strong understanding of how arcade gameplay loops function internally.
Once you master these mechanics you can reuse them in many other games including memory games, puzzle games, reaction games, and competitive challenge systems.
Find Me proves that small gameplay ideas can become highly engaging when movement, focus, and player feedback work together correctly.