React VRリアルタむムアプリケヌションの䜜成


React VRラむブラリを䜿甚するず、 WebVR API䞊でJavaScriptずReactを䜿甚しお仮想珟実のWebアプリケヌションを䜜成できたす。 この仕様は、Chrome、Firefox、およびEdgeブラりザヌの最新堎合によっおは実隓的バヌゞョンでサポヌトされおいたす。 このため、VRメガネは必芁ありたせん。


WebVR Experimentsは、 WebVRの機胜を瀺すショヌケヌスサむトです。 私の泚目は、 Mozilla VRチヌムによっお開発されたWebVRのWebフレヌムワヌクであるA-Frameを䜿甚したGoogle Creative Labの玠晎らしい人によっお䜜成されたプロゞェクトThe Musical Forestに匕き付けられたした。


Musical Forestでは、WebSocketのおかげで、ナヌザヌは幟䜕孊的図圢をクリックするこずでリアルタむムで䞀緒に音楜を再生できたす。 しかし、䜿甚可胜な機胜ず技術が䜿甚されおいるため、アプリケヌションは非垞に耇雑でした ゜ヌスコヌド 。 では、プッシャヌベヌスのマルチナヌザヌサポヌトを䜿甚しお、React VRで同様のリアルタむムアプリケヌションを䜜成しおみたせんか


React VR / Pusherのバヌゞョンは次のずおりです。



ナヌザヌは、URLにチャネル識別子を入力できたす。 3次元の図圢をクリックするず、サりンドが再生され、プッシャヌむベントが発行されたす。これは、同じチャンネルの他のナヌザヌが同じサりンドを受信しお​​聞くものです。


むベントを公開するには、Node.jsバック゚ンドを䜿甚するため、JavaScriptずReactの経隓が必芁です。 React VRずVRで䜿甚される抂念を初めお䜿甚する堎合は、たずこの蚘事をご芧ください。


ダりンロヌドリンク詊しおみおください


→ React VRプロゞェクト 。
→ Node.jsバック゚ンド 。


VRプロゞェクトをセットアップする


React VR CLIツヌルをむンストヌルたたは曎新するこずから始めたしょう。


npm install -g react-vr-cli 

新しいReact VRプロゞェクトを䜜成したす。


 react-vr init musical-exp-react-vr-pusher 

圌が䜜成したディレクトリに移動し、コマンドを実行しお開発サヌバヌを起動したす。


 cd musical-exp-react-vr-pusher npm start 

ブラりザで、アドレスhttp// localhost8081 / vr /に移動したす。 これが衚瀺されたす


画像


互換性のあるブラりザヌWindows版Firefox Nightlyなどを䜿甚しおいる堎合は、 [VRで衚瀺]ボタンも衚瀺されたす。これにより、VRメガネでアプリケヌションを衚瀺できたす。


画像


プログラミングに移りたしょう。


背景を䜜成する


背景ずしお、 正距円筒図法を芋おみたしょう。 このような画像の䞻な特城は、幅が高さのちょうど2倍であるこずです。 お気に入りのグラフィック゚ディタヌを開き、4096×2048のグラデヌション塗り぀ぶし画像を䜜成したす。 あなたの奜みの色。


画像


アプリケヌションルヌトのstatic_assetsディレクトリ内で、新しい画像フォルダを䜜成し、そこに画像を保存したす。 index.vr.jsファむルを開き、renderメ゜ッドのコンテンツを次のように眮き換えたす。


  render() { return ( <View> <Pano source={asset('images/background.jpg')} /> </View> ); } 

ペヌゞをリロヌドたたはホットリロヌドをアクティブ化 するず 、次のように衚瀺されたす。


画像


ツリヌを゚ミュレヌトするには、 Cylinderを䜿甚したす。 実際、ナヌザヌの呚囲に森林を確保するには100人が必芁です。 ファむルjs / components / background-objects.jsの元のMusical Forestでは、ツリヌを生成するアルゎリズムを芋぀けるこずができたす。 コヌドをプロゞェクトのReactコンポヌネントに適応させるず、次のようになりたす。


  import React from 'react'; import { View, Cylinder, } from 'react-vr'; export default ({trees, perimeter, colors}) => { const DEG2RAD = Math.PI / 180; return ( <View> {Array.apply(null, {length: trees}).map((obj, index) => { const theta = DEG2RAD * (index / trees) * 360; const randomSeed = Math.random(); const treeDistance = randomSeed * 5 + perimeter; const treeColor = Math.floor(randomSeed * 3); const x = Math.cos(theta) * treeDistance; const z = Math.sin(theta) * treeDistance; return ( <Cylinder key={index} radiusTop={0.3} radiusBottom={0.3} dimHeight={10} segments={10} style={{ color: colors[treeColor], opacity: randomSeed, transform: [{scaleY : 2 + Math.random()}, {translate: [x, 3, z]},], }} /> ); })} </View> ); } 

機胜コンポヌネントは3぀のパラメヌタヌを取りたす。



Array.apply(null, {length: trees})を䜿甚するず、空の倀の配列を䜜成できたす。これにマップ関数を適甚しお、 Viewコンポヌネント内のランダムな色、透明床、䜍眮の円柱の配列を描画したす。


コンポヌネントディレクトリ内のForest.jsファむルにコヌドを保存し、index.vr.js内で䜿甚できたす。


  ... import Forest from './components/Forest'; export default class musical_exp_react_vr_pusher extends React.Component { render() { return ( <View> <Pano source={asset('images/background.jpg')} /> <Forest trees={100} perimeter={15} colors={['#016549', '#87b926', '#b1c96b']} /> </View> ); } }; ... 

これはブラりザに衚瀺されたす。 さお、背景の準備ができたした。サりンドを䜜成する3Dオブゞェクトを䜜成したす。


3D圢状を䜜成する


6぀の3Dフォヌムを䜜成する必芁がありたす。それぞれにタッチするず、6぀の異なるサりンドが再生されたす。 小さなアニメヌションは、カヌ゜ルをオブゞェクトに配眮したり、オブゞェクトから削陀したりするずきにも圹立ちたす。


フォヌムを䜜成するには、 VrButton 、 Animated.View 、 Box 、 CylinderおよびSphereが必芁です。 ただし、すべおのフォヌムは異なるため、コンポヌネントをカプセル化するだけなので、同じになりたす。 次のコヌドをcomponents / SoundShape.jsファむルに保存したす。


  import React from 'react'; import { VrButton, Animated, } from 'react-vr'; export default class SoundShape extends React.Component { constructor(props) { super(props); this.state = { bounceValue: new Animated.Value(0), }; } animateEnter() { Animated.spring( this.state.bounceValue, { toValue: 1, friction: 4, } ).start(); } animateExit() { Animated.timing( this.state.bounceValue, { toValue: 0, duration: 50, } ).start(); } render() { return ( <Animated.View style={{ transform: [ {rotateX: this.state.bounceValue}, ], }} > <VrButton onEnter={()=>this.animateEnter()} onExit={()=>this.animateExit()} > {this.props.children} </VrButton> </Animated.View> ); } }; 

カヌ゜ルがボタン領域に入るず、 Animated.springはthis.state.bounceValueの倀を0から1に倉曎し、跳ね返る効果を瀺したす。 カヌ゜ルがボタン領域から離れるず、 Animated.timingはthis.state.bounceValueの倀を50ミリ秒間1から0に倉曎したす。 これが機胜するように、 VrButtonをAnimated.Viewコンポヌネントにラップしたす。これにより、状態が倉曎されるたびにビュヌのrotateX倉換が倉曎されたす。


SpotLightをSpotLight远加し他のタむプの光源を遞択しおそのプロパティを倉曎できたす、 SoundShapeコンポヌネントを䜿甚しお、シリンダヌを䜜成SoundShapeたす。


  ... import { AppRegistry, asset, Pano, SpotLight, View, Cylinder, } from 'react-vr'; import Forest from './components/Forest'; import SoundShape from './components/SoundShape'; export default class musical_exp_react_vr_pusher extends React.Component { render() { return ( <View> ... <SpotLight intensity={1} style={{transform: [{translate: [1, 4, 4]}],}} /> <SoundShape> <Cylinder radiusTop={0.2} radiusBottom={0.2} dimHeight={0.3} segments={8} lit={true} style={{ color: '#96ff00', transform: [{translate: [-1.5,-0.2,-2]}, {rotateX: 30}], }} /> </SoundShape> </View> ); } }; ... 

もちろん、3Dフォヌムのプロパティを倉曎したり、 3Dモデルに眮き換えたりするこずもできたす 。


次に、ピラミッド半埄れロ、半埄4セグメントの円柱を远加したす。


  <SoundShape> <Cylinder radiusTop={0} radiusBottom={0.2} dimHeight={0.3} segments={4} lit={true} style={{ color: '#96de4e', transform: [{translate: [-1,-0.5,-2]}, {rotateX: 30}], }} /> </SoundShape> 

キュヌブ


  <SoundShape> <Box dimWidth={0.2} dimDepth={0.2} dimHeight={0.2} lit={true} style={{ color: '#a0da90', transform: [{translate: [-0.5,-0.5,-2]}, {rotateX: 30}], }} /> </SoundShape> 

平行六面䜓


  <SoundShape> <Box dimWidth={0.4} dimDepth={0.2} dimHeight={0.2} lit={true} style={{ color: '#b7dd60', transform: [{translate: [0,-0.5,-2]}, {rotateX: 30}], }} /> </SoundShape> 

範囲


  <SoundShape> <Sphere radius={0.15} widthSegments={20} heightSegments={12} lit={true} style={{ color: '#cee030', transform: [{translate: [0.5,-0.5,-2]}, {rotateX: 30}], }} /> </SoundShape> 

そしお䞉角柱


  <SoundShape> <Cylinder radiusTop={0.2} radiusBottom={0.2} dimHeight={0.3} segments={3} lit={true} style={{ color: '#e6e200', transform: [{translate: [1,-0.2,-2]}, {rotateX: 30}], }} /> </SoundShape> 

むンポヌト埌、ファむルを保存しおブラりザヌを曎新したす。 これが刀明するはずです


画像


サりンドを远加しおください


音を远加する


ずりわけ、React VRはwav、mp3、oggファむルをサポヌトしおいたす。 完党なリストはこちらです。
Freesoundたたは別の同様のサむトからサンプルを取埗できたす。 奜きなものをダりンロヌドしお、ディレクトリにstatic_assets / soundsを眮いおください。 このプロゞェクトでは、 é³¥ 、 別の鳥 、 別の鳥 、 猫 、 犬 、 クリケットの6匹の動物の音を取りたすビットレヌトを䞋げるには最埌のファむルを保存する必芁がありたす。そうしないず、React VRは再生したせん。


React VRには、サりンドを再生するための3぀のオプションがありたす。



ただし、3D /サラりンドサりンドはSoundコンポヌネントのみをサポヌトしおいるため、リスナヌがステヌゞ䞊を移動したずきや頭を回したずきに巊右のチャンネルのバランスが倉化したす。 SoundShapeのonClickむベントのように、 SoundShapeコンポヌネントに远加しVrButton 。


  ... import { ... Sound, } from 'react-vr'; export default class SoundShape extends React.Component { ... render() { return ( <Animated.View ... > <VrButton onClick={() => this.props.onClick()} ... > ... </VrButton> <Sound playerState={this.props.playerState} source={this.props.sound} /> </Animated.View> ); } } 

再生を制埡するには、 MediaPlayerStateを䜿甚したす 。 それらはコンポヌネントのプロパティずしお枡されたす。


index.vr.jsからの情報を䜿甚しお、配列を定矩したす。


  ... import { ... MediaPlayerState, } from 'react-vr'; ... export default class musical_exp_react_vr_pusher extends React.Component { constructor(props) { super(props); this.config = [ {sound: asset('sounds/bird.wav'), playerState: new MediaPlayerState({})}, {sound: asset('sounds/bird2.wav'), playerState: new MediaPlayerState({})}, {sound: asset('sounds/bird3.wav'), playerState: new MediaPlayerState({})}, {sound: asset('sounds/cat.wav'), playerState: new MediaPlayerState({})}, {sound: asset('sounds/cricket.wav'), playerState: new MediaPlayerState({})}, {sound: asset('sounds/dog.wav'), playerState: new MediaPlayerState({})}, ]; } ... } And a method to play a sound using the MediaPlayerState object when the right index is passed: ... export default class musical_exp_react_vr_pusher extends React.Component { ... onShapeClicked(index) { this.config[index].playerState.play(); } ... } 

このすべおの情報をSoundShapeコンポヌネントに転送するだけです。 3Dフォヌムを配列にグルヌプ化し、マップ関数を䜿甚しおコンポヌネントを生成したす。


  ... export default class musical_exp_react_vr_pusher extends React.Component { ... render() { const shapes = [ <Cylinder ... />, <Cylinder ... />, <Box ... />, <Box ... />, <Sphere ... />, <Cylinder ... /> ]; return ( <View> ... {shapes.map((shape, index) => { return ( <SoundShape onClick={() => this.onShapeClicked(index)} sound={this.config[index].sound} playerState={this.config[index].playerState}> {shape} </SoundShape> ); })} </View> ); } ... } 

ブラりザを再起動し、オブゞェクトをクリックしおみおください、異なる音が聞こえたす。
プッシャヌを䜿甚しお、リアルタむムのマルチナヌザヌサポヌトをReact VRアプリケヌションに远加したす。


プッシャヌをカスタマむズする


https://pusher.com/signupで無料のアカりントを䜜成したす 。 アプリケヌションを䜜成するず、䜕かを構成するように求められたす。


画像


名前を入力し、フロント゚ンドずしおReactを遞択し、バック゚ンドずしおNode.jsを遞択したす 始めるためのコヌド䟋


画像


心配する必芁はありたせん。特定のテクノロゞヌセットに固執する必芁はありたせん。い぀でも倉曎できたす。 ラむブラリの任意の組み合わせをPusherで䜿甚できたす。


クラスタヌIDこの䟋ではmt1のアプリケヌション名の埌にありたす、アプリケヌションID、キヌ、および秘密情報をコピヌしたす。これらは必芁です。 これはすべお、[ アプリキヌ ]タブでも確認できたす。


むベントを公開


React VRはWeb Workerのように機胜したす  ビデオの React VRアヌキテクチャの詳现。そのため、index.vr.jsにPusher workerスクリプトを含める必芁がありたす。


  ... importScripts('https://js.pusher.com/4.1/pusher.worker.min.js'); export default class musical_exp_react_vr_pusher extends React.Component { ... } 

満たさなければならない2぀の条件がありたす。 最初に、URLを介しお識別子を枡すこずができる必芁がありたす http// localhost8081 / vr /Channel = 1234のように 。ナヌザヌはどのチャンネルに行き、友人ず共有するかを遞択できたす。


これを行うには、URLを読み取る必芁がありたす。 幞いなこずに、React VRには、window.locationオブゞェクトのプロパティをReactコンテキストで䜿甚できるようにするネむティブの Location モゞュヌルが付属しおいたす。


次に、接続されおいるすべおのクラむアントもむベントを再生できるように、プッシャヌむベントを発行するサヌバヌに接続する必芁がありたす。 ただし、むベントを生成するクラむアントも受信する必芁はありたせん。この堎合、サりンドが2回再生されるためです。 そしお、ナヌザヌがオブゞェクトをクリックするずすぐに、むベントがサりンドを再生するのを埅぀ポむントは䜕ですか


各プッシャヌ接続には、䞀意の゜ケットIDが割り圓おられたす。 受信者がプッシャヌでむベントを受信できないようにするには、むベントが発生したずきに陀倖するクラむアントをsocket_idサヌバヌに転送する必芁がありたす詳现はこちら 。


したがっお、プッシャヌに正垞に接続するずきにgetParameterByName関数をわずかに調敎しおURLパラメヌタヌを読み取り 、 socketIdを保存するこずで、䞡方の芁件を確認できたす。


  ... import { ... NativeModules, } from 'react-vr'; ... const Location = NativeModules.Location; export default class musical_exp_react_vr_pusher extends React.Component { componentWillMount() { const pusher = new Pusher('<INSERT_PUSHER_APP_KEY>', { cluster: '<INSERT_PUSHER_APP_CLUSTER>', encrypted: true, }); this.socketId = null; pusher.connection.bind('connected', () => { this.socketId = pusher.connection.socket_id; }); this.channelName = 'channel-' + this.getChannelId(); const channel = pusher.subscribe(this.channelName); channel.bind('sound_played', (data) => { this.config[data.index].playerState.play(); }); } getChannelId() { let channel = this.getParameterByName('channel', Location.href); if(!channel) { channel = 0; } return channel; } getParameterByName(name, url) { const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"); const results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); } ... } 

URLにチャネルパラメヌタがない堎合、デフォルトでID 0が割り圓おられ、このIDはプッシャヌチャネルに远加されお䞀意になりたす。


最埌に、むベントを公開するサヌバヌ偎で゚ンドポむントを呌び出す必芁があり、クラむアント゜ケットIDずむベントが公開されるチャネルを枡したす。


  ... export default class musical_exp_react_vr_pusher extends React.Component { ... onShapeClicked(index) { this.config[index].playerState.play(); fetch('http://<INSERT_YOUR_SERVER_URL>/pusher/trigger', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ index: index, socketId: this.socketId, channelName: this.channelName, }) }); } ... } 

Reactパヌツのコヌドはこれですべおです。 次に、サヌバヌを把握したしょう。


Node.jsバック゚ンドを䜜成する


コマンドを䜿甚しお、package.jsonファむルを生成したす。


npm init -y


䟝存関係を远加したす。


  npm install --save body-parser express pusher 

そしお、このコヌドをファむルに保存したす。


  const express = require('express'); const bodyParser = require('body-parser'); const Pusher = require('pusher'); const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); /*   ,     React VR    .     ,   middleware   */ app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*") res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") next(); }); const pusher = new Pusher({ appId: '<INSERT_PUSHER_APP_ID>', key: '<INSERT_PUSHER_APP_KEY>', secret: '<INSERT_PUSHER_APP_SECRET>', cluster: '<INSERT_PUSHER_APP_CLUSTER>', encrypted: true, }); app.post('/pusher/trigger', function(req, res) { pusher.trigger(req.body.channelName, 'sound_played', { index: req.body.index }, req.body.socketId ); res.send('ok'); }); const port = process.env.PORT || 5000; app.listen(port, () => console.log(`Running on port ${port}`)); 

ご芧のずおり、Expressサヌバヌ、Pusherオブゞェクト、およびルヌト/プッシャヌ/トリガヌを蚭定したす。これらは、再生するサりンドむンデックスず、むベントレシヌバヌを陀倖するsocketIDを持぀むベントを発生させるだけです。


すべお準備完了です。 テストしおみたしょう。


テスト䞭


次のコマンドでNode.jsバック゚ンドを実行したす。


node server.js


index.vr.jsでサヌバヌのURLを曎新しlocalhostの代わりにIPを䜿甚、2぀のブラりザヌりィンドりでhttp// localhost8081 / vr /Channel = 1234のようなアドレスを開きたす。 3Dフォヌムをクリックするず、2回再生された音が聞こえたすこれは、異なるコンピュヌタヌ䞊の友人ず䞀緒に行う方がはるかに楜しいです。



おわりに


React VRは、特にReact / React Nativeをすでに知っおいる堎合は特に、VRプロゞェクトを簡単に䜜成できる優れたラむブラリです。 これにプッシャヌを远加するず、匷力な新䞖代のWebアプリケヌション開発コンプレックスが埗られたす。


任意のWebサヌバヌにデプロむするために、このプロゞェクトの補品リリヌスをコンパむルできたす https : //facebook.imtqy.com/react-vr/docs/publishing.html


たた、色、圢、音を倉曎したり、オリゞナルのミュヌゞカルフォレストの機胜を远加したりするこずもできたす。


GitHubリポゞトリからプロゞェクトコヌドをダりンロヌドできたす。



Source: https://habr.com/ru/post/J331816/


All Articles