12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.jsでWebVR(A-Frame)を扱うととても便利

Posted at

この度、作品投稿プラットフォームのFantasficでWebVRを使ったイラスト閲覧機能をリリースしました。

この機能は投稿された画像作品をWebVR上で閲覧できるというシンプルなものです。
Vue.jsを介してA-Frameを扱うことで実現しています。

A-FrameはDOMで表現した3DモデルをVRでレンダリングしてくれるライブラリです。
DOMで空間を表現するため、Vue.jsなどのフロントエンドフレームワークと親和性が高い作りになっています。

イベントもDOMの仕組みで処理できます。なので、3Dの平面オブジェクトを表すDOMに直接@clickでイベントリスナーを定義してイベントを受け取るといったことができます。下記がコード例です。

<template>
  <a-scene ref="scene">
    <!-- 平面を表示 -->
    <a-plane width="1" height="1" :position="planePosition" cursor @click="onClickPlane" />
  </a-scene>
</template>
<script>
export default {
  data() {
    return {
      planePositionSrc: {x: 0, y: 0, z: -1}
    }
  },
  computed: {
    planePosition () {
      const pos = this.planePositionSrc
      return `${pos.x} ${pos.y} ${pos.z}`
    }
  },
  methods: {
    // クリックすると平面が遠のく
    onClickPlane() {
      this.planePositionSrc.z += 0.5
    }
  }
}
</script>

フロントエンドフレームワークで実装するとコンポーネント化も簡単です。
下記はOculusのControllerを独自に拡張したコンポーネントの実装例です。
(若干きもちわるいことしてますが許してください)

<template>
  <a-entity
    id="controller"
    app-oculus-controls
    oculus-go-controls
    daydream-controls
    @triggerup="onTriggerUp"
    @triggerdown="onTriggerDown"
    @trackpadup="onTrackpadUp"
    @trackpaddown="onTrackpadDown"
    @touchstart="onTouchStart"
    @touchend="onTouchEnd"
    @axismove="onAxisMove"
  ></a-entity>
</template>
<script>
const handler = {
  get: function(target, name) {
    if (target.controller) {
      return target.controller[name]
    }
    return _ => null
  }
}
const target = {}
const proxy = new Proxy(target, handler)
window.AFRAME.registerComponent('app-oculus-controls', {
  init: function() {
    console.log('initialize controller')
  },
  tick: function() {
    proxy.onTick(this.el)
  }
})

export default {
  data() {
    return {
      trigger: false,
      touch: false
    }
  },
  mounted() {
    target.controller = this
  },
  destroy() {
    target.controller = null
  },
  methods: {
    onTriggerUp(e) {
      this.trigger = false
      this._startTriggerPos = null
      this.$emit('triggerup', e)
    },
    onTriggerDown(e) {
      this.trigger = true
      this._startTriggerPos = e.target.object3D.position.clone()
      this.$emit('triggerdown', e)
    },
    onTrackpadUp(e) {
      this.trackpad = false
      this._trackpadDownPos = null
      this.$emit('trackpadup', e)
    },
    onTrackpadDown(e) {
      this.trackpad = true
      this._trackpadDownPos = e.target.object3D.position.clone()
      this.$emit('trackpaddown', e)
    },
    onTouchStart(e) {
      this.touch = true
      this._startTouchPos = e.target.object3D.position.clone()
      this.$emit('touchstart', e)
    },
    onTouchEnd(e) {
      this.touch = false
      this._startTouchPos = null
      this._lastAxis = null
      this.$emit('touchend', e)
    },
    onAxisMove(e) {
      this.$emit('axismove', e)
      if (this.touch && !this.trackpad) {
        const axis = e.detail.axis
        if (this._lastAxis === null) {
          this._lastAxis = [].concat(axis)
        }
        if (this._lastAxis && this._lastAxis.length > 0) {
          const dx = axis[0] - this._lastAxis[0]
          const dy = axis[1] - this._lastAxis[1]
          this.$emit('axischange', { diff: { x: dx, y: dy } })
        }
      }
    },
    onTick(target) {
      if (this.trigger && this._startTriggerPos) {
        const c = target.object3D.position.clone()
        const s = this._startTriggerPos
        const diff = new window.THREE.Vector3(s.x - c.x, s.y - c.y, s.z - c.z)
        this.$emit('triggerpressmove', { diff })
        this._startTriggerPos = c
      }
      if (this.trackpad && this._trackpadDownPos) {
        const c = target.object3D.position.clone()
        const s = this._trackpadDownPos
        const diff = new window.THREE.Vector3(s.x - c.x, s.y - c.y, s.z - c.z)
        this.$emit('trackpadpressmove', { diff })
        this._trackpadDownPos = c
      }
    }
  }
}
</script>

いくらかThree.jsについて知っている必要があるものの、Vueで普通のWebフロントエンドを書くような感覚でVRアプリを実装できるのは助かりますね。
Oculus Questの販売も控えていますし、VRは今年も躍進すると信じています。VRアプリをネイティブで記述するのは敷居が高いと感じる方はVueやReactを使ってWebVRから挑戦してみると入りやすいかもしれません。

12
10
0

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
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?