20201204のvue.jsに関する記事は4件です。

[AWS] -- Architecture Supporter[2] -- Setting Local Development Environment

At first.

This content is written by AWS Beginner.
So this content is a possibility that isn't Best Practice.
Please read this content as reference information.

Work on this article.

  • Install "Docker" on Windows.
  • Install "Visual Studio Code".
  • Setup Docker.
  • Setup Visual Studio Code.
  • Try connecting from Visual Studio Code to Docker.

My PC Environment.

  • Windows10 Home

1.Install "Docker" on Windows

(1) Open the page of 「"Download for Docker Installer"」.
(2) Click 「Docker Desktop for Windows」 --> Download Installer Exe File.
(3) Execute Installer.
※Installer is very simple. So, No description.

2.Install "Visual Studio Code" on Windows

(1) Open the page of 「"Download Visual Studio Code"]」.
(2) Click 「Windows(Widnwos 7,8,10)」
(3) Execute Installer.
(4) Setup added task.
vsinstalle.png
(5) Click "Next" and "Install" and "Finish".

3. Install Visual Studio Code extend function.

(1) Search "Docker" extension function And click install.
docker.png
(2) Search "Remote Container" extension function and click install.
remotecontainer.png

4. Create a Docker Image File.

(1) Create a Workspace directory.
I create a directory that name is "work".
 ※Name is any.
 ※This sample has created a directory under "C Drive"
(2) Create "DockerFile" in Created The directory.
DockerFile content is below.

Dockerfile
FROM node:lts-alpine

WORKDIR c:\\work

・・・

・・・Sorry, this content is not completed.
・・・today is the end.
・・・to be continue...


◆Related article

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity と Vue3 と GitHub Pages と GitHub Actions のお話

初めに

こんにちは、Bugfire です。
クラウドワークス Advent Calendar 2020 の12日目になりました。

タイトルはてんこ盛りですが、内容は薄いです!

結果だけ知りたい人に

なぜこんなふわっとした物に

当初 Project Tiny (Unity のインスタントアプリ用のフレームワーク) で小さくなった Unity をいっちょ試してみるか!と思っていたのですが、自分が試した時点ではサンプルすらビルドできないので諦めました。

すごい勢いで変化しているので、古いもの試してもしょうがないですし(言い訳)

というわけで、使い古されたネタの Unity WebGL ビルド + Vue.js を試してみます。Vue3 の Composition API を使ったことがないので、それもついでに試しつつ、GitHub Pages w/ GitHub Acitons でリリースをします。

Unity

環境

Unity のバージョンは 2019.4.15f1 LTS を使いました。弱気ですね。
安定バージョンだけあって、WebGL のビルドはすんなりいきます。

プロジェクトファイル

中身作るの大変なので、CubeWorld をベースに作業を行なってみます。
というか、すごいですねこれ。MineCraft クローンですが、よくできています。

CubeWorld ScreenShot

UnityLoader.js の変更

このバージョンでは、mac OS Big Sur (Version 11)の UserAgent に対応していないので、少し手を加えましょう。

UnityLoader.js は難読化が行われているので、扱いくいです。読みやすく変更しましょう。
自分は js-beautify を使いました。

npx js-beautify -s 2 < original/UnityLoader.js > new/UnityLoader.js
@@ -1996,7 +1996,7 @@
     var p = n;
     switch (/Windows/.test(u) && (p = /Windows (.*)/.exec(u)[1], u = "Windows"), u) {
       case "Mac OS X":
-        p = /Mac OS X (10[\.\_\d]+)/.exec(i)[1];
+        p = /Mac OS X (1\d[\.\_\d]+)/.exec(i)[1];
         break;
       case "Android":
         p = /Android ([\.\_\d]+)/.exec(i)[1];

こんなしょうもない差分です。

もう一つあります、スマートフォンの場合は、

  compatibilityCheck: function(e, t, r) {
    UnityLoader.SystemInfo.hasWebGL ? UnityLoader.SystemInfo.mobile ? e.popup("Please note that Unity WebGL is not currently supported on mobiles. Press OK if you wish to continue anyway.", [{
      text: "OK",
      callback: t
    }]) : ["Edge", "Firefox", "Chrome", "Safari"].indexOf(UnityLoader.SystemInfo.browser) == -1 ? e.popup("Please note that your browser is not currently supported for this Unity WebGL content. Press OK if you wish to continue anyway.", [{
      text: "OK",
      callback: t
    }]) : t() : e.popup("Your browser does not support WebGL", [{
      text: "OK",
      callback: r
    }])
  },

ここでダイアログが表示されてしまうので、コードから削除を行うか

UnityLoader.SystemInfo.mobile = false;

を行うことで無理矢理チェックを回避します。

ビルド環境

私は UnityCloudBuild (以下UCB) に課金していないので、手動でビルドして、生成物をレポジトリに Commit していますが、通常であれば、UCB でビルドを行うのが良いでしょう。

その場合は C# で Build PostProcess で Upload なりすると良いと思います。

Vue.js

環境構築

$ npm init -y
$ npm install -L -S -D @vue/cli
$ npx vue create app
  • ❯ Manually select features で細かく指定
  • Choose Vue version, TypeScript, Router, Linter をアリに
  • ❯ 3.x (Preview)
  • ? Use class-style component syntax? (y/N)NO

あとは適当に

vue-unity-webgl のような便利コンポーネントもありますが、ここは自分でコンポーネントを作ります。

UnityLoader.js のローダー

まず最初にできなかったことから...。最初 UnityLoader.js をモジュールとして実行しようと思いましたが、global スコープを前提としているところがあり、諦めました。

export interface UnityInstance {
  SetFullscreen(mode: number): void;
  SendMessage(gameObject: string, method: string, param: string): void;
}

export interface UnityLoader {
  instantiate(
    container: string,
    configUrl: string,
    options: { onProgress: (instance: UnityInstance, progress: number) => void }
  ): UnityInstance;
}

declare let UnityLoader: UnityLoader | undefined;

export function GetUnityLoader(url: string): Promise<UnityLoader> {
  return new Promise((resolve, reject) => {
    if (typeof UnityLoader !== "undefined") {
      return resolve(UnityLoader);
    }
    const s = document.createElement("script");
    s.type = "text/javascript";
    s.src = url;
    s.async = true;
    s.defer = true;
    s.onload = () => {
      if (typeof UnityLoader !== "undefined") {
        return resolve(UnityLoader);
      } else {
        return reject(`Load error on UnityLoader (${url})`);
      }
    };
    s.onerror = () => reject(`Load error on UnityLoader (${url})`);
    document.head.appendChild(s);
  });
}

script 要素を作って無理矢理呼び出します。形として getter はありますが、グローバルに読み込まれます。

Vue コンポーネント

Composition API を使ってみました。Composition API の説明自体は各所にあるので略。

<template>
  <div :id="unityContainerId"></div>
</template>

<script lang="ts">
import { defineComponent, onMounted, reactive, PropType } from "vue";
import { GetUnityLoader, UnityInstance } from "./UnityLoaderUtil";

export default defineComponent({
  name: "Unity",
  props: {
    loaderUrl: {
      type: String,
      required: true
    },
    configUrl: {
      type: String,
      required: true
    },
    onLoad: {
      type: Function as PropType<null | ((instance: UnityInstance) => void)>,
      required: false,
      default: null
    },
    onProgressChanged: {
      type: Function as PropType<null | ((progress: number) => void)>,
      required: false,
      default: null
    }
  },
  setup(props) {
    const unityContainerId = `unityContainer-${Math.random()
      .toFixed(10)
      .toString()
      .substr(2)}`;
    const state = reactive<{ hasError: boolean }>({
      hasError: false
    });

    onMounted(async () => {
      try {
        const unityLoader = await GetUnityLoader(props.loaderUrl);
        unityLoader.instantiate(unityContainerId, props.configUrl, {
          onProgress: (instance, progress) => {
            if (props.onProgressChanged !== null) {
              props.onProgressChanged(progress);
            }
            if (progress >= 1 && props.onLoad !== null) {
              props.onLoad(instance);
            }
          }
        });
      } catch (e) {
        state.hasError = true;
      }
    });

    return { unityContainerId };
  }
});
</script>

emit で型の付け方がわからなかったので、コールバック関数を Props に渡すようにしました。自分に型は必要です。(そのあたり論争があるようですね)

Unity と Vue.js 間での通信

まったく Unity と Vue.js の間で通信をしないのであれば良いですが、普通はそうはいきませんね。

Vue.js から Unity への通信は簡単です。初期化時、もしくは Progress イベントで伝わる UnityInstance の SendMessage メソッドで、GameObject のメソッドを呼び出すことができます。文字列の引数が渡せるので、必要があれば JSON などを使うのも良いです。

逆方向はちょっとだけ面倒です。

  • JS 側は Unity 側のプロジェクトで .jslib ファイルの JS で記述された mergeInto() で export する
  • Unity 側は System.Runtime.InteropServices.DllImport で上の関数の import する。
  • Unity 公式サイトのドキュメント に詳しく書いてあります。

自分は Unity(jslib) 側の責務を最小にするため、

mergeInto(LibraryManager.library, {
  GameToWebNative: function(tag, message) {
    if (typeof window.UnityGameToWebHandler === 'function') {
      window.UnityGameToWebHandler(Pointer_stringify(tag), Pointer_stringify(message));
    }
  }
});

のように行いました。window にUnity をロードする前から変なのをはやしておくことで、実態を Vue 側の実装に委譲しています。
一般的には postMessage を用いるのが良いかと思います。

余談

当初 UI は HTML/CSS で、ゲーム内容は Unity/WebGL とし、オーバレイ表示することで、ブラウザの機能を生かして最高の体験を!!!、と作業を進めましたが、面倒すぎました。

特に Unity/WebGL のビルドは死ぬほど遅いのと、環境が分散するため、とても作業し辛いです。
業務であれば、開発中は UnityEditor 側に mock を作って、環境を Unity に閉じることはできそうです。片手間ではちょっと...。

なので、Vue らしさは全然ないです。中途半端な記事で申し訳ありません。

デプロイ

ついでに GitHub Actions から GitHub Pages にデプロイしましょう。

name: github pages

on:
  push:
    branches:
      - develop

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    env:
      TARGET_PATH: CubeWorld/Web
      CACHE_VERSION: v1
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2.1.2
        with:
          node-version: 12.x
          check-latest: true
      - uses: actions/cache@v2
        with:
          path: ${{ env.TARGET_PATH }}/node_modules
          key: ${{ env.CACHE_VERSION }}-${{ runner.os }}-v12-${{ hashFiles(format('{0}/package-lock.json', env.TARGET_PATH)) }}
          restore-keys: |
            ${{ env.CACHE_VERSION }}-${{ runner.os }}-v12-
      - name: Install
        run: |
          cd ${{ env.TARGET_PATH }}
          npm install
      - name: Build
        run: |
          cd ${{ env.TARGET_PATH }}
          npm run build
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ${{ env.TARGET_PATH }}/dist

取り立てて書くことはないですね。peaceiris/actions-gh-pages は便利。他レポジトリへの deploy もできるので、公開用レポジトリと非公開レポジトリに分けるのも良いでしょう。

Unity Project での変更

ぶっちゃけ、ここが一番時間かかりました。疲れました。

Unity WebGL での問題点

マルチタッチがバグっています

マルチタッチでのコントロールがうまくいかなくて TouchScript を試していたりしましたが、そもそも Input.GetTouch() で取りこぼしがあります。(Unity 2019.4.15f1時点)
(TouchScript の issue でも取り沙汰されていますが、TouchScript の問題点ではありません)

https://github.com/Bugfire/CubeWorld/blob/develop/CubeWorld/Assets/QubeWorld/Shared/Components/WebGLInputWorkaround.cs

かなり無理矢理な対処をしたので、興味がある方はここのソースを読んでみてください。

Cube World の変更

もともとのプロジェクトは、

  • PC 用でキーボード中心の操作系
  • IMGUI ベースのメニュー

でした。そりゃ、8年前のプロジェクトですからね!

操作系の変更

  • タッチ中心の操作系に変更
  • 画面中心のみターゲットから、タッチした場所をターゲットに変更

メニューの変更

  • ある程度を uGUI ベースに変更しました。
    • アイテムウィンドウ関連がまだ残っています (組み合わせでバグっています)

追加機能

  • Web/Unity 間通信のサンプルとして、チャットボタンを作りました
    • 押下すると Web 側でテキスト入力を行い、Native 側に結果を投げ、表示します。
  • 追記
    • Photon REALTIME の無料プランを使って簡易的なチャットを実装しました。便利、かんたん。

動いていないところ

  • Network/Multiplay
    • 動きません(実装していません)

Cube World の感想

本当によくできています。
エンジン部分が Unity に非依存に作ってあるので、Unity に限らずお好きな C# ランタイムでサーバ検証ができそうです。

逆に Physics や raycast は Unity の便利な機能が使えませんが、エフェクトやキャラクタの表示のみに使用を限るのも良いというか、よくあるMMOの実装ではそうするでしょう。

おそらく高速化のために、ある程度大きなの粒度で動的に mesh を作成しています。
(聞いた話、本家もライティングの結果をランタイムで mesh にベイクしているみたいですね)

時間があればもう少し改造してみたいです。

参考

以下のサイトを使用してリソースを作成しました。ありがたや。

終わりに

しくじり一覧

  • Project Tiny 使わなかった
  • UnityLoader.js をモジュール化できなかった
  • UI を Vue.js に寄せることができなかった
  • 時間のほとんどを Unity のプロジェクト変更に費やしてしまった
  • テキスト入力を Web 側の form で行おうとしたら、iOS でフォーカスがうまく戻ってこなくて諦めた

Unity も Vue.js も GitHub Actions もみんな楽しいですね!
Unity のプロジェクトの CubeWorld を UI の世代更新や、スマートフォン対応にするのに一番時間がかかりました。久しぶりの Unity 楽しかったです。

We're hiring!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vueにaxiosを導入するまでのプロセスを記録した(エラー含む)

$ vue add axios

その後、ブラウザを確認。生成されたaxios.jsファイルのoptionが使われていないというエラー。

src/plugins/axios.js
                               これが使われていない
Plugin.install = function(Vue,option) {
  Vue.axios = _axios;
  window.axios = _axios;
  Object.defineProperties(Vue.prototype, {
    axios: {
      get() {
        return _axios;
      }
    },
    $axios: {
      get() {
        return _axios;
      }
    },
  });
};

手動で消した。

src/plugins/axios.js
Plugin.install = function(Vue) {
  Vue.axios = _axios;
  window.axios = _axios;
  Object.defineProperties(Vue.prototype, {
    axios: {
      get() {
        return _axios;
      }
    },
    $axios: {
      get() {
        return _axios;
      }
    },
  });
};


再度ブラウザで確認

error

compile error
./src/main.js Module build failed (from ./node_modules/@vue/cli-plugin-eslint/node_modules/eslint-loader/index.js):
Error: Cannot find module 'eslint/lib/formatters/stylish' Require stack:

この記事(https://tmegos.hatenablog.jp/entry/package-json-dependencies-caret)
を参考にして、

package.json
"resolutions": {
// "eslint-loader": “2.1.2”→これを消した
"eslint-loader": "3.0.3"
}

そして、

$ yarn add -D eslint-loader


エラー解消&axiosがワークした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでオセロ盤を作ってみる

はじめに

本日は12月4日❗️
チノちゃん誕生日おめでとう???

本題

オセロ盤はビットボードというデータ構造で実装するのが良いらしい⚪️⚫️✨
Vue.jsで石を並べるところまでやるぞ❗️?

ビットボード(Wikipedia)

※この記事では、ビットボードの難しい説明を省略します?
ビットボードで石を返す処理はサーバー側で行い、その結果の石情報が渡ってくるところからやります✊?❗️

あらかじめ仕込んでおく3分クッキングスタイルですね??✨

早速実装

残念ながらJavaScriptの数値は32bitなので、1つの変数で64マス表すことができません❗️?
ゆるせねぇ...

というわけで、サーバー側で1次元配列に変換してフロント側に渡ってくるってことにしました✊?❗️

最初に思い付いたやつ

Board.vue
<template>
  <div class="board">
    <div v-for="i in 8" class="row">
      <div v-for="j in 8" class="cell">
        <div v-if="blackStones[i * 8 + j]" class="stone black"></div>
        <div v-else-if="whiteStones[i * 8 + j]" class="stone white"></div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      blackStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ],
      whiteStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ]
    }
  }
}
</script>

二重ループで回して 1 が立ってたらそれぞれの石の要素を追加する感じ

しかしここで問題が…
v-for="i in 8" のindexが1始まりなのです?
[i * 8 + j][(i -1) * 8 + j - 1] と書かなければならないのでわかりづらい❗️?

改良

Board.vue
<template>
  <div class="board">
    <div v-for="i in Array(boardSize).keys()" class="row">
      <div v-for="j in Array(boardSize).keys()" class="cell">
        <div v-if="blackStones[i * boardSize + j]" class="stone black"></div>
        <div v-else-if="whiteStones[i * boardSize + j]" class="stone white"></div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      boardSize: 8,
      blackStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ],
      whiteStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ]
    }
  }
}
</script>

いろんな方法がありそうだけど、一番短そうな Array(8).keys() で動いたのでこれでいいや❗️?

完成版

Board.vue
<template>
  <div class="board">
    <div class="logo rot">Othello</div>
    <div class="board-main">
      <div v-for="i in Array(boardSize).keys()" class="row">
        <div v-for="j in Array(boardSize).keys()" class="cell">
          <div v-if="blackStones[i * boardSize + j]" class="stone black"></div>
          <div v-else-if="whiteStones[i * boardSize + j]" class="stone white"></div>
        </div>
      </div>
    </div>
    <div class="logo">Othello</div>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      boardSize: 8,
      blackStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ],
      whiteStones: [
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 1, 0, 0, 0, 0,
        0, 0, 0, 0, 1, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
      ]
    }
  }
}
</script>

<style lang="scss" scoped>
.board-frame {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  margin: auto;
  width: 430px;
  height: 470px;
  background: #333;
  border-top: 3px solid rgba(255, 255, 255, 0.2);
  border-right: 5px solid rgba(0, 0, 0, 0.2);
  border-bottom: 5px solid rgba(0, 0, 0, 0.2);
  border-left: 3px solid rgba(255, 255, 255, 0.2);
  border-radius: 8px;

  .logo {
    color: gold;
    text-align: center;
  }

  .rot {
    transform: scale(-1,-1);
  }

  .board {
    display: flex;
    flex-direction: column;
    margin: auto;
    width: 408px;
    height: 408px;
    background: darkgreen;
    border-left: 1px solid #000;
    border-top: 1px solid #000;

    .row {
      display: flex;
    }

    .cell {
      display: flex;
      justify-content: center;
      align-items: center;
      border-bottom: 1px solid #000;
      border-right: 1px solid #000;
      width: 50px;
      height: 50px;

      .stone {
        border-radius: 50%;
        width: 85%;
        height: 85%;
      }

      .black {
        background: #333;
      }

      .white {
        background: #eee;
      }
    }
  }
}
</style>

CSSでオセロ盤っぽくした?✨
コメント 2020-11-23 143746.png

あとは、なんらかのイベントと紐付けて、 blackStones whiteStones の値が変わったら盤面を差分レンダリングする感じですね❗️?

おしまい?

※気が向いたらPHP版のビットボード実装の記事書きます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む