🎨 Blenderブログ
← 記事一覧に戻る

Blenderで「箱が開くアニメーション」を作る方法

完成イメージ

  • 赤いギフトボックスが閉じた状態からスタート
  • 蓋が後ろに開く(フレーム30〜80)
  • 4つの側面が花びらのように外側に倒れる(フレーム85〜130)
  • フタの上に金色のリボン・蝶結び付き

必要なもの

  • Blender 4.0 以上(このチュートリアルは 5.1.1 で動作確認)
  • 追加アドオン・プラグインなし
  • Python の知識なくてもOK(スクリプトをコピペするだけ)

仕組みの説明

Blenderには Pythonスクリプト をそのまま実行できる機能があります。
このスクリプトが以下をすべて自動でやってくれます:

やること 内容
箱を作る 底・4側面・蓋を別々のオブジェクトとして生成
ヒンジを設定 各パネルの根元に「回転の軸(Empty)」を配置
親子関係 各パネルをヒンジに従わせる
アニメーション キーフレームで回転を記録
ライト・カメラ 自動で配置

手順

ステップ1:Blenderを開く

Blenderを起動し、デフォルトの立方体がある状態でOKです。


ステップ2:Scriptingタブを開く

画面上部のタブから 「Scripting」 をクリックします。

Layout | Modeling | Sculpting | ... | Scripting ← これをクリック

ステップ3:新しいスクリプトを作成

Scriptingワークスペース内の 「New」 ボタンをクリックして、空のテキストエリアを作ります。


ステップ4:スクリプトを貼り付ける

下のスクリプトを全部コピーして、テキストエリアに貼り付けます。

import bpy
import math

# ===== シーンのリセット =====
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

scene = bpy.context.scene
scene.render.fps = 24
scene.frame_start = 1
scene.frame_end = 160

# ===== サイズ定数 =====
S = 2.0    # 箱の一辺
T = 0.09   # 板の厚さ
H = S / 2  # 半辺

# ===== マテリアル作成 =====
def make_mat(name, color, metallic=0.0, roughness=0.5):
    mat = bpy.data.materials.new(name=name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes["Principled BSDF"]
    bsdf.inputs["Base Color"].default_value = (*color, 1.0)
    bsdf.inputs["Metallic"].default_value = metallic
    bsdf.inputs["Roughness"].default_value = roughness
    return mat

mat_box    = make_mat("BoxRed",  (0.60, 0.04, 0.04), roughness=0.55)
mat_lid    = make_mat("LidRed",  (0.70, 0.07, 0.04), roughness=0.40)
mat_ribbon = make_mat("Ribbon",  (1.00, 0.82, 0.00), metallic=0.2, roughness=0.35)
mat_bow    = make_mat("Bow",     (1.00, 0.75, 0.00), metallic=0.3, roughness=0.3)
mat_inside = make_mat("Inside",  (0.92, 0.87, 0.72))
mat_floor  = make_mat("Floor",   (0.85, 0.85, 0.85), roughness=0.8)

# ===== ヘルパー関数 =====
def add_box(name, loc, size, mat):
    bpy.ops.mesh.primitive_cube_add(size=1, location=loc)
    obj = bpy.context.active_object
    obj.name = name
    obj.scale = size
    bpy.ops.object.transform_apply(scale=True)
    obj.data.materials.append(mat)
    return obj

def add_empty(name, loc):
    bpy.ops.object.empty_add(type='PLAIN_AXES', location=loc)
    e = bpy.context.active_object
    e.name = name
    return e

def parent_keep_transform(child, parent):
    child.parent = parent
    child.matrix_parent_inverse = parent.matrix_world.inverted()

def keyframe_rot(obj, frame, rx=0.0, ry=0.0, rz=0.0):
    obj.rotation_euler = (math.radians(rx), math.radians(ry), math.radians(rz))
    obj.keyframe_insert(data_path="rotation_euler", frame=frame)

def smooth_all(obj):
    if not (obj.animation_data and obj.animation_data.action):
        return
    action = obj.animation_data.action
    fcurves = []
    try:
        fcurves = list(action.fcurves)
    except AttributeError:
        try:
            slot = obj.animation_data.action_slot
            for layer in action.layers:
                for strip in layer.strips:
                    try:
                        cb = strip.channelbag(slot)
                        fcurves.extend(cb.fcurves)
                    except Exception:
                        pass
        except Exception:
            pass
    for fc in fcurves:
        for kp in fc.keyframe_points:
            kp.interpolation = 'BEZIER'
            kp.easing = 'EASE_IN_OUT'

# ===== 床 =====
floor = add_box("Floor", (0, 0, -T - 0.5), (12, 12, 1.0), mat_floor)

# ===== 箱の底板 =====
bottom = add_box("Bottom", (0, 0, -T / 2), (S, S, T), mat_box)
inside_btm = add_box("InsideBottom", (0, 0, T / 2 + 0.001), (S - T * 2, S - T * 2, 0.01), mat_inside)

# ===== 4つの側面 =====
front_hinge = add_empty("FrontHinge", (0,  H, 0))
front       = add_box("Front", (0,  H + T / 2, H), (S, T, S), mat_box)
parent_keep_transform(front, front_hinge)

back_hinge  = add_empty("BackHinge",  (0, -H, 0))
back        = add_box("Back",  (0, -(H + T / 2), H), (S, T, S), mat_box)
parent_keep_transform(back, back_hinge)

left_hinge  = add_empty("LeftHinge",  (-H, 0, 0))
left        = add_box("Left",  (-(H + T / 2), 0, H), (T, S + T * 2, S), mat_box)
parent_keep_transform(left, left_hinge)

right_hinge = add_empty("RightHinge", ( H, 0, 0))
right       = add_box("Right", ( H + T / 2, 0, H), (T, S + T * 2, S), mat_box)
parent_keep_transform(right, right_hinge)

# ===== 蓋 =====
lid_hinge   = add_empty("LidHinge", (0, -H, S))
lid         = add_box("Lid",  (0, 0, S + T / 2), (S + T * 2, S + T * 2, T), mat_lid)
parent_keep_transform(lid, lid_hinge)

# ===== 蓋のリボン =====
rib_lid_v = add_box("RibbonLidV", (0, 0, S + T + 0.002), (T * 1.8, S + T * 2, T * 1.2), mat_ribbon)
rib_lid_h = add_box("RibbonLidH", (0, 0, S + T + 0.002), (S + T * 2, T * 1.8, T * 1.2), mat_ribbon)
parent_keep_transform(rib_lid_v, lid_hinge)
parent_keep_transform(rib_lid_h, lid_hinge)

# ===== 蝶結び =====
bow_l = add_box("BowLeft",   (-T * 2.5, 0, S + T * 1.5), (T * 2.2, T * 0.8, T * 2.2), mat_bow)
bow_r = add_box("BowRight",  ( T * 2.5, 0, S + T * 1.5), (T * 2.2, T * 0.8, T * 2.2), mat_bow)
bow_c = add_box("BowCenter", (0,        0, S + T * 1.5), (T * 1.2, T * 0.8, T * 1.2), mat_bow)
for b in [bow_l, bow_r, bow_c]:
    parent_keep_transform(b, lid_hinge)

# ===== キーフレームアニメーション =====
side_hinges = [front_hinge, back_hinge, left_hinge, right_hinge]
all_hinges  = side_hinges + [lid_hinge]

for hinge in all_hinges:
    keyframe_rot(hinge, 1)
    keyframe_rot(hinge, 30)

# 蓋が開く(フレーム30〜80)
keyframe_rot(lid_hinge, 30, rx=0)
keyframe_rot(lid_hinge, 80, rx=112)

# 4面が展開(フレーム85〜130)
for hinge in side_hinges:
    keyframe_rot(hinge, 85)

keyframe_rot(front_hinge, 130, rx=-90)
keyframe_rot(back_hinge,  130, rx= 90)
keyframe_rot(left_hinge,  130, ry=-90)
keyframe_rot(right_hinge, 130, ry= 90)

for hinge in all_hinges:
    keyframe_rot(hinge, 160, **{
        'rx': -90 if hinge == front_hinge else
               90 if hinge == back_hinge else
              112 if hinge == lid_hinge else 0,
        'ry': -90 if hinge == left_hinge else
               90 if hinge == right_hinge else 0,
    })

for hinge in all_hinges:
    smooth_all(hinge)

# ===== ライティング =====
for obj in list(bpy.data.objects):
    if obj.type == 'LIGHT':
        bpy.data.objects.remove(obj, do_unlink=True)

bpy.ops.object.light_add(type='AREA', location=(4, -3, 6))
main_light = bpy.context.active_object
main_light.data.energy = 300
main_light.data.size   = 4
main_light.rotation_euler = (math.radians(40), 0, math.radians(40))

bpy.ops.object.light_add(type='AREA', location=(-4, 3, 3))
fill_light = bpy.context.active_object
fill_light.data.energy = 100
fill_light.data.size   = 5
fill_light.data.color  = (0.8, 0.88, 1.0)

bpy.ops.object.light_add(type='SPOT', location=(0, 5, 5))
back_light = bpy.context.active_object
back_light.data.energy = 150
back_light.data.spot_size = math.radians(60)
back_light.rotation_euler = (math.radians(-45), 0, 0)

# ===== カメラ =====
for obj in list(bpy.data.objects):
    if obj.type == 'CAMERA':
        bpy.data.objects.remove(obj, do_unlink=True)

bpy.ops.object.camera_add(location=(5.5, -5.5, 4.5))
cam = bpy.context.active_object
cam.name = "MainCamera"
cam.rotation_euler = (math.radians(52), 0, math.radians(45))
cam.data.lens = 50
scene.camera = cam

scene.frame_set(1)
print("完成!スペースキーで再生できます。")

ステップ5:スクリプトを実行する

右上の ▶ ボタン(または Alt + P)をクリック。

数秒で箱・ライト・カメラが自動生成されます。


ステップ6:アニメーションを再生する

  1. 上の 「Layout」 タブをクリック
  2. 3Dビューポート内をクリック
  3. スペースキー で再生スタート!

カスタマイズガイド

スクリプトの上部にある定数を変えるだけで見た目が変わります。

S = 2.0    # 箱の大きさ(大きくするには 3.0 など)
T = 0.09   # 板の厚さ(薄くするには 0.05 など)

色を変える

mat_box = make_mat("BoxRed", (0.60, 0.04, 0.04))
#                              R     G     B
# 青い箱にしたい場合:(0.04, 0.10, 0.60)
# 白い箱にしたい場合:(0.95, 0.95, 0.95)

アニメーションのタイミングを変える

変数 意味
frame_end = 160 アニメーション全体の長さ
keyframe_rot(lid_hinge, 30〜80) 蓋が開くタイミング
keyframe_rot(hinge, 85〜130) 側面が開くタイミング

仕組みの解説(もう少し詳しく)

なぜ「Empty(空オブジェクト)」を使うのか?

Blenderでオブジェクトを回転させると、オブジェクト自身の中心点を軸に回ります。
箱の側面を「底辺を軸に」倒すには、底辺の位置に回転の軸が必要です。

そこで Empty(見えない軸) を底辺に置き、そこにパネルを子として紐付けます。
Emptyを回転させると、パネルがその端を軸に倒れていく仕組みです。

[Empty(ヒンジ)] ← 底辺の位置に配置
    └── [パネル] ← Emptyの子として紐付け

キーフレームの仕組み

keyframe_rot(lid_hinge, 30, rx=0)    # フレーム30:閉じている(0度)
keyframe_rot(lid_hinge, 80, rx=112)  # フレーム80:開いている(112度)

Blenderがフレーム30〜80の間を自動的に補間して、なめらかな動きを作ります。


よくある問題

問題 原因・対処
エラーが出て止まる Blenderのバージョンが古い可能性。4.0以上を使ってください
色が表示されない ビューポートが「ソリッドモード」になっています。「マテリアルプレビュー」に切り替えてください(ヘッダーの球アイコン)
アニメーションが動かない 3Dビューポート内をクリックしてからスペースキーを押してください

まとめ

ステップ 操作
1 Blenderを開く
2 Scriptingタブを開く
3 Newでスクリプトを作成
4 スクリプトをコピペ
5 ▶ボタンで実行
6 Layoutタブ → スペースキーで再生

Pythonの知識がなくても、コピペするだけで本格的なアニメーションが作れます。
色や大きさを変えて、オリジナルのギフトボックスを作ってみてください!