レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

令和の「ゲームばっかりしてないで本でも読みなさい」

僕らが子供の頃は、「テレビばかり見ないで『本』でも読みなさい」と言われた。

 

僕が子供に言ってるのは「子供達よ『Youtube』ばっかり見てないで『テレビ』でもみなさい」だ。

 

語彙力アップには、YouTubeよりテレビだ。お気に入りの人ばかりの動画を見続けるYoutubeでは語彙力がそだってない。

 

そんなことを言う未来が来るとは思っていなかった。

 

...いや、少し思っていた。

 

うちらの親世代は「『本』ばっかり読んでないで」勉強しなさい or 働きなさい。と言われてきたらしい。

僕らは、「テレビばっかり見てないで、本でも読みなさい」だった。

 

徐々に推奨メディアが変わってる。時代の変化を体感してる。

 

ソフトウェア開発者の自分としては、子供にいうことに次がある。」

「『Youtube』ばっかり見てないで、『ゲーム』でもしなさい」だ。

 

コンピューターリテラシーの基礎教育としてゲームが重要だ。

 

例えば、僕と僕の息子(小2)にとって、フラグという概念を体験的に学んだのはドラクエ2のサマルトリアの王子である。

どこへいっても居ないサマルトリアの王子は、1サマルトリア王、2泉、3ローレシア王のそれぞれの話を聞かないとリリザの街に現れない。

幼少期の僕は、この時点でレベル15になって途方にくれてた。


はなしがそれた。

 

多分この先「メタバースばっかりやってないでゲームでもしなさい」とか

「AIとばっかりはなしてないで、生身の人と話しなさい」とか

「空間コンピューティングばっかりやってないで、PCを触りなさい」とかが出てくるのだろう。

 

 

AframeのマーカーARでa-cameraやa-marker-cameraを使うとマーカーと物体位置がズレる

なぜかなと思って、ずっと別の方法で誤魔化していたが解決したので記。

現象

ダメなパターンは以下、a-cameraを使っている。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
    <script type="text/javascript" src="https://aframe.io/releases/1.2.0/aframe.min.js "></script>
    <script type="text/javascript" src="https://raw.githack.com/AR-js-org/AR.js/master/aframe/build/aframe-ar-nft.js" ></script>
  </head>
  <body style="margin: 0; overflow: hidden; height: 100%">
    <a-scene
      embedded
      arjs="trackingMethod: best; sourceType: webcam; detectionMode:mono_and_matrix; debugUIEnabled: false;"
    >
      <a-camera></a-camera>
      <a-marker type="barcode" value="1" >
        <a-box position="0 0.5 0" wireframe="true" color="black"></a-box>
      </a-marker>
    </a-scene>
  </body>
</html> 

実行するとこんな感じ

解決

変えたところだけ。 これを

<a-camera></a-camera>

こう変える

 <a-entity camera></a-entity>

ピッタリ来た。

原因

a-cameraはVRようにY=1.6になってる。

<a-entity camera>でプレーンなカメラを作ることで解消するということ

わかってみれば簡単だが、長らくわからなくてドキュメントもGPTもあてにならなかった。

a-marker-cameraも同様にYを持ってるので厄介。 すっきりしたのでよかった・。

astro+Restのサービスを作る時にCORSにならない方法

Astroからaxiosやfetchやらで、RestAPIを呼ぼうとするとCORSの制限に引っかかる。

ローカルだとClientとServerをそれぞれポートを分けて立てたりするから。

仮に以下のようにするとする

Astro => http://localhost:3000/

Server => http://localhost:8080/api/

Astro側のpackage.json"proxy": "http://localhost:8080/と、サーバーのホストを知らせてやれば良い。

{

 "name": "sampleservice",
 "type": "module",
 "proxy": "http://localhost:8080/,
 "version": "0.0.1",
 ・・・
}

サーバーサイドにも

import cors from "cors";

// 中略


// CORSをexpressに設定
const origin ="http://localhost:3000";
const app = express();
app.use(
  cors({
    origin: origin,
    credentials: true,
    preflightContinue: true,
  })
);

// 中略

const server = app.listen(config.get("server_port"), () => {
  console.log(config);
  console.log("listen to " + config.get("server_port"));
});

覚書:git である程度大きいファイルをアップする時に、pushが進まなくなる現象の対処

Git のプッシュでWriteが終わり、Total 6626 (delta 1700), reused 0 (delta 0), pack-reused 0と出たあとに止まってしまう。 ある程度大きなファイルがあるときは、以下のようにバッファを増やしてからpush する。

git config http.postBuffer 157M

大きすぎてエラーが出る場合は、LFSを使う。

A-FrameでVRを始めるときにトラブらない最低限の設定

A-Frameを使ったVRをいくつか作っていて、いつも設定で回避していることを備忘録的に記載します。

最終形

最終形を始めに示し、以降で構成要素を見ていくことにします。

<!DOCTYPE html>
<html >
  <head>
    <meta name="description" content="VR template" />
    <meta charset="utf-8" />
    <title>VR Template</title>
    <script type="text/javascript" src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@v6.1.1/dist/aframe-extras.min.js"></script>

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"
    ></script>
    <style>
    html{
      height: 100%;
    }
    body{
      font-size: 0.625%;
      overflow: hidden;
      touch-action: manipulation;
      margin: 0;
      height: 100%;
    }
    </style>
    <script type="text/babel">
    AFRAME.registerComponent("init", {
      init: async () => {
        const message:string = "init start";
        console.log(message);
      }
    });
    </script>
    <a id="myEnterARButton" href="#"></a>
  </head>
  <body>
    <a-scene init  renderer="colorManagement: true" vr-mode-ui="enterARButton: #myEnterARButton"
    device-orientation-permission-ui="enabled:true;denyButtonText:いいえ;allowButtonText:はい;cancelButtonText:キャンセル;deviceMotionMessage:向きのセンサーにアクセスしてよろしいですか;mobileDesktopMessage:モバイル用Webサイトに切り替えてこの画面をリロードしてください。"

    gltf-model="dracoDecoderPath:https://www.gstatic.com/draco/v1/decoders/" >
      <a-assets>
        <!-- <a-asset-item id="asset1" src="xxx.glb"></a-asset-item> -->
      </a-assets>

      <a-box  color="red" ></a-box>

      <a-entity id="rig" movement-controls position="0 0 4">
        <a-entity
          look-controls="pointerLockEnabled: false"
          id="main-camera"
          camera=""
        >
          <a-entity
            id="cursor"
            cursor="rayOrigin: mouse;fuse:false"
            raycaster="objects: .clickable"
          ></a-entity>
        </a-entity>
      </a-entity>
      <a-entity laser-controls raycaster="objects: .clickable; far: 10"></a-entity>
      <a-entity laser-controls raycaster="objects: .clickable; far: 10"></a-entity>
    </a-scene>
  </body>
</html>

説明のいらないところから

とりあえず、最終形から分解して説明のいらない基本セットを用意します。 A-Frameのライブラリを呼んで、a-sceneを読み込むだけです。これだけでも3Dシーンが作られるのがA-Frameの手軽なところですね。

<!DOCTYPE html>
<html >
  <head>
    <title>VR Template</title>
    <script type="text/javascript" src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
  </head>
  <body >
    <a-scene >
    </a-scene>
  </body>
</html>

スタイル

スタイル設定は主にスマホでのズームを抑制するものです。

    <style>
    html{
      height: 100%;
    }
    body{
      font-size: 0.625%;
      overflow: hidden;
      touch-action: manipulation;
      margin: 0;
      height: 100%;
    }
    </style>

viewportメタタグも拡大縮小を抑制します。

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />

シーンの設定について

a-scene要素での設定は、以下です。

<a-scene init  renderer="colorManagement: true" vr-mode-ui="enterARButton: #myEnterARButton"
    device-orientation-permission-ui="enabled:true;denyButtonText:いいえ;allowButtonText:はい;cancelButtonText:キャンセル;deviceMotionMessage:向きのセンサーにアクセスしてよろしいですか;mobileDesktopMessage:モバイル用Webサイトに切り替えてこの画面をリロードしてください。"
  • init
    • registerComponentで登録したinitコンポーネントを読み込む。registerComponentのパートは後述。
  • renderer="colorManagement: true"
    • Glbの色が暗くなる問題の対応
  • vr-mode-ui="enterARButton: #myEnterARButton"

    • AndroidバイスでARボタンがVRボタンと重なって表示される。問題を回避します。 head内の<a id="myEnterARButton" href="#"></a>のコードもセットで使います。
  • device-orientation-permission-ui

    • iPhoneジャイロセンサーにアクセスする許可ダイアログを日本語化
      • enabled:true;
      • denyButtonText:いいえ;
      • allowButtonText:はい;
      • cancelButtonText:キャンセル;
      • deviceMotionMessage:向きのセンサーにアクセスしてよろしいですか;
      • mobileDesktopMessage:モバイル用Webサイトに切り替えてこの画面をリロードしてください。
  • gltf-model="dracoDecoderPath:https://www.gstatic.com/draco/v1/decoders/"
    • gltfモデルのDraco圧縮を有効にする

draco圧縮

Draco圧縮は、Googleオープンソース技術でZIP圧縮したものよりも、圧倒的に高い圧縮率を実現できます。

https://2.bp.blogspot.com/-_zGI5CHskzE/WHaG88sMbfI/AAAAAAAABAQ/2nQERexN08AzGNf3N5OYOOCURV7TCyZdgCLcB/s1600/Draco1.png

Draco圧縮するためのツールをインストールします。

npm install -g gltf-pipeline

Draco圧縮をコマンドラインで実施します。

gltf-pipeline -i xxx_from.glb -o xxx_to.glb -d

カメラはリグに入れる

カメラをリグに入れることで、カメラの方向をプログラムから変えることができます。 マウスのタップを有効にするには、カメラの配下にraycaster属性を持った要素を持ちます。 cursorのrayOriginをmouseにするだけかと思いきや、fuse:falseにしないと、マウスクリックやタップで2回クリックが発生するという罠があります

      <a-entity id="rig" movement-controls position="0 0 4">
        <a-entity
          look-controls="pointerLockEnabled: false"
          id="main-camera"
          camera=""
        >
          <a-entity
            id="cursor"
            cursor="rayOrigin: mouse;fuse:false"
            raycaster="objects: .clickable"
          ></a-entity>
        </a-entity>
      </a-entity>
      <a-entity laser-controls raycaster="objects: .clickable; far: 10"></a-entity>
      <a-entity laser-controls raycaster="objects: .clickable; far: 10"></a-entity>
    </a-scene>

この設定にあるmovement-controls属性のために以下のaframe-extrasが必要です。

<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@v6.1.1/dist/aframe-extras.min.js"></script>

movement-controlsを設定すると、スマホで画面タップしたときに直進する機能が付加されます

AFrameのコンポーネントを作るためのregisterComponentをTypescriptで書けるようにtype="text/babel"でスクリプトを作ります。 このコンポーネントは、a-sceneで設定しています。

    <script type="text/babel">
    AFRAME.registerComponent("init", {
      init: async () => {
        const message:string = "init start";
        console.log(message);
      }
    });
    </script>

Babelのためにインポートが必要ですね。TypescriptにしないまたはWebpackなどでビルドするなら不要です。

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>