2021年12月13日
プログラミング
【A-Frame】WebARでつくる着せ替えゲーム
はじめに
この記事は新しい技術にチャレンジし続けるpalanのアドベントカレンダーDay13の記事です!
昨日は「FigmaのLibraryを活用する」についての記事でした。
今回は、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モデルの着せ替え
まずはこの着せ替え機能を実装していきます。
これは、帽子や服などの装飾類の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つのマーカーを線で繋ぐ
次は、トナカイのマーカーが読み込まれた時に
サンタとトナカイを線で繋ぐ実装をしていきます。
この実装は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でつくる着せ替えゲーム」を紹介しました。
このように、複数のマーカーを読み取ることで変化のあるコンテンツが作れるのでアイデアも広がりますよね!
全体のソースコードはこちらで確認いただけます。
最後までご覧いただき、ありがとうございました!
JavaScriptのお仕事に関するご相談
Bageleeの運営会社、palanではJavaScriptに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。
この記事は
参考になりましたか?
0
0
関連記事
2018年7月6日
【js】jQueryでできるハンバーガーメニューの作成
2018年6月18日
プログレスバーを簡単に実装できるprogressbar.js
2018年5月8日
画像に簡単にアニメーションをつけるcurtain.js
2018年2月14日
【js】jQueryでできるアコーディオンメニューの作成
2017年9月4日
【JS】スクロールした時にCSSanimationを発動させる「scrollMonitor」
簡単に自分で作れるWebAR
「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。
palanARへpalanでは一緒に働く仲間を募集しています
正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。
運営メンバー
Eishi Saito 総務
SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。
sasakki デザイナー
アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。
やまかわたかし デザイナー
フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。
Sayaka Osanai デザイナー
Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。
はらた エンジニア
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
こぼり ともろう エンジニア
サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。
ささい エンジニア
フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。
Damien
WebAR/VRの企画・開発をやっています。森に住んでいます。
ゲスト bagelee
かっきー
まりな
suzuki
miyagi
ogawa
雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。
いわもと
デザイナーをしています。 好きな食べ物はラーメンです。
taishi kobari
フロントエンドの開発を主に担当してます。Blitz.js好きです。
kubota shogo
サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!
nishi tomoya
aihara
グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。
nagao
SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。
Kainuma
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
sugimoto
asama
ando
iwasawa ayane
oshimo
異業界からやってきたデザイナー。 palanARのUIをメインに担当してます。 これからたくさん吸収していきます!