2021年12月22日水曜日

memo アプリの紹介

memo アプリの紹介

適当に作ったアプリがいい感じになったので紹介します

URL

現在は Heroku にデプロイしています
個人のサーバやローカルマシンにデプロイして使うこともできます

使い方

好きなテキストや手書きの画像を保存できます
アカウントを作成しなくても利用できます
他の人に見られたくないメモはアカウントを作成しログインしてから保存します

テキストは Home (/) で使えます
チェックボックスをオンにすると複数行のテキストも入力できます

Board (/image) に移動すると手書きのボードが出てきます
マウスでペイントのように使えます
画像も好きなように保存できます
書いた画像を保存しないでダウンロードするだけもできます

機能一覧

  • 好きなテキスト保存/取得
  • 好きな手書き画像の保存/取得
  • アカウント作成/削除
  • アカウントログイン/ログアウト
  • レスポンシブデザイン対応

構成

  • 言語・・・go
  • フレームワーク・・・beego v2
  • データストア・・・Redis

TODO

  • アカウント作成時のセキュリティの考慮
    • パスワードのポリシールールを設ける
    • 2auth など
    • メジャーサービスの OAuth2 対応
  • API 化
    • 現在は WebUI のみ提供
  • 独自ドメインでの運用
    • Heroku or VPS
  • beego v2 の脆弱性対応

2021年12月16日木曜日

2021 できたこと・できなかったこと

2021 できたこと・できなかったこと

去年に引き続き今年もやりました

できたこと

時系列

2月に何もしてないがそれ以外は何かしらアウトプットができたのがよかった
既存アプリのバグフィックスやエンハンスはプラットフォーム側の更新に合わせて継続的にやっていきたいがかなり面倒

資格はこれで 11 個になった

新しい技術への挑戦はあったがやや少ない印象
また技術以外への新しいことや趣味への挑戦がなかったのが残念

その他

  • ブログ執筆数: 269 (12/16 時点)
  • Podcast エピソード数: 12
  • 断捨離

これらは去年から引き続きがんばれたかなと思う
Podcast は一ヶ月に一回ペースを守れた

今年はいろいろなものを断捨離できたのがすごいよかった
身軽になることでいろいろな管理や運用から開放される感じが良い

できなかったこと

  • Youtube Live (去年に引き続き)
  • 新規アプリ開発

Youtube Live は永遠の課題なのかもしれない
アプリはアイデアがない

来年の豊富

  • 新規アプリ開発
  • モチベーションを維持する
  • 引き続き断捨離 and アウトプット

今年はよくアウトプットできたと思う
記事、動画、Podcast をうまく使い分けてアウトプットできた

Unity or Unreal で 3D ゲームを作りたいがこれ以上管理するアプリやサービスが増えると破綻するので悩み中

資格は来年も取得予定

2021年10月1日金曜日

IoT Gateway for BLE meets ESP32

IoT Gateway for BLE meets ESP32

Video

Environments

  • ESP32 devkitc v4
  • IoT Gateway for BLE v1.17
  • CloudMQTT
  • Chrome

Sketch

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 255;

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

void onButton() {
  // sedning integer 255 via notify
  pCharacteristic->setValue((uint8_t*)&value, 4);
  pCharacteristic->notify();
}

void setup() {
  Serial.begin(115200);
  pinMode(0, INPUT_PULLUP);

  // Create the BLE Device
  BLEDevice::init("ESP32");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );
  pCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
  BLEDevice::startAdvertising();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {
  // notify value no button pushed
  if (deviceConnected) {
    static uint8_t lastPinState = 1;
    uint8_t pinState = digitalRead(0);
    if (!pinState && lastPinState) {
      onButton();
    }
    lastPinState = pinState;
  }
  // disconnecting
  if (!deviceConnected && oldDeviceConnected) {
    delay(500); // give the bluetooth stack the chance to get things ready
    pServer->startAdvertising(); // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected) {
    // do stuff here on connecting
    oldDeviceConnected = deviceConnected;
  }
}

Websocket

<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
  <script type="text/javascript">
    var wsbroker = "m11.cloudmqtt.com";
    var wsport = 30707
    var username = "xxxxxxx"
    var password = "xxxxxxx"

    var client = new Paho.MQTT.Client(wsbroker, wsport, "myclientid_" + parseInt(Math.random() * 100, 10));
    client.onConnectionLost = function (responseObject) {
      console.log("connection lost: " + responseObject.errorMessage);
    };
    client.onMessageArrived = function (message) {
      console.log(message.destinationName, ' -- ', message.payloadString);
      var counter = $("#counter");
      var num = parseInt(counter.text());
      num++;
      counter.text(num);
    };
    var options = {
      useSSL: true,
      timeout: 3,
      onSuccess: function () {
        console.log("MQTT connected");
        client.subscribe('#', {qos: 1});
      },
      onFailure: function (message) {
        console.log("MQTT disconnected: " + message.errorMessage);
      }
    };
    options.userName = username;
    options.password = password;
    function init() {
      client.connect(options);
    }
    </script>
    <style type="text/css">
      #counter {
        color: #40e0d0
      }
      .container {
        width: 100%;
        margin: 0 auto;
      }
      h1 {
        text-align: center;
      }
    </style>
  </head>
  <body onload="init();">
    <div class="container">
      <header>
        <h1 id="msg">Pushed button count: <span id="counter">0</span></h1>
      </header>
    </div>
  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/FitText.js/1.2.0/jquery.fittext.min.js" integrity="sha512-e2WVdoOGqKU97DHH6tYamn+eAwLDpyHKqPy4uSv0aGlwDXZKGwyS27sfiIUT8gpZ88/Lr4UZpbRt93QkGRgpug==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    $("#msg").fitText(1.2);
  </script>
</html>

2021年9月23日木曜日

BluetoothLE デバイスを作る

BluetoothLE デバイスを作る

自分のアプリ用の BLE デバイスが無いということに気がついたので作りました
BLE モジュールは手元にあった BLESerial2 を使いました

環境

  • macOS 11.6
  • arduino IDE 1.8.16
  • Arduino Pro mini
  • BLESerial2

コンセプト

BLE デバイスと接続だけできてもダメなので Notify でデータを飛ばせるようにします

データを飛ばすには Serial.write するだけなのでタクトスイッチを押したらシリアル通信するような構成にします

送信するデータは何でも OK ですが今回はテストなので「0」を飛ばします

あとは通電や動作確認用に LED を設置します

とりあえずプロトタイプを作成する

ブレッドボード上にそれっぽく配置していきます

写真の上で BLESerial2 の制御をしています
Arduino の TX ピント BLESerial2 の TX ピンをつないでいます

写真の下でタクトスイッチと LED の制御をしています
適当なピンを使って digitalRead と digitalWrite をしているだけです

スケッチ

Arduino Pro mini に書き込むスケッチは以下の通りです
PULLUP なのでタクトスイッチが押された場合にはピンの情報は「0」になります
「0」になったら LED を光らせるために degitalWrite しています
また Serial.write もし BLESerial2 に信号を送信しています

連打できないように delay を 1.5 秒入れています

const int SWITCH_PIN = 7;
const int LED_PIN = 4;

void setup(){
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  int value;
  value = digitalRead(SWITCH_PIN);
  if (value == 0) {
    digitalWrite(LED_PIN, HIGH);
    Serial.write(0);
    delay(1000);
  }
  digitalWrite(LED_PIN, LOW);
  delay(500);
}

ユニバーサル基板上に実装する

ブレッドボードだとかっこ悪いのでユニバーサル基板にはんだして回路を実装していきます

かなり適当な回路なので保護回路などはありません
みなさんはマイコンやモジュールを破壊しないようにちゃんと保護回路を組んであげてください

で、できたのが以下です



さすがに Arduino と BLESerial2 は他でも使う可能性があるのでピンヘッダをつけて取り外せるようにしています

Arduino Pro mini の部分は面倒だったのでそのまま使っているので電源の供給は FTDI のシリアル変換器を使って 5V をコンセントなどから取ります

たぶんこの部分もシリアルピンがあるマイコンで代用できるので ATTiny などを使えばさらに小型化できると思います

モジュールの配置とジャンパ線の配線がキレイにできるようになると上級者な気がします
自分は下手くそなので妥協して絶縁テープでまかればジャンパ線を使ってずるしています

動作確認

自分の Bluetooth アプリ「IoT Gateway for BLE」をスマホにインストールしてためします

チャンネルは CloudMQTT を使っています
BLE デバイスを登録してチャンネルと紐付けて Gateway を作成しスタートすれば OK です

今回は写真だけです
SensorTag を使ったデモ動画を過去にあげているので実際に動いている様子はそちらを御覧ください

費用

たぶん 3,500 円くらいです
だいぶ昔に購入したのであまり記憶にないです

  • BLESerial2・・・2,000円
  • Arduino Pro mini・・・1,000円
  • その他 (抵抗やジャンパ、基盤など)・・・500円

最近だとラズパイ4や ESP32 などすでに開発ボードに BLE モジュールが搭載されているケースがあるのでそれを購入したほうが回路も作成する必要もないし安く済むと思うのでそちらを購入したほうがいいかなと思います

作成時間

プロトタイプからユニバーサル基板上への実装までで 4 時間くらいかかりました
ブレッドボードで 1時間30分でユニバーサル基板上への実装で 2時間30分くらいの内訳です

久しぶりに電子工作した割にはスムーズにできたかなと思います

最後に

はんだしているときは無心になれるのでオススメです

メモ

  • BLESerial2 は 3.3v 動作なのだがなぜか分圧した 3.3v だと動作しなかったので Arduion の VCC (5V) から取っている
    • もしかするとそのうち BLESerial2 が壊れるかもしれない

2021年8月16日月曜日

ワクチン注射打ったらぶっ倒れて救急車で運ばれた話

ワクチン注射打ったらぶっ倒れて救急車で運ばれた話

再発防止として残しておきます
油断はよくないということを学びました
そして無事です
そしてお世話になったお医者様、看護師の皆様本当にありがとうございます

結論

結論としてはワクチンの副作用で倒れたわけではなく「迷走神経反射」で倒れました

おそらくコロナワクチンで倒れたということもあり念の為しっかり診察や処置をしていただいたんだと思います

時系列

ざっくり以下のような感じです
4 箇所注射してます

  • 11:45 ワクチン接種
  • 11:50 倒れる (一時意識不明、大げさに書いていますが一瞬意識を失ったようです)
  • 12:00 緊急処置?(点滴とか血圧上げる注射とか、ここからずっとベット)
  • 12:30 救急隊到着
  • 12:35 救急車で大きな病院に運ばれる
  • 13:00 大きい病院到着
  • 13:10 診察と治療開始 (採血、心電図、脳CT)
  • 14:00 診察結果を聞く
  • 14:05 ひたすら休む (血圧は 10 分起きくらいに測りました)
  • 15:30 トイレ
  • 16:00 復活、精算、帰宅

背景

もともと注射が嫌いで過去にも採血で倒れたことがありました
注射するときは横になってしてもらうのですが今回は調子に乗って座ったまま注射したのが最大の原因かなと思います(これが今回の教訓でもあり今後はちゃんと横になるようにしたいと思います)

迷走神経反射で救急車で運ばれるのか?と思う方もいるかなと思いますが今回は「コロナワクチン」だったのでアナフィラキシーの可能性もあり、もしワクチン後に倒れた場合は救急で大きな病院で詳細に診察してもらうのが決まりのようです
というのもあり今回は少し大きな話になってしまいました

教訓

先にも述べましたがまずは横になって注射するのを肝に銘じたいと思います

本当にいろいろな方に迷惑を掛けたのでそうしないためにも万全の状態で注射します

掛かったお金

結構お高くつきましたが生きていることと教訓を学べと考えれば安いもんかなと思います

  • ワクチン接種・・・0円
  • クリニックの治療代・・・2,330円
  • 病院での治療代・・・10,750円
  • その他 (タクシー代など)・・・4,000円

最後に

本当は 2 回目も接種する予定だったのですがお医者様から「今回はやめておいたほうがいいかも」と言われたので今回の2回目の接種やめておこうかなと思います

そして明日以降副反応で更に辛くなるのかと思うと泣けます

追記: 副反応について

翌日以降の副反応についてメモしておきます

  • 1日目・・・倦怠感ほぼなし、熱:36.5、関節の痛みなし、注射した肩の部分が痛む
  • 2日目・・・倦怠感なし、熱:36.1、関節の痛みなし、注射した肩の部分の痛みなし
  • 3日目・・・全開

2021年8月4日水曜日

氷をすくうスコップの作り方

氷をすくうスコップの作り方

モデリングからプリント方法まで動画に撮ってみました
自分はだいたいいつもこんな感じで 3D プリンタを使っています
ポイントなど詳しく紹介します

環境

  • macOS 11.5
  • blender 2.93.1
  • QIDI-Print 5.3.2
  • X-Smart

モデリング

3D プリンタでプリントするためには 3Dモデルデータが必要になります
拡張子でいうと「.stl」や「.obj」ファイルになります

blender はフリーのモデリングツールでゲームなどでも使えるレベルのモデリングが可能です
自分は blender でモデリングしますがモデリング自体が面倒な場合は Thingverse などで好きな stl ファイルを探すのもありです

スコップをモデリングした流れは以下のような感じです

  • 四角のオブジェクトを追加
  • 大きさをスコップの大体の大きさに合わせる
  • 全面を Extrude や Subdivide などを使って作成
    • このとき Vertex や Face が被らないように調整する
  • 位置の調整をして形を整える
  • Inset と Extrude で取っ手の部分を作成
  • 最後に全体を Bevel して角をなくす

という感じです
最終的には以下のようなモデルを作成しました
.stl ファイルでエクスポートすれば完了です

3Dプリンタで出力する場合はこれで OK ですがゲームなどのデータとして使う場合はこれでは不十分で Animation や Texture の設定が更に必要になります

スライシング

スライシングは作成した 3Dモデリングデータをプリンタでプリントできる形式に変換する作業のことを言います

スライスツールにはいろいろあり今回しようした QIDI-Print は QIDI-Tech というプリンタを販売しているメーカが無料で公開しているスライスツールになります
基本的にはプリンタのメーカがプリンタとスライスツールをセットで提供してくれていることが多いです

どんなスライスツールを使っても問題ないですがプリンタによっては使えないオプションなどがあるので注意しましょう (例えばサポートの細かい設定やノズルの速度など)

実は 3Dプリンタでプリントする際にはモデリングよりもこのスライシングの作業のほうが重要かもしれません

うまくサポートを設定できなかったり大きさの調整をミスると思ったとおりにプリントすることができなくなります
個人的に特に大事だと思うのは「ノズルの速度」と「サポートの調整」になります
ノズルの速度は一番下のレイヤーがうまくプリントボードに接着できるかに影響します
ノズル速度が早すぎるとうまくプリントボードに接着せず出来上がった際に反り返りが発生します
サポートの調整はオーバハングに影響します
自分は 75 - 80 度くらいのオーバハングまではサポートを使わないようにしています
なるべくサポートを使わないほうがプリントのミスを防ぐことができますがサポートがないとオーバハングには対応できないので 90 度の部分などは必須になります
特に端の部分のサポートがうまく設定できていないと印刷ミスになったりします
このあたりが 3Dプリンタ職人の腕の見せどころになるかなと思います

問題なければ出力します
最終的には「.gcode」というファイル形式を出力できれば OK です

プリント

あとは出力します
素材は PLA を使っています
だいたい 6 時間くらいかかっています

プリントが始まったら後は失敗しないように願うしかありません
一層目がうまくプリントできていれば基本的には大丈夫なはずです

あとはノズルのつまりやフィラメント切れの可能性がありますがほぼ起きることはないと思います

PLA はノズルから排出されてから冷えることで固まるので風を当てたりカバーを外して通気を良くすることでよりキレイにプリントできるかなと思います

プリントボードから剥がす

X-Smart 付属のプリントボードは曲がるので簡単に剥がすことができます
曲がらない場合や手でうまく剥がせない場合にはヘラなどを使って剥がしましょう

サポートも今回は手で取れました
これくらい大きなサポートであれば簡単に外せますが細かい部分のサポートは本体と密着することがあり外すのが難しくなります
そういう場合はペンチやニッパを使って外しましょう

試す

動画を見てください
それなりに使えていると思います

感想

そんなに難しいモデリングではなかったのですぐにできました
買おうと思えば買えるけどせっかく 3Dプリンタがあったので作ってみました
モデリングの勉強にもなったので良かったかなと思います

取っ手のオーバハングしている部分はあとでトリミングしてある程度キレイにしましたがもう少し丁寧にサポートを設定してもよかったかなと思います
オーバハング問題は FDM 形式の 3Dプリンタでは正直どうしようもないかなと思います

取っ手の部分に穴を開けてフックで掛けられるようにしたのは Good ポイントかなと思います
こんな感じで好きな形にできるのは自作の良いところかもしれません

2021年7月6日火曜日

飲酒量をロギングしてみた

飲酒量をロギングしてみた

1 年間ほどアルコールの飲酒量をロギングしてみたので紹介します
思った以上に飲んでいることがわかりました

サマリ

まずはデータのサマリを紹介します

  • 記録日数: 405日
  • 飲酒した日数: 234日
  • 飲酒しなかった日数: 171日
  • 一日の最大飲料: 2800ml
  • 一日の平均飲料: 563ml

平均すると毎日 500ml 缶を 1 本飲んでいるという結果になりました

作ったもの

一応可視化できるようにしました

@kakakikikeke - 飲酒量のロギング
https://kakakikikeke.com/alcohol

仕組みは後述しますが縦軸が飲酒量 (ml) で横軸が日にちになります
なので一日あたりどらくらい飲酒したのかのグラフになります

ちょっと仕組み紹介

実はデータは GoogleSpreadSheet で全部管理していました

ここでグラフにしてもよかったのですがせっかくなので Web で見れるようにしてみました

使用しているツールは Charts.js になります
v3 になってかなりいろいろなグラフが描画できるのとアニメーションも豊富だったのでこれにしました
あとは jQuery などで DOM 操作しています

SpreadSheet にあるデータは Google Apps Script を使って JavaScript で使えるデータに変換しています
なので定期的に手動でデータを取り込む必要があります
データベースに移行してそっちに貯めるようにしてもいいのですがさすがに面倒なのでやっていません

感想

平日は飲まないようにしているつもりでしたが結構飲んでいるなという印象です
というかグラフにするとほぼ飲んでいるようなグラフになっています

また平日飲まなくても週末に飲みすぎているせいで一日平均すると結構な量になっていました
連休があるところは特に飲んでいた印象です

「コロナのせい」なのかは不明ですが個人的に飲んでる量が増えた感はないです

現状体調の変化や体型の変化はないのですがこのまま続けるといずれガタが来るかなと思っているので健康には気をつけようと思います

今後

せっかくなのでもう少し記録してみようと思います
Web で見れるグラフはリアルタイムではないですがデータが溜まったら定期的にアップデートしていこうと思います

2021年5月10日月曜日

今更ながら DDD について考えてみた

今更ながら DDD について考えてみた

2003 年ごろに提唱された DDD がなぜか今盛り上がっている気がするので今更ながら DDD (Domain Driven Development) について考えてみました
なお原著であるエリック・エヴァンスのドメイン駆動設計は読んでいません
完全に個人的な主観で記載しているので解釈の間違い等はご容赦ください
また DDD のすべてに触れているわけではないのでそれもご容赦ください
文字が多いです

あくまでも「設計手法」であることを忘れてはいけない

いざ実装となるとそのプロダクトの性質や方向性が実装に大きく影響します
それに対して絶対 DDD を適用することは難しいと思っています

設計手法=数ある解決手段の中の一つにしかすぎないと思っています
何でもかんでも DDD を使うのは良くないと思うのでプロダクトのこの部分は適用できそうだとかここは難しいという判断が重要だと思います

DDD は完璧な銀の弾丸ではないかなと思います
もちろんトライすることは良いことだと思います

オブジェクト指向の拡張

に近いかなと個人的には思っています
オブジェクト指向のクラス=ドメインモデルになるケースは結構あるかなと思っています

ただ大きく違うのは粒度と性質かなと思っておりドメインモデルは「値オブジェクト」や「エンティティ」という表現を使っておりオブジェクト指向で言うところのフィールドが Immutable であるかどうかなどを決める必要があります
また振る舞いは「ドメインサービス」として別クラスに書いたりします

オブジェクト指向でもそのようなことはできますが基本的にはフィールドの Settter さえあれば値は変更できます
振る舞いに関しても同じクラスに書くことが多いと思います

また自分がオブジェクトの拡張だと思う理由のもう一つとしては DDD のサンプルをいろいろ調べると大抵のケースでクラスを使っています
当然クラスを使わないで DDD するのは不可能ですがクラスが使える言語は大抵オブジェクト指向な言語で書くことが可能な言語であることが多いのでそう感じているのかもしれないです

オブジェクト指向をしっかり使えていれば実は勝手に DDD っぽく書けているケースがあるかも?

ドメインモデルをどこまで抽出するか

例えば以下のようなコードがあるとします
何らおかしなところはないユーザを管理するクラスです

class User
  def initialize(name, age)
    @name = name
    @age = age
  end

  attr_accessor :name, :age
end

ですが DDD 的には以下のように値オブジェクトとして name と age をドメインモデルとして抽出します

class UserName
  def initialize(value)
    @value = value
  end

  attr_reader :value
end

class UserAge
  def initialize(value)
    @value = value
  end

  attr_reader :value
end

class User
  def initialize(name, age)
    @name = name
    @age = age
  end

  attr_accessor :name, :age
end

UserName や UserAge クラスを新たにドメインモデルとして抽出しています
DDD 的にはこうすることでユーザ名や年齢のバリデーションを各クラスに持たせることがよしとされています (値オブジェクト)

かなり極端な例ですが個人的にはオーバーエンジニアリングな感じがあります
このようにどこまでをドメインモデル (クラス) として抽出するかの判断が非常に難しいと思っています

一応判断基準はあるのですが正直「経験」というか「感覚」が物を言う世界かなと思っています
やりすぎも良くないと思うのでいい塩梅のモデリングレベルが要求されるのでそれだけでもかなりの学習コストがあるのかなと感じます

フレームワークの流儀に従うかどうか

Web アプリなどを開発する場合は基本的には Web フレームワークを使うかなと思います
自分であればよく Sinatra という Ruby 製の軽量フレームワークを使います
また Ruby には有名な Rails というフレームワークもあります

基本的にはどちらも MVC を使って開発することがほぼんどです
そういったフレームワークが提供する機能というか書き方がある程度整っている状態で DDD を採用しようとすると MVC に対して無理やり DDD を適用するようなことが発生します

もちろん適用することは可能です
ルーティングの部分はそのままでモデルの部分だけを DDD っぽく書くなんてことはできるかなと思います
ただ Rails を使う場合には active_record と Model の関係がかなり密接に絡んでおり無理に Model を書き換えて DDD を適用しようとすると大変なことになりそうな気がしています (やったことはないのであくまでも予想です)

フレームワークにはそれなりの流儀というか書き方のルールがあるかなと思っています
個人的にはまずはそれを優先するべきかなと考えています
その上で一部のロジックを DDD にするというのはありなのかなーという気はしていますが、いろいろと設計思想が混同したコードはあまり良い方向にいかないような気もしています

DDD の知識がないとコードの可読性は落ちるかも

単純なことですが DDD にはある程度書き方やルールが決まっているのでそれを知らないでコードを読んでも逆に読みづらいのかなという気がしています

値オブジェクトやエンティティに関しては「なんでこんなに細かく抽象化しているのだろうか」とか感じたりするかもしれません
ドメインサービスに関しては「なんでこれはわざわざ別クラスとして切り出しているのだろう」と感じたりするかもしれません

しかもそのルールを知らないと実際にメンテナンスするときに振る舞いをドメインモデル側に記載したりしてしまうかもしれません

もちろんレビューしたり学習したり回避方法はいくらでもありますが完全に初見でコードをみたときに「あ、これは DDD だな」と気づく人は少ないんじゃないかなと思います

DDD で高メンテナンス性は担保できるのか

ドメインモデルを適切に抽出できていればモデルのクラスを見ただけで確かにコードを理解できるかもしれません

しかしそれだけではそのコードをメンテナンスすることはできません
あくまでもコードを修正してテストしてバグを修正できて初めてメンテナンスできるようになると思います

修正箇所を小さく局所的にすること=高メンテンス性なコードであるとは思います
しかしあくまでも修正できるかどうかはある程度の経験が必要だと思います
ここで言う経験は「コードのビルド」すなわちトライアンドエラーすることであると考えておりそれは CI/CD などで担保するものかなと思っています

要するに DDD だけでメンテナンスしやすいコードにするのは難しいのでちゃんと初心者がトライアンドエラーしやすい環境を作っておくことも重要だと言うことです

言語との相性

冒頭でも記載していますが個人的にはオブジェクト指向の拡張かなと思っています
なのでオブジェクト指向ができる言語とは相性が良いと思っています

もう少しいうとゴリゴリの型付け言語のほうが相性が良いかなと思っています (DDD の紹介書籍などを見るとそう感じると思います、たぶん)

具体的には C# や Java などが相性が良いかなと思っています
もちろん Ruby や Python、Swift などの高級言語でも可能だとは思います
ただ Python などはオブジェクト指向から外れたスクリプトみたいな書き方も簡単に含められるので、そういう便利機能というか DDD っぽくない書き方が含まれていると可読性などは落ちるのかなという気もしています

JavaScript とかでもやっているのをちらほら見ますが、まぁどうなんでしょうか、、?
(nodejs ならできるのかな?できないということはないと思うが)

ドメインモデルのスコープをどうするか

基本的にはクライアントから操作できるのはアプリケーションサービスのみです
しかし言語の仕様や実装上の namespace の問題でドメインモデルにアクセスできることはできてしまいます

代替手段はいろいろとありますが本来は機能として触れないようにするべきです (例えば外部に公開しないドメインモデルには private 修飾子を設定するなど)
しかしどうしてもできないこともあるはずです
そういった場合に「ルール」を作るのですがあくまでもルールでありやろうと思えばドメインモデルを直接使うことができてしまいます

個人的にはこのスコープを問題は大したことではないと思うのですが考える必要がある点に注意が必要です
なるべく Getter を作らないという考えも同じようなことだと思います

インターフェース

主にアプリケーションサービスとリポジトリでインターフェースなどで使うことになると思うのですがインターフェースが言語の機能としてない場合があるのでその場合にどうするか考える必要があるかなと思います

ガッツリ設計するような開発手法にはあっているかも

例えばウォーターフローモデルのような開発には向いているかもしれません
DDD は設計の段階でかなり時間を割くイメージがあり、かっちり決めてから開発することになると思うのでアジャイルやプロトタイピングのように書きながらとなると、それなりの DDD の経験と知識がないと難しいかもしれません

その他要素 (集約、委譲 (DI)、トランザクション、アーキテクチャなど) について

結局考える必要が出てくるので学習しておく必要があります
学習しておかなくても最悪良いのですが知っていたほうがいろいろと便利だし DDD の原則に従うことができます

特にアーキテクチャそれだけかなりの学習コストがあるので大変です

最後に

用法用量を守って正しくお使いください
あとは書籍や Web の紹介を読んだだけだと理解できないので実際に自分で手を動かしてコーティングしてみることをオススメします

用語集

  • ドメインモデル
  • ドメインオブジェクト
    • 値オブジェクト
    • エンティティ
  • ドメインサービス
    • 評価 (Specification)
  • リポジトリ (インタフェース)
    • クエリサービス
  • アプリケーションサービス (インタフェース)
  • 集約 (ルール)
  • アーキテクチャ
    • レイヤード
    • ヘキサゴナル
    • クリーン

2021年5月9日日曜日

ウマ娘プレイメモ&感想

ウマ娘プレイメモ&感想

巷で話題のウマ娘を 3 ヶ月ほどプレイしてみました
Podcast ep45 でも少し話しています

結論

  • よく作り込まれているゲームだと感じた (グラフィックやレースの順位計算など)
  • 単純におもろしろい、ひまつぶしにも良い
  • ガチ勢には勝てない

プレイスタイル

  • 無課金
  • ログインは毎日する
  • 育成やレースは気分で消化する
  • まったり

システムが結構複雑

よく言われるのはパワプロ+ダービースタリオンのようです
育成しながら対人の勝負をする感じです

ゲームシステム的にブラックボックスな部分が多いなと感じました
例えばレースの順位決めやスキルの発動条件などです
理解するまでは結構時間がかかった気がします
理解すればゲームもサクサク進めまれます

またアイテムの数も多く使いこなすのが難しかった印象です

結局ランキングで上位になるために育成するゲーム

RPG ではないのでエンドコンテンツ的なのはなさそうです
超高難易度レースが最終ステージになっておりそれがエンドコンテンツというわけでもなさそうです

あくまでも対人勝負で上位のランキングを目指すのがメインの目的になりそうです

因子ゲー

ゲームの重要な要素に「因子」という要素があります
ここでは詳しく説明しませんがこの因子を作成するのが大変です
しかも強い因子がないと強いウマも育成できないのでこの因子を集めるということが非常に重要な要素になっているなと感じました

自分は SS+ 2面待ちで周回しています

オール B 育成はかなり難しい印象です
自分はメジロマックイーンやシンボリルドルフの場合は根性以外の 4B を目指して育成しました

ちなみに自分が出した青星3 因子は 4/ 体です

1.7% の確率で出現しています

サポートカード

これも育成の重要な要素です
基本はガチャで手に入れます
因子と合わせて強いステータスのウマを育成するには強いサポートカードが必須です
このゲームの良いところは SR のサポートカードでも限界突破させれば優秀なサポートカードになるということです

SSR は 3% でしか手に入れないのでなかなか限界突破できません

金欠になりやすいかも

お金の概念があるのですがこれが結構なくなりがちでした
サポートカードのレベル上げや覚醒レベルの上昇などお金を使う場面が多いです
自分の主な収入源はデイリーレースだけでした
育成周回でもお金稼ぎはできるらしいのですが自分はやりませんでした

レースやライブは一切見なかった

イベントの会話も全部最速スキップしていました
アニメにもなっているのでそれを見てからプレイするともっと楽しめるかもしれません

育成について

育成時に重要だと感じた点を列挙します

  • 因子は可能な限り青因子3を持つ親から継承する
  • サポートカードは無理に SSR ではなく SR で良い
  • 絶好調は維持
  • イベントの内容を覚えることは重要
    • 体力を回復するイベント
    • やる気を上げるイベント
    • 状態を変化させるイベント (練習上手、愛嬌など)
  • 運も絡む
    • やる気を問答無用で下げるイベント
    • 得意率など

保持育成ウマ

SSR は 3 種類しかありません

保持サポートカード

SSR は 16 種類でうち 8 種類は 1 凸以上しています

SR は 27 種類あります
ほぼすべてで 1 凸以上しています

最後に

今度どれくらいまでインフレするのかが楽しみです

2021年4月17日土曜日

スマホ版ロマサガ2プレイメモ

スマホ版ロマサガ2プレイメモ

セール中に 1,100 円で購入したロマサガ2 を今更プレイしたので感想
ゲームの内容や攻略情報は他にいろいろあるのでそういった情報はなし

とりあえず感想

  • ラスボスは決まっているがゲームの進行は自由に決められる
  • 逆に言うとゲームの流れが決まっていないのでドラクエや FF のような一本道の RPG に慣れていると難しく感じるかも
  • レベルを上げて上から殴ればクリアできるゲームじゃない、ちゃんと戦略を練って戦わないといくらレベル上げしても勝てない
  • 術をちゃんと使わないとクリアは難しい
  • 追加コンテンツはあるが当時の SFC の内容がベースなので慣れてしまえば攻略は簡単
  • 強くてニューゲームがあるが普通に負ける
  • クイックタイムはずるい
  • シンボルエンカウントを避けるのが難しい

総プレイ時間

  • 約30 - 40 時間

スマホでのゲーム操作について

  • 十字キーはなくソフトウェア十字キーで操作する
  • 指の感覚だけでキャラクターやメニュー操作するので慣れないとかなりつらい
    • 村人や宝箱を開けるのがつらい
    • シンボルを避けるのがコントローラに比べて難しいはず
  • タップが決定になっておりキャンセルボタンはタップ操作では行えないので画面に表示されるキャンセルボタンをタップまたはカーソルを移動して決定しかない

ゲームについて

  • 年代ジャンプ時や全滅時に毎回、装備と技と術を毎回セットするのがかなり面倒だった
  • 追加コンテンツの「追憶の迷宮」や「追加クラス」「ヴィクトールの亡霊」は強い装備も手に入るのでよかった
  • オートセーブやクラウドセーブがあるのはうれしい

やりこみ要素

  • 強くてニューゲーム
  • マスターレベルを上げる
  • RTA
  • 追憶の迷宮
  • 技の閃き
  • 術の研究
  • レアドロップアイテムの入手
  • 周回による最強レベルの装備入手

攻略の流れ

全種族を仲間にしつつ強力な武器も手に入れるルート
ヴィクトールの亡霊も発生させられる

  • 北バレンヌ制圧
    • クジンシー
    • ウォッチマン
    • ゴブリンの巣 (クリアせず残しておいたほうがいいかも)
  • 南バレンヌ制圧
    • 格闘家を仲間にする
    • 運河要塞
    • シティシーフを仲間にする
  • 北ロンギット制圧
    • 武装商船団仲間にする
    • 船は奪ったほうが早い
    • 武装商船団を皇帝にしてラピッドストリームを習得
  • 南ロンギット
    • 追憶の遺跡に行き運命の石を取得
    • マーメイドで人魚を見ておき人魚イベントを進めておく
    • 海燕の巣
    • 南ロンギット制圧この時点では難しいの放置、海女は最終皇帝のときでも仲間にできる
    • 追憶の遺跡で忍者と陰陽師を仲間にする (あとでもいい)
  • カンバーランド制圧
    • ソーモンから船で行く
    • ホーリーオーダ仲間にする
    • 大学作成 (軍師を仲間にする)
    • 術研究
  • ステップ制圧
    • 大学があるのでボクオーンを倒す
    • ノーマッドを仲間にする
  • サバンナ制圧
    • アリを倒す
    • モールを仲間にする
    • たまごのから
    • ハンターを仲間にする
  • コムルーン制圧
    • サラマットのムニエから船で行く
    • ついでにサラマットで石の舟を拾っておく
    • サラマンダー加入は再噴火後
  • サラマット制圧
    • 女帝 or テンプテーションの見切り or クリックタイムでロックブーケを倒す
    • アマゾネスの村
    • エイルネップ
    • 沈んだ塔
    • アマゾネスを仲間にする
  • メルー制圧
    • ノエルを倒す
    • 移動湖
    • デザートガードを仲間にする
  • ルドン制圧
    • 宝石鉱山
    • アクア湖にいって月光のクシの依頼を受け終わらせる
    • 最終皇帝になるとネレイドは仲間にできないので必ずここで仲間にする
    • ネレイドを仲間にする
  • ダンターク (ナゼール制圧)
    • サイゴ族を仲間にする
    • おそらくここで年代ジャンプして最終皇帝になるはず
  • ミラマーで橋を掛けておく
    • 適当に戦闘して壊しておく (いらないかも)
  • コムルーンの再噴火イベントをクリアする
    • サラマンダーを仲間にする
  • ヤウダに行って詩人イベントを開始する
    • テレルテパから船で行く
  • ヤウダ制圧
    • ワグナスを倒す
    • 詩人が集まっている想定なのでイーリスを仲間にする
    • イーストガードを仲間にする
  • 南ロンギット制圧
    • スービエを倒す
    • ワイリンガ湖に行って主と話しておく (話さなくてもいい)
    • モーベルムから船を操縦してロンギット海に行ける (気づきにくい)
    • 海女を仲間にする
  • 最終皇帝
    • インペリアルガードを仲間にする
    • 新市街建築
    • ヴィクトールの亡霊 (これが一番難しいかも)
    • アリ 2 回目
    • トーレンス (雪の遺跡)
    • 氷の遺跡
  • ラスダン

その他攻略メモ

  • 技術点稼ぎは水龍や金龍などを狩るのが早い
  • 閃きはアルビオンで OK だが、キャラによってそもそも閃ける技と閃けない技があるので注意
  • ヴィクトール亡霊の出し方はこちら
  • 追憶の迷宮でアイテムを厳選する方法はこちら