目次

VCIでライブのようなカメラの動きを作る

vci.studio(ExportStudio)&(ExportSystemItem)を使ってカメラを動かします。
Luaでモーションを制御してるので、下記のプログラミングを書き換える事でアングルをアレンジする事ができます。
下記スクリプトは変更したり、自由にコピー&ペーストしても問題ありません。

サンプルデータ

https://virtualcast.jp/products/d386a9ccd752e956c4408bcef1c9098d6f81437404b088df53b57e2b6d88f81d

コンポーネント設定

Targetは演者の人に装着してもらい、演者の位置を取得するのに使用します。
Targetの位置と向きを基準にカメラが自動で動きます。また、ターゲットの方向を注視します。

VCIスクリプト

main.lua
local _target = vci.assets.GetTransform("Target")
local _motionType = 1 -- 現在再生中のモーション
local _lastMotionType = 0 -- 前回再生したモーションを保存する
local _motionIsActive = false -- trueならカメラの移動を実行
local _resetIsActive = false -- trueならカメラの位置をリセット
 
-- カット切り替え時のターゲットの位置・回転を保存
local _fixTargetPosition
local _fixTargetRotation
 
-- LookAtの補正
local _LookAtoffset = 10.0 -- +の値でカメラは上方向を注視するようになります
 
-- カメラの位置
local _camPosition -- Min ~ Max の範囲でランダムな値になります
local _posxMax = 1.6 -- X軸(左右)
local _posxMin = -1.6
local _posyMax = 1.5 -- Y軸(上下)
local _posyMin = 0.5
local _poszMax = 2.0 -- Z軸(前後)
local _poszMin = 0.8
 
-- カメラとターゲットの間隔
local _camDistance
local _distanceMax = 3.5 -- 最長距離
local _distanceMin = 1.5 -- 最短距離
 
-- カメラが回転するのにかかる時間
local _camCycleTime
local _cycleTimeMax = 30.0
local _cycleTimeMin = 10.0
 
------------------------------------------------------------
 
-- カメラの位置をランダムで決定
function randomPosition()
    local x = 0.1 * math.random(10.0 * _posxMin, 10.0 * _posxMax)
    local y = 0.1 * math.random(10.0 * _posyMin, 10.0 * _posyMax)
    local z = 0.1 * math.random(10.0 * _poszMin, 10.0 * _poszMax)
    _camPosition = Vector3.__new(x, y, z)
end
 
-- カメラとターゲットの間隔をランダムで決定
function randomDistance()
    _camDistance = 0.1 * math.random(10.0 * _distanceMin, 10.0 * _distanceMax)
end
 
-- カメラが回転にかかる時間をランダムで決定
function randomSpeed()
    _camCycleTime = 0.1 * math.random(10.0 * _cycleTimeMin, 10.0 * _cycleTimeMax)
end
 
-- 次に使用するカメラのモーションをランダムで決定
function randomMotion()
    while _lastMotionType == _motionType do
        -- カメラモーションの数
        _motionType = math.random(1, 4)
    end
    _lastMotionType = _motionType
end
 
-- 初期値の決定
randomDistance() -- _camDistance
randomPosition() -- _camPosition
randomSpeed() -- _camCycleTime
_fixTargetPosition = _target.GetPosition()
_fixTargetRotation = _target.GetRotation()
 
function onUse(use)
    -- カメラのモーション切り替え
    if use == "Switcher" then
        randomMotion()
        randomDistance()
        randomPosition()
        randomSpeed()
        _fixTargetPosition = _target.GetPosition()
        _fixTargetRotation = _target.GetRotation()
        print("Motion Type : "..tostring(_motionType))
        print("cam Position : "..tostring(_camPosition))
        print("cam Distance : "..tostring(_camDistance))
        print("cam Speed : "..tostring(_camCycleTime))
    end
 
    -- 固定のカメラモーションに切り替え
    if use == "FixSwitcher" then
        _motionType = 3
        _lastMotionType = _motionType
        _fixTargetPosition = _target.GetPosition()
        print("Motion Type : "..tostring(_motionType))
    end
 
    -- カメラモーションの有効・無効
    if use == "Power" then
        _motionIsActive = not(_motionIsActive)
        print("Active : "..tostring(_motionIsActive))
    end
 
    -- カメラモーションを停止して位置をリセット
    if use == "Reset" then
        _motionIsActive = false
        _resetIsActive = true
        print("Active : "..tostring(_motionIsActive))
    end
end
 
function onUnuse(use)
    -- カメラモーションを停止して位置をリセット
    if use == "Reset" then
        _resetIsActive = false
    end
end
 
function update()
    cameraMotion()
    resetPosition()
end
 
--カメラモーションの実行
function cameraMotion()
    -- カメラモーションが無効なので終了
    if _motionIsActive == false then
        return
    end
 
    --ハンディカメラが存在しない場合終了
    if vci.studio.HasHandiCamera() == false then
        -- print("ハンディカメラが存在しません")
        return
    end
 
    -- モーション割り振り
    if _motionType == 1 then motionType1() end
    if _motionType == 2 then motionType2() end
    if _motionType == 3 then motionType3() end
    if _motionType == 4 then motionType4() end
end
 
-- LookAt
function LookAt()
    local cam = vci.studio.GetHandiCamera()
    local forward = _target.GetPosition() - cam.GetPosition()
    forward = forward.normalized
    local rot = Quaternion.LookRotation(forward, Vector3.up)
    rot = rot * Quaternion.AngleAxis(_LookAtoffset, Vector3.left) --向き補正
    cam.SetRotation(rot)
end
 
-- LookAt(Fix)
function LookAtFix()
    local cam = vci.studio.GetHandiCamera()
    local forward = _fixTargetPosition - cam.GetPosition()
    forward = forward.normalized
    local rot = Quaternion.LookRotation(forward, Vector3.up)
    rot = rot * Quaternion.AngleAxis(_LookAtoffset, Vector3.left) --向き補正
    cam.SetRotation(rot)
end
 
-- LookAt(Lerp)
function LookAtLerp()
    local cam = vci.studio.GetHandiCamera()
    local forward = _target.GetPosition() - cam.GetPosition()
    forward = forward.normalized
    local target = Quaternion.LookRotation(forward, Vector3.up)
    target = target * Quaternion.AngleAxis(_LookAtoffset, Vector3.left) --向き補正
    local rot = Quaternion.Lerp(cam.GetRotation(), target, 0.016)
    cam.SetRotation(rot)
end
 
-- ターゲットの周りを回転しながら、ターゲットの方向を向く
function motionType1()
    local cam = vci.studio.GetHandiCamera()
    -- 回転処理
    local cycleTime = _camCycleTime --cycleTimeを増やすと1周にかかる時間が増えます
    local f = 1.0 / cycleTime
    local time = 6.283185 * f * os.time()
    local sin = math.sin(time)
    local cos = math.cos(time)
    -- 位置 & offset
    local y = _camPosition.y -- カメラの高さ
    local x = sin * _camDistance + _fixTargetPosition.x
    local z = cos * _camDistance + _fixTargetPosition.z
    local pos = Vector3.__new(x, y, z)
    -- SetPosition
    cam.SetPosition(pos)
    -- LookAt
    LookAtFix()
end
 
-- ターゲットの位置を基準にランダムな位置へカメラを移動し、ターゲットの方向を向く
-- スイッチングカメラと似た挙動ですが、デフォルト設定だと正面方向のみから位置が選出されます
function motionType2()
    local cam = vci.studio.GetHandiCamera()
    -- 位置
    local pos = _fixTargetRotation * _camPosition
    -- offset
    pos = pos + _fixTargetPosition
    -- SetPosition
    cam.SetPosition(pos)
    -- LookAt
    LookAtFix()
end
 
-- 正面方向からバストアップ
function motionType3()
    local cam = vci.studio.GetHandiCamera()
    -- 位置
    local distance = 0.8 --ターゲットとカメラの間隔
    local height = 0.2 --カメラの高さ
    local pos = _target.GetRotation() * Vector3.__new(0, height, 1.0) * distance
    -- offset
    pos = pos + _target.GetPosition()
    -- SetPosition
    cam.SetPosition(pos)
    -- LookAt
    LookAtLerp()
end
 
-- 斜め正面方向からバストアップ
function motionType4()
    local cam = vci.studio.GetHandiCamera()
    -- 位置
    local distance = 0.7 --ターゲットとカメラの間隔
    local height = 0.2 --カメラの高さ
    local pos = _target.GetRotation() * Vector3.__new(0, height, 1.0) * distance
    pos = Quaternion.AngleAxis(45, Vector3.up) * pos
    pos = Vector3.Lerp(pos, cam.GetPosition(), 0.02)
    -- offset
    pos = pos + _target.GetPosition()
    -- SetPosition
 
    cam.SetPosition(pos)
    -- LookAt
    LookAtLerp()
end
 
-- 位置をリセット
function resetPosition()
    -- リセット中でなければ終了
    if _resetIsActive == false then
        return
    end
    local cam = vci.studio.GetHandiCamera()
    -- 位置
    local front = _target.GetRotation() * Vector3.__new(0, 0, 0.5)
    front = front + _target.GetPosition()
    cam.SetPosition(front)
    cam.SetRotation(Quaternion.identity)
end

使い方(スクリプトの調整方法)

_LookAtoffset

こちらの値を+にするとターゲットより上の方向にカメラが向き、-の値にするとターゲットより下を向きます。
値を0にする事で、ターゲットの方向に向くようになります。

_camPosition

スイッチング時にランダムで位置を決定しているので、MinとMaxの値を変える事でランダムの範囲を制御できます。
_poszMinにマイナスの値を入れると背後から撮るアングルも選択されるようになります。

_camDistance

ターゲットを中心にして回転するアングルを選択した時の距離。
Maxの値を大きくとると、ターゲットから離れたアングルを選択するようになります。

_camCycleTime

カメラの回転速度になります。回転速度を上げたい場合はMinとMaxの両方の値を上げます。
_camCycleTimeが定数になるように書き換えれば回転速度は一定になります。

LookAt()

実行するとカメラがターゲットの方向を向きます。
LookAt()は補間なしでターゲットの方向を向き、LookAtFix()はスイッチングした直後の向きで固定します。
LookAtLerp()は補間ありで、なめらかにターゲットの方向を向きます。

ランダム関数について

local num = math.random(1, 10)

Luaのランダムは第一引数から第二引数の値で整数の値を返します。
なので上の場合、numは1,2,3…10という値になります。
しかし、ランダムで位置を決めるような場合、上記の書き方では1m毎になってしまうのであまり好ましくありません。
なので下記のような書き方をします。

local Min = 1
local Max = 10
local num = 0.1 * math.random(10.0 * Min, 10.0 * Max)

MinとMaxを10倍してから、結果を0.1倍すると、0.1刻みのランダムを得る事ができます。
同様に 100倍して結果を0.01倍すると、0.01刻みのランダムになります。
用途によってランダムの調整をする事ができます。