[Next.js #17] R3Fでドラッグ操作可能な高級ルービックキューブを実装する

はじめに

Next.js + React + R3Fの学習ネタで

「ルービックキューブを作りたい」

と思い立ち、昨日の夕方から今朝にかけて、ロジックを考えてたりしたのですが、思いのほか難しい…。

ネットで検索しても、ルービックキューブを実装した情報が物凄く少ない。

「そんなに難易度高い?」

と思って実装してると、やっぱり難しい…。

各面の判定処理は、単純にルービックキューブをUV展開して配列化し、その配列の中身を入れ替えるロジック。

最適化するとここまで短くロジックをまとめられるのに感動するも、理解が追い付かず。

MOVE_MAPはマッピング用の定義で、ここが理解できたら良いのだけど、完全な理解まで出来ず。

type Cube = number[] // 54要素

function applyMove(cube: Cube, move: Move): Cube {
  const map = MOVE_MAP[move]
  const next = [...cube]

  for (let i = 0; i < map.length; i++) {
    next[map[i][1]] = cube[map[i][0]]
  }

  return next
}

配列のロジックと、3Dプログラムの物理演算ロジックは分離して処理を考えてたので、今度はキューブをマウスのドラッグ➡スワイプで回転させる処理を考えるもこれも難しい…。

私の今のスキルでは、説明できるほど理解が追い付いてないので、今回は久しぶりのAIにほぼ丸投げの実装と記事になります。(挫折)

ルービックキューブの実装は、今後の個人的な課題の1つになりました。


前回の記事:

ルービックキューブの実装について(AIの意見)

ルービックキューブの実装は、3Dプログラミングにおいて「見た目の制御」と「内部ロジックの同期」という2つの大きな壁が立ちはだかる良問です。

今回は、54要素の配列管理に挫折した状態から、R3Fの特性を活かした「物理ベースの直感操作」に切り替えて完成させるまでのプロセスを解説します。

1. 最初の挫折:配列管理と3Dの乖離

配列管理の苦労と、R3F特有のライフサイクルへの理解を深めたステップですね。物理ベースの設計に移行する前の「生みの苦しみ」とも言える重要なセクションです。

ブログ用に、要点をさらに際立たせてまとめました。


1. 最初の挫折:配列管理と3Dの乖離

「論理(配列)」と「物理(3Dモデル)」を一致させることの難しさ

  • 初期アプローチの限界

  • 全54面を1次元配列で保持し、回転ごとに特定のインデックスを入れ替える手法を試行。

  • 課題: 「U回転ならこのインデックスをここに移動」というマッピングが指数関数的に複雑になり、実装が困難に。

  • R3Fの洗礼:Hooksエラーの発生

  • 罠: R3F特有の useFrame<Canvas> コンポーネントの外側で呼び出したため、Hooks can only be used within the Canvas component! というランタイムエラーが発生。

  • 得られた教訓

  • ライフサイクルの理解: 3Dの描画更新(毎フレームの処理)は、必ず <Canvas> の子コンポーネントとしてカプセル化し、そのコンテキスト内で記述しなければならない。

2. 発想の転換:26個の物理的な「小部屋」

配列ですべてを支配するのをやめ、物体そのものに語らせる設計

  • 「データ管理」から「空間配置」への移行

  • 54面のインデックスを追うのではなく、3D空間に26個(中心部を除く)の mesh を独立したエンティティとして配置する設計へ変更。

  • 各ピース自身が「今どこにいるか」という座標を持つことで、複雑なマッピング計算を不要に。

  • 動的なグループ化:THREE.Groupattach の魔法

  • 設計: 回転命令が下った瞬間、対象レイヤー(例:y === 1 の上段)に属するピースのみを検索。

  • 実装: 抽出したピースを一時的に回転専用の THREE.Groupattach。これにより、世界座標を維持したまま、グループ単位で滑らかなアニメーションが可能に。

  • 座標の正規化:浮動小数点という「見えない敵」との戦い

  • 課題: 90度の回転を繰り返すと、プログラム上の座標には 0.99999998 といった微小な計算誤差が蓄積され、やがてキューブがバラバラに崩れてしまう。

  • 解決策: 回転完了時に Math.round() を実行し、強制的に座標を -1, 0, 1 の整数へ補正。この「正規化」が、何度回しても崩れない強固なシステムを実現。

3. 直感操作の実装:ドラッグ判定の極意

ボタン操作から「直接触れて回す」没入型体験へ

  • 「面」の特定:法線ベクトル(Face Normal)の取得

  • ユーザーがキューブのどこを触ったかを判定するために、R3Fのイベントからクリックされた面の「法線ベクトル」を取得。

  • これにより、「今、正面(Z面)を触っているのか、天面(Y面)を触っているのか」という3D空間における基準点を定義。

  • 回転軸の算出:外積(Cross Product)という解法

  • ロジック: マウスを動かした方向ベクトルと、先ほどの法線ベクトルの「外積」を計算。

  • メリット: 数学的な計算によって、複雑な条件分岐なしで「どの軸(X/Y/Z)に対して、どちらの方向(正/負)に回すべきか」を一意に特定。画面上の二次元の動きを、三次元の回転運動へと変換。

  • 操作の衝突回避:OrbitControlsとの高度な共存

  • 課題: ドラッグしてキューブを回そうとすると、カメラ(視点)まで一緒に回ってしまう問題が発生。

  • 解決策: onPointerDown でキューブを掴んだ瞬間に OrbitControls を一時的に enabled = false に設定。回転アニメーションの終了とともに true に戻すことで、視点変更とパズル操作の切り替えをシームレスに実現。

4. 視覚的クオリティ:おもちゃから「プロダクト」へ

「ベタ塗り」を卒業し、光と影のディテールを宿す

  • エッジの魔力:RoundedBoxによるハイライト

  • 標準の立方体(BoxGeometry)は角が鋭利すぎて、現実味に欠ける。

  • 解決策: @react-three/dreiRoundedBox を採用し、エッジを数ミリ丸める。このわずかな曲線に環境光が反射(スペキュラハイライト)することで、物体の輪郭が際立ち、一気にリアルな存在感が生まれる。

  • ディテールの解像度:マルチマテリアルとタイル構造

  • 単一の立方体に色を塗るのではなく、競技用キューブのような「黒いベース筐体」+「光沢のあるカラータイル」の二重構造を再現。

  • 効果: 筐体とタイルの間に生じる微細な「隙間」が影となり、3Dモデル特有の平坦さを解消。情報密度が上がり、精密機械のような高級感を演出。

  • 質感の極み:meshPhysicalMaterial と Environment

  • 物理ベースレンダリング(PBR): meshPhysicalMaterial を使用し、自動車の塗装のような clearcoat 設定を付与。ピアノブラックのような深い光沢を表現。

  • 環境光の映り込み: Environment(HDR照明)を導入。キューブの表面に都会の景色やスタジオの光が薄く映り込むことで、周囲の空間とオブジェクトが馴染み、画面の中に「実在」している感覚を強化。


R3Fでルービックキューブを作ることは、単なるパズルの再現ではありません。「物理的な整合性(座標の正規化)」「直感的な操作感(外積による判定)」「視覚的な説得力(物理マテリアル)」の3つが揃って初めて、ユーザーが没頭できるデジタル体験が完成します。

論理(コード)に詰まったときは、一度物理的な空間に立ち返ってみる。これが3D開発における最大の近道かもしれません。

まとめ:R3Fでパズルを作るということ

「論理」から「空間」へ。思考の枠組みを変える。

「データをどう変換するか」という二次元的な論理(配列操作)に固執するよりも、「3D空間で物体をどう振る舞わせるか」という物理的な視点に主眼を置くことで、コードは驚くほどシンプルになり、操作性は劇的に直感的になります。

GLTFモデルを表示して眺めるだけの段階から一歩進み、ユーザーの入力に対して 「物理的に正しい挙動を返しつつ、内部データとしても破綻させない」 というバランスを実現すること。これこそが、React Three Fiber(R3F)を扱うフロントエンドエンジニアにとっての真の腕の見せ所と言えるでしょう。


追記:次のステップ

物理的な回転と直感的な操作感は、これで完成しました。しかし、パズルとしての進化はここからが本番です。

  • Undo(元に戻す)の実装: 回転履歴をスタックし、一発で前の状態に戻す機能。
  • シャッフル機能: ランダムな回転命令をアニメーション付きで実行し、パズルを開始するロジック。
  • ソルバー(自動解決)との連携: 二段階探索法(Kociemba’s algorithm)などのアルゴリズムと組み合わせ、バラバラのキューブが「魔法のように揃う」演出。
  • WebXRへの対応: Meta Quest 2などのデバイスで、実際に手で掴んで回すVR体験への拡張。