takumifukasawa’s blog

WebGL, Shader, Unity, UE4

【Javascript】ビット演算で2の累乗判定する関数

2の累乗判定をするときに、ビット演算だとかなりシンプルになることを知ったのでそのメモになります。

こちらがその関数です。

const isPowerOfTwo = x => (x & (x - 1)) === 0

原理は以下のようなものです。

数値が2の累乗の場合は必ず最上位ビットのみが1になります。 その数値に1を引くとすべてのビットが反転するので、元の数値-1をした数値論理積 & を使うと 0 になる性質を利用しています。

2 ... (10 & 01) === 0 => 00 === 0 => true
3 ... (11 & 10) === 0 => 10 === 0 => false
4 ... (100 & 011) === 0 => 000 === 0 => true
5 ... (101 & 100) === 0 => 100 === 0 => false
8 ... (1000 & 0111) === 0 => 0000 === 0 => true

【vscode】MacのUnity開発でC#の補完が動かなくなったので対処

MacvscodeでUnity C#の補完が効かなくなったので原因を探りました。

起こっていたこと

環境は、 mac OSbig sur, unity は 2019.4.25f です。

vscode の Omnisharp Log を見てみると、大きく2つのエラーが発生していました。

  • Could not load file or assembly 'Microsoft.CodeAnalysis.Workspaces ...

  • OmniSharp server load timed out. Use the 'omnisharp.projectLoadTimeout' setting to override the default delay (one minute).

どうやら読み込めていないモジュールがあり、読み込みのタイムアウトが起こっているようです。

解決

まず2つ目のエラーに着目して、 omnisharp.projectLoadTimeout の時間をデフォルトの60秒よりも長い設定にしても変わらずでした。

最終的に、vscode の拡張の C# for Visual Studio Code のバージョンを最新の 1.23.12 から 1.23.11 に戻すことで直りました。

f:id:takumifukasawa:20210618235857p:plain

必要な assembly がなかった関係でロードのタイムアウトが起こっていたようです。

【Unity】Timelineで枠外に隠れたカーブの全体を表示するショートカット

現象

Timeline の graph view で key の位置やイージングの調整をしたいとき、下の画像のようにカーブ全体が表示されないという現象が度々発生して困ることが多くありました。

f:id:takumifukasawa:20210309222434p:plain

[Edit] -> [Shortcuts...] を覗いてみても、カーブ全体を表示するようなショートカットはありませんでした。

f:id:takumifukasawa:20210309222458p:plain

解決

F キーを押すことでカーブの全体表示がされるようになります。

FキーはScene View などで特定のオブジェクトにフォーカスする用途で頻繁に使いますが、Timeline のカーブにも同様の処理がされるようです。

f:id:takumifukasawa:20210309222509p:plain

Timeline に限らず、VFX Graph などでも特定のノード・ブロックにフォーカスしたいときはFキーを使うので、どのTabかを問わず「何かが隠れている or 小さく表示されている」->「全体表示する or フォーカスする」という挙動をさせたいときにはひとまずFキーを押してみるのは有用かもしれません。

【Node.js】画像群を一括圧縮するスクリプト

画像圧縮をする際、jpegminiやimagealphaなどのアプリを使ったりPCにインストールしたcli経由で行うことが多かったのですが、これらの方法だと画像圧縮の方法がプロジェクトの各人に依存してしまうという問題があります。 そこで、画像圧縮ツールもnpmで管理する形だと人に依存しない & スクリプトを使い回すことができるなと思ったので、nodeスクリプトを作成してみました。

ディレクトリ指定をするとそのディレクトリ以下を再帰的に、単一ファイル指定だとそのファイルのみを圧縮するようにしました。pngとjpgに対応しています。

圧縮には imagemin を用いました。ファイルにもよりますが、100近いファイルを圧縮したところ全体で3分の1ぐらいまでに減りました。

github.com

サービスがクローズしたjsdoitのコードをgithub-pagesに移行するまで

1年半ほど前にクローズになった、jsdoitというソースコード共有サービスがあります。

サービスが稼働している時は500以上のコードを上げていて、サービスクローズ前に全てのコードをローカルに落として個人のgoogle driveに保管していたのですが、パッと見返したいコードなどがあるため、git管理をしてgithub-pagesで見られるように移行しました。

takumifukasawa.github.io

f:id:takumifukasawa:20210304194024p:plain

ソースコードのOGP対応もさせてみました。

takumifukasawa.github.io

いざ作業に取り掛かると、知らなかったツール・使ってみたいと思っていたツールに触れることができたのでよい機会でした。

せっかくなので、移行までの簡単なフローと、便利だと思ったツールを書いていこうと思います。

jsdoitのファイル群

jsdoitからソースをダウンロードするとhtml,js,cssのファイルが格納されています。 ただ、画像や動画、音声などのアセットは含まれていませんでした。

https://github.com/takumifukasawa/jsdoitArchives/tree/master/src/archives/%5B2016.2.4%5D%20%E3%82%B5%E3%82%AB%E3%83%8A%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%80%8C%E5%A4%9C%E3%81%AE%E8%B8%8A%E3%82%8A%E5%AD%90%E3%80%8D%E3%82%AB%E3%83%90%E3%83%BC%E9%A2%A8

移行フロー

大まかな流れはこちらです。全てnodeのスクリプトを書いています。

https://github.com/takumifukasawa/jsdoitArchives/tree/master/batches

1. ソースコードのパース・変換
  - 画像など切れているリンクの置き換え
  - opg用のmetaタグ挿入
2. サムネイル生成
  - アーカイブ一覧ページのhtml用の画像(465x465)
  - ogp画像(1200x630)
3. アーカイブ一覧ページのhtml生成

使用ツール・サービス

unpkg

リンクの置き換えに関して、jsdoitのサーバーにアップされていたライブラリのリンクは全て置き換えていく必要があるので、 可能な限り洗い出し、正規表現で検知・置き換えをしていきました。

ライブラリに関しては、動くデモを作るために可能な限りコードを書いた当時のバージョンを使っていきたい意図がありました。 主要ライブラリだと何かしらのcdnに上がっているのですが、ライブラリに依存するライブラリは見つからない場合があります。 threejsのOrbitControlsはまさにそのパターンです。バージョンが異なるとエラーが出て動かない場合があります。

unpkgはnpmのバージョンごとにコンテンツを配信してくれているサービスです。 threejsや関連モジュールはunpkgを参照するようにリンクを変換し、各ライブラリのバージョンを揃えることができました。

unpkg.com

node-html-parser

ダウンロードしたhtmlの中にはogp向けのmetaタグは入っていなかったので、ogp用のmetaタグは全て新規追加していく必要がありました。

htmlそのものはテキストファイルなので、htmlファイルを読み込んでもdocument.appendChildなどのnodeベースで要素を追加していくことができないため、どう変換するのがよいか考えながらツールを探した結果こちらが便利でした。

www.npmjs.com

htmlの中身をパースしてquerySelectorを用い要素にアクセスすることができるインターフェースになっている & 文字列への変換メソッドが生えている点が便利で、以下のような形でogp用のmetaタグを追加していきました。

import { parse } from 'node-html-parser';

...

// htmlファイルを読み込む
const htmlContent = fs.readFileSync( ... );

// htmlファイルの内容をパースし、head要素を取得
const root = parse(htmlContent);
const head = root.querySelector("head");

// headの中身を文字列にしてキャッシュ
const headText = head.toString();

// ogp向けのmetaを後続に追加
headText += "<meta ...  />";
...

// headの中身を置き換え
head.set_content(haadText);

// htmlファイルを書き込み
fs.writeFileSync( ... );

puppeteer

画像キャプチャはheadlessブラウザのpuppeteerを使いました。

https://pptr.dev/

公式のサンプルを引用させていただくと以下のようなコードのみで画像キャプチャをしてくれます。 headlessブラウザを使ったことがなかったのでよい機会になりました。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'example.png' });

  await browser.close();
})();

余談

playwrightも流行っているみたいです。

playwright.dev


9割方は、ひとまず動くところまで機械的に変換することができました。 残りは手動になりそうですが、見つけ次第整えていきたいと思います。

【Javascript】Promiseを直列実行

内容は短めですが、よく使うのでそのメモになります。

複雑なアニメーションなど、とあるPromiseがresolveになったら違うPromiseの状態の監視を始めたい場合が頻繁にあります。

thenで繋いでもよいのですが、Promiseが増えるごとにthenの記述も合わせて増えていくため少し冗長です。

そこで以下のような関数を用意し、Promiseを返す関数の配列をreduceして繋いであげることで、記述量を減らすようにしました。

rejectな場合への対応がいらない場合に、順番に実行していきたい時に多用しています。

async function execPromiseInSequence (arr) {
  return arr.reduce(
    (chained, func) => chained.then(func),
    Promise.resolve()
  );
};

// ex
async function main() {
  // 上から順に実行される
  await execPromiseInSequence([
    async () => { ... },
    async () => { ... }
    ...
  ]);
}

【firebase】functions x hosting 環境でBasic認証をかけている時のBasic認証の挙動確認

firebase functions と firebase hosting を併用しているプロジェクトで Basic認証をかけている時の Basic認証の対象の挙動が分からなかったので確認しました。

firebase で Basic認証をかける

firebase で Basic 認証をかけるためには functions を使用する必要があります。

このようなフォルダ構成の時、

- functions // functionsにアップロードされる対象
  - index.js
  - static
    - index.html

- public // hostingにアップロードされる対象
  - sample.png

- firebase.json

以下のようなfunctionsでBasic認証をかけることができます。

import * as functions from "firebase-functions";
import express from "express";
import path from "path";

const basicAuth = require("basic-auth-connect");

//---------------------------------------------------------------------------------------------
// constants
//---------------------------------------------------------------------------------------------

const USE_BASIC_AUTH = true;

const AUTH_USER = "user";
const AUTH_PASSWORD = "pass";

//---------------------------------------------------------------------------------------------
// main
//---------------------------------------------------------------------------------------------

const server = express();

// basic認証
if (USE_BASIC_AUTH) {
  server.use(basicAuth(AUTH_USER, AUTH_PASSWORD));
}

server.use(express.static(path.join(`${__dirname}/static/`)));

export const hostingApp = functions.https.onRequest(server);

firebase.jsonはこのようになります。

{
  "functions": {
    "source": "./functions"
  },
  "hosting": {
    "public": "./public",
    "rewrites": [
      {
        "source": "**",
        "function": "hostingApp"
      }
    ],

    ...

  }
}

hosting にアップされたファイルにはBasic認証がかからない

hosting の rewrite ルールの優先度はドキュメントにこのように書いてあります。

https://firebase.google.com/docs/hosting/functions?hl=ja#use_a_web_framework

注: public ディレクトリ内の静的ファイルは rewrite よりも優先されるため、静的ファイルは Cloud Functions エンドポイントと一緒に提供されます。

つまり、hosting は functions でかけているBasic認証の対象にならないので、先ほどのフォルダ構成とBasic認証がかかるかかからないかの関係性はこのようになります。

- functions // functionsにアップロードされる対象
  - index.js
  - static
    - index.html <- Basic認証がかかる

- public // hostingの対象
  - sample.png <- Basic認証がかからない

逆にいうと、基本的にはBasic認証をかけたいが、とあるファイルだけBasic認証の対象から外したい、という場合には rewrite のルールを書かずに hostingにアップロードするのが良いのかもしれません。