【初心者向け】FreeCADとPythonで作る!3Dプリント対応のリアルなサイコロの作り方

3Dプリンターの普及により、自宅でオリジナルアイテムを作る楽しみが広がっています。今回は、**無料の3D CADソフト「FreeCAD」**を使って、**リアルな6面体サイコロ(ダイス)**を作成する方法をご紹介します。
Pythonスクリプトを使うことで、寸法やデザインの調整がしやすく、角が丸く・目が凹んでいる実用的なデータを簡単に作ることが可能になります。3Dプリントにもそのまま使えるモデルに仕上がるので、オリジナルサイコロを作ってみたい方にはピッタリの内容です。
サイコロ作成に必要なツール
今回使用するのは以下の2つだけです。
- FreeCAD(最新版)
オープンソースの3D CADソフト。Windows / macOS / Linux に対応。公式サイトから無料でダウンロードできます。 - Python(FreeCAD内蔵)
FreeCADにはPythonスクリプト機能が標準搭載されており、マクロや自動モデリングに便利です。
インストール後、FreeCADの「マクロ」機能からスクリプトを実行するだけで、サイコロの3Dモデルが完成します。
今回作るサイコロの特徴
作成するサイコロは以下のような仕様になっています:
- ✅ 対面の目の合計が「7」
- ✅ 目(ピップ)は凹んだデザイン
- ✅ 角はフィレットで丸め処理
実際のサイコロと同様、目の配置にもこだわっており、見た目にも実用的なデザインになっています。
目の配置ルールと正確なモデリング
サイコロの面は1〜6の数字で構成されており、対面同士の合計が7になるように設計されています。
面の数字 | 対面の数字 |
---|---|
1 | 6 |
2 | 5 |
3 | 4 |
このルールに沿って、各面の中心位置や向きに基づいてピップ(目)を配置していきます。立方体の各面の中心点と法線ベクトルをもとにして、凹ませる球体を配置することで、立体的な目を再現しています。
フィレット(角丸)で柔らかい見た目に
サイコロは転がして使うアイテムなので、エッジが鋭いと手を傷つけたり、家具を傷めることもあります。そこで、FreeCADのフィレット機能を使って、全12辺の角を滑らかに処理しています。
これは見た目の美しさだけでなく、安全性や耐久性にもつながる大切な工程です。
凹みの目(ピップ)はどう作る?
目の部分は球体を作成し、それをサイコロ本体から**差し引く(Boolean Difference)**ことで、リアルな凹みを表現しています。
この方式は、
- ✅ 実際のサイコロに近い見た目
- ✅ 3Dプリント時のサポート材の削減
- ✅ 着色しやすい構造
という利点があります。しかもスクリプトで自動化されているので、変更も簡単。たとえば目の深さやサイズを変えたいときも、数値を修正するだけで調整可能です。
出力と3Dプリントへの応用
完成したモデルは、FreeCADからSTL形式でエクスポートすることで、一般的な3Dプリンターに対応できます。
凹みがしっかりしているので、色分けもやりやすく、仕上がりの美しさも抜群です。
応用アイデア:自分だけのオリジナルサイコロへ
このサイコロの作り方をベースに、以下のような応用も可能です:
- 🎲 12面体・20面体などの多面サイコロ
- 🎨 表面に数字や記号を彫刻
- 🧒 子供向けの教育サイコロ(絵柄入りや言葉入り)
- 🌟 空洞構造の軽量サイコロ(内部をくり抜き)
- 😈 重心を変えてイカサマサイコロ
FreeCAD + Python の組み合わせは柔軟性が高く、デザインの自由度が非常に高いのが特徴です。
まとめ:Pythonを活用したFreeCADの自動モデリングは超便利!
今回の方法では、FreeCADとPythonを組み合わせて、フィレット付き・凹み付きサイコロを簡単に自動生成できました。
手動でのモデリングだと時間がかかるような処理も、スクリプトを使えば再利用も一瞬。寸法やデザインの変更にも強く、繰り返し使えるテンプレートとして非常に便利です。
今後もこうしたPythonスクリプトによる自動モデリングを活用して、オリジナルアイテムをどんどん作ってみてはいかがでしょうか?
おまけ
📌 FreeCADのマクロ機能にスクリプトを貼り付けて実行するだけでOK!
興味のある方は、ぜひ一度試してみてくださいね。

以下をマクロにコピー&ペースト
import FreeCAD as App
import FreeCADGui as Gui
import Part
doc = App.newDocument("Dice_Rounded_Indented")
# サイズ・設定
dice_size = 20
pip_radius = 1.5
pip_distance = 5
pip_depth = 1.0
fillet_radius = 1.5
# 元のキューブ作成
cube = Part.makeBox(dice_size, dice_size, dice_size)
# 角を丸める(フィレット適用)
# まず形状をBodyとして扱う
shape = cube
edges_to_fillet = [e for e in shape.Edges]
cube_with_fillet = shape.makeFillet(fillet_radius, edges_to_fillet)
# 表示用にセット
cube_obj = Part.show(cube_with_fillet)
cube_gui = Gui.getDocument(doc.Name).getObject(cube_obj.Name)
cube_gui.ShapeColor = (1.0, 1.0, 1.0) # 白
# 対面=7の目割り当て
face_data = {
1: ((dice_size/2, dice_size/2, dice_size), (0, 0, 1)),
6: ((dice_size/2, dice_size/2, 0), (0, 0, -1)),
2: ((dice_size/2, dice_size, dice_size/2), (0, 1, 0)),
5: ((dice_size/2, 0, dice_size/2), (0, -1, 0)),
3: ((dice_size, dice_size/2, dice_size/2), (1, 0, 0)),
4: ((0, dice_size/2, dice_size/2), (-1, 0, 0))
}
# ピップの配置
pip_patterns = {
1: [(0, 0)],
2: [(-pip_distance, -pip_distance), (pip_distance, pip_distance)],
3: [(-pip_distance, -pip_distance), (0, 0), (pip_distance, pip_distance)],
4: [(-pip_distance, pip_distance), (-pip_distance, -pip_distance),
(pip_distance, pip_distance), (pip_distance, -pip_distance)],
5: [(-pip_distance, pip_distance), (-pip_distance, -pip_distance), (0, 0),
(pip_distance, pip_distance), (pip_distance, -pip_distance)],
6: [(-pip_distance, pip_distance), (-pip_distance, 0), (-pip_distance, -pip_distance),
(pip_distance, pip_distance), (pip_distance, 0), (pip_distance, -pip_distance)]
}
# 凹み球体の収集
all_pips = []
for face_num, positions in pip_patterns.items():
center, normal = face_data[face_num]
for x, y in positions:
# ピップ位置を3D空間で算出(凹み分だけ奥に)
if abs(normal[2]) == 1: # Z面
pos = App.Vector(center[0] + x, center[1] + y, center[2] - pip_depth * normal[2])
elif abs(normal[1]) == 1: # Y面
pos = App.Vector(center[0] + x, center[1] - pip_depth * normal[1], center[2] + y)
else: # X面
pos = App.Vector(center[0] - pip_depth * normal[0], center[1] + x, center[2] + y)
# 凹み用球体(差分用)
pip_cut = Part.makeSphere(pip_radius, pos)
all_pips.append(pip_cut)
# サイコロからピップを差し引く
for pip in all_pips:
cube_with_fillet = cube_with_fillet.cut(pip)
# 差し引き後のサイコロを再表示
doc.getObject("Shape").Shape = cube_with_fillet
doc.recompute()