Robloxで1メッシュ鍵盤のクリック位置取得

ピアノ鍵盤を1つのMeshPart(1メッシュ)で作った場合でも、画面上のクリックからワールド座標をレイキャストで取得し、メッシュ上の衝突点を位置として使えます。クリック位置からメッシュのローカル座標に変換すれば、どの鍵を押したかの判定まで可能です Zenn


できることと基本方針

  • クリック位置の取得: カメラからクリック方向にレイを飛ばし、MeshPartに当たった地点のRaycastResult.Positionを得る Zenn
  • 鍵判定: 当たった位置をメッシュ(モデル)のローカル座標に変換して、X方向の範囲から鍵番号を割り出す。
  • 注意点: ClickDetectorは「クリックされた」というイベントは取れるが、正確なヒット位置は返さないため鍵番号判定には不向き。位置が必要ならレイキャストを使う Zenn

実装ステップ

  1. レイキャストでクリックヒット情報を取得

    • ポイント: UserInputService:GetMouseLocation()Camera:ViewportPointToRay()workspace:Raycast() の流れ。
    • フィルタ: 自分のキャラクターは除外。距離は適宜(例: 1000) Zenn
    • サンプル(LocalScript, StarterPlayerScripts)
      local UIS = game:GetService("UserInputService")
      local camera = workspace.CurrentCamera
      local player = game.Players.LocalPlayer
      
      local function raycastFromMouse()
          local pos = UIS:GetMouseLocation()
          local ray = camera:ViewportPointToRay(pos.X, pos.Y)
          local params = RaycastParams.new()
          params.FilterType = Enum.RaycastFilterType.Exclude
          params.FilterDescendantsInstances = { player.Character }
          return workspace:Raycast(ray.Origin, ray.Direction * 1000, params)
      end
      
      UIS.InputBegan:Connect(function(input, gp)
          if gp or input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
          local result = raycastFromMouse()
          if not result then return end
          local hitPart = result.Instance
          local hitWorld = result.Position
          -- ここから鍵判定へ
      end)
      

      Sources: Zenn

  2. ワールド座標をローカル座標へ変換

    • ポイント: 1メッシュならそのMeshPartのCFrame:ToObjectSpace()を使う。モデルならPrimaryPart基準で。
    • サンプル
      local mesh = hitPart  -- 1メッシュの場合
      local localCF = mesh.CFrame:ToObjectSpace(CFrame.new(hitWorld))
      local localPos = localCF.Position  -- メッシュ原点基準のXYZ
      
  3. ローカルX位置から鍵番号を算出

    • 前提: 鍵がX方向に並んでいるとする。白鍵幅whiteWidth、白鍵数numWhiteKeysなどを決める。
    • サンプル(単純化)
      local originX = - (numWhiteKeys * whiteWidth) / 2  -- 左端X
      local x = localPos.X
      local keyIndex = math.floor((x - originX) / whiteWidth) + 1
      if keyIndex >= 1 and keyIndex <= numWhiteKeys then
          print("White key pressed:", keyIndex)
      end
      -- 黒鍵はX範囲+Z(奥行き)で別判定を重ねる
      

精度を上げるための設計ヒント

  • 衝突精度: MeshPartのCollisionFidelityを「Precise」に、CanQueryを有効にしておくと当たり判定が正確になりやすい。
  • ヒット領域の補助: 完全1メッシュでの黒鍵・白鍵判定は境界がシビア。透明な薄いPartを各鍵位置に配置してレイキャスト対象を鍵ごとに分けると堅牢。ClickDetectorを各Partに付ければクリック検出は簡単だが、位置情報は不要な場合のみ有効 Zenn Qiita
  • UIとの併用: ラベル表示やハイライトはBillboardGuiやSurfaceGuiで視覚フィードバックを追加すると分かりやすい。

ClickDetectorを使う場合の補足

  • 用途: 「押された」事実だけ取れれば十分なケースでは簡便。MouseClick, MouseHoverEnter/Leave, RightMouseClickなどのイベントを提供 Zenn
  • 限界: ヒット座標は返らないため、1メッシュで鍵番号を厳密に決める用途には不向き。鍵ごとにPartを分ける設計なら適合 Zenn Qiita

Sources: Zenn Qiita


結論

  • 可能: 1メッシュでもレイキャストでクリックヒット位置を取得し、ローカル座標へ変換して鍵判定できる。
  • 推奨: 実用性・保守性を優先するなら「見た目は1メッシュ、判定は不可視キーPart」で階層を分離する設計が安定。ワークフロー上の梯子として「表示構造」と「判定構造」を分けると拡張が楽です Zenn Zenn