Blender 2.9, Python, 座標で複数メッシュ選択

3*5*5個の球体を作って、一部をz座標条件で選択、色をつける実験です。
前回の延長だからすぐできるんだろうと甘く見ていたら、「メッシュ複数選択」だけでも考える時間が結構かかりました。いろいろ調べて、obj.location.zと言う便利な指定方法があることがわかりました。 3色指定の色合いは 「坊ちゃん団子 / つぼや菓子舗」より。オブジェクトたくさん作って一部を選択するやり方の他にも、一つ作ってそれを複製、の方法もあるはずなので次回はそちらも挑戦の予定。

なお、オブジェクト の選択selection, アクティブ化activation について、参考にしたのは
[オブジェクトのアクティブ化【第 4 回 Python × Blender】](https://tamaki-py.hatenablog.com/entry/2019/05/28/180042)です。ただしblender 2.7向けというのが要注意です。
動画1秒 はtwitterに投稿。[blender 2.9, python.  animation 1 sec. 動画1秒] (https://twitter.com/naohiko7/status/1308406202020265986)
（ご意見も歓迎します。pythonスクリプトをもっと効率よく書けるとか、この1行は不要だろうとか、ご意見ください。）　
![b3d_boccha2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690216/fda5f4f8-befd-9c21-1dfe-04d9bbe2f07d.png)



```python3
#bpy_nh21 (bochan dango)  create spheres, select multiple spheres,  assign material
import bpy

# ========= DELETE ALL mesh, light, camera,  =========
for item in bpy.data.objects:
    bpy.data.objects.remove(item)
# ========= 1st FLOOR height 
z1f = -6 # first floor sphere height 

# ============== "light_spot1" ==== HIGH 
# create light datablock, set attributes
light_data = bpy.data.lights.new(name="light_spot1", type='SPOT')
light_data.energy = 700
# create new object with our light datablock
light_object1 = bpy.data.objects.new(name="light_spot1", object_data=light_data)
# link light object
bpy.context.collection.objects.link(light_object1)
# make it active 
bpy.context.view_layer.objects.active = light_object1
#change location
light_object1.location = (-5, -7, 4+z1f)
light_object1.delta_rotation_euler = (1.3, 0, -0.3) #ゼロゼロゼロで真下を向く。
# update scene, if needed
dg = bpy.context.evaluated_depsgraph_get() 
dg.update()        

# ============== "light_spot2" ==== HIGH 
# create light datablock, set attributes
light_data = bpy.data.lights.new(name="light_spot1", type='SPOT')
light_data.energy = 2000
# create new object with our light datablock
light_object1 = bpy.data.objects.new(name="light_spot1", object_data=light_data)
# link light object
bpy.context.collection.objects.link(light_object1)
# make it active 
bpy.context.view_layer.objects.active = light_object1
#change location
light_object1.location = (10, -7, 7+z1f)
light_object1.delta_rotation_euler = (1.3, 0, 0.3) #ゼロゼロゼロで真下を向く。
# update scene, if needed
dg = bpy.context.evaluated_depsgraph_get() 
dg.update()        

#bpy.ops.object.camera_add(enter_editmode=False, align='VIEW', location=(10, -10, 8), rotation=(1.2, 0, 0.5)) # (fixed CAMERA)

# ====  hex COLOR CODE to R,G,B
def hex_to_rgb( hex_value ):
    b = (hex_value & 0xFF) / 255.0
    g = ((hex_value >> 8) & 0xFF) / 255.0
    r = ((hex_value >> 16) & 0xFF) / 255.0
    return r, g, b

# ==== spheres 5*5*3

for x in range (5):
    for y in range (5):
        for z in range (3):
            bpy.ops.mesh.primitive_uv_sphere_add(radius=1, align='WORLD', location=(x*2, y*2, z*2+z1f), scale=(1, 1, 1))

for obj in bpy.data.objects: #scan every object 
    obj.select_set(False) # deselect all 

# ==== select (obj.location.z) lowest 5*5
for obj in bpy.data.objects: #scan every object, select multiple objects with Z
    loc_z = (obj.location.z)
    if loc_z == z1f: 
        obj.select_set(True)
# ====  set material  1st floor
h = 0x8b4513  #saddlebrown#8b4513
for x in bpy.context.selected_objects:
    obj =  x.data
    mat1 = bpy.data.materials.new('Brown')
    mat1.diffuse_color = (*hex_to_rgb(h), 0)
    obj.materials.append(mat1)
    
for obj in bpy.data.objects: #scan every object 
    obj.select_set(False) # deselect all 
    
# ====  select with z 2nd floor
for obj in bpy.data.objects: #scan every object, select multiple objects with Z
    loc_z = (obj.location.z)
    if loc_z == z1f+2:
        obj.select_set(True)

bpy.data.objects['light_spot1'].select_set(False)

# ====  set material 2nd floor 
h = 0xffdead #navajowhite#ffdead
for x in bpy.context.selected_objects:
    obj =  x.data
    mat2 = bpy.data.materials.new('n_white')
    mat2.diffuse_color = (*hex_to_rgb(h), 0)
    obj.materials.append(mat2)
    
for obj in bpy.data.objects: #scan every object 
    obj.select_set(False) # deselect all 

# ====  select with z  3rd floor 
for obj in bpy.data.objects: #scan every object, select multiple objects with Z
    loc_z = (obj.location.z)
    if loc_z == z1f+4:
        obj.select_set(True)

bpy.data.objects['light_spot1'].select_set(False)

# ====  set material 3rd floor 
h = 0x808000   #olive#808000
for x in bpy.context.selected_objects:
    obj =  x.data
    mat3 = bpy.data.materials.new('olive')
    mat3.diffuse_color = (*hex_to_rgb(h), 0)
    obj.materials.append(mat3)
    
for obj in bpy.data.objects: #scan every object 
    obj.select_set(False) # deselect all 

#  ====== add a camera, camera movement (bpy_nh13)
bpy.ops.curve.primitive_bezier_circle_add(enter_editmode=False, align='WORLD', location=(0, 0, 0))
bpy.context.object.scale[0] = 12
bpy.context.object.scale[1] = 12
bpy.ops.object.empty_add(type='CUBE', align='WORLD', location=(0, 0, 0))
bpy.ops.object.camera_add(enter_editmode=False, align='VIEW', location=(0, 0, 0), rotation=(0, 0, 0))

bpy.data.objects['Empty'].select_set(True)
bpy.data.objects['Camera'].select_set(True)

bpy.context.view_layer.objects.active = bpy.data.objects['Empty']
bpy.ops.object.parent_set(type='OBJECT')

bpy.data.objects['Camera'].select_set(False)
bpy.data.objects['Empty'].select_set(True)

bpy.ops.object.constraint_add(type='FOLLOW_PATH')
bpy.context.object.constraints["Follow Path"].target = bpy.data.objects["BezierCircle"]

bpy.context.object.constraints["Follow Path"].use_curve_follow = True
bpy.context.object.constraints["Follow Path"].use_fixed_location = True

bpy.data.objects['Empty'].select_set(False)
bpy.data.objects['Camera'].select_set(True)

bpy.ops.object.constraint_add(type='TRACK_TO')
bpy.context.object.constraints["Track To"].target = bpy.data.objects["Sphere.032"]
bpy.context.object.constraints["Track To"].up_axis = 'UP_Y'
bpy.context.object.constraints["Track To"].track_axis = 'TRACK_NEGATIVE_Z'  #5m00sec
#Camera Keyframe #(Insert keyframe to object's Offset Factor Python API - stack exchange)
bpy.data.objects['Camera'].select_set(False)
bpy.data.objects['Empty'].select_set(True)
bpy.context.scene.frame_current = 1
bpy.context.object.constraints["Follow Path"].offset_factor = 0
ob = bpy.context.object
# ob.constraints['Follow Path']
# bpy.data.objects['Empty'].constraints["Follow Path"]
# [bpy.data.objects['Empty'].constraints["Follow Path"]]
con = ob.constraints.get("Follow Path")
con.offset_factor = 0.0
con.keyframe_insert("offset_factor", frame=1)
con.offset_factor = 0.15
con.keyframe_insert("offset_factor", frame=8)
con.offset_factor = 0.4
con.keyframe_insert("offset_factor", frame=16)
con.offset_factor = 0.3
con.keyframe_insert("offset_factor", frame=18)
con.offset_factor = 0.0
con.keyframe_insert("offset_factor", frame=30)
# ======= 
```