2021年12月13日

プログラミング

【A-Frame】WebARでつくる着せ替えゲーム

目次

  1. はじめに
  2. 3Dモデルの着せ替え
  3. 2つのマーカーを線で繋ぐ
  4. さいごに

はじめに

この記事は新しい技術にチャレンジし続けるpalanのアドベントカレンダーDay13の記事です!

昨日は「FigmaのLibraryを活用する」についての記事でした。

Figmaのライブラリの使い方

今回は、A-Frameで簡単な着せ替えゲームを作ったのでご紹介です!
肌着姿のサンタさんに服を着せてクリスマスの準備をしていきます。

こちらが完成したものの動画です。

やりたいこと

  • 服のマーカーをサンタに近づけると着せ替えができる
  • トナカイのマーカーを近づけると、サンタとトナカイが紐で繋がる

HTMLは以下をベースに進めていきます。
必要なマーカーとアセットを用意し、それぞれマーカーを読み込むと
対象の3Dモデルが表示されるようになっています。


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>着せ替えサンタ</title>
  <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  <script src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar.js"></script>
  <script src="./index.js"></script>
</head>
<body>
<a-scene embedded vr-mode-ui="enabled: false;" renderer="logarithmicDepthBuffer: true;">
  <a-assets>
    <!-- 各3Dモデルの読み込み -->
    <a-asset-item id="a-santa_body" src="./model/santa_body.glb"></a-asset-item>
    <a-asset-item id="a-santa_boushi" src="./model/santa_boushi.glb"></a-asset-item>
    <a-asset-item id="a-santa_huku" src="./model/santa_huku.glb"></a-asset-item>
    <a-asset-item id="a-tonakai" src="./model/tonakai.glb"></a-asset-item>
  </a-assets>

  <!-- サンタの3Dモデルを表示するためのマーカー -->
  <a-marker preset="custom" type="pattern" url="./marker/santa.patt" id="m_santa">
    <a-entity gltf-model="#a-santa_body" scale="0.5 0.5 0.5"></a-entity>
  </a-marker>

  <!-- 帽子の3Dモデルを表示するためのマーカー -->
  <a-marker preset="custom" type="pattern" url="./marker/boushi.patt" id="m_boushi">
    <a-entity gltf-model="#a-santa_boushi" scale="0.5 0.5 0.5"></a-entity>
  </a-marker>

  <!-- 服の3Dモデルを表示するためのマーカー -->
  <a-marker preset="custom" type="pattern" url="./marker/huku.patt" id="m_huku">
    <a-entity gltf-model="#a-santa_huku" scale="0.5 0.5 0.5"></a-entity>
  </a-marker>

  <!-- トナカイの3Dモデルを表示するためのマーカー -->
  <a-marker preset="custom" type="pattern" url="./marker/tonakai.patt" id="m_tonakai">
    <a-entity gltf-model="#a-tonakai" scale="0.5 0.5 0.5"></a-entity>
  </a-marker>

  <a-entity camera></a-entity>
</a-scene>
</body>

a-sceneタグのrenderer="logarithmicDepthBuffer: true;"はz-fitingの発生を防ぐためのものです

3Dモデルの着せ替え

まずはこの着せ替え機能を実装していきます。
e19a43696af998d92c9d8cd485d98e75.gif (3.1 MB)

これは、帽子や服などの装飾類のa-entity
あらかじめサンタと同じa-marker内に入れておきます。

POINT!!

ただし、最初は装飾系を非表示にしておきたいのでgltf-model属性は記述しません。
また、同じa-marker内にいれないと多少位置がズレるので注意です。


<!-- サンタのモデルを表示するためのマーカー -->
<a-marker preset="custom" type="pattern" url="./marker/santa.patt" id="m_santa">
  <a-entity gltf-model="#a-santa_body" scale="0.5 0.5 0.5"></a-entity>
+   <a-entity id="boushi" scale="0.5 0.5 0.5"></a-entity>
+   <a-entity id="huku" scale="0.5 0.5 0.5"></a-entity>
</a-marker>

更にa-sceneタグ内一番後ろに次のa-entityを追加します。


...
  <a-entity camera></a-entity>
+   <a-entity run></a-entity>
</a-scene>

次に、サンタのマーカーと、装飾類のマーカーが
同時に読み込まれた時のみ着せ替えるようJavaScriptで実装していきます。

まずは<a-entity run>のinitで
各マーカーを認識したらisMarkerFoundをtrueに。
認識できなくなったらfalseにするよう処理を書きます。


// 各マーカーが今現在読み込まれているかどうか
const isMarkerFound = {
  m_santa: false,
  m_boushi: false,
  m_huku: false,
  m_tonakai: false,
}

AFRAME.registerComponent('run', {
  init: () => {
    document.querySelectorAll('a-marker').forEach(marker => {
      marker.addEventListener('markerFound', () => {
        isMarkerFound[marker.id] = true
      })
      marker.addEventListener('markerLost', () => {
        isMarkerFound[marker.id] = false
      })
    })
  }
})

次に、毎フレーム実行されるtickで
サンタのマーカーと装飾類のマーカーが同時に認識された時に
サンタのマーカー内にある対象の装飾のモデルを表示します。


// 既に装飾を身につけているかどうか
const isDressed = {
  m_boushi: false,
  m_huku: false,
}

AFRAME.registerComponent('run', {
  init: () => {...},
  tick: () => {
    // サンタのマーカーを認識していないならreturn
    if(!isMarkerFound.m_santa) return

    // 帽子のマーカーを認識&まだ身につけていない場合、
    // サンタ用a-marker内にある、帽子のa-eneityにgltf-model属性を付与
    if(isMarkerFound.m_boushi && !isDressed.m_boushi) {
      document.getElementById('boushi').setAttribute('gltf-model', '#a-santa_boushi')
      isDressed.m_boushi = true
      return
    }
    // 服のマーカーを認識&まだ身につけていない場合、
    // サンタ用a-marker内にある、服のa-eneityにgltf-model属性を付与
    if(isMarkerFound.m_huku && !isDressed.m_huku) {
      document.getElementById('huku').setAttribute('gltf-model', '#a-santa_huku')
      isDressed.m_huku = true
      return
    }
  }
})

これで、サンタのマーカーと装飾類のマーカーが
同時に読み込まれた時のみ着せ替えができるようになりました。

2つのマーカーを線で繋ぐ

次は、トナカイのマーカーが読み込まれた時に
サンタとトナカイを線で繋ぐ実装をしていきます。

bcdc12b304331c9ad84a5434eb8423c7.gif (6.1 MB)

この実装はsuo-takefumiさんのこちらの記事を参考にしています。

まずは<a-entity run>のinitで紐となるジオメトリをthree.jsで生成します。


AFRAME.registerComponent('run', {
  init: () => {
    // 紐となるジオメトリを生成
    const geometry = new THREE.CylinderGeometry(0.03, 0.03, 1, 10)
    geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, 0.5, 0))
    geometry.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad( 90 )))
    const material = new THREE.MeshLambertMaterial({color: 0xFF0000})
    this.cylinder = new THREE.Mesh(geometry, material)
    // <a-entity id="leash">に生成したジオメトリを追加する
    this.cylinderGroup = document.getElementById('leash').object3D
    this.cylinderGroup.add(this.cylinder)
    ...
  },
  tick: () => {...}
})

そして生成したジオメトリを追加するためのa-entityをトナカイに追加します。


<!-- トナカイの3Dモデルを表示するためのマーカー -->
<a-marker preset="custom" type="pattern" url="./marker/tonakai.patt" id="m_tonakai">
  <a-entity gltf-model="#a-tonakai" scale="0.5 0.5 0.5"></a-entity>
  <!-- ↓three.jsで生成した紐を追加するためのa-entityタグを追加 -->
+   <a-entity id="leash" position="0 0.5 0"></a-entity>
</a-marker>

最後に、サンタとトナカイ、両方のマーカーが認識された時に
生成した紐用ジオメトリの端の片方をサンタのマーカーの位置に合わせます。


AFRAME.registerComponent('run', {
  init: () => {
+   this.santa = document.getElementById('m_santa')
+   this.tonakai = document.getElementById('m_tonakai')

+   this.p_santa = new THREE.Vector3()
+   this.p_tonakai = new THREE.Vector3()
    ...
  },
  tick: () => {
    ...
+   if(isMarkerFound.m_tonakai) {
      // サンタとトナカイのワールド座標を取得
+     this.tonakai.object3D.getWorldPosition(this.p_tonakai)
+     this.santa.object3D.getWorldPosition(this.p_santa)
      // サンタ側は手の高さに、トナカイ側は首の高さに位置を調整
+     p_santa.z = p_santa.z + 0.6
+     p_santa.x = p_santa.x + 0.1

      // トナカイからサンタまでの距離を算出し、
      // サンタ側の紐の端に設定する
+     const distance = this.p_tonakai.distanceTo(this.p_santa)
+     this.cylinderGroup.lookAt(this.p_santa)
+     this.cylinder.scale.set(1, 1, distance)
+   }
  }
})

以上で、マーカーを動かしても
サンタとトナカイが紐で繋がれるようになりました!

完成!

さいごに

今回はA-Frameを使用して、「WebARでつくる着せ替えゲーム」を紹介しました。
このように、複数のマーカーを読み取ることで変化のあるコンテンツが作れるのでアイデアも広がりますよね!
全体のソースコードはこちらで確認いただけます。

最後までご覧いただき、ありがとうございました!
image.jpg (531.5 kB)

JavaScriptのお仕事に関するご相談

Bageleeの運営会社、palanではJavaScriptに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。

無料相談フォームへ

0

0

AUTHOR

aihara

aihara

グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。

アプリでもっと便利に!気になる記事をチェック!

記事のお気に入り登録やランキングが表示される昨日に対応!毎日の情報収集や調べ物にもっと身近なメディアになりました。

簡単に自分で作れるWebAR

「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。

palanARへ
palanar

palanはWebARの開発を
行っています

弊社では企画からサービスの公開終了まで一緒に関わらせていただきます。 企画からシステム開発、3DCG、デザインまで一貫して承ります。

webar_waterpark

palanでは一緒に働く仲間を募集しています

正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。

話を聞いてみたい

運営メンバー

eishis

Eishi Saito 総務

SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。

sasakki デザイナー

アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。

yamakawa

やまかわたかし デザイナー

フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。

Sayaka Osanai デザイナー

Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。

はらた

はらた エンジニア

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

kobori

こぼり ともろう エンジニア

サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。

sasai

ささい エンジニア

フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。

damien

Damien

WebAR/VRの企画・開発をやっています。森に住んでいます。

ゲスト bagelee

ゲスト bagelee

かっきー

かっきー

まりな

まりな

suzuki

suzuki

miyagi

ogawa

ogawa

雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。

いわもと

いわもと

デザイナーをしています。 好きな食べ物はラーメンです。

kobari

taishi kobari

フロントエンドの開発を主に担当してます。Blitz.js好きです。

shogokubota

kubota shogo

サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!

nishi tomoya

aihara

aihara

グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。

nagao

SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。

kainuma

Kainuma

サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています

sugimoto

sugimoto

asama

ando

CONTACT PAGE TOP