エクスペリエンス内の任意の環境で、モーションを作成すると、世界が臨場感があり、よりリアリスティックになるようになります。これは、環境の木の移動、プレイヤーの交流
嵐を作成する
The storm went through many iterations 以前に、私たちはミステリードヴァルドライブの中で何がライブかについて何がライブかについて考えました。最初は、私たちはストームを巨大な黒曜石の柱として考えました、そして、後のイテレーションでは、それを腐敗した宇宙への巨大なポータルとして考えました。私
- The storm should give players a sense of the このイベントが世界に与える影響 , including trees blowing and debris flying around.
- クラウド自体の回転する渦巻きは、プレイヤーに中央ポータルを 見た目 でチェックするようになります。これは、プレイヤーがもっと調べるように促します。
- より緩和されたポイントで、 家の構成 に焦点を合わせることができます。これは、メインキャラクターであり、ゲームプレイのほとんどが位置する場所です。
嵐を環境内でダイナミックであり、攻撃的であり、常に変化するようにするために、以下のシステムと機能を使用しました:
- TweenService > - クラウド移動用。
- 照明の変更 - クラウドに雲をインターミットするために。
- ビームズ > - 「ボリューム照明」とライトニングボルトの場合。
- 粒子エミッター - 風が吹くためにポータルに向かって飛び出し、周囲に飛び回ります。
- アニメーション > - 風で吹き飛ばされた木のために。
テクスチャで雲を追加する
ダイナミッククラウド は、通常、高い高度の現実的なクラウドにとって優れていますが
家を完全に囲むためには、各クラウドメッシュが大きすぎて伝えることができるのは、どれほど巨大かつ暴風雨の影響を受けているかを知ることでした。そして、どのように巨大かつ嵐の影響を受けているかを伝えるために、個々のクラウドメッシュに使用したいテクスチャをタイルする必要がありました。これらの�
パーティクルエミッターやビームとは異なり、メッシュは、各メッシュから光を反射させることができるため、雲間のライトを実装するために重要でした。我々はまた、メッシュが雲間のライトを反射することが深さを持っているように見えるように、ツイストしたモデルを作成しました。これは特に、エクスペ
回転クラウドメッシュ
私たちは雲の全体的な視覚的な外観に満足した後、それを動かす必要がありました!私たちは各クラウドレイヤーの一般的な形状を置いていましたが、スピンエフェクトが実際に良く見えるかどうかを確認するためにいくつかの試練とエラー
私たちは、LocalScript を使用して、AxisやDelay、または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 parentTransformif parentObj:IsA("Model") thenif not parentObj.PrimaryPart then-- 主要なパーツはまだストリームされていない可能性がありますcontinue -- メインパーツのレプリケートを待つendparentTransform = parentObj.PrimaryPart.CFrameelseparentTransform = parentObj.CFrameendcurObjInfo.curAngle += dT * curObjInfo.timeToAnglelocal rotatedLocalCFrame = curObjInfo.origLocalCFrame * CFrame.Angles( curObjInfo.axisMask.X * curObjInfo.curAngle, curObjInfo.axisMask.Y * curObjInfo.curAngle, curObjInfo.axisMask.Z * curObjInfo.curAngle )if obj:IsA("Model") thenobj.PrimaryPart.CFrame = parentTransform * rotatedLocalCFrameelseobj.CFrame = parentTransform * rotatedLocalCFrameend
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 はまだこの機能を提供しないため、照明ボルトの時間を制御するスクリプトを作成しました。この効果のスクリプトは比較的単純ですが、次の重要な目標を達成します:
- ライトニングボルトのストライク、例えば、テクスチャ、明るさ、遅延、はすべてストライクごとにランダム化されます。
- オーディオとポスト FX の変更は、ストライク FX とシンクロされています。
- 屋内にいるか、壊れたエリアにいるプレイヤーは、彼らを見たり聞こえたりすることはできません。
私たちは、さまざまなパラメータと時間を計算し、すべてのクライアントに送信し、ランダムな時間を待つサーバーサイドの 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 = 10ppCC.Brightness = maxPPBrightnessppBloom.Intensity = 1.1bottom.Position = top.PositiontweenBrightness:Play()tweenPPBrightness:Play()tweenPPBrightness:Play()tweenBottomPos:Play()tweenBrightness.Completed:Wait()-- オーディオif audioFolder and audioPart thenif audioFolder.Value and audioPart.Value thenaudioUtils.PlayOneShot(audioObj, audioFolder.Value, audioPart.Value)endendtask.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 インポーター を通じ
そのツリーの結果に満足した後、別のツリーで同じアニメーションをテストする時が来ました!私たちはすでにそれぞれのツリータイプの違うリグ間のアニメーションが同じであることを知っていたので、私たちのアニメーションが Redwood の高いツリーと頑丈な Beechwood ツリーの両方で一般的なように見えるようにしました
これをするには、Forest Pack から Beechwood のツリーを取得し、同じ名前のリグを構築しました。これは、ジョイントに同じ正確な名前を使用しているため、以前にインポートしたアニメーションがこのツリーにも適用できます。ジョイントはすべて回転するジョイントで構成されているため、ツリーのサイズは関係ありま
私たちが リグし、その後、ビーチウッドの木をスキンした後、それをインポートし、同じアニメーションを適用することができます。これは、一つのファイルでのみイテレーションと編集が必要であり、エクスペリエンスの実行中にパフォーマンスに最小限のアニメーションのみが保存されることを意味します。
すべてのツリータイプがアニメーション化したいときに、私たちはそれらを パッケージ にまとめましたので、エクスペリエンスのメインエリアのアニメーションを編集および更新し続けることができました。それらがパフォーマンスコストを持っていることを知っていたので、家の
ストームデブリを作成する
雨を重いようにし、霧と破片が木に吹き抜けるようにしました。これをするには、Studio のパーティクル数制限により、子供の パーティクルエミッター がプレイ可能エリアの空間全体にわたって使用できませんでし
雨は、新しいパーティクルエミッタープロパティ ParticleEmitter.Squash を利用して、パーティクルを長くするか、あ
霧、霧、葉っぱが飛び抜ける中で、単一の大きな部分のボリュームを追加するのは、頑張りする必要がないほど簡単でした。私たちは、ボリュームを設定し、特定のパーティクルの周波数を入手しました、それらが必要なところにありました。私たちは始めにボリュームを設定し、パーティクルの周波数を入手しました、それらが必要なところにあ
その後、私たちは葉っぱを吹き、風をテクスチャにし、パーティクルをすべて同じ速度で回転/移動させ、開始しました。これは、大きな霧パーティクルが自然に交流し、重複するテクスチャではなく、特にサイズが大きいために見えなくなることを意味しました。
結果は、木々が動く、窓が吹く、雷が閃電を創造して、ストームの中心の目の前の効果を作成するための何かの大きなアクションでした。
嵐の目を設定する
輝くコアの石の目は、プレイヤーに家で何か不気味で難解なことが起きていることを最初に示すヒントを提供します。場景が暗いため、目が天空にあるため、石の目はそれを信じられないように見せる必要がありま
プレイヤーからの距離は、目の表面の詳細について、通常のマップに完全に依拠することを意味したので、メッシュは単にプレーンな球です! 私たちは、メッシュを高度なポリメッシュに彫刻し、通常のマップをはるかに低いポリメッシュに焼き付けるため、私たちはすべての美しい詳細を手に入れることができました。
目に超自然感を追加するために、我々は目を通して流れる霊感を強調するために、目に輝く、ネオンのマグマを作成しました。 表面の外
目を作成するとき、ストリーミング という使用を使用して、目の距離をプレイヤーからの距離に加えたこ
同じスクリプトを使用して、クラウドメッシュを回転しているときに、目とそのリングに動きを追加することができました。
パントリーの拡張を作成する
最も楽しいことの 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.AngularVelocity、HingeConstraint.MotorMaxAcceleration、および HingeConstraint.MotorMaxTorque プロパティを値に設定し、プレイヤーがジャンプすると、移動を防止し、中断を防止することができま
さあ、回転する壁を作る必要がありました。壁はそれぞれの中心に回転する必要があり、我々はそれらがレベルの残りの部分に対する任意の方向に処理できることを望んでいました。プラットフォームと同様に、私たちはこれらを作成し、すべての壁がモーター_ターンに縛定されていないことを確認しました。
我々は、Textureオブジェクトの上に、SurfaceAppearanceオブジェクトを使用して、ベースの素材にい
いくつかのプラットフォームと回転する壁をテストした後、私たちはいくつかのバリエーションを作成し、配置を調整して、障害物コースがチャレンジ的であり、心を絶やさせ、またクリアであることを確認するためにプレイしました! プラットフォームと壁が互
物理オブジェクトがヒットするのかどうかわからない場合は、物理オブジェクトの可視化オプション ウィジェットの右上隅にある コリジョンフィデルティ を切り替えることができます。
ドア/窓の洞窟の下にあるのが見えるが、サブパネルのような詳細は見えない。これは、壁の CollisionFidelity プロパティが ボックス に設定されているためです。私たちはこれらのパネルに精密さが必要ない