4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VCIAdvent Calendar 2019

Day 14

VCIで3D空間にグラフを描画する試み

Last updated at Posted at 2019-12-14

はじめに

この記事は、VCI Advent Calendar 2019の14日目の記事です。

バーチャルキャストの世界の中で、データを3次元のグラフとして描画したい。
無理やり実装ですが、それを実現したVCI「ぐらんまてぃか」の実現方法を記載するものになります。

3D世界にグラフを描きたい!

バーチャルキャストでVCIのスクリプトが使えるようになって、やりたいと思ったこと。
その一つがSFアニメなので出てくるグラフ描写。
あれを実データでちゃんと描画したいとわたしは思ったのでした。

要件は以下のようなもの

  • Vキャス内で意図した数値データをグラフにして描画したい
  • カラフルで半透明できれいにしたい
  • 同じ物を使って、いろんなグラフが描けるようにしたい

今回の実現方法は以下の通り

考え方

  • 当たったアイテムの名前をデータ受け渡し文字列として使い「データ構文」を決めておく
  • そこそこの数の小さな箱と球体を本体VCIに格納しておく
  • 小さな箱と球体のXYZ方向への拡大縮小を調整してグラフを書く

実際の処理の流れ

  1. 当たったアイテムの名前から配列を作る
  2. 配列の2つ目の文字列でなんのグラフを描くのかを判定する
  3. グラフ毎に配列の3つめの文字列以降を使って描画処理を実行する

「データ構文」に従った名前を持つアイテムを「ぐらんまてぃか」に当てればグラフが描画できる
といった形で要件を実装します。

本体VCIを作る

image.png
グラフの描画に使う箱と球体をとりあえず入れておきます

image (1).png
箱の設定はこんな感じ

データ構文を考える

まず、データ描画に使うデータ構文を考えます。

  • "_"を区切り文字とする。
  • 区切った時の1番目の文字列で、本VCI「ぐらんまてぃか」対応であるかを判定する。
  • 区切った時の2番目の文字列で、どんなグラフを描画するか判定する。
  • 区切った時の3番目以降の文字列は、グラフによって利用意味を変える。

現時点では「棒グラフ(縦)」「棒グラフ(横)」「ネットワークグラフ」に対応するようにしました。

構文に従った文字列は以下のようになります

  • 棒グラフ(縦) MEME_BarChart_12_12_3_41_46_11_43_23_5_8_9_12_3
  • 棒グラフ(横) MAME_BarGraph_1_5_21_21_2_11_13_30_52_8_19_22_7
  • ネットワークグラフ MAME_Network_Node_a_12_b_12_c_3_d_41_e_46_f_11_g_43_h_23_i_5_j_8_Edge_a-b_3_a-c_1_a-d_4_a-g_3_b-j_6_d-f_4_d-g_6_e-f_3_e-h_4_g-h_1_g-i_3_g-j_3_h-j_2

グラフ毎の構文については、後半で解説します。

実際の処理とコード

1. アイテム名から配列を作る

アイテムが触れたらアイテム名の文字列内の"_"を区切り文字として分離し、配列にします。


function split(str, ts)
    -- 引数がないときは空tableを返す
    if ts == nil then return {} end
    local t = {} ; 
    i=1
    for s in string.gmatch(str, "([^"..ts.."]+)") do
        t[i] = s
        i = i + 1
    end  
    return t
end
function onTriggerEnter(item, hit)
    local array = split(hit, "_")
~~~~~~~~~~~~以下略~~~~~~~~~~~~

split構文がデフォルトではないようなので、コピペでsplit構文は作ってあります。

onTriggerEnter(item, hit)は、Unity上の設定でisTriggerにチェックをつけて出力したVCIの場合にアイテム同士が触れると発生してくれるイベントです。

  • 第一引数(item)が触れられたアイテムの名前
  • 第二引数(hit)は触れたアイテムの名前
    になります。

ここでの処理は、触れたアイテムの名前文字列(hit)を"_"で区切って配列(array)にしています。

2. 配列の2つ目の文字列でなんのグラフを描くのかを判定する


~~~~~~~~~~~~以下略~~~~~~~~~~~~
if array[2] == "BarChart" then
~~~~~~~~~~~~以下略~~~~~~~~~~~~
elseif array[2] == "BarGraph" then
~~~~~~~~~~~~以下略~~~~~~~~~~~~
elseif array[2] == "Network" then
~~~~~~~~~~~~以下略~~~~~~~~~~~~

考えた構文に従って、配列の2番目の文字列で描画するグラフに処理を分岐させます。

3.グラフ毎に配列の3つめの文字列以降を使って描画処理を実行する

3-A.棒グラフ

        --- 棒グラフ(縦)
        for v in pairs(array) do
            if v > 2 then
                local item =  vci.assets.GetSubItem("Cubeword"..(v-2))
                item.SetVelocity(Vector3.__new(0,0,0))
                item.SetLocalPosition(Vector3.__new(0,0,0))
                item.SetRotation(Quaternion.__new(0,0,0,1.0))
                item.SetLocalPosition(Vector3.__new(0.15*v,0.2+0.1*array[v]/2,0))
                item.SetLocalScale(Vector3.__new(0.1,0.1*array[v],0.1))
            end
        end

区切った3番目以降の値は、棒グラフの縦の高さとして処理します。
local item = vci.assets.GetSubItem("Cubeword"..(v-2))
で箱を指定し、array[v]のサイズにY軸(縦)方向に拡大、起点からY軸の+ー両方に拡大するので、位置も半分サイズ分あげます。
D5ArKfTUwAAATpY.png

3-B.ネットワークグラフ

ネットワークグラフは、3文字以降に「Node」か「Edge」の文字列が存在するかどうかで、そこからの処理を切り替えます。
Nodeがネットワークの点になる部分で、Edgeがネットワークの線の部分です。
文字列を再度みます。

MAME_Network_Node_a_12_b_12_c_3_d_41_e_46_f_11_g_43_h_23_i_5_j_8_Edge_a-b_3_a-c_1_a-d_4_a-g_3_b-j_6_d-f_4_d-g_6_e-f_3_e-h_4_g-h_1_g-i_3_g-j_3_h-j_2

  • Node
    Node_a_12_b_12_c_3...
    の部分は、点aを描画し、サイズは12です。点bを描画し、サイズは12です。点cを描画し、サイズは3です。というような意味になります。

  • Edge
    Edge_a-b_3_a-c_1_a-d_4
    の部分は、点aと点bの間に線を描き、その線の太さは3です。点aと点cの間に線を描き、その線の太さは1です。点aと点dの間に線を描き、その線の太さは4です。というような意味になります。

コードは以下のようになっています。

--- ネットワーク図
        local flg = ""
        local node = {}
        local nodeids = {}
        local edge = {}
        local nodeNo = 0
        local edgeNo = 0
        for v in pairs(array) do
            if array[v]=="Node" then
                flg = "Node"
            elseif array[v]=="Edge" then
                flg = "Edge"
            else
                if flg == "Node" and v%2==0 then
                    nodeNo = nodeNo +1
                    node[array[v]] = {0.5*math.random(10),0.5*math.random(10),0.5*math.random(10)}
                    nodeids[array[v]] = nodeNo
                    local item =  vci.assets.GetSubItem("SphereWord"..nodeNo)
                    item.SetLocalPosition(Vector3.__new(node[array[v]][1],node[array[v]][2],node[array[v]][3]))
                    item.SetLocalScale(Vector3.__new(0.02*array[v+1],0.02*array[v+1],0.02*array[v+1]))
                elseif flg == "Edge" and v%2==1 then
                    edgeNo = edgeNo+1
                    local set = split(array[v], "-")
                    local ds = math.sqrt((node[set[1]][1]-node[set[2]][1])*(node[set[1]][1]-node[set[2]][1]) + (node[set[1]][2]-node[set[2]][2])*(node[set[1]][2]-node[set[2]][2]) + (node[set[1]][3]-node[set[2]][3])*(node[set[1]][3]-node[set[2]][3]))
                    local item = vci.assets.GetSubItem("Cubeword"..edgeNo)
                    item.SetVelocity(Vector3.__new(0,0,0)) -- 動きオフ
                    item.SetRotation(Quaternion.__new(0,0,0,1.0)) -- 角度初期化
                    item.SetLocalPosition(Vector3.zero) -- 位置初期化
                    item.SetLocalRotation(Quaternion.LookRotation(Vector3.__new(node[set[2]][1],node[set[2]][2],node[set[2]][3])-Vector3.__new(node[set[1]][1],node[set[1]][2],node[set[1]][3]))) -- 方向を指定
                    item.SetLocalPosition(Vector3.__new((node[set[1]][1]+node[set[2]][1])/2,(node[set[1]][2]+node[set[2]][2])/2,(node[set[1]][3]+node[set[2]][3])/2)) -- 位置を指定
                    item.SetLocalScale(Vector3.__new(0.02,0.02,ds))
                    edge[array[v]] = array[v+1]
                end
            end
        end

実態は棒グラフと同じようなことをやっています。
点は球体を指定されたサイズで置く。
線は箱を引き伸ばして、球を結ぶように見えるように置く。

本当は重みづけだったり、配置の工夫だったりいろいろやってみたいことはありますが、とりあえず描画はこんな感じ。

20190401-031330_62.jpg

終わりに

こんな感じで、今回は名前でグラフを描画するVCIをどう実現したかをかきました。
こちらを作ったのは、まだ、message構文などがない頃でしたが、現在ではmessage構文などもあるので、それを使って描画したいデータの情報を受け渡すという方がスタンダードかもしれないですね。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?