Dell Visor (Windows MR)



先日、Vive Proが発売されましたが、Dell Visorを購入しました。

6年くらいまえに、ソニーのヘッドマウントディスプレイHMZ-T1を購入して、3回しか被らなかった経験から、HMDの購入には慎重になっていて、いろいろ考えていくうちに、Windows MRに絞られました。

Windows MR
・パネルはIPS 1440x1440が2枚(2880x1440)
・PCの性能により、視野角が水平95度、105度と変化する
・少しだけお安い

SteamVRのパフォーマンステストはこんな感じ。


外部センサーが無いので、繋ぐだけで設定は終わってしまうのですが、MRコントローラーの設定でだいぶ時間がかかりました。



2つのMRコントローラーをBluetoothでペアリングしてもMixed Realityポータルで「追跡できなくなりました」と表示されたり、そもそもMRコントローラーをMRポータルで認識してくれず、VR空間にもコントローラーが表示されない状態。Bluetoothデバイスの解除とペアリングを何回か繰り返しているうちに、2つとも認識されるようになりました。

ヘッドセットの装着感は良好で、眼鏡をしていても圧迫感は無いのですが、バイザーを跳ね上げるときに、眼鏡も一緒に跳ね上がってしまうので、バイザーの上げ下げの時にヘッドセットを少しずらす必要があります。

近いうちにマルチモニタをやめて4Kモニタ1台だけにするつもりなんですが、仕事でウィンドウをたくさん開いて作業しなければならないときに、Steamで購入したVirtual Desktop(VR HMDにデスクトップ環境を表示するアプリ)で作業できないかなーと思っております。

Craft CMSのcraft.entries.searchは安易に使ってはいけない話

以前のエントリーで、タグ検索は
$criteria          = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = "sampleSection";
$criteria->search = "sampleTags:test";
$entries = $criteria->find();

こう書くよ、と記載したのですが、エントリー数が数万〜数十万になると、とっても遅いのでした。

searchを使った場合、craft_searchindexテーブルが参照されるのですが、検索対象のIDが列挙されるので
SELECT * FROM craft_searchindex WHERE elementId IN (1,2,3,4,5,6,7,8 ...)

のように、WHERE IN句に、数万のIDが列挙されたりします。結果、生成されたSQLをMySQL Workbenchに貼り付けると、アプリが落ちるほど巨大なクエリーになります。

なので、正しいクエリー方法は以下のようになります。
$criteria = craft()->elements->getCriteria(ElementType::Tag);
$criteria->groupId = craft()->tags->getTagGroupByHandle('sampleGroup')->id;
$criteria->title = 'test';
$tags = $criteria->find();

$criteria = craft()->elements->getCriteria(ElementType::Entry);
$criteria->section = "sampleSection";
$criteria->relatedTo = ['targetElement' => $tags, 'field' => 'sampleTags'],
$entries = $criteria->find();

relatedToを使用した場合、craft_relationsテーブルを参照した割と普通なクエリーになります。

エントリー数が20万ほどある状況だと、searchを使ったタグ検索は4秒、relatedToを使ったタグ検索は0.1秒以下でした。なので、searchはエントリー総数が少ないセクションに限定して使わないとダメそうです。

【追記】
Twigで記述する場合に
{% set entries = craft.entries.section('sampleSection').relatedTo(tags) %}

のような書き方が普通みたいな感じになっていますが、エントリー数が多い場合は、これも遅いです。
fieldを指定したほうが、場合によっては数百倍速いので、ちゃんと指定しましょう。
{% set entries = craft.entries.section('sampleSection').relatedTo({'targetElement':tags, 'field':'sampleTags'}) %}

最近プレイしたゲーム(1〜3月)


Far Cry 5


リリースされたばかりで、少ししかプレイしていませんが、今作のプレイヤーは警官で、カルト教団と対峙するストーリーのようです。

モンスターハンター:ワールド


現在、ハンターランク95。武器は弓とランスのみ。今作からR2でタメ動作に入るようになったので、移動しながらカメラ操作するためにモンハン持ちをしなくていいという、極々普通のことに感動します。

Kenshi


RPG+RTS。アーリーアクセスな事もあり、チュートリアルがわかりにくかったりして、最初は何をしていいのか、まったくわかりません。そんな中、やりたいことを見つけても、今度は操作方法がわからないという…。Wikiや動画を見て、基本的な操作方法がわかると楽しいです。

RimWorld


コロニーシム。見た目がPrison Architectに似ているので、同じ開発元かと思ったら全然違いました(作者はPAのアーティストと繋がりがあるようです)。プレイヤーは入植者のタスクとシフトを組んで、ただ眺めているだけのゲームですが、ハマる人はかなりハマると思います。放火魔は即追放です。

Surviving Mars


コロニーシム。開発元はトロピコシリーズで有名。ドローンを使って火星に入植する準備をするところから始まり、ドームを建設して人間を招致するのが目標。

施設がすぐ壊れる>修理で資材を消費・枯渇>資材の生産には人間が必要>生産量を上げるには入植者を増やすしか無い>増えた人口を支えるために更に大量の施設が必要>最初に戻る

という修理ゲーなので、負のスパイラルの中、なんとかマイルストーンを達成していくゲームです。需要と供給のバランスが取れて安定することなんて無いです。人間の居ない火星は平和なんです。

Besiege


破壊マシンシム? 与えられたお題(破壊活動)を達成するためだけの専用マシンを建設して、ターゲットをぶっ壊すとステージクリアというパズルゲーム。割とちゃんとした物理演算があるので、法則を無視したマシンは機能しないし、汎用マシンを1つ作ったら終わりではなく、いくつかのベースマシンを設計しておいて、各ステージ毎に改良しないとクリアできなかったりして楽しいです。

Undertale


名作と言われているUndertaleですが、ずっときっかけが無くて、ようやくプレイ。Steamのレビューで「レビューを読んでる暇があるならプレイしろ」みたいなコメントがあったんですが、そのとおりだなと思います。


BGMもかなり良くて、中でもLast Goodbyeがお気に入り。

Craft CMS プラグインの作法(Matrixフィールド内にSuperTableフィールドがある場合の新規ブロック追加)

番外編。

Matrixフィールド内にSuperTableフィールドをレイアウトしている場合、プラグインから新規ブロックを追加するのが、結構わかりにくいです。

'sampleMatrix'フィールドに、'sampleBlock'というブロックがあり、そのブロックに'superTableField'というSuperTableフィールドがあるとします。
$matrix["new1"] = [
'type' => 'sampleBlock',
'enabled' => true,
'fields' => [
'textField' => 'abcdefg',
'numField' => 123456,
'superTableField' => [
'new1' => [
'type' => 0,
'enabled' => true,
'fields' => [
'columnOne' => 'sample one',
'columnTwo' => 'sample two',
],
],
],
],
];
$entry->setContentFromPost(array('sampleMatrix' => $matrix));
$success = craft()->entries->saveEntry($entry);
本当は、こう書きたいところですが、これではエラーになります…。
問題は、ブロック内のsuperTableFieldのtypeに設定するブロックタイプIDが決定していない点にあります。

ということで、ブロックタイプIDを取得するメソッドを作ってみます。
public function getSuperTableFieldId($ownerId, $matrixFieldHandle, $blockHandle, $tableFieldName) {
$criteria = craft()->elements->getCriteria(ElementType::MatrixBlock);
$criteria->ownerId = (int)$ownerId;
$criteria->type = $blockHandle;
$criteria->fieldId = (int)craft()->fields->getFieldByHandle($matrixFieldHandle)->id;
$blocks = $criteria->find();
if (!empty($blocks)) {
$blockTypes = craft()->superTable->getBlockTypesByFieldId($blocks[0]->$tableFieldName->fieldId);
if (!empty($blockTypes)) {
return $blockTypes[0]->id;
}
}
return false;
}
ただし、このメソッドはentryにmatrixブロックが1つ以上、存在する状態でないと機能しません。

以上を踏まえて、作成したメソッドを利用して新規ブロック追加をしてみます。
// 一旦superTableFieldは空にしておく
$matrix["new1"] = [
'type' => 'sampleBlock',
'enabled' => true,
'fields' => [
'textField' => 'abcdefg',
'numField' => 123456,
'superTableField' => [],
],
],
];

$entry->setContentFromPost(array('sampleMatrix' => $matrix));
// ブロックを追加するために、一旦保存
$success = craft()->entries->saveEntry($entry);

// superTableFieldを追加
$table["new1"] = [
'type' => $this->getSuperTableFieldId($entry->id, 'sampleMatrix', 'sampleBlock', 'superTableField'),
'enabled' => true,
'fields' => [
'columnOne' => 'sample one',
'columnTwo' => 'sample two',
],
];
$matrix['new1']['fields'][] = $table;

$entry->setContentFromPost(array('sampleMatrix' => $matrix));
$success = craft()->entries->saveEntry($entry);

一旦saveEntry()を実行してブロックを保存していますが、ブロックだけ更新するような場合は、MatrixServiceのsaveBlock()でも良いと思います。ただ、エントリーの他のフィールドも一緒に更新する場合は、saveEntry()でまとめて保存してしまったほうが良い気もするので、お好みで。

Craft CMS プラグインの作法(メール関連)

Craft CMSの設定ページでメールテンプレートを編集できますが、そのテンプレートを追加したい場合の方法。

1)プラグイン本体に、registerEmailMessages()を書く。配列に記述するのはテンプレートのキー名。
public function registerEmailMessages() {
return array(
'template_one',
'template_two',
);
}


2)プラグインのtranslationsフォルダにja.phpやen.phpを作成して、各言語毎のデフォルトテンプレートを定義する。
return array(
'template_one_heading' => 'hogehogeされた時',
'template_one_subject' => 'メール件名',
'template_one_body' => "Dear {{ user.username }}¥n¥n" . "本文.¥n¥n",
'template_two_heading' => 'hogehogeされた時',
'template_two_subject' => 'メール件名',
'template_two_body' => "Hello, {{ user.username }}¥n¥n" . "本文.¥n¥n",
);
※Craft CMSの設定ページでテンプレートを編集すると、データベースに保存されます。

3)サービス等から、テンプレート名をキーにして、送信する。配列で渡すのはテンプレートで使用するデータ。
craft()->email->sendEmailByKey($user, 'template_one', array(
'user' => $user,
'entry' => $entry,
));