スクリーンショット_2019-08-27_18

[BPY28]再帰問題(ボーンの子を全て選択)

(※ここの冒頭に出てくるスクリプトは(多分)2.69以前の、.childrenがなかった頃のコードです)

はい、じゃあ「ポーズモードで選択したボーンの子を全て選択」してみてください。

えーっと前提としてどういうシチュエーションですか?

まずは Rigifyを有効にして、

[Shift]+[A]のAddメニューから Armature>Basic>Basic Human をクリックして

Human アーマチュアオブジェクトをシーンに追加します。

ポーズモードにして

spine.003 を選択します。

はい、準備できたので、こちら流してもらえますか?インタラクティブコンソールで。

def selectchilds_recv(tree_dict,bone):
   list = tree_dict[bone.name]
   for x in list:
       x.select = True
       selectchilds_recv(tree_dict,x)
   return

arm = C.active_object
bones = arm.data.bones
tree_dict ={ x.name : [] for x in bones}

for x in bones:
   p = x.parent
   if p != None:
       tree_dict[p.name].append(x)

abone = C.active_bone
selectchilds_recv(tree_dict,abone)

(いきなりぶち込んでくるのかよ)了解しました。あっ、肩から先、首から頭のボーンが選択されましたね!

ではこれを手動でやってみてください。何か使えそうなコマンドはありますか?

えっ、あっ、探してみますね。Extend Child がそれっぽそう。

あれ?一つの子に対してなんだ。

[L]キーのSelect Linked も違いますね。

親子関係だから…Select Hierarchy はオブジェクトのみっぽいな。

すみません、見当たりません。

そうですね。「無いなら作れ」ですね。

ところで、先ほど実行したスクリプトはどんな処理をしているんですか?

では処理を見ていきますね。

arm = C.active_object
bones = arm.data.bones
tree_dict ={ x.name : [] for x in bones}

tree_dict にはボーン名をキーとした空の配列が格納されます。

>>> tree_dict
{'spine': [], 'spine.001': [], 'spine.002': [], 'spine.003': [], 'spine.004': [], 'spine.005': [], 'spine.006': [], 'shoulder.L': [], 'upper_arm.L': [], 'forearm.L': [], 'hand.L': [], 'shoulder.R': [], 'upper_arm.R': [], 'forearm.R': [], 'hand.R': [], 'breast.L': [], 'breast.R': [], 'pelvis.L': [], 'pelvis.R': [], 'thigh.L': [], 'shin.L': [], 'foot.L': [], 'toe.L': [], 'heel.02.L': [], 'thigh.R': [], 'shin.R': [], 'foot.R': [], 'toe.R': [], 'heel.02.R': []}

>>> 

ここで空の配列に、「それぞれのボーンが子とするボーン」の情報を追加していきます。

for x in bones:
  p = x.parent
  if p != None:
      tree_dict[p.name].append(x)

実はオブジェクトもボーンも「親へ」の情報しか持てないんですよ(これはのちにウソだとわかる)。なので読み替えの処理をして親から子への情報をここで作ってあげます。

>>> tree_dict['spine.003']
[bpy.data.armatures['metarig.002'].bones["spine.004"], bpy.data.armatures['metarig.002'].bones["shoulder.L"], bpy.data.armatures['metarig.002'].bones["shoulder.R"], bpy.data.armatures['metarig.002'].bones["breast.L"], bpy.data.armatures['metarig.002'].bones["breast.R"]]

>>> 

もうちょっとわかりやすい形にすると、こう。

>>> for x in tree_dict['spine.003']:
...     print(x.name)
...     
spine.004
shoulder.L
shoulder.R
breast.L
breast.R

>>> 

そして、spine.003の子であるshoulder.Rupper_arm.Rという子を持っています。

>>> tree_dict['shoulder.R']
[bpy.data.armatures['metarig.002'].bones["upper_arm.R"]]

>>>

これを現在アクティブ選択状態のボーンから開始、ということで、selectchilds_recvを呼び出します。

abone = C.active_bone
selectchilds_recv(tree_dict,abone)

子供のリストを取得し、さらにselectchilds_recvを呼び出し、といった具合でリストの中身が無くなるまで続けます。ここでボーンを選択状態にしていますね。

def selectchilds_recv(tree_dict,bone):
  list = tree_dict[bone.name]
  for x in list:
      x.select = True
      selectchilds_recv(tree_dict,x)
  return

わかりましたか?

さっぱりわかりません。




というか、.children 使えばよくないですか

def select_childs_recv(bones):
   for x in bones:
       x.select = True
       if len(x.children) > 0:
           select_childs_recv(x.children)
   return

select_childs_recv(C.active_bone.children)



お読みいただきありがとうございます。サポートいただいた分はおやつのグレードアップに使おうかと思います。スキ、SNSにシェアもよろしくお願いします!