移動世界を開発する

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

エクスペリエンス内の任意の環境で、モーションを作成すると、世界が臨場感があり、よりリアリスティックになるようになります。これは、環境の木の移動、プレイヤーの交流

嵐を作成する

The storm went through many iterations 以前に、私たちはミステリードヴァルドライブの中で何がライブかについて何がライブかについて考えました。最初は、私たちはストームを巨大な黒曜石の柱として考えました、そして、後のイテレーションでは、それを腐敗した宇宙への巨大なポータルとして考えました。私

  • The storm should give players a sense of the このイベントが世界に与える影響 , including trees blowing and debris flying around.
  • クラウド自体の回転する渦巻きは、プレイヤーに中央ポータルを 見た目 でチェックするようになります。これは、プレイヤーがもっと調べるように促します。
  • より緩和されたポイントで、 家の構成 に焦点を合わせることができます。これは、メインキャラクターであり、ゲームプレイのほとんどが位置する場所です。

嵐を環境内でダイナミックであり、攻撃的であり、常に変化するようにするために、以下のシステムと機能を使用しました:

  1. TweenService > - クラウド移動用。
  2. 照明の変更 - クラウドに雲をインターミットするために。
  3. ビームズ > - 「ボリューム照明」とライトニングボルトの場合。
  4. 粒子エミッター - 風が吹くためにポータルに向かって飛び出し、周囲に飛び回ります。
  5. アニメーション > - 風で吹き飛ばされた木のために。

テクスチャで雲を追加する

ダイナミッククラウド は、通常、高い高度の現実的なクラウドにとって優れていますが

単一のクラウドメッシュ。
彼らのテクスチャなしにレイヤー付きの雲メッシュ!

家を完全に囲むためには、各クラウドメッシュが大きすぎて伝えることができるのは、どれほど巨大かつ暴風雨の影響を受けているかを知ることでした。そして、どのように巨大かつ嵐の影響を受けているかを伝えるために、個々のクラウドメッシュに使用したいテクスチャをタイルする必要がありました。これらの�

パーティクルエミッターやビームとは異なり、メッシュは、各メッシュから光を反射させることができるため、雲間のライトを実装するために重要でした。我々はまた、メッシュが雲間のライトを反射することが深さを持っているように見えるように、ツイストしたモデルを作成しました。これは特に、エクスペ

照明を追加するようになったら、メッシュに詳細を追加して、照明に対してより良く反応するようにする必要がありました!

回転クラウドメッシュ

私たちは雲の全体的な視覚的な外観に満足した後、それを動かす必要がありました!私たちは各クラウドレイヤーの一般的な形状を置いていましたが、スピンエフェクトが実際に良く見えるかどうかを確認するためにいくつかの試練とエラー

私たちは、LocalScript を使用して、AxisDelay、または2>重

デモの多くの場合、LocalSpaceRotation タグを使用して、インスタンスタグを使用するプラグインを介して Studio で影響を受けたインスタンスを管理できました。LocalScript の単一の CollectionService を使用して、2>Class.CollectionService2> を介してすべ


local function Init()
for _, obj in CollectionService:GetTagged("LocalSpaceRotation") do
if obj:IsDescendantOf(workspace) then
SetupObj(obj)
end
end
end
CollectionService:GetInstanceAddedSignal("LocalSpaceRotation"):Connect(function(obj)
objInfoQueue[obj] = true
end)
CollectionService:GetInstanceRemovedSignal("LocalSpaceRotation"):Connect(function(obj)
if objInfo[obj] then
objInfo[obj] = nil
if objInfoQueue[obj] then
objInfoQueue[obj] = nil
end
end
end)
Init()

objInfo は、すべての関連するオブジェクトの情報を含むマップです。例えば、その回転速度や軸などの情報です。注意して、SetupObj から

We rotated the instances in the Update function connected to heartbeat. We got the parent transform (parentTransform),累�


local parentTransform
if parentObj:IsA("Model") then
if not parentObj.PrimaryPart then
-- 主要なパーツはまだストリームされていない可能性があります
continue -- メインパーツのレプリケートを待つ
end
parentTransform = parentObj.PrimaryPart.CFrame
else
parentTransform = parentObj.CFrame
end
curObjInfo.curAngle += dT * curObjInfo.timeToAngle
local rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle )
if obj:IsA("Model") then
obj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrame
else
obj.CFrame = parentTransform * rotatedLocalCFrame
end

We checked for a valid Model.PrimaryPart to be set to handle streaming. If an Update was called on our object while a Model.PrimaryPart (that can point to a child mesh) wasn't streamed yet, we would just skip the update. The current system is a second iteration of object rotation, and the previous system worked differently: the values were 12 times different! To

ライトニングストライクのデザイン

Studio はオールドライト生成機を提供しません 、そしてパーティクルシステムは、ヒーローのライトニングストライクには機能しない限りがありました、私たちはヒーローのライトニングストライクを構成するためにクリエイティブになる必要がありました。私たちはスク

ビームのテクスチャ

通常、シーケンサーまたはタイムラインツールを使用して、照明ボルトのストライクエフェクトの時間を制御するためにシーケンスを作成しますが、Studio はまだこの機能を提供しないため、照明ボルトの時間を制御するスクリプトを作成しました。この効果のスクリプトは比較的単純ですが、次の重要な目標を達成します:

  1. ライトニングボルトのストライク、例えば、テクスチャ、明るさ、遅延、はすべてストライクごとにランダム化されます。
  2. オーディオとポスト FX の変更は、ストライク FX とシンクロされています。
  3. 屋内にいるか、壊れたエリアにいるプレイヤーは、彼らを見たり聞こえたりすることはできません。

私たちは、さまざまなパラメータと時間を計算し、すべてのクライアントに送信し、ランダムな時間を待つサーバーサイドの Script を持っています:


local function LightningUpdate()
while true do
task.wait(rand:NextNumber(3.0, 10.0))
local info = CreateFXData()
lightningEvent:FireAllClients(info)
end
end

CreateFXData の内部で、情報構造を満たし、すべてのクライアントが同じパラメータを取得できるようにします。

クライアント側 ( LightningVFXClient ) で、このクライアントが FX を実行するかどうかをチェックします。


local function LightningFunc(info)
-- 室内には FX なし
if inVolumesCheckerFunc:Invoke() then
return
end
-- 「通常」の世界にいないときは FX なし
if not gameStateInfoFunc:Invoke("IsInNormal") then
return
end

さらに、テクスチャ、位置、および明るさを設定するためのシーケンスを実行し、task.wait(number) を実行します。ランダムパラメーターは、サーバーから受信した情報構造から取得され、一部の数値は固定されています。


beam.Texture = textures[info.textIdx]
beamPart.Position = Vector3.new(info.center.X + og_center.X, og_center.Y, info.center.Y + og_center.Z)
-- ワイプ
beam.Brightness = 10
ppCC.Brightness = maxPPBrightness
ppBloom.Intensity = 1.1
bottom.Position = top.Position
tweenBrightness:Play()
tweenPPBrightness:Play()
tweenPPBrightness:Play()
tweenBottomPos:Play()
tweenBrightness.Completed:Wait()
-- オーディオ
if audioFolder and audioPart then
if audioFolder.Value and audioPart.Value then
audioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)
end
end
task.wait(info.waitTillFlashes)
-- and so on

プレイヤーが室内にいるかどうかをチェックするには、ヘルパー inVolumesCheckerFunc 機能を使用します。これは、室内のエリアに近づくプリースプレースのボリュームをオーバーラップし、プレイヤーの位置がどのボリュー

プレイヤーが破損したエリアにあるかどうかをチェックするには、gameStateInfoFunc ヘルパー機能を呼び出します。これは、現在のゲーム状態をチェックします。PlayOneShot ファイルをランダムに再生するには、Outer Glow ヘルパー機能を追

パーティクルエミッターシステムを使用する

ヒーローのライトニングストライクは、遠くのライトニングを示すパーティクルシステムによってサポートされています。これは、背景の雲のレイヤーを作成することで、遠くのライトニングを示すように、またはクラウド-トゥ-クラウド照明を通じて、雲のビルボードを閃かせます。この効果は、非常に����

風に吹き飛ばすツリーを作成する

クラウドとライトニングが機能する方法を我々が望んでいたのと同じように、我々は次に風と雨を追加する必要がありました:風と雨!これらの要素は、我々の物理と特殊エフェクトシステムの現在の制限を超えているため、風と雨を実際に移動させる

We knew to really sell the effect of the wind and rain, we needed the trees themselves to move. 木は、プラグイン を使用して、公開されている、または、TweenService を使用して、モデルを直接アニメートすることができます。我々の目的では、アニメーションが、木

私たちは、Endorse Model Pack - Forest Assets からいくつかの木をスキンし始めました。これらの木はすでに存在していたので、私たちの経験は太平洋北西部にあったため、各ツリーモデルを作成する必要はありませんでした。

森林パックには、複数のツリータイプが含まれており、自分のエクスペリエンスで時間を節約できます。

私たちが木を選んだ後、私たちはそれらをスキンにする必要があることを知りました。メッシュをスキンにする は、Blender や Maya などの他の 3D モデリングアプリケーションでメッシュにジョイント (または

同じアニメーションを再使用したいということを知っていたので、最初のツリーリグを構築し、共通名を一致させるためにジョイント名を一致させました。また、他のツリーで同じ名前を使用

ツリーには、主要、補助、および第三のジョイントがあり、風に吹き飛ばされることにより、信頼できる動きを得ることができます。

私たちの関節/骨を作成したら、スタジオのすべての関節と骨を動かすテストアニメーションを作成する時が来ました。これをするには、カスタムリグ 設定の3D インポーター を通じ

Studio の同じ階層。

そのツリーの結果に満足した後、別のツリーで同じアニメーションをテストする時が来ました!私たちはすでにそれぞれのツリータイプの違うリグ間のアニメーションが同じであることを知っていたので、私たちのアニメーションが Redwood の高いツリーと頑丈な Beechwood ツリーの両方で一般的なように見えるようにしました

Redwood ツリーにインポートしたアニメーション。

これをするには、Forest Pack から Beechwood のツリーを取得し、同じ名前のリグを構築しました。これは、ジョイントに同じ正確な名前を使用しているため、以前にインポートしたアニメーションがこのツリーにも適用できます。ジョイントはすべて回転するジョイントで構成されているため、ツリーのサイズは関係ありま

ビーチウッドのツリーには、同じ正確な名前が付けられています。ただし、同じ量ではありません。これは、アニメーションシステムが名前を一致させるのは、その特定の関節のみであるためです。このため、同じアニメーションを適用できます。

私たちが リグし、その後、ビーチウッドの木をスキンした後、それをインポートし、同じアニメーションを適用することができます。これは、一つのファイルでのみイテレーションと編集が必要であり、エクスペリエンスの実行中にパフォーマンスに最小限のアニメーションのみが保存されることを意味します。

アニメーションエディタを使用して、同じ Redwood ツリーのアニメーションを Beechwood ツリーに適用できます!

すべてのツリータイプがアニメーション化したいときに、私たちはそれらを パッケージ にまとめましたので、エクスペリエンスのメインエリアのアニメーションを編集および更新し続けることができました。それらがパフォーマンスコストを持っていることを知っていたので、家の

私たちは、渦巻きが最も強い場所の家の周りのアニメーションツリーを使用しました、そしてビジュアルエフェクトはプレイヤーに最も影響を与えるでしょう。

ストームデブリを作成する

雨を重いようにし、霧と破片が木に吹き抜けるようにしました。これをするには、Studio のパーティクル数制限により、子供の パーティクルエミッター がプレイ可能エリアの空間全体にわたって使用できませんでし

私たちは雨の量と、私たちが望んでいた雨の特定のカバーを両方取得するためにいくつかのボリュームを使用しました。

雨は、新しいパーティクルエミッタープロパティ ParticleEmitter.Squash を利用して、パーティクルを長くするか、あ

3 のスクワッシュ値は、テクスチャを長く伸ばし始めます。
パーティクルのスクワッシュ値が 20 の場合、パーティクルの長さがはるかに増加するが、サイズ値も増加する必要があります。

霧、霧、葉っぱが飛び抜ける中で、単一の大きな部分のボリュームを追加するのは、頑張りする必要がないほど簡単でした。私たちは、ボリュームを設定し、特定のパーティクルの周波数を入手しました、それらが必要なところにありました。私たちは始めにボリュームを設定し、パーティクルの周波数を入手しました、それらが必要なところにあ

いくつかのパーティクルパーツのボリュームがあったので、家に入るパーティクルがありませんでした、そして、霧のように木の中を移動する必要がないと感じたので。
パーティクルが大きかったため、霧の粒子パーツのボリュームが大幅に拡大し、場所に関して精密になる必要はありませんでした。

その後、私たちは葉っぱを吹き、風をテクスチャにし、パーティクルをすべて同じ速度で回転/移動させ、開始しました。これは、大きな霧パーティクルが自然に交流し、重複するテクスチャではなく、特にサイズが大きいために見えなくなることを意味しました。

霧粒子
葉っぱパーティクル

結果は、木々が動く、窓が吹く、雷が閃電を創造して、ストームの中心の目の前の効果を作成するための何かの大きなアクションでした。

嵐の目を設定する

輝くコアの石の目は、プレイヤーに家で何か不気味で難解なことが起きていることを最初に示すヒントを提供します。場景が暗いため、目が天空にあるため、石の目はそれを信じられないように見せる必要がありま

シーンの照明を最初に設定すると、必要ない作業を大幅に削減できます。 私たちのシーンの照明と照合のリングに表面の詳細を見ることはできないので、そこに時間を費やす必要はありません!

プレイヤーからの距離は、目の表面の詳細について、通常のマップに完全に依拠することを意味したので、メッシュは単にプレーンな球です! 私たちは、メッシュを高度なポリメッシュに彫刻し、通常のマップをはるかに低いポリメッシュに焼き付けるため、私たちはすべての美しい詳細を手に入れることができました。

高度なポリスカプト
低いポリメッシュ
高度なポリスカルプトが焼き付けられた低いポリメッシュによる通常の情報を持つメッシュ低いメッシュ

目に超自然感を追加するために、我々は目を通して流れる霊感を強調するために、目に輝く、ネオンのマグマを作成しました。 表面の外

内球のベクトル画。 目の周りが最も軽いため、より深い感覚と視覚的な興味を与えるために、創造しました。

目を作成するとき、ストリーミング という使用を使用して、目の距離をプレイヤーからの距離に加えたこ

同じスクリプトを使用して、クラウドメッシュを回転しているときに、目とそのリングに動きを追加することができました。

私たちは世界を雲の上にある世界以外の世界の偽像を作成するために画像を使用しました。プレイヤーが遠く離れている場合、単純な画像で世界の深さと複雑さを創造できます。

パントリーの拡張を作成する

最も楽しいことの 1 つは、破損したスペースを作成することでした。ここでは、プレイヤーの期待を現実に捏造するために、文字通りその周りを変更することができます。たとえば、父のパズルでは、怖い夢を見ているように見える場所を実際に怖い夢で満たしたいと思い

私たちは壁の単純な移動でこれを設定し、パントリーの両側に表示される賢明なレイアウトでルームを設置しました。ルームの通常の状態では、パントリーは単純な廊下でしたが、破損した空間では、複数のウィングと偽の壁がありました!

キッチンパントリーの状態が破損しています。
プレイヤーから離れている偽の壁。

偽の壁は、プレイヤーがトリガーボリュームに入るときに戻すモデルグループでした。これは、パントリーのすべてのドアに使用される透明な部分でした。そのトリガーは、TweenService を使用して、壁の開始と終了位置を移動する拡張機能を実装しました

パーツのボリュームは、その後ろにある偽の壁をトリガーして、その端に移動します。この画像では、黄色の色合いで表示されています。
ターゲット_クローズは、ドアのどこで回転するかに使用している一般的な目標部分でした。ここは、廊下の壁に再処理されて、どこに行くかを伝えました。

Class.TweenService は、すべてのサイトの壁データモデルに共通するコンポーネントを含める必要があります。たとえ、次の画像は、「Grow_Wall」モデルの下の「値」で定義されたサウンドを呼び出す一般的なドアスクリプトの例です。

同じスクリプト、次のコードサンプルにいくつかの変更を加えた、パントリーが移動するためのオーディオもトリガーされました。これにより、移動に追加されたたくさんのものがあります!


local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local model = script.Parent
local sound = model.Sound.Value
local trigger = model.Trigger
local left = model.TargetL_Closed
local right = model.TargetR_Closed
local tweenInfo = TweenInfo.new(
model.Speed.Value, --ドアの Tween の時間/速度
Enum.EasingStyle.Quart, --矢印のスタイル
Enum.EasingDirection.InOut, --方向転換
0, --カウントを繰り返す
false, --真逆
0 --遅延
)
local DoorState = {
["Closed"] = 1,
["Opening"] = 2,
["Open"] = 3,
["Closing"] = 4,
}
local doorState = DoorState.Closed
local playersNear = {}
local tweenL = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Open.CFrame})
local tweenR = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Open.CFrame})
local tweenLClose = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Closed.CFrame})
local tweenRClose = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Closed.CFrame})
local function StartOpening()
doorState = DoorState.Opening
sound:Play()
tweenL:Play()
tweenR:Play()
end
local function StartClosing()
doorState = DoorState.Closing
--model ["Door"]:プレイ()
tweenLClose:Play()
tweenRClose:Play()
end
local function tweenOpenCompleted(playbackState)
if next(playersNear) == nil then
StartClosing()
else
doorState = DoorState.Open
end
end
local function tweenCloseCompleted(playbackState)
if next(playersNear) ~= nil then
StartOpening()
else
doorState = DoorState.Closed
end
end
tweenL.Completed:Connect(tweenOpenCompleted)
tweenLClose.Completed:Connect(tweenCloseCompleted)
local function touched(otherPart)
if otherPart.Name == "HumanoidRootPart" then
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
--print("触って")
playersNear[player] = 1
if doorState == DoorState.Closed then
StartOpening()
end
end
end
end

部屋の奥に偽の壁が動くと、コンテンツを移動するためには、それと一緒に動く必要があるコンテンツの残りを必要としました。これをするには、パントリー内のすべてのロードアイテムを壁の動きと一緒に溶接する必要がありました。これを「Weld Constraints

邪悪なツリーハウスを作成する

Studio は、あなたがスイングゲートからスピンプラットフォームまで、すべてを作成するために使用できる素晴らしい物理ベースのエンジンです。私たちのデモでは、 制限 を使用して、現実的でないセットの環境内で物理を作成することを目的としました。制限 を使用することで、あな

制限機構 は、オブジェクトを正確に配置し、動作を制限する物理モーターのグループです。たとえば、ロッド制限 を使用して、オ

息子のパズルは同じ部屋にいるプレイヤーたちから始まりましたが、全ては横に流れていました。

プレイヤーがパズルのメインエリアに作業し始めたとき、Roblox はプレイヤーに馴染み深い景色で歓迎されました:障害物コース。この特定の障害物コースは、複数の回転プラットフォームと回転する壁を含む、ストーリーを進行させる "安全エリア" で構成されています。私たちは回転/回転する要素に焦点を当てま

気持ち悪い見た目は、ここのゲームプレイがとても単純だったことを隠していました。

なぜここに制限を使用しましたか? TweenService や他のメソッドは、プレイヤーがその上に立っている間、プレイヤーを動かすことはできません。プレイヤーがその上に立っている間、オブジェクトがプレイヤー

障害物コースをナビゲートしながら、友達がどこでスピンしているかも見ることもできます。

これをするには、まず現在のキットからアセットを使用し、ビジュアルエフェクトのために新しいコンテンツを追加する必要がありました。私たちはいくつかの不完全な壁とプラットフォームを作成し、木の小屋を建造するおばあちゃんのストーリーを伝えるために新しいコンテンツを追加しました。

私たちは、制限を使用していたため、これらのメッシュをアンカーできないということを知っていました。制限が存在

さあ、実際のハングルの制限自体の動作を設定し、部品とハングルの間のオリエンテーションを代行するアタッチメントを追加しました。私たちはモーター_ターンに配置した ハ

プラットフォームを常に同じ速度で回転させるには、HingeConstraint.AngularVelocityHingeConstraint.MotorMaxAcceleration、および HingeConstraint.MotorMaxTorque プロパティを値に設定し、プレイヤーがジャンプすると、移動を防止し、中断を防止することができま

アタッチメント0 はヒンジのアンカーであり、アタッチメント1 はヒンジ自体を表しました。

さあ、回転する壁を作る必要がありました。壁はそれぞれの中心に回転する必要があり、我々はそれらがレベルの残りの部分に対する任意の方向に処理できることを望んでいました。プラットフォームと同様に、私たちはこれらを作成し、すべての壁がモーター_ターンに縛定されていないことを確認しました。

実際のツリーハウスメッシュのほとんどを再利用して、パフォーマンスのために保存したいと思いました。それゆえ、プラットフォームと同じようなパスをたどりました。さまざまな壁タイプが作成され、いくつかのバリアションで一緒に接続できました。

我々は、Textureオブジェクトの上に、SurfaceAppearanceオブジェクトを使用して、ベースの素材にい

同様の動作と設定を見ることができますが、Texture を使用して、ヒンジの制限を設定しました。

いくつかのプラットフォームと回転する壁をテストした後、私たちはいくつかのバリエーションを作成し、配置を調整して、障害物コースがチャレンジ的であり、心を絶やさせ、またクリアであることを確認するためにプレイしました! プラットフォームと壁が互

物理オブジェクトがヒットするのかどうかわからない場合は、物理オブジェクトの可視化オプション ウィジェットの右上隅にある コリジョンフィデルティ を切り替えることができます。

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.
当たり判定ビジュアライズが無効になっていると、ゲーム内に表示される通常のジオメトリの表示が可能になります。
当たり判定ビジュアルが有効になっていると、木の葉が当たり判定をしないため、スピンプラットフォームや壁に影響しません。

ドア/窓の洞窟の下にあるのが見えるが、サブパネルのような詳細は見えない。これは、壁の CollisionFidelity プロパティが ボックス に設定されているためです。私たちはこれらのパネルに精密さが必要ない