How Programming Constraints Changed the Way I Designed Whisker 2
I used to think programming was just about implementing designs. Then I learned that programming constraints actually make me a better designer.
The Original Design
For Whisker 2, I wanted a cooking system where you could combine any ingredients in any order to make custom dishes. Players would experiment and discover recipes.
The design doc in my head:
- 20+ ingredients
- Infinite possible combinations
- Players create their own recipes
- Customers request specific dishes
This sounded amazing and creative.
The Programming Reality
Then I tried to implement it.
// My first attempt
public class CookingSystem : MonoBehaviour {
public List<Ingredient> availableIngredients;
public Dish CreateDish(List<Ingredient> ingredients) {
// How do I check if this is a valid dish?
// How do I know what the customer wanted?
// How do I give feedback if it's wrong?
// ...this is going to be way more complicated than I thought
}
}
The problems became obvious fast:
Problem 1: How do I validate "correct" dishes?
If players can make anything, how does the game know what's a sandwich vs what's garbage? Do I hardcode every possible combination?
if (ingredients.Contains(bread) && ingredients.Contains(cheese)) {
return "Sandwich";
} else if (ingredients.Contains(flour) && ingredients.Contains(sugar)) {
return "Cake";
}
// ...repeat for 100+ combinations?
That would be nightmare to maintain.
Problem 2: How do customers request custom dishes?
If dishes are freeform, I can't show a picture of what they want. I can't say "I want a sandwich with cheese and lettuce" because that assumes sandwiches exist as a concept.
Problem 3: How do players learn recipes?
With no recipe book, players would just throw random ingredients together hoping something works. That's not fun—that's frustrating.
The Constraint-Driven Redesign
I stepped back and asked: What's the smallest system that still feels creative?
The answer: Predefined recipes, but with customization options.
New design:
- 6 base recipes (sandwich, cake, soup, salad, pizza, pasta)
- Each recipe has 2-3 required ingredients
- Each recipe has 2-3 optional ingredients for customization
- Customers request a base dish, sometimes with specific extras
This was way simpler to program:
[System.Serializable]
public class Recipe {
public string dishName;
public List<Ingredient> requiredIngredients;
public List<Ingredient> optionalIngredients;
}
public Dish CreateDish(List<Ingredient> playerIngredients) {
foreach (Recipe recipe in availableRecipes) {
if (HasAllRequired(playerIngredients, recipe.requiredIngredients)) {
Dish dish = new Dish(recipe.dishName);
dish.AddExtras(GetOptionalIngredients(playerIngredients, recipe.optionalIngredients));
return dish;
}
}
return null; // Invalid dish
}
private bool HasAllRequired(List<Ingredient> playerIngredients, List<Ingredient> required) {
foreach (Ingredient req in required) {
if (!playerIngredients.Contains(req)) return false;
}
return true;
}
Why the Constrained Version is Actually Better
At first, I was disappointed. The original infinite-possibility system felt more "creative" and "ambitious."
But the constrained version had huge advantages:
1. Players could learn the system
With 6 base recipes, players learned them fast. After 2-3 levels, they didn't need to think about "what makes a sandwich." They just grabbed bread, cheese, and lettuce automatically.
2. Customization still felt meaningful
"Make a sandwich" → You choose what goes in it
"Make a sandwich with tomatoes" → You have to remember to add tomatoes
This gave players agency without overwhelming them.
3. The code was maintainable
Adding a new recipe meant creating one Recipe object. Not rewriting the entire validation system.
4. I could add tutorial hints
if (customer.wantsExtraIngredient && !dish.HasExtra(customer.wantedExtra)) {
ShowHint("The customer wanted extra " + customer.wantedExtra.name + "!");
}
With freeform dishes, I couldn't give feedback like this.
5. The game felt more focused
Instead of "figure out what's possible," the game became "master these recipes and serve them fast." That's a clearer, more satisfying challenge.
What This Taught Me About Design
Programming constraints aren't limitations—they're design tools.
When I hit a programming wall, it's usually because my design is too vague or too complicated.
The constraint forced me to ask:
- What's the core experience I want?
- What's the simplest version that delivers that experience?
- What can I cut without losing what matters?
Now when I design a system, I prototype it in code early. Not to "lock in" the design, but to find out if it actually works.
The Process I Use Now
-
Design the ideal version
(What would this look like with infinite time and perfect code?) -
Write pseudocode or a rough implementation
(What are the actual technical challenges?) -
Identify the constraints
(What's hard? What's repetitive? What's fragile?) -
Redesign around the constraints
(What's a simpler version that solves the same problem?) -
Test if the simpler version still feels good
(Did I lose something important?)
Usually, the simpler version is better anyway.
Examples from Other Systems
Oven Timer System
Original idea: Players manually control temperature and time
Programming constraint: That's a lot of UI and state management
Better design: Click to start cooking, done automatically after fixed time
Result: Still satisfying, way easier to implement
Customer Patience System
Original idea: Patience decreases based on real-time clock
Programming constraint: What happens if player pauses? What if frame rate drops?
Better design: Patience decreases based on game ticks, not wall time
Result: More predictable, works the same on any device
Ingredient Placement
Original idea: Drag and drop ingredients anywhere on counters
Programming constraint: Collision detection and state management nightmare
Better design: Click ingredient → Click counter → Automatically placed in grid
Result: Faster to play, easier to code
The Lesson
Good code makes good games. Good games make good code.
I used to see programming as the "technical part" after design. Now I see them as the same thing.
The best designs are the ones that are simple to implement, easy to maintain, and clear to players. Programming constraints help me find those designs.
When something is hard to code, it's often a sign that the design needs another pass.
