Blender2.80のスクリプトだけで例のゲームっぽい舞台を生み出す

　Blender2.8のスクリプトの練習として、**例のゲーム**っぽい舞台を作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/31c2a01d-307e-9a61-5ff8-f7e16ac37c0f.png)
（イメージ映像）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d7b74cc9-7aff-1a44-30a8-39b57dbc6b79.png)
（これは実在の[ハイ・トレッスル・トレイル橋（アイオワ州）](https://en.wikipedia.org/wiki/High_Trestle_Trail)）

#完成映像

　このようなものができる予定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/1f97ca84-e743-a7f8-caa2-64b0d08d204c.png)

#ソースコード

```Python
import bpy
import numpy as np

#現状をリセット
for mat in bpy.data.materials:
    bpy.data.materials.remove(mat)
for cam in bpy.data.cameras:
    bpy.data.cameras.remove(cam)
for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)
for obj in bpy.context.scene.objects:
    bpy.data.objects.remove(obj)
```

　何はなくともモジュールのインポートと現状のリセット。

#足場

　とりあえず足場を作ります。まずはキューブ。

```Python
#足場を作る
bpy.ops.mesh.primitive_cube_add(location=(0,0,-50))
bpy.context.object.scale = (2,2,50)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/c43e2073-f67d-e57b-a768-ab18da71ed82.png)

　マテリアルを作って適用させます。

```Python
#黒マテリアルを設定
bpy.data.materials.new(name = 'black')
mat = bpy.data.materials['black']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes['Principled BSDF']
mat_node.inputs['Base Color'].default_value = (0,0,0,1)
mat_node.inputs['Metallic'].default_value = 1
mat_node.inputs['Specular'].default_value = 1

#黒マテリアルを足場に適用
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/cd80bd9b-ae41-7d29-c0a2-90ab7dbe1d79.png)

#足場（枠）

　枠を作ります。`WIREFRAME`モディファイアを利用します。

```Python
#枠を作る
bpy.ops.mesh.primitive_cube_add(location=(0,0,-50))
bpy.data.objects['Cube.001'].scale=(2,2,50)
bpy.ops.object.transform_apply(scale=True)
bpy.ops.object.modifier_add(type='WIREFRAME')
bpy.context.object.modifiers['Wireframe'].thickness = 0.05
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/c2fec133-863b-2e2c-9b18-5b4d0f04146e.png)

　青く光るマテリアルを作って、枠に適用します。

```Python
#発光マテリアル（青）を設定
bpy.data.materials.new(name = 'blue')
mat = bpy.data.materials['blue']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes.new('ShaderNodeEmission')
mat_node.inputs['Color'].default_value = (0,0,1,1)
mat_node.inputs['Strength'].default_value = 500
matout = node_tree.nodes['Material Output']
node_tree.links.new(mat_node.outputs[0], matout.inputs[0])

#発光マテリアル（青）を枠に適用
mat = bpy.data.materials['blue']
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/e315b338-321e-a274-5b14-236cfa678c9c.png)

　`use_nodes`を`True`にした場合、デフォルトでは`Principled BSDF`が適用されますが、それ以外のシェーダを使う場合は`node_tree.nodes.new`で新しくノードを作成し、それと出力ノードをリンクさせてあげなければいけません。そのあたりの処理が、

```Python
mat_node = node_tree.nodes.new('ShaderNodeEmission')
```
　と

```Python
node_tree.links.new(mat_node.outputs[0], matout.inputs[0])
```
　になります。

#ねじれ枠

　はるかかなたに伸びるねじれ枠を作っていきます。平面をひねりながら生成していき、また`WIREFRAME`を利用します。

```Python
#ねじれ枠を作る
for i in range(50):
    bpy.ops.mesh.primitive_plane_add(size=15,location=(0,i*5+10,0),rotation=(np.pi/2,i/5,0))
    bpy.ops.object.modifier_add(type='WIREFRAME')
    bpy.context.object.modifiers['Wireframe'].thickness = 0.8
    mat = bpy.data.materials['black']
    bpy.ops.object.material_slot_add()
    bpy.context.object.active_material=mat
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/adc9d4d7-affc-31fa-5421-ef8f35263fc8.png)

 黒マテリアルはすでに作成しているので、`mat = bpy.data.materials['black']`で呼び出すだけで再利用できます。

#レールを作る

　赤と青のレールを敷いてあげましょう。先に発光マテリアルの赤バージョンを作成します。

```Python
#発光マテリアル（赤）を設定
bpy.data.materials.new(name = 'red')
mat = bpy.data.materials['red']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes.new('ShaderNodeEmission')
mat_node.inputs['Color'].default_value = (1,0,0,1)
mat_node.inputs['Strength'].default_value = 100
matout = node_tree.nodes['Material Output']
node_tree.links.new(mat_node.outputs[0], matout.inputs[0])
```

　そして改めてレール作成。

```Python
#レールを作る
poslist = [
    [ 4,0,-2],
    [ 4,0, 2],
    [-4,0,-2],
    [-4,0, 2]
    ]

for pos in poslist:
    bpy.ops.mesh.primitive_cube_add(size=1,location=pos)
    bpy.context.object.scale = [0.2,800,0.2]
    if bpy.context.object.location[2] > 1:
        mat = bpy.data.materials['red']
        bpy.ops.object.material_slot_add()
        bpy.context.object.active_material=mat
    else:
        mat = bpy.data.materials['blue']
        bpy.ops.object.material_slot_add()
        bpy.context.object.active_material=mat
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/d9b4287c-f32e-729a-9a00-3ac0cc14d328.png)

#カメラ

　最後にカメラをつけます。

```Python
#カメラを設定
bpy.ops.object.camera_add(location=(2,-10,2))
bpy.data.objects['Camera'].rotation_euler = (np.pi*1/2, 0, 0)
bpy.data.cameras[0].lens = 20
```

　あとは背景を黒くしてレンダリングすれば、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/1f97ca84-e743-a7f8-caa2-64b0d08d204c.png)

　このような感じになります。

#全ソースコード

　今までのを1つのソースにまとめます。

```Python
import bpy
import numpy as np

#現状をリセット
for mat in bpy.data.materials:
    bpy.data.materials.remove(mat)
for cam in bpy.data.cameras:
    bpy.data.cameras.remove(cam)
for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)
for obj in bpy.context.scene.objects:
    bpy.data.objects.remove(obj)

#足場を作る
bpy.ops.mesh.primitive_cube_add(location=(0,0,-50))
bpy.context.object.scale = (2,2,50)

#黒マテリアルを設定
bpy.data.materials.new(name = 'black')
mat = bpy.data.materials['black']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes['Principled BSDF']
mat_node.inputs['Base Color'].default_value = (0,0,0,1)
mat_node.inputs['Metallic'].default_value = 1
mat_node.inputs['Specular'].default_value = 1

#黒マテリアルを足場に適用
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat

#枠を作る
bpy.ops.mesh.primitive_cube_add(location=(0,0,-50))
bpy.data.objects['Cube.001'].scale=(2,2,50)
bpy.ops.object.transform_apply(scale=True)
bpy.ops.object.modifier_add(type='WIREFRAME')
bpy.context.object.modifiers['Wireframe'].thickness = 0.050

#発光マテリアル（青）を設定
bpy.data.materials.new(name = 'blue')
mat = bpy.data.materials['blue']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes.new('ShaderNodeEmission')
mat_node.inputs['Color'].default_value = (0,0,1,1)
mat_node.inputs['Strength'].default_value = 500
matout = node_tree.nodes['Material Output']
node_tree.links.new(mat_node.outputs[0], matout.inputs[0])

#発光マテリアル（青）を枠に適用
mat = bpy.data.materials['blue']
bpy.ops.object.material_slot_add()
bpy.context.object.active_material=mat

#ねじれ枠を作る
for i in range(50):
    bpy.ops.mesh.primitive_plane_add(size=15,location=(0,i*5+10,0),rotation=(np.pi/2,i/5,0))
    bpy.ops.object.modifier_add(type='WIREFRAME')
    bpy.context.object.modifiers['Wireframe'].thickness = 0.8
    mat = bpy.data.materials['black']
    bpy.ops.object.material_slot_add()
    bpy.context.object.active_material=mat

#発光マテリアル（赤）を設定
bpy.data.materials.new(name = 'red')
mat = bpy.data.materials['red']
mat.use_nodes = True
node_tree = mat.node_tree
mat_node = node_tree.nodes.new('ShaderNodeEmission')
mat_node.inputs['Color'].default_value = (1,0,0,1)
mat_node.inputs['Strength'].default_value = 100
matout = node_tree.nodes['Material Output']
node_tree.links.new(mat_node.outputs[0], matout.inputs[0])

#レールを作る
poslist = [
    [ 4,0,-2],
    [ 4,0, 2],
    [-4,0,-2],
    [-4,0, 2]
    ]

for pos in poslist:
    bpy.ops.mesh.primitive_cube_add(size=1,location=pos)
    bpy.context.object.scale = [0.2,800,0.2]
    if bpy.context.object.location[2] > 1:
        mat = bpy.data.materials['red']
        bpy.ops.object.material_slot_add()
        bpy.context.object.active_material=mat
    else:
        mat = bpy.data.materials['blue']
        bpy.ops.object.material_slot_add()
        bpy.context.object.active_material=mat

#カメラを設定
bpy.ops.object.camera_add(location=(2,-10,2))
bpy.data.objects['Camera'].rotation_euler = (np.pi*1/2, 0, 0)
bpy.data.cameras[0].lens = 20
```