1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Todoアプリに「バーチャルペット育成」を入れたら、タスク消化率が目に見えて上がった話

1
Posted at

はじめに

Todoアプリって、だいたい続かない。

UIを頑張っても
機能を盛っても
3日後には開かれなくなる。

そこで作ったのが、
タスクを完了すると感情が変化・成長するバーチャルペット「ぴよこ」 を組み込んだ Todo アプリ。

この記事では、

  • なぜ「感情」を持たせたのか
  • どうやって気分を計算しているのか
  • 実装上のポイント

を、実際の TypeScript コードを交えて解説する。

なぜバーチャルペットなのか

結論から言うと、

「タスク完了=即時フィードバック」が弱いから続かない

チェックボックスが埋まるだけだと、脳が全然喜ばない。

そこで、

  • タスク完了 → 反応する存在
  • 行動量 → 感情に反映
  • ランダム要素 → 予測不能性

この3点を満たす仕組みとして、
感情を持つペット という形にした。

実装した体験

  • タスク完了でぴよこが喜ぶ(エフェクト+メッセージ)
  • 時間帯・曜日・進捗・ランダム要素で気分が変化
  • 16種類の感情 + 30%でサブ感情
  • 3日連続達成で「ありがとう」状態
  • 10%で理不尽に「おなかすいた」

「ちゃんと生きてる感」を最優先。

アーキテクチャ概要

Frontend: React + TypeScript + Zustand
   ↓ API
Backend: Express + TypeScript
   ↓ ORM
Database: SQLite (Prisma)

気分計算はすべてバックエンドで行い、
フロントは 結果を表示するだけ にしている。

感情の型定義

拡張前提なので、ベース感情と拡張感情を分離。

export type BaseMood = 'happy' | 'normal' | 'tired' | 'sad';

export type ExtendedMood =
  | 'excited'
  | 'relaxed'
  | 'curious'
  | 'sleepy'
  | 'playful'
  | 'proud'
  | 'hungry'
  | 'cozy'
  | 'energetic'
  | 'dreamy'
  | 'grateful'
  | 'lonely';

export type PiyocoMood = BaseMood | ExtendedMood;

export interface MoodCandidate {
  mood: PiyocoMood;
  weight: number;
  reason: string;
}

weight(重み) を持たせているのが重要。

時間帯による気分補正

朝・昼・夕方・夜で傾向を変える。

function getTimeBasedMoodTendency(): MoodCandidate[] {
  // morning / afternoon / evening / night
}

ここは完全に「雰囲気作り」用。
ユーザー行動には直接関係しない。

曜日補正

月曜は疲れ、金曜はテンション高め。

function getDayBasedMoodTendency(): MoodCandidate | null {
  // 日付による軽い補正
}

影響度は低め。
あくまで味付け。

タスク進捗ベースの気分(最重要)

ここがコア。

  • 今日の完了率
  • 直近7日間の完了率
  • 完了タスク数

をスコア化する。

let score = 50;

if (todayTasks > 0) score += (todayCompleted / todayTasks) * 25;
if (totalTasks > 0) score += (completedTasks / totalTasks) * 15;
if (todayCompleted > 0) score += Math.min(todayCompleted * 3, 10);

設計意図

  • 何もしてなくても「普通」にはなる
  • 今日の行動を最優先で評価
  • タスク0件は「lonely」

ユーザーを責めない設計。

ランダム変動(生き物感)

if (Math.random() < 0.1) {
  // 突然の気分変化
}

予測できるAIは飽きる。
理不尽さは正義。

最終的な気分決定

すべての候補を集めて、
重み付きランダム選択

const totalWeight = candidates.reduce((s, c) => s + c.weight, 0);
let random = Math.random() * totalWeight;

さらに30%でサブ感情を付与。

UIへの反映

感情ごとに、

  • 表示名
  • メッセージ
  • 行動テキスト

を分離定義。

const MOOD_MESSAGES: Record<PiyocoMood, string[]> = {
  happy: ['今日はご機嫌だよ', ...],
};

ランダム文言必須
同じ台詞はすぐ飽きる。

タスク完了コンボ

10秒以内に連続完了するとコンボ発生。

  • エフェクト強化
  • まとめて消化を促す
const COMBO_TIMEOUT_MS = 10000;

地味だけど、行動パターンが変わる。

結果

数値はまだ検証中だが、

  • 連続起動率の向上
  • まとめてタスク消化する傾向
  • 「ぴよこ見に来た」という動機

が明確に増えた。

学び

  • ゲーミフィケーションは 派手さより設計
  • 感情は「報酬」として機能する
  • ランダム性は少量が一番効く

Todoアプリを作るなら、
「完了した後に何が起きるか」 を設計した方がいい。

チェックマークだけじゃ弱い。

技術スタック

  • React 18
  • TypeScript
  • Zustand
  • Express
  • Prisma
  • SQLite
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?