見出し画像

Pythonでマイクラを作る ②3Dゲームの基礎(座標、色、シーングラフ)

Pythonでマイクラを作る 第2回です。前回の宿題である 「座標、色、シーングラフ」について説明します。 これらは3Dゲームの基礎として重要な概念です。しっかり押さえておきましょう。
まずは座標について説明します。

3次元直交座標系

"""02_01_coordinate.py"""
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *


class App(ShowBase):
    # コンストラクタ
    def __init__(self):
        # ShowBaseを継承する
        ShowBase.__init__(self)

        # ウインドウの設定
        self.properties = WindowProperties()
        self.properties.setTitle('Color sample')
        self.properties.setSize(1200, 800)
        self.win.requestProperties(self.properties)
        self.setBackgroundColor(0, 0, 0)

        # マウス操作を禁止
        self.disableMouse()
        # カメラの設定
        self.camera.setPos(40, -50, 50)
        self.camera.lookAt(0, 0, 0)

        # 座標軸
        self.axis = self.loader.loadModel('models/zup-axis')
        self.axis.setPos(0, 0, 0)
        self.axis.setScale(1.5)
        self.axis.reparentTo(self.render)

        # ブロックを置く
        self.cube1 = self.loader.loadModel('models/misc/rgbCube')
        self.cube1.setPos(10, 0, 0)
        self.cube1.reparentTo(self.render)

        self.cube2 = self.loader.loadModel('models/misc/rgbCube')
        self.cube2.setPos(0, 10, 0)
        self.cube2.reparentTo(self.render)

        self.cube3 = self.loader.loadModel('models/misc/rgbCube')
        self.cube3.setPos(10, 10, 0)
        self.cube3.reparentTo(self.render)

        self.cube4 = self.loader.loadModel('models/misc/rgbCube')
        self.cube4.setPos(10, 10, 10)
        self.cube4.reparentTo(self.render)


app = App()
app.run()

座標系にはたくさんの種類があります。 Panda3Dでは3次元直交座標系が採用されています。上記のコードを入力して実行してください。

02_01_coordinate.py

3次元直交座標系では、X軸Y軸Z軸の3つの座標軸で空間上の位置を特定します。この座標系は学校で勉強するので馴染みがあるかと思います。ある点Pの位置を特定するために、3つの数字を指定します。YZ平面までの距離をx、ZX平面までの距離をy、XY平面までの距離をz として、座標 P(x, y, z) と表されます。

3次元座標系に4つのキューブを配置しました。cube1は座標 (10, 0, 0) に配置しました。cube2は座標 (0, 10, 0)、cube3は座標 (10, 10, 0)、cube4は座標 (10, 10, 10)に配置しました。カメラは (40, -50, 50) に移動して、斜め上から見下ろした画面になります。コードと実行結果をよく見比べてください。

座標系の理解には実践が一番です。02_01_coordinate.py を改造して、キューブの位置を移動させて(setPos の括弧内の数字を変更して)、どこにキューブが現れるか試してみてください。キューブの位置がわかってきたらカメラの位置を動かして、どのように画面が見えるか試してみましょう。何度か繰り返すとコツが掴めてくるはずです。

カメラについて補足
デフォルトのカメラは、動かさない時は座標 (0, 0, 0) に配置され、Y軸と同じ向きになります。デフォルトのカメラ以外にも、カメラを複数設置して切り替えて使うことができます。

色の指定(RGB値)

"""02_02_color.py"""
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *


class App(ShowBase):
    # コンストラクタ
    def __init__(self):
        # ShowBaseを継承する
        ShowBase.__init__(self)

        # ウインドウの設定
        self.properties = WindowProperties()
        self.properties.setTitle('Color sample')
        self.properties.setSize(1200, 800)
        self.win.requestProperties(self.properties)
        self.setBackgroundColor(0, 0, 0)

        # マウス操作を禁止
        self.disableMouse()
        # カメラの設定
        self.camera.setPos(0, -50, 0)
        self.camera.lookAt(0, 0, 0)

        # ブロックを置く
        for i in range(20):
            for j in range(20):
                cube = self.loader.loadModel('models/misc/rgbCube')
                cube.setPos(i - 10, 0, j - 10)
                # 2色の合成
                cube.setColor(j / 20, i / 20, 0)
                cube.reparentTo(self.render)


app = App()
app.run()
02_02_color.py

Panda3Dでは、色を数字表すために RGB値を採用しています。RGB値は、色を赤(Red)、緑(Green)、青(Blue)の割合として定義します。例えば赤は (1, 0, 0) 、緑は (0, 1, 0) 、青は (0, 0, 1) と表すことができます。

中間色を表すためのコード(02_02_color.py)を実行してください。赤要素と緑要素からたくさんの色を作ることができます。黄色は (1, 1, 0) で表せることもわかりました。今度は緑要素と青要素で中間色を作ります。コードを一部修正してください。


# 2色の合成
# cube.setColor(j / 20, i / 20, 0)
cube.setColor(0, j / 20, i / 20)
02_02_color_2.py

緑と青の合成色を作りました。緑と青を 1対 1で合成すると、色はシアン (0, 1, 1) になります。最後、青と赤の合成色を見てみましょう。

# 2色の合成
# cube.setColor(j / 20, i / 20, 0)
# cube.setColor(0, j / 20, i / 20)
cube.setColor(i / 20, 0, j / 20)
02_02_color_3.py

青と赤の合成色を表示できました。青と赤 1対 1でを合成すると、マゼンタ (1, 0, 1) になります。様々な色を三つの数字で表すことができることが少し理解できたことと思います。

2つの色の合成について見てきました。次は3つの色の合成を試してみてください。次のコードを実行してください。結果はやってみてのお楽しみということで。


# 2色の合成
# cube.setColor(j / 20, i / 20, 0)
# cube.setColor(0, j / 20, i / 20)
# cube.setColor(i / 20, 0, j / 20)
# 3色の合成
cube.setColor(j / 20, i / 20, 1)
# cube.setColor(1, j / 20, i / 20)
# cube.setColor(i / 20, 1, j / 20)

色についての補足
 RGB値は 0 〜255 の整数で表されることが多いです。赤は (255, 0, 0)、緑は (0, 255, 0)、青は (0, 0, 255) になります。Panda3Dでは、0 〜 1 の小数で指定する点に注意が必要です。

シーングラフ

シーングラフ

シーングラフは、オブジェクトを階層構造で管理する仕組みのことです。3Dゲームではたくさんの要素(オブジェクト)が表示されますが、これらをいくつかのグループに分けて管理したい場合にシーングラフが使われます。グループの分岐点のことをノードを呼びます。Panda3Dでは階層の一番上のノードを render と呼びます。

例えば、20個のキューブがあり、10個のキューブだけ同じ操作を行いたいとします。そのとき、self.render.attachNewNode() により新しいノードを作り、そこに10個のキューブを配置します。これをコードで表すと次のようになります。

"""02_03_scene_graph.py"""
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *


class App(ShowBase):
    # コンストラクタ
    def __init__(self):
        # ShowBaseを継承する
        ShowBase.__init__(self)

        # ウインドウの設定
        self.properties = WindowProperties()
        self.properties.setTitle('Scene Graph sample')
        self.properties.setSize(1200, 800)
        self.win.requestProperties(self.properties)
        self.setBackgroundColor(0, 0, 0)

        # マウス操作を禁止
        self.disableMouse()
        # カメラの設定
        self.camera.setPos(0, -50, 0)
        self.camera.lookAt(0, 0, 0)

        # シーングラフ
        self.scene_node = self.render.attachNewNode(PandaNode('scene_node'))

        # ブロックを置く
        for i in range(20):
            cube = self.loader.loadModel('models/misc/rgbCube')
            cube.setPos(i - 10, 0, 0)
            cube.setColor(i / 20, 0, 1)
            if i < 10:
                cube.reparentTo(self.render)
            else:
                cube.reparentTo(self.scene_node)


app = App()
app.run()
02_03_scene_graph.py

cube.reparentTo(self.render) により、左から10個のキューブは render に表示しています。cube.reparentTo(self.scene_node) により、右側の10個はscene_nodeノードに表示しました。右側の10個をまとめて斜め上に動かしてみます。コンストラクタの最後に次のコードを追記します。



# scene_nodeを斜め上に移動
self.scene_node.setPos(-1, 0, 1)
02_03_scene_graph_02.py

右側の10個をまとめて移動できました。グループの要素一つずつを操作したいときは次のようにコードを書くことができます。


# scene_nodeを斜め上に移動
self.scene_node.setPos(-1, 0, 1)

# 色を赤にして、階段上に並べる
for i, child in enumerate(self.scene_node.getChildren()):
    child.setColor(1, 0, 0)
    child.setZ(child.getZ() + i)
02_03_scene_graph_03.py

self.scene_node.getChildren() によりノードに含まれるオブジェクトを全て取得できます。for文を使って、キューブを赤にして階段上に移動できました。getZ()、setZ() はオブジェクトのZ座標値を取得、設定するメソッドです。

今回は3Dゲームの基礎である、座標、色、シーングラフについて学びました。ゲームだけでなく、3Dモデル作成や3D設計などにも登場する基礎知識になりますので、ここでマスターしておきましょう。

次回は、いよいよマイクラ・クローンの作成に入っていきます。マイクラのブロックを表示する方法を説明していきます。


前の記事
Pythonでマイクラを作る ①Panda3Dの基礎
次の記事
Pythonでマイクラを作る ③ブロックのモデルを作成する

その他のタイトルはこちら

いいなと思ったら応援しよう!