LoginSignup
6

More than 3 years have passed since last update.

Luaスクリプトの基本構文とVCIアイテムでの使いどころ

Last updated at Posted at 2019-04-29

はじめに

本記事では、GWアドベントカレンダーという企画の「VCIアドベントカレンダー GW ver」3日目の記事です。
VRライブ・コミュニケーションサービス「バーチャルキャスト」に2019年2月末より、追加されたVCI(Virtual Cast Interactive)に組み込むことが可能なLua言語の基本構文と、各基本構文のVCIアイテムで使用する場面について記載していきます。
ターゲットとしては、Unity等を使用し、VCIアイテムを作成した経験はあるが、プログラミングの経験がなく、Luaスクリプトの書き方が分からないという方を対象としています。

VCIとは

VCIとは、ユーザーが作成、バーチャルキャスト上へ持ち込み可能なアイテムです。
VCIは3Dモデルをアイテムとして、バーチャルキャスト上へ持ち込むことが可能ですが、Lua言語のスクリプトを組み込むことで、アイテムを触った際に、音を鳴らしたり、移動させたりすることができます。
詳しくは初日の記事を参照してください。

Luaとは

リオデジャネイロ・カトリカ大学で設計開発されたプログラミング言語です。
Luaの特徴の中には以下のようなものがあり、この特徴を買われVCIへ採用されたと思われます。

  • 高速な動作
  • 組み込みに特化

高速な動作
Luaの実行速度は非常に早く、同様のスクリプト言語であるPython等より高速に動作するとされています。
組み込みに特化
LuaはC言語プログラムに組み込まれることを目的として設計されており、C言語に限らず、他の言語で作成されたプログラムから読み込み実行させることが容易に行えます。
VCIではMoonSharpという変換プログラムを通し、Luaスクリプトが実行されます。

Luaの基本構文

本章以降は、Luaの基本構文についてと、VCIアイテムにスクリプトを組み込む際、どのような場面でその構文を使用するかを記載していきます。
先日、twitterで以下のようなツイートを見かけました。

例えば

if、for、配列

これだけしか使わずにちょっとしたアプリを作ることもできます

VCIを作る際もluaの構文として使用するものは、上記の3種が大半ですので、そこ+α(変数)に絞って解説します。

変数

変数とは数値や文字列といったデータを覚えておけるデータ領域・場所のことです。
VCIアイテムの作成を行う際は、ありとあらゆる場面で使用します。

図1 変数とは

変数を使用する理由

以下のような、マテリアル"cube"が設定されたVCIアイテムがあったとします。
図2. cubeのVCIアイテム

以下のようなスクリプトをVCIアイテムに組み込むことで、cubeのVCIサブアイテムを握り、グリップボタンを押すことで、
白色のアイテムの色が、赤色に代わり、グリップボタンを離すと、白色に戻ります。

-- マテリアルを白色で初期化する。
vci.assets._ALL_SetMaterialColorFromName("cube", Color.white)

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    -- アイテムを握って、グリップボタンを押したとき、
    -- マテリアルを赤色に変更する。
    vci.assets._ALL_SetMaterialColorFromName("cube", Color.red)
end

---[not SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押してはなしたときに呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUnuse(use)
    -- ブリップボタンを離した時、マテリアルを白色に戻す。
    vci.assets._ALL_SetMaterialColorFromName("cube", Color.white)
end

このVCIアイテムのマテリアルの名前を"cube"から、別のものに変更した場合(ここでは"CUBE"とします)、
vci.assets._ALL_SetMaterialColorFromNameを使用している箇所の"cube"を全て、"CUBE"に変更する必要があります。
変数を使用し、マテリアルの名前を覚えている場合、変数(下記例ではMATERIAL_NAME)にデータを設定する箇所のみ修正するだけで済みます。

-- マテリアルの名前を覚える変数
MATERIAL_NAME = "CUBE"

-- マテリアルを白色で初期化する。
vci.assets._ALL_SetMaterialColorFromName(MATERIAL_NAME , Color.white)

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    -- アイテムを握って、グリップボタンを押したとき、
    -- マテリアルを赤色に変更する。
    vci.assets._ALL_SetMaterialColorFromName(MATERIAL_NAME , Color.red)
end

---[not SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押してはなしたときに呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUnuse(use)
    -- ブリップボタンを離した時、マテリアルを白色に戻す。
    vci.assets._ALL_SetMaterialColorFromName(MATERIAL_NAME , Color.white)
end


変数宣言と代入

変数の宣言

変数を使用する際は、「変数名」を宣言する必要があります。宣言は「これからデータを覚えるデータ領域・場所を使用します」ということを表します。
図1でいうところの、「x」、「name」を使いますというのが、宣言に当たります。
宣言可能な変数名にはいくつかルールがあります。

  1. 変数名に使用できる文字は、文字、数字、アンダースコア(_)
  2. 変数名に数字は使用できるが、変数名の先頭文字に使用することは出来ない。
  3. luaではいくつかのキーワードが、使い方が決められており、変数名に使用できません。
and       break     do        else      elseif    end
false     for       function  goto      if        in
local     nil       not       or        repeat    return
then      true      until     while

上記を踏まえた変数の宣言例は以下のようになります。

x -- OK
name -- OK
name1 -- OK
name-1 -- 変数名で使用出来ない記号が含まれるため、エラー
1name -- 数字から始まるため、エラー
and -- 使用方法が決まっているキーワードのため、エラー

変数へのデータの代入

図1では「xという変数に1という数字を設定し、覚えておく」、「nameという変数に"あいえるたん"という文字列を設定し、覚えておく」と書きました。
変数にデータを設定することをプログラミングにおいては、代入と呼びます。
また、変数の宣言と代入は同時に行うことも出来ます。

x -- 変数xの宣言
name = "あいえるたん" -- 変数nameを宣言し、同時に"あいえるたん"という文字列を代入
x = 1 -- 変数xに1を代入


変数の再代入

同じ変数への代入は、繰り返し行うことができ、以下の図で示すように、変数にセットされた値は上書きされます。
図3 変数の再代入

luaにおける変数の型

データにはと呼ばれるどのようなデータかを示すものがあります。
また、変数は代入されたデータによって、型が変わります。

説明
nil 何もないを表します。
変数を宣言し、データを代入していない場合、この型になります。
nilの変数に代入以外の操作を行おうとした場合、エラーが発生し、その時点でスクリプトの実行が停止しますので、
変数にnilがセットされているかどうか、意識する必要があります。
boolean 真偽値が設定された場合のデータ型です。
ture(真)、false(偽)の2値が設定されます。
VCIでは握ったアイテムが、特定のアイテムか判定し、その結果を代入する等で使用します。
number 数値が設定された場合のデータ型です。
VCIアイテムでの使用例は、アイテムを握った回数をカウントする際に、使用しています。
string 文字列が設定された場合のデータ型です。
VCIではVCIサブアイテムの名前、マテリアルの名前等、名前をセットして使用することが多いです。
function 関数が設定された場合のデータ型です。
関数については、関数の章で説明します。
userdata ユーザデータが設定された場合のデータ型です。
設定されることはありますが、意識して使ったことがないので、よく分かってないです。
thread スレッドが設定された場合のデータ型です。
使ったことがないので、よく分かってないです。
table テーブルが設定された場合のデータ型です。
詳しくは次項で説明します。

vciにおいては、VCIサブアイテムを操作する際などに、変数(データ)の型を意識します。
一例として、下記はアニメーションを再生するVCIの処理ですが、
_ALL_PlayAnimationFromNameはfunction型
nameはstring型
isloopはbooloan型
になります。

_ALL_PlayAnimationFromName
function(name: string, isloop: bool)

_ALL_PlayAnimationFromNameの型はあまり意識することはありませんが、
nameやisloopについては、誤った型を指定した場合、正常に動作せず、エラーが発生する等しますので、意識するようにしましょう。
変数の型は以下のように、print()とtype()を組み合わせることで、確認することができます。(print()やtype()については関数の章で説明します。)

変数の型の確認方法

x
print(type(x))
name = "あいえるたん"
print(type(name))
x = 1
print(type(x))

実行結果

nil
string
number


テーブル

テーブルはluaにおける唯一の、1つの変数で複数のデータを扱える方法になります。
VCIにおいては、VCIアイテムの初期値を覚えておく、アイテムのリストを作成する等で使われています。

  • テーブルの宣言

テーブルを宣言する際は、以下のように{}で括った中にセットしたいデータを記述します。
名前1、名前2をキーと呼びます。

テーブル変数の名前 = { 名前1 = データ, 名前2 = データ ・・・ }
  • テーブルへのアクセス

テーブルの宣言時に宣言した、"名前1"、"名前2"を使用し、テーブルのデータにアクセスします。

テーブル変数の名前.名前1
テーブル変数の名前["名前2"]
  • テーブル宣言やアクセスの実例

以下の画像は、VCIサブアイテム名"cube"、座標(1.5, 0, 0)が設定されたVCIアイテムです。

図4 テーブル例

上記のアイテムを例に、アイテムを握り、グリップボタンが押された際に、アイテムを初期位置に戻すスクリプト例でテーブルの使用方法を説明します。
やっていることは、グリップボタンを押したときに位置や回転をリセットするのサンプルと同じです。

-- "cube"のVCIサブアイテムの初期座標をテーブルで宣言する。
-- テーブル変数の名前がcube_postion
-- 名前1がx、名前2がy、名前3がzにあたります
cube_postion = { x = 1.5, y = 0, z = 0 }

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    -- "cube"のサブアイテムの名前を使って、サブアイテムを取得する。
    Cube = vci.assets.GetSubItem("cube")
    -- テーブルのデータにアクセスして、テーブルに設定されたデータを取得する。
    -- データへのアクセスは「テーブル変数の名前.名前1」、「テーブル変数の名前["名前2"]」どちらで行ってもよいです。
    x = cube_postion.x
    y = cube_postion.y
    z = cube_postion["z"]
    position = Vector3.__new(x, y, z)
    Cube.SetPosition(position)
end


  • シーケンス(配列)

テーブルの中でも、キーが数字で1から順番に増えているものをシーケンス(配列)と呼びます。キーに数値以外が存在する場合、数値のみでも、1, 3, 5のように、1ずつ増えないものは、シーケンスではありません。
シーケンス(配列)はテーブル型の書き方の1つですが、シーケンスのみで使用出来るライブラリ関数がある等、通常のテーブルとは異なった扱い方がされます。(詳細はよく使う、luaの標準ライブラリ関数の項を参照)
シーケンスを使用する場合、テーブルのキーは省略可能です。

シーケンスを用い、「アイテムを握り、グリップボタンが押された際に、アイテムを初期位置に戻すスクリプト例」を書くと、以下のようになります。

-- "cube"のVCIサブアイテムの初期座標をテーブルで宣言する。
-- テーブル変数の名前がcube_postion
-- シーケンスの場合、キーは省略可能です。
cube_postion = { 1.5, 0, 0 }

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    -- "cube"のサブアイテムの名前を使って、サブアイテムを取得する。
    Cube = vci.assets.GetSubItem("cube")
    -- テーブルのデータにアクセスして、テーブルに設定されたデータを取得する。
    -- データへのアクセスは「テーブル変数の名前.名前1」、「テーブル変数の名前["名前2"]」どちらで行ってもよいです。
    x = cube_postion[1]
    y = cube_postion[2]
    z = cube_postion[3]
    position = Vector3.__new(x, y, z)
    Cube.SetPosition(position)
end


算術演算

プログラミングにおいて、数値や変数の計算を行うことができます。
VCIアイテムにスクリプトを組み込む際にも、座標の計算や、握った回数をカウントするなどに使用します。

算術演算子 意味
+ 足し算
- 引き算
* 掛け算
/ 割り算
% 余剰
^ べき乗

算術演算を使用したスクリプト例が、以下になります。
アイテムを握ってグリップボタンを押した回数を数え、3の倍数の時、あほになります。
※スクリプト内で出てくる、ifについては、if文の項で詳しく解説します。

useCount = 0
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    useCount = useCount + 1 -- useCountに1を足す。
    amari = useCount % 3 -- useCountを3で割ったときの余りを求める

    -- 余りが0の場合、3の倍数の時あほになる音声を再生する。
    if amari == 0 then
        vci.assets._ALL_PlayAudioFromName("ナベアツ")
    end
end


文字列連結

luaにおいて、..を使用することで、文字列と文字列、文字列を数字などを連結することができます。
一般的には、print()で使用されることが多いですが、以下のように変数と、変数やテーブルのデータを連結し、サブアイテムの名前を組み立てることにも使えます。

BUTTON_NAME = "button"
COLOR_LIST = { 
    red  = "red",
    green = "green",
    white = "white",
    blue =  "blue",
    -- 色を追加する場合、"blue"と"master"の間に追加する。
    master = "master"
}
itemName = ""..BUTTON_NAME.."_"..COLOR_LIST.master..""
subItem = vci.assets.GetSubItem(itemName)

上記は早押しボタンのスクリプトの一部を抜粋したものになります。

グローバル変数とローカル変数

ここまで、変数について説明してきましたが、変数には大きく分けて2種類あります。

  • グローバル変数 ・・・ プログラム中のどこからでもアクセス出来る変数
  • ローカル変数 ・・・決まった範囲でのみアクセス出来る変数

luaにおいて、変数の宣言時に何もつけずに宣言すると、グローバル変数になります。
ローカル変数を宣言する際は、localをつけて、宣言する必要があります。
以下にグローバル変数と、ローカル変数を使用したスクリプト例を示します。

useCount = 0 -- グローバル変数
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    useCount = useCount + 1 -- グローバル変数はプログラム中のどこでもアクセス可
    local amari = useCount % 3 -- ローカル変数の宣言、変数宣言時にlocalをつける

    -- 余りが0の場合、3の倍数の時あほになる音声を再生する。
    if amari == 0 then
        vci.assets._ALL_PlayAudioFromName("ナベアツ")
    end
-- amariはfunction onUse(use)の中でだけ有効
end

-- amariはfunction onUse(use)の中でだけ有効なので、ここではアクセス出来ない。
print(amari)

グローバル変数は、プログラム中のどこからでもアクセスでき、便利な反面、予期しない代入で値が変わってしまう場合があります。
そのため、計算に使用する変数はローカル変数で宣言し、VCIアイテム名や、初期位置のテーブル等はグローバル変数で宣言する等、使い分けることをお勧めします。

制御構文

VCIアイテムをスクリプトで動作させる際、「特定のアイテム握った際だけアニメーションを再生したい」や「握ったアイテムによって、アニメーションの再生などの動作を変更したい」など、動作を変更したい場合があります。
そのような場合、本章で学習する制御構文を利用することで、実現することが可能です。

ここからは、以下のVCIアイテムを例に、実装例を説明していきます。
図5 制御構文

作るもの

Cube 座標(1.5, 0 , 0)、色(162, 162, 162, 255) 握ったとき、Cubeの色を赤色(255, 0, 0, 255)に変更
Sphere 座標(0, 0 , 0)、色(255, 255, 255, 255) 握ったとき、全てのアイテムの座標を初期位置に戻す。
Cylinder 座標(-1.5, 0 , 0)、色(85, 197, 0, 255) 握ったとき、全てのアイテムの色を初期色に戻す。

if文

英語で「もしxxxならば」というので、if文を習ったかもしれません。
プログラミングの世界においても、「もしxxxならば、○○○する」という処理を実現するのに、if文を使用します。

構文

if 条件 then
   if文の条件に当てはまった場合の処理
end

elseを使用することで、if文の条件に当てはまらなかった場合の処理を書くことも出来ます。

if 条件 then
   if文の条件に当てはまった場合の処理
else
   if文の条件に当てはまらなかった場合の処理
end

elseifを使用することで、if文の条件に当てはまらなかった場合、別の条件に当てはまるか判定することも出来ます。

if 条件1 then
   if文の条件1に当てはまった場合の処理
elseif 条件2 then
   if文の条件1に当てはまったが、条件2に当てはまった場合の処理
else
   if文の条件1にも条件2にも当てはまらなかった場合の処理
end

比較演算子と論理演算子

前項でif 条件 thenという記述が出てきました。
この"条件"とは、右辺と左辺の比較するものです。比較結果はBooleanで返却されます。

比較に使える演算子(比較演算子)

演算子 説明
== 右辺と左辺が(データ型を含め)等しいか
~= 右辺と左辺が異なるか
< 右辺が左辺未満か
> 右辺が左辺より大きいか
<= 右辺が左辺以下か
>= 右辺が左辺以上か

論理演算子

if文を使って比較を行う際、複数の条件を設定したい場合があります。
その際、論理演算子を用いることで、複数の条件で比較することができます。

演算子 説明
not 後ろに続く式の結果を反転させる
and andの左辺、右辺両方の式がtrueを返却した場合、trueを返却する。
それ以外の場合は、falseを返却する。
or orの左辺、右辺の式、どちらか一方でもtrueを返却した場合、trueを返却する。
両辺がfalseを返却したときのみfalseを返却する。

以下に、論理演算でtrue/falseの入力を受け取った際の返却値を記載します。
論理演算を行う際の、参考にしてください。

not演算

input output
t f
f t

or演算

- t f
t t t
f t f

and演算

- t f
t t f
f f f

実装例

以下のスクリプトでは、握ってグリップボタンが押されたアイテムが何かを判定しています。

ITEM_POS = {
{ name = "Cube", x = 1.5, y = 0, z = 0 },
{ name = "Sphere", x = 0, y = 0, z = 0 },
{ name = "Cylinder", x = -1.5, y = 0, z = 0 }
}
ITEM_COLOR = {
Cube = { r = 162, g = 162, b = 162, a = 255 },
Sphere = { r = 255, g = 255, b = 255, a = 255 },
Cylinder = { r = 85, g = 197, b = 0, a = 255 }
}
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    local r
    local g
    local b
    local a
    if use == "Cube" then
        -- グリップボタンが押されたアイテムが"Cube"の時の処理
        r = 255
        g = 0
        b = 0
        a = 255
        local color = Color.__new(r, g, b, a)
        vci.assets._ALL_SetMaterialColorFromName("Cube", color)
    elseif use == "Sphere" then
        -- グリップボタンが押されたアイテムが"Sphere"の時の処理
        -- 中身の処理は次項以降で実装します。
        local x
        local y
        local z
    elseif use == "Cylinder" then
        -- グリップボタンが押されたアイテムが"Cylinder"の時の処理
        -- 中身の処理は次項以降で実装します。
    else
        -- それ以外、今回は存在しませんが、説明のために記述します。
    end
end

for文

同じような処理を繰り返し行いたい場合、for文を使用することで実現できます。
VCIにおいては、テーブルを使用し、同じような処理を繰り返したい場合などによく使用されます。

for文には2種類あり、数値for文汎用for文があります。
数値for文はシーケンスを操作する場合に、汎用for文は通常のテーブルを操作する場合によく用いられます。

構文

数値for文

for 変数 = 初期値, 終了値 (, 増分) do
  繰り返したい処理
end
増分は省略可能で、省略した場合は、1ずつ変数がカウントアップされます。

for i = 1, 5 do
  繰り返したい処理
end

汎用for文

for 変数リスト in イテレータ関数 (, 状態 (, 初期値)) do
  ブロック
end
状態, 初期値については省略可能で、あまり意識する必要はありません。
気になる人は、luaのリファレンスマニュアルのpairs()やnext()のあたりを見てください。

for key, value in pairs(table) do
  print(key, value)
end

実装例

ITEM_POS = {
{ name = "Cube", x = 1.5, y = 0, z = 0 },
{ name = "Sphere", x = 0, y = 0, z = 0 },
{ name = "Cylinder", x = -1.5, y = 0, z = 0 }
}
ITEM_COLOR = {
Cube = { r = 162, g = 162, b = 162, a = 255 },
Sphere = { r = 255, g = 255, b = 255, a = 255 },
Cylinder = { r = 85, g = 197, b = 0, a = 255 }
}
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    local r
    local g
    local b
    local a
    if use == "Cube" then
        -- グリップボタンが押されたアイテムが"Cube"の時の処理
        r = 255
        g = 0
        b = 0
        a = 255
        local color = Color.__new(r, g, b, a)
        vci.assets._ALL_SetMaterialColorFromName("Cube", color)
    elseif use == "Sphere" then
        -- グリップボタンが押されたアイテムが"Sphere"の時の処理
        local subItemName
        local x
        local y
        local z
        -- 数値for文を使用し、変数iが初期値1から3まで繰り返し処理を行う。
        for i = 1, 3 do
            -- テーブルITEM_POSはシーケンスなので、ITEM_POS[i]の方法でアクセスできる。
            -- テーブルに設定された各データをローカル変数に格納する。
            -- それぞれのローカル変数に設定される値は、以下のようになります。
            -- iが1の時、{ name = "Cube", x = 1.5, y = 0, z = 0 }
            -- iが2の時、{ name = "Sphere", x = 0, y = 0, z = 0 }
            -- iが3の時、{ name = "Cylinder", x = -1.5, y = 0, z = 0 }
            subItemName = ITEM_POS[i].name
            x = ITEM_POS[i].x
            y = ITEM_POS[i].y
            z = ITEM_POS[i].z

            local subItem = vci.assets.GetSubItem(subItemName)
            local pos = Vector3.__new(x, y, z)
            subItem.SetPosition(pos)
        end
    elseif use == "Cylinder" then
        -- グリップボタンが押されたアイテムが"Cylinder"の時の処理
        -- 汎用for文を使用し、繰り返し処理を行う。
        for key, value in pairs(ITEM_COLOR) do
            -- 汎用for文を使用する場合、pairs(テーブル名)でキーとテーブルデータの組み合わせを取得することができます。(pairs以外にもあります)
            -- 今回の例では繰り返し処理が行われる中で、以下の組み合わせが取得されます。
            -- key = Cube, value = { r = 162, g = 162, b = 162, a = 255 }
            -- key = Sphere, value = { r = 255, g = 255, b = 255, a = 255 }
            -- key = Cylinder, value = { r = 85, g = 197, b = 0, a = 255 }
            r = value.r
            g = value.g
            b = value.b
            a = value.a
            local color = Color.__new(r, g, b, a)
            vci.assets._ALL_SetMaterialColorFromName(key, color)
        end
    else
        -- それ以外の場合、今回は存在しませんが、説明のために記述します。
    end
end

ここまでの内容がluaスクリプトの基本になります。
VCIアイテムにスクリプトを組み込む場合、ここまで学んだ内容と、VCIスクリプトリファレンスに記載されている、VCI用の処理を組み合わせることでほぼ実現することができます。
以下は、余力があれば見てみてください。

関数

関数とは、複数の処理を1つにまとめて、定義したものになります。
関数を定義せずとも、処理を書くことは出来ますが、

  • プログラムが長くなってしまう
  • 同じ処理が複数箇所あった場合、処理の修正が必要な場合に、何か所も修正が必要になる

といったことが発生します。関数を定義することで、上記のようなことを減らすことができます。
※これまで登場した、アイテムを握り、グリップボタンを押した際に実行されるfunction onUse(use)も関数です。

構文

戻り値を返さない場合

関数の定義
function 関数名 (引数リスト)
   処理
end

関数の呼び出し
関数名 (引数リスト)

例)
function setColor( matName, r, g, b, a)
            local color = Color.__new(r, g, b, a)
            vci.assets._ALL_SetMaterialColorFromName(matName, color)
end
setColor( "Cube", 255, 255, 255, 255)

戻り値を返す場合

関数の定義
function 関数名 (引数リスト)
   処理
   return
end

関数の呼び出し
変数 = 関数名 (引数リスト)

例)
-- 引数で受け取った、aとbの合計を返却する。
function sumAandB(a, b)
    return a + b
end

sum = sumAandB(1, 2)
print(sum) -- 3が表示される。

実装例

for文の項で作成した、スクリプトでは、"Cube"を握ってグリップボタンを押した時と、"Cylinder"を握ってグリップボタンを押した時に同じようなマテリアルの色変更を行っています。
そこで、色変更を行う関数setColor()を作り、共通処理を関数化してみます。

ITEM_POS = {
{ name = "Cube", x = 1.5, y = 0, z = 0 },
{ name = "Sphere", x = 0, y = 0, z = 0 },
{ name = "Cylinder", x = -1.5, y = 0, z = 0 }
}
ITEM_COLOR = {
Cube = { r = 162, g = 162, b = 162, a = 255 },
Sphere = { r = 255, g = 255, b = 255, a = 255 },
Cylinder = { r = 85, g = 197, b = 0, a = 255 }
}

function setColor( matName, r, g, b, a)
            local color = Color.__new(r, g, b, a)
            vci.assets._ALL_SetMaterialColorFromName(matName, color)
end 

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    local r
    local g
    local b
    local a
    if use == "Cube" then
        -- グリップボタンが押されたアイテムが"Cube"の時の処理
        setColor( "Cube", 255, 0, 0, 255)
    elseif use == "Sphere" then
        -- グリップボタンが押されたアイテムが"Sphere"の時の処理
        local subItemName
        local x
        local y
        local z
        -- 数値for文を使用し、変数iが初期値1から3まで繰り返し処理を行う。
        for i = 1, 3 do
            -- テーブルITEM_POSはシーケンスなので、ITEM_POS[i]の方法でアクセスできる。
            -- テーブルに設定された各データをローカル変数に格納する。
            -- それぞれのローカル変数に設定される値は、以下のようになります。
            -- iが1の時、{ name = "Cube", x = 1.5, y = 0, z = 0 }
            -- iが2の時、{ name = "Sphere", x = 0, y = 0, z = 0 }
            -- iが3の時、{ name = "Cylinder", x = -1.5, y = 0, z = 0 }
            subItemName = ITEM_POS[i].name
            x = ITEM_POS[i].x
            y = ITEM_POS[i].y
            z = ITEM_POS[i].z

            local subItem = vci.assets.GetSubItem(subItemName)
            local pos = Vector3.__new(x, y, z)
            subItem.SetPosition(pos)
        end
    elseif use == "Cylinder" then
        -- グリップボタンが押されたアイテムが"Cylinder"の時の処理
        -- 汎用for文を使用し、繰り返し処理を行う。
        for key, value in pairs(ITEM_COLOR) do
            -- 汎用for文を使用する場合、pairs(テーブル名)でキーとテーブルデータの組み合わせを取得することができます。(pairs以外にもあります)
            -- 今回の例では繰り返し処理が行われる中で、以下の組み合わせが取得されます。
            -- key = Cube, value = { r = 162, g = 162, b = 162, a = 255 }
            -- key = Sphere, value = { r = 255, g = 255, b = 255, a = 255 }
            -- key = Cylinder, value = { r = 85, g = 197, b = 0, a = 255 }
            r = value.r
            g = value.g
            b = value.b
            a = value.a
            setColor( key, r, g, b, a)
        end
    else
        -- それ以外の場合、今回は存在しませんが、説明のために記述します。
    end
end

よく使う、luaの標準ライブラリ関数

関数 構文 説明
print print (···) 任意の数の引数を受け取り、文字列として出力します。
VCIではVRコンソールに変数の値を表示するのに用います。
type type (v) 変数vの型を取得できます。print()と組み合わせて使用したり、変数がnilかどうかチェックするのに、使用します
os.date os.date () 時刻を取得するのに、使用します。
時計や早押しボタン等、時刻を取得したいアイテムを作成する場合に使用します。
table.insert table.insert (list, [pos,] value) シーケンスlistのpos番目の位置に、valueを挿入します。
posを省略した場合は、テーブルlistの最後尾にvalueが追加されます。
スクリプトの中で、テーブルを組み立てたい場合に使用します。
table.remove table.remove (list [, pos]) シーケンスlistのpos番目のデータを削除します。
posを省略した場合は、テーブルlistの最後尾が削除されます。
スクリプトの中で、テーブルのデータを削除したい場合に使用します。

テクニック

後日追記予定

おわりに

今回紹介したのは、luaスクリプトの基本構文になります。
基本的に、今回説明しtがテーブル(変数)、if文、for文の3つを押さえておけば、ほとんどの処理を作ることが出来ると思います。
ですが、より簡潔に書ける方法等もありますので、参考資料等を参考に新しい書き方を身につけてみてください。
また、プログラミングを学ぶ上で、上達する方法の1つとして、他の人が書いたスクリプト、ソースコードを読むというものがあります。
幸い、VCIアイテムに組み込まれたスクリプトは、[EmbeddedScriptWorkspace]フォルダから読むことが出来ますので、参考に見てみるとよいかもしれません。(自分のコードを見られるのを嫌う人もいますが・・・)
誤りや読みづらい等ありましたら、編集リクエストください。

参考資料

バーチャルキャストwikiにも記載されているluaの基本構文のリンクと、私がVCIアイテムを作る際に参考にしたページへのリンクを記載します。

チュートリアル(VCIScript)
Lua 5.2 リファレンスマニュアル
Lua言語の文法
C/C++ 言語プログラマのための Lua 入門リファレンス

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
6