📖 前回の記事: 2D RPG 制作ツールを作ろうぜ(^~^)? <その1>
「 👆 MAUI は タップ仕様なんで マウスに対応してない。これでも ちょっとは マシにした方」
(Zzz Zzz Zzz Zzz)
「 枠線が太くて タイルがよく見えない。タイルをでかく表示してくれだぜ」
「 ズームか。
係数を ちゃんと正しく書けば いけるはず……」
「 キャンバスがやってくれるのか、画像がやってくれるのか分からん」
「 キャンバスの 横幅と 縦幅を変えるだけで ズームしてくれるんじゃないの?」
(カタ カタ カタ カタ)
「 グリッドは 画像として実装しているので、 ズームされると ばかでかい画像になるんだが」
「 グリッドは 画像に対して かけるのではなく、
画面に かければいいんじゃないの?」
「 グリッドは 元画像のサイズとは関係なく 画像サイズも指定することにしたい」
「 グリッドの線には太さがあって 元の画像から 1px はみ出るけど、
1024 x 1024 pixels の画像にかけるグリッドが 1026 x 1026 pixels なの、気持ち悪くない?」
「 ゲームの実行時に合わせて最適化すると、開発環境は 割り食うが、
ゲームの実行環境より、開発環境の方が 大幅にマシン性能が良いということを期待して、
開発環境は 割り食うことにしようぜ?」
「 👆 まず、 グリッド全体
と呼んでいたものを、 グリッド位相
に改名する」
「 上側のメニュー、 画像
の1行じゃなくて、
元画像
と ビュー
の2段にしないと のちのち 混乱するんじゃない?」
「 👆 利用者としては 不要な情報だが、
これがないと 開発中の わたしが混乱するから 付けておこう」
「 Width と Height を変えて画像がデカくなるの 元画像を引き延ばすだけなら そうかもしれないが、
いろいろ 加工したいので ズームは自力実装することにするぜ」
「 👆 数字を ちょこちょこ変更できる入力欄に すぐ反応するように ズーム機能を実装するの 大変だな。
要らん処理しないようにすることが」
「 せっかくの年休なのに お父んが ヘロヘロだ。
寝てるぐらいなら 風呂掃除でもしてくれだぜ」
「 ユニットバスの床だけ 洗い流した。
全部きっちりやろうとすると つらいので これで終わり」
「 グリッドも画像だからな。ズームをしたときに
グリッドの画像サイズも変えてないので 切れてるんだぜ」
「 画面上に グリッドの画像サイズを出そうかな。わたしがデバッグするために」
「 グリッドは なんかトリック・コード書いていて やりづらいな……」
「 トリック無しで グリッドを再描画できないの?
Invalidate()
メソッドとか使えば 再描画できないの?」
「 なんらかのプロパティが変わってないと 再描画しないようだぜ」
「 👆 ズームに実数を入れると、ずれてしまうな。
計算式の端数の丸め方が 揃っていないのだろう」
「 for文で 横幅ずつ 横に移動しているところが int 型なの、これが怪しいな double 型へ変更しよう」
「 SkiaSharp のメソッドが float 型を受け取るようだから、 double 型ではなく、 float 型にしよ」
「 👆 テキストボックスの 小数点の桁数を何桁表示するかのフォーマットって 設定できないかだぜ?」
「 じゃあ 表示用に整形したプロパティを別途用意するかだぜ」
📖 Microsoft > Standard numeric format strings
「 👆 "F1" にすれば 小数点以下1桁 になるみたいだぜ」
「 👆 カーソルや、カラー・マップを ズームに対応させるの まだまだ かかりそう。
今日はもう寝る」
休み
休み
📖 バトル1
「 👆 この List<TileRecord> RecordList
というのが タイル1個分のデータだが、
ズーム後の表示サイズのようなものは 記録しないぜ」
「 そこで 例えば List<TileViewModelRecord> RecordViewModelList
のように
ズーム後の表示サイズも記録するように 拡張するのが 基本的な考え方だぜ」
「 👆 TileRecord
ってこういうモデルだが、 これとは別に TileRecordViewModel
というビューモデルを作ることにしよう」
「 👆 主な違いは、位置とサイズを、元データとズーム後の2つに分けて持つことだぜ」
「 👆 タイル設定ファイルも ビューモデル バージョンを作っておこうぜ」
「 👆 そして ズーム欄の数値を変更したタイミングで、
カラー・マップの 色矩形の位置とサイズを 全部更新すればいいわけだぜ」
「 ズームの数値を変えたときに 起こることが
タイルセット画像の伸縮と、グリッドの伸縮と、
切抜きカーソルや、画面上のオブジェクトの位置とサイズの変更と、
やることが多いのよねえ」
「 お父ん、タイルセット画像を暗くしていたのが 効いてないぜ」
「 お父ん、マウスカーソルが指している位置と、切抜きカーソルが出てくる位置がずれているぜ」
「 入力は ズームかかってないからな。そこも実装しないといけないか~」
「 👆 ズームを変えても、切抜きカーソルが 置いてけぼりをくらってるぜ」
「 ズームの数字が変わったら、切抜きカーソルの矩形の位置とサイズも 再設定しないといけないな」
「 👆 カーソルも ズームに置いてけぼりされないようにしたぜ」
「 既存の登録タイルと重なっているときは タイルを追加できないようにすればいいのでは?」
「 既存のタイルと重なっている と、ユーザーにどうやって 気づかせるんだぜ?」
「 とりあえず 追加ボタンが押せなければいいんじゃないの?」
「 じゃあ 切抜きカーソルの位置を決めるために マウスを動かしている間、
インターセクション(Intersection;重なり合った領域)があるかどうか 判定すればいいんだぜ」
📖 矩形同士の交差
「 👆 登録されているすべてのタイルと 比較すればいいわけだが、
リストではなく、 IEnumerator
型を使うのが ひと工夫だぜ」
「 👆 このように ストリームで渡せるようにしておくと、
ビューモデルも 同じアルゴリズムを使い回せるな」
「 👆 お父ん、コングルエンス(congruence ;合同)と インターセクション(Intersection;交差)は 区別してくれだぜ」
「 考え出すと お互いに部分的な交差、完全に覆っている交差、完全に覆われている交差、いろいろあるな。
インターセクションから コングルエンスを除外するか、それとも インターセクションでありコングルエンスでもあることがあるか?」
「 インターセクションを見つけることと、コングルエンスを見つけることは 時間が異なるんじゃない?
同じアルゴリズムで一緒にやろうとしたら 実行時間が最悪のケースにならない?」
「 👆 合同のときは 上書きできるように直したが、
今度は削除ボタンが効かなくなった。調べるぜ」
「 論理削除されているものを 保存から除外してたから 保存されなかったんだ。
論理削除されているものも 保存するように直そう」
「 👆 データの中に 合同の矩形が2つある!
片方を論理削除しても、もう片方が残ってる!」
「 ファイルの内容をチェックするプログラムを入れておくべきでは?」
「 不活性のボタンは 文字が黒い青いボタンになってるけど、
グレーのボタンにできないの?」
「 👆 ここに指定している通りに 表示してくれたらいいのに……」
「 その上で BackgroundColor
を指定しているのが ダメなんだろうかだぜ?」
「 タイルセット画像と同じだけ 明度を落とした白にすんのよ」
「 SkiaSharp には RGBじゃなくて 色相と明度で指定する方法 無いの?」
「 👇 HSVか。有るかも知れないな。調べてみるか。有った」
📖 SKColor.FromHsv(Single, Single, Single, Byte) Method
「 ここまで画面ができあがってくると、画面上に出ている 作業中 の表示は もう要らないんじゃないか?」
「 コメントアウトの切替えで復活できるようには しておきたいな」
「 その画面に入る前に 本当は タイルセット画像を選ぶ画面が要るだろ」
「 フォルダーや ファイルの概念が分からない ツクラー種族や、スマホ種族を どうしてくれよう」
「 👆 このフォルダーに置け、と決めて置いたら いいんじゃない?」
「 タイルセット画像というものが、ファイル名ぐらいでしか管理されてないことに
前時代的なものを感じるぜ」
「 全ての .png
画像に、 .toml
設定ファイルを添えるかな?」
「 画像フォルダーに .toml
入ってたら 邪魔じゃない?」
「 ファイルの説明のファイルだから、ファイルの隣に有ってほしいんだぜ。
同じファイル・パスですぐ見つかる感じで」
「 といっても テーブル・ビューを使えばいいのか、
コレクション・ビューを使えばいいのか、 リスト・ビューを使えばいいのか、
さっぱり分からないが……」
📖 .NET MAUIのCollectionViewを試してみる
📖 DotNet > maui-samples
📖 https://github.com/dotnet/maui-samples/tree/main/7.0/UserInterface/ControlGallery
「 👆 これが コレクション・ビュー らしいんだが、何がいいんだろなあ?」
「 👆 これが リスト・ビュー らしいんだが、何がいいんだろなあ?」
「 👆 これが リフレッシュ・ビュー らしいんだが、何がいいんだろなあ?」
「 👆 これが スワイプ・ビュー らしいんだが、何がいいんだろなあ?」
「 👆 これが フォームが乗っているテーブル・ビュー らしいんだが、何がいいんだろなあ?」
「 👆 これが メニューになっているテーブル・ビュー らしいんだが、クリックすると画面遷移するぜ」
「 グーグルのイメージ検索結果みたいに 画像を並べるのがいいんじゃない?」
「 👆 じゃあ コレクション・ビューの XAML を見てみるか」
ItemsSource="{Binding Monkeys}"
「 MVVM 使ったサンプルなのかと思ったら MVVM 使ってねー」
「 Monkey 型は、 Name, Location, Details, ImageURL のプロパティを持っているぜ」
「 作業用BGMを掛けようぜ?
YouTube に何かないかな?」
「 それは 作業用BGMじゃなくて 違法アップロード動画なのよ」
📺 Rockman Arrange Album - iwao
「 また ROCKMAN だ。 お父んは 音楽の聞き分けは ROCKMAN かそうでないか ぐらいしか 耳が分からないんだ」
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
LineBreakMode="TailTruncation"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</DataTemplate>
「 👆 じゃあ <DataTemplate>
というのは ただのグリッド・レイアウトだから
それ以外のところに コレクション・ビュー 独自のものがあるはずよ」
ItemsLayout="VerticalGrid, 2"
「 👇 ItemsLayout
は ここにまとまってあるぜ」
「 じゃあ Monkey クラスに当たるものを作ろう。
TilesetRecord
みたいな名前でいいかな」
「 👆 とりあえず 深く考えず TilesetRecord
を作った」
「 internal
プロパティは XAMLから見えないのか。 public
プロパティに変えよ」
「 モデルのプロパティに public
を強要するのは おかしい。
本来は ビューモデル にするべきでは?」
「 MVVMのサンプルじゃないしなあ。
MVVUでやるなら ビューモデルかな」
「 👆 こういう見た目になった。 サムネイル画像が無いと 寂しい」
「 サムネイル画像を生成するプログラムを 先に書いたらいいんじゃない?」
「 👆 ユニティ・プロジェクトに 一時ファイルを入れたくないんで、
ローカルPCのフォルダーに 著者名、作品名で フォルダー切った方がいいかだぜ?」
「 Windows べったりな発想だな。MAUI に テンポラリー・フォルダーの概念 無いのかだぜ?
👇 調べろだぜ」
📖 How can I save temporary file in .NET MAUI?
📖 File system helpers
「 MAUI の流儀では テンポラリー(Temporary;一時的な)と呼ばず キャッシュ(Cache;隠し場)って呼ぶんだな」
(カタ カタ カタ カタ)
「 👇 C# で Windows のファイル・エクスプローラーを開く方法も調べとこう」
📖 How can I open a folder in Windows Explorer?
「 👆 キャッシュ・ディレクトリーを開こうとしたら アクセスを拒否されたぜ」
(カタ カタ カタ カタ)
「 あれっ? 隠しフォルダーではないフォルダーからも アクセスを拒否されたぜ」
「 MAUI で ファイル・エクスプローラーを開けることが イレギュラーなんじゃないか?」
「 👆 とりあえず キャッシュ・ディレクトリーの場所だけ示すことにした」
「 キャッシュ・ディレクトリーの下は どのように使うかといった流儀は あるのかだぜ?」
「 MAUI より、 UWP を調べた方がいいんじゃない?」
「 分からん。 とりあえず 勝手に使って、文句出たら 変えよう」
「 👆 170文字ぐらいあるが、だいたい 255文字しか入らないのに大丈夫か?」
「 お父んのソースコードでは ディレクトリ
という単語は0件で、 フォルダ
で統一されているぜ。
しかし C# の入出力ライブラリーのプロパティ名は Folder
ではなく Directory
だぜ」
「 ファイルで連想する対になるものは フォルダーだぜ。
ディレクトリーは 歴史的な名称だぜ」
「 Tileset フォルダーなの? Tilesets フォルダーなの?」
「 タイルセットが いくつか入っていることを期待するから Tilesets
でフォルダーだぜ。
うおお。 名前の付け直しだ」
「 とりあえず、タイルセット画像を、そのまま複製して キャッシュ・フォルダーの下に置くプログラムを考えてみようぜ?」
「 👆 ファイルのコピーは できたが、
サイズを調整したいぜ。 MAUI ではできないから、 SkiaSharp でやるかな」
「 👇 SkiaSharp で作ったビットマップを保存する方法が分からん」
📖 Using SkiaSharp, how to save a SKBitmap ?
「 インターネット上の すべてのタイルセット画像のファイル名が 被らないようにする対策は してんの?」
"adventure_field" ----> "86A25699-E391-4D61-85A5-356BA8049881"
"map-tileset-format-8x19.png" ----> "E7911DAD-15AC-44F4-A95D-74AB940A19FB"
「 👇 C# は GUID という名前になってて、UUID とは別物らしいわよ?」
📖 How can I generate UUID in C#
String UUID = Guid.NewGuid().ToString();
be6c25e9-2bca-40ba-84b4-b9f24bef6df3
95DE5BEE-A51E-41D1-B068-C6F436603AD4
「 📂 Tilesets
フォルダーの下に 📄 {名前}.png
画像を放り込んでおけば、
名前が UUID じゃないとき UUID にしてくれだぜ」
「 対応する TOML ファイルも勝手に作ってくれたら便利じゃない?」
「 画像ファイルを コピー貼り付けしたら、勝手に UUID 振られる 地獄のフォルダーになるぜ?」
「 ユーザーの知らない所で 元ファイルを変更すると ユーザーのストレスになるケースもあるから、
タイルセット一覧画面
で 『ファイルを変更しますか?』 か何かのメッセージを出してもいいかもしらん」
「 👆 うそ画像を 水増しして Tilesets
フォルダーに放り込もう」
「 もっと 詰めて表示しなきゃ サムネイル画像を小さくした意味がなくない?」
「 👆 ItemsLayout
属性を HorizontalList
にすると 1段になってしまうんだな」
「 👇 Collection View には Flex Layout が無いらしいぜ」
📖 [Enhancement] WrapLayout support in the CollectionView #1808
「 MAUI のパターンから外れると つらそうなので 妥協しようぜ?」
「 と思ったが HorizontalGrid, 2
が機能しない。なぜだかは分からない」
「 👆 VerticalGrid, 10
。 静止画だと できているように見えるが、
ウィンドウを広げても ずっと 10列」
「 ウィンドウを広げるという概念は、 MAUI が大好きな Android アプリには無いのでは?」
「 妥協しようと思ったが その妥協ラインでもダメだった 手詰まりだぜ」
「 👆 リスト・ビューは 縦長で、なんか選べそうな色が付いてるな」
「 画面全体を使って サムネイルを並べる フラットなレイアウトは無いの?」
「 CollectionView の VerticalGrid
というのは 縦長のスマホで何列にするか という意味なのでは?」
「 それでも HorizontalGrid
が機能しない理由が分からない」
「 縦でも 横でもなく ウィンドウ・サイズの面積を 最大限に使ってほしいのよ」
「 じゃあ VerticalGrid, 4
で進めようぜ?」
「 👆 コレクション・ビューは 初期状態では 項目の選択はできないみたいだな」
「 👆 こんなUIが良いとは思わないが これで我慢するかだぜ」
「 ユーザーが ウィンドウサイズを変更するたび、
列数を変えることはできないの?」
「 👆 コンテント・ページのサイズは取れるけど、
コレクション・ビューの ItemsLayout プロパティに Binding した文字列が効いてないぜ」
「 👆 GridItemsLayout
クラスを使ってみるが、効かないなあ」
📖 CollectionView does not update when changing ItemsLayout #7747
📖 [Windows] Update CollectionView changing ItemsLayout #14532
「 👇 これで できたという報告なんだが、わたしには 理解できないなあ」
📖 [Windows] Update CollectionView changing ItemsLayout #14532
📖 AdaptiveCollectionView.xaml
📖 AdaptiveCollectionView.xaml.cs
「 👆 回転したり 半透明にしたりすることはできるのに、
11列にするとか 8列にするとか、列数を変えることは 頑としてできない」
📖 CollectionView with GridItemsLayout: Issues when changing Span #8387
📖 [Windows] Notify changes in CollectionView Layouts #13137
「 MAUI のバージョンって どうやってチェックできる?」
「 👆 マイクロソフトは 直ったと言い張り、わたしの環境では 直っていないので これは放置して次へ進むことにする」
「 ページを開いたときに ページの縦横幅を知ることができるなら、
ページを開いた直後なら コレクション・ビューの列数を 調整することができるんじゃない?」
「 👆 XAMLの中で ビューモデルを埋め込んでいたが、
このビューモデルの中で ItemsLayout を初期化していて、その値を そのあと一切 上書きできないというのが
今回の不具合だぜ」
「 XAMLの中に ビューモデルを埋め込むのを 止めろだぜ」
「 👆 InitializeComponent()
の中で ItemsLayout
の読込もしてるようなので、
それより前に ItemsLayout
をセットしないとな」
「 👆 ページのコンストラクターでは、ページの横幅を取れないぜ?」
「 MAUI は InitializeComponent( )
の中で どうやって ページの横幅を算出するの?」
[TilesetListPage.xaml.cs TilesetListPage] this.WidthRequest: -1, this.MaximumWidthRequest: ∞, this.MinimumWidthRequest: -1, this.Width: -1
「 this.GetParentWindow()
はヌルだぜ」
「 このページに遷移してくる前に 横幅を渡せばいいんじゃない?」
「 画面の遷移は自動化されていて、
遷移前に値を渡す方法は 分からないぜ」
「 そして ページのコンストラクターは 1回呼ばれると
画面遷移して戻ってきても 2回目のコンストラクターは呼び出されないぜ」
「 👆 NavigatingFrom
イベントハンドラで メイン・ページの横幅を取れたぜ」
「 👆 これは挫折。 マイクロソフトの仕様の前に 失望し、意志を曲げ、諦めよう」
「 じゃあ タイルセットをクリックして、この前作った タイル切抜き画面へ 遷移してくれだぜ」
「 クリックして すぐ画面遷移すると タイルセットのデータを見れないんで、
タイルセットを選んだあと ボタンを押してから画面遷移するようにするぜ」
「 ファイル名を UUID に変えてもらいたいんだが、
ユーザーに どう説明して
ファイル名を UUID に強制的に変えさすかな?」
「 『ファイル名をUUIDに変更する(言うことを聞け)』ボタンを置けだぜ」
「 UUIDになってるか、そうでないか 判定する方法が欲しいな」
「 👆 とりあえず 画面レイアウトは こんな感じだとしようぜ?」
「 1つのタイルセットに 200か国分の添付ファイルが付いてくるのとか 嫌だな……」
「 添付の TOML の中にロケールを詰め込んだらどうだぜ?」
「 1つのファイルに 複数のロケールが入っているのは
使わないものが必ず付いてくる という 効率が最悪のケースになるかも知らん。
ロケールのフォルダーを作って そこに .toml
ファイルを置いた方がマシか……?」
C:\Users\むずでょ\Documents\Unity Projects
└── 📂 Negiramen Practice
└── 📂 Assets
└── 📂 Doujin Circle Negiramen
└── 📂 Negiramen Quest
└── 📂 Auto Generated
└── 📂 Images
└── 📂 Tilesets
├── 📄 86A25699-E391-4D61-85A5-356BA8049881.png
└── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
C:\Users\むずでょ\Documents\Unity Projects
└── 📂 Negiramen Practice
└── 📂 Assets
└── 📂 Doujin Circle Negiramen
└── 📂 Negiramen Quest
└── 📂 Auto Generated
└── 📂 Images
└── 📂 Tilesets
├── 📂 Locales
│ ├── 📂 ja-JP
👉 │ │ ├── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
│ └── 📂 en-US
👉 │ └── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
└── 📄 86A25699-E391-4D61-85A5-356BA8049881.png
「 どの言語にも差が無い共通の設定は ja-JP
に入れるの?」
「 en-US
で編集したら ja-JP
に保存されるみたいなことがあるの?」
「 編集したのなら、すべての言語ファイルに更新を掛けないと おかしくないか?」
C:\Users\むずでょ\Documents\Unity Projects
└── 📂 Negiramen Practice
└── 📂 Assets
└── 📂 Doujin Circle Negiramen
└── 📂 Negiramen Quest
└── 📂 Auto Generated
└── 📂 Images
└── 📂 Tilesets
├── 📂 Locales
│ ├── 📂 ja-JP
│ │ ├── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
│ └── 📂 en-US
│ └── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
├── 📄 86A25699-E391-4D61-85A5-356BA8049881.png
👉 └── 📄 86A25699-E391-4D61-85A5-356BA8049881.toml
「 👆 ベースとしての設定ファイルは、ロケールとは別に 要るのでは?」
「 Author
とか どうすんだぜ?
Michael さんは ja-JP
では ミカエルさんになるのかだぜ?
ベースの設定ファイルには どっちが入ってる?」
「 ja-JP
から en-US
に切り替えてから保存したら、
en-US
の設定ファイルが 日本語で上書きされるのかだぜ?」
「 全ての翻訳可能なプロパティは 個別にロケールを記憶しておけばどうだぜ?」
title = "冒険の荒野"
title_locale = "ja-JP"
author = "むずでょ"
author_locale = "ja-JP"
[title]
value = "冒険の荒野"
locale = "ja-JP"
[author]
value = "むずでょ"
locale = "ja-JP"
「 画面を 英語で表示して、日本語入力したい人は どうすんの?」
「 素直に 設定されてないロケールで見たら 空欄が楽かなあ?」
ベース:
[global]
uuid = "86A25699-E391-4D61-85A5-356BA8049881"
extension = ".png"
publish_date = 2023-06-26T00:00:00+09:00
[user_defined]
ロケールが ja-JP
:
[local]
title = "冒険の荒野"
author = "むずでょ"
[user_defined]
# 自由に使える欄
ロケールが en-US
:
[local]
title = "adventure field"
author = "Muzudho"
[user_defined]
# Please feel free to use here.
「 UUID ではないファイル名は どこにセットしたらいいんだぜ?」
「 じゃあ fileStem
というフィールドも増やすかだぜ」
(カタ カタ カタ カタ)
「 サークル名と、作品名は アプリケーションが始まる最初に入力しておかないと、
構成ファイルの場所を取得するために 構成ファイルを見る、という循環参照が起こるぜ」
「 トップ・ページは 構成ファイルを読まなくても 表示できるようにしておかないといけないのよ」
「 構成ファイルの場所 が先に在って、 構成ファイルそれ自体 は後に在るのか」
「 すると構成画面に入る前に 毎回、サークル名と 作品名を 入力する必要が出てくるぜ?」
「 じゃあ 構成ファイルは サークル名とパスワードのペアを 覚えておけばいいのよ」
[[entry]]
circle = "Doujin Circle Grayscale"
work = "Negiramen Quest"
[[entry]]
circle = "Apple Banana Cherry"
work = "Dragon Fruits"
[[entry]]
circle = "Shogi Club"
work = "Furi Bisha"
「 現在の configuration.toml
ファイルは、 Doujin Circle Grayscale/Negiramen Quest/project.toml
みたいな名前に変えるべきでは?」
「 あっ! だったら Unity の Assets フォルダーの場所も 入力しておいてほしいぜ!」
「 今の構成ページを、ログイン・ページに変えたらいいんじゃない?」
「 メインページより先にログインページを出すには どうやったらいいんだぜ?」
「 👆 試しに AppShell.xaml を いじったったらどうだぜ?」
「 👆 この Workspace フォルダーへのパスを入力させるの、難しいな」
「 ユーザーを想定して 開発者がパスの設定をするのは大変だから ユーザーが設定してほしい」
「 このフォルダーの中で作業するのではなく、開始時にデプロイする初期ファイルが入ってるだけだから
フォルダーの名前も Starter Kit
とかに変えたいぜ」
「 ログイン後は 2度と使わないんじゃないの? このディレクトリー・パス」
「 Starter Kit
という名前にリネームしていくぜ」
(カタ カタ カタ カタ)
「 じゃあ、 user_configuration.toml
というファイルは 現状、 configuration.toml
で置き場所を設定できるようにしていたが、
大変なので Starter Kit
フォルダーの直下に置くように 固定していい?」
「 設定できることは少ない方が ユーザー・サポートが楽になるぜ」
「 👆 今 こんな感じだが、 [profile]
テーブルを、 [[entry]]
テーブル配列に変えるぜ」
(カタ カタ カタ カタ)
「 あっ、 次回 どの設定で 再開するか 覚えておく必要があるぜ!」
[remember]
# あたなのサークル名
your_circle_name = "Doujin Circle Negiramen"
# あなたの作品名
your_work_name = "Negiramen Quest"
「 エントリーは ドロップ・ダウン・リストでいいかな。 50 件も 100 件もあるようなデータじゃないだろ」
「 👆 構成ページではなく、 別途 ログイン・ページが要ると思う。それも2ページ構成で」
「 LoginPage1
にすると LoginPage1ViewModel
になるのが カッコ悪いから、
Login1Page
にして Login1PageViewModel
になるようにする」
「 👆 これを整形したり 動きを付けたりしないといけない 大変だ……」
(カタ カタ カタ カタ)
「 👆👇 まず レイアウトから。 MAUI はバグ放置があって 思ったように レイアウトできない」
📖 Picker width on Windows not filling container #6391
「 👆 文字数のカウントは付けたが、すでに登録されているかどうかで
新規作成か 続きからかを分けるところ 作るの めんどくさいな」
「 1つ1つ解消していきなさい。まず リストを出してみなさい」
<Picker Grid.Row="1" Grid.Column="2"
VerticalOptions="Center"
HorizontalOptions="StartAndExpand"
WidthRequest="300"
ItemsSource="{Binding EntryList}"
SelectedItem="{Binding SelectedEntry}"
ItemDisplayBinding="{Binding PresentableTextAsStr}"/>
「 👆 ItemDisplayBinding
属性に リストの要素のメソッド名を入れれば 表示文字列になってくれるのか」
「 👆 サークル名と 作品名を 構成ファイルから取ってくるようにしたぜ」
「 文字数がサークル名を、 使える文字が作品名を指しているように 誤解を与えるんじゃない?」
「 入力したサークル名と 作品名が 既存のときは 次へボタンではなく、
続きから始めるボタンが出ろだぜ」
「 サークル名、作品名は フォルダ―名に使うのに、
このままだと 長い名前を入力されるんじゃない?」
「 フォルダー名に使うサークル名 という日本語で2回 名 が出てくるのが煩わしいぜ」
「 じゃあ サークル名 じゃなくて サークル・フォルダー だろ」
「 推奨する使える文字じゃなくて、アスキー・コードで縛った方がいいんじゃないの?」
「 何度も サークル・フォルダ名、作品フォルダ名を入力するのが 手間だから
前回使った値を 最初から入れておくようにしましょう」
「 👆 構成ファイルから 初期値を読み取るのは作ったぜ。
構成ファイルに保存する方は まだだけど」
「 ページの Loaded
イベント・ハンドラーって 1回実行されたら ページは破棄されていないのか、
2回来訪しても 呼び出されないんだな」
「 👆 ページを最初に読みこんだときと、ページに再び訪れたタイミングで
ピッカーの中身を入れ替えればいいんだぜ」
「 項目多くなったら ウザいわよね。
リストの項目に 削除ボタンを付けれないの?」
「 その削除ボタンは、リストから項目を削除するボタンなのか、
それとも プロジェクトを丸ごと消すボタンなのか?」
「 一度 非表示にしたものは、どこから 再表示できるんだぜ?」
「 昔のRPGの皮袋みたいに 要らない道具は 皮袋に詰め込めば
預かりもの屋に テレポーテーションすればいいのよ」
「 預かりもの屋も 作らないといけないしな。 いったん保留かな」
「 あのピッカーの項目は、構成のエントリーというより、プロジェクトの識別子だよな」
[[project]]
# あなたのサークル・フォルダ名
id.your_circle_folder_name = ""
# あなたの作品フォルダ名
id.your_work_folder_name = ""
# Unity の 📂 `Assets` フォルダ―へのパス
unity_assets_folder = ""
[[project]]
# (Id)あなたのサークル・フォルダ名
your_circle_folder_name = ""
# (Id)あなたの作品フォルダ名
your_work_folder_name = ""
# Unity の 📂 `Assets` フォルダ―へのパス
unity_assets_folder = ""
「 unity_assets_folder
は プロジェクトの構成ファイルが 持つべきで、
ネギラーメンの構成ファイルが持っては いけないのでは?」
「 それもそう。じゃあ プロジェクト構成ファイルを作るか~」
「 変数の整合性を きつくしたいので、
テキスト・ボックスに入れた値が 現在の設定だ、という風にするぜ」
「 👆 このリストは 構成ファイルに記憶するのではなく、
ディレクトリ階層を 探索するだけで良かったのでは?」
「 ディレクトリを探索できない権限のケースもあるから、
構成ファイルに記録するプログラムは 有るっちゃ有るぜ」
「 作ってみたあとに気づく設計が成立してない矛盾と、
押されたくないボタンが押せてしまう状態の管理漏れ、
そしてバリデーション・チェックが通らなかったときの表示だぜ」
「 👆 とりあえず こんなもんで勘弁しろだぜ。
入力チェックは パス」
「 しかし、 あなたのサークル・フォルダ名って何だろな? と、ユーザーは思わないかだぜ?」
「 👆 あなたのサークル・フォルダ名のテキストボックスの右端に ×ボタンが付いているんだが、
わたしが付けたわけじゃないし、取り除く方法も分からないぜ」
「 ログインページから始まるように、 MainPage をログインページにしようぜ?」
「 なんで 最初のページの名前が MainPage
なんだぜ やめてくれだぜ」
(カタ カタ カタ カタ)
「 差替えたぜ。これで ひとまず ログイン・ページは終わりだぜ」
「 サークル・フォルダ名、作品フォルダ名を作ったあとは、初回ログイン時に
スターターキットをユニティのAssetsフォルダーへコピーするようにしよう」
(カタ カタ カタ カタ)
「 タイルセット一覧画面にインポート・ボタンを付けようぜ??」
「 ドラッグ&ドロップの方がいいかも知れないが、
とりあえず インポートボタンは要るかだぜ」
「 こんなでかいボタン、噴飯ものだが MAUI だから仕方ない。良いんじゃないか?」
「 👇 ファイル・ピッカーの説明が 何言ってるか分からないな」
「 👇 サンプル・プログラムに ファイル・ピッカーがないか 調べてみるか」
📖 jfversluis > MauiFilePickerSample
Zzz
「 ファイル・ピッカーは コントロールではなく、ただの関数なんだな」
「 ファイル・ピッカーに取り掛かる前に コントロールの色々なことが分からないので 本番に組み込む前に 練習したいぜ」
📖 Muzudho > MAUI-Control-Practice
「 👇 画像ファイルを ネギラーメンに ドラッグ アンド ドロップ できるかな?」
📖 Microsoft > ドラッグ アンド ドロップ ジェスチャを認識する
📖 Microsoft > Microsoft.Maui.Controls.Shapes Namespace
「 MAUI の Rectangle は、 XAML の Rectangle 要素なんだぜ」
「 👆 矩形を ドラッグ&ドロップ しようとしたら こんな見た目になるのか」
「 👆 ドラッグ・イベントの仕事は、 禁止マークとか コピー・マークとか 表示するところにありそうだぜ」
「 Windowsのファイル・エクスプローラー上の画像ファイルを MAUIへドラッグ&ドロップできるの?」
「 👆 できそうな見た目をしているが、ドロップすると 強制終了するぜ」
「 👆 エラーの理由は ヌルにできないパラメーターに ヌルを入れたかららしいが、
分けわからん」
「 👇 MAUI にデスクトップ・アプリケーションの機能が無いのは 鉄板の話題らしい」
📖 Does MAUI support file drag and drop to other application (Windows, Linux, macOS)? #7508
「 じゃあ MAUI は WPF より先に廃止するかもしれないな」
「 👆 うーん、ファイルのフル・パスは取れるのか。
この ダイアログボックスみたいなの 現代では アラートと呼ぶらしいが、
キャンセル・ボタンも出せるだろうか?」
「 👆 ひとまず 上図のような ファイル・コピーをやってみるかだぜ」
System.IO.File.Copy(
sourceFileName: result.FullPath,
destFileName: tilesetPngLocation.Path.AsStr);
「 Windows の API 使ってるの、いいのか知らんけど」
「 👆 ファイルのコピーはでけたけど、コレクション・ビューにも追加しないと 画面に出てこないな」
「 コレクション・ビューに入れるのではなく、
アイテム・ソースに入れるんじゃないか?」
「 削除ボタンを どこに置くかという レイアウトも困るよな」
「 危ないから間違って押したくないボタンなのに、主張が激しくて むしろ目が そっちに行くんじゃない?」
「 頻繁に マウスカーソルが近づくとこに 削除ボタンを置いては いけないのでは?」
「 👆 右上に ペール・バイオレット・レッド のボタンを置くというのは どうだぜ?」
「 ロケールのピッカーの真下なのが気になるけど、ちょっと ずらしたら どう?」
「 あっ、この ペール・バイオレット・レッド のボタン、
不活性にしても グレーにならねーっ!」
「 👆 このボタンのスタイル、 削除ボタンだけ 別のを使うようにできないのかだぜ?」
「 x:Name
属性を使って なんとかならないかしらねえ?」
「 コンカレント処理に強ければ なんでもいいと思ったんだぜ」
「 👇 ConcurrentBag
も要素を削除できないのか」
📖 How to remove a single, specific object from a ConcurrentBag?
「 非同期処理をしているときには どんなコレクションが使えるんだぜ?」
「 👆 連続読込は ConcurrentBag、 UI は同期のコレクションに変えた。
インポートと タイルセット削除は でけたぜ」
「 PNG画像があって、TOMLファイルが無いといったペアがあるとき、
TOMLファイルを自動生成する機能が要るぜ」
「 タイミングとしては、インポート、タイルセット削除、ファイルのリネームの3か所だな」
(カタ カタ カタ カタ)
「 👆 万国共通の構成ファイルの方は 自動生成するようにしたが、
画像のタイトルは ロケール別の構成ファイルの方に入れる仕様だぜ」
cultureInfo:
DisplayName: 日本語 (日本),
EnglishName: Japanese (Japan),
Name: ja-JP,
NativeName: 日本語 (日本),
IetfLanguageTag: ja-JP,
TwoLetterISOLanguageName: ja,
ToString(): ja-JP
cultureInfo:
DisplayName: 英語 (アメリカ合衆国),
EnglishName: English (United States),
Name: en-US,
NativeName: English (United States),
IetfLanguageTag: en-US,
TwoLetterISOLanguageName: en,
ToString(): en-US
cultureInfo:
DisplayName: 中国語 (簡体字),
EnglishName: Chinese (Simplified),
Name: zh-Hans,
NativeName: 中文(简体),
IetfLanguageTag: zh-Hans,
TwoLetterISOLanguageName: zh,
ToString(): zh-Hans
「 👆 画面のピッカーには NativeName
で表示した方がいいのかな……?」
「 👆 NativeName
の方が かっこいいな こっちにしよ」
「 👆 ひとまず ロケール用のフォルダーと、空のファイルを作成……」
「 コレクション・ビューの選択中の色、グレーで見にくいな!」
「 スタイル 見たけど さっぱり分からん。後回し。ダークモード消したった」
[global]
uuid = "6FBD83F1-D2A3-45EE-AFC5-411CD6E50144"
extension = ".toml"
[local_placeholder]
title = "適当に作った画像"
author = "むずでょ"
[user_defined]
「 👆 タイルセット・グローバル構成ファイルに local_placeholder
という項目が欲しいぜ」
「 英語で設定されていて、日本語で設定されていないとき、
英語の設定を プレースホルダーで表示してくれてもいいじゃないか、というものだぜ」
「 第1言語、第2言語を選べるようにすりゃいいんじゃない?」
「 じゃあ 画面から タイトルを変更できるようにしてくれだぜ」
「 エントリー(テキストボックス)で1文字入力するたびにビュー・モデルが更新されるのつらい」
「 👇 このサイトは 細かく書いてるが 知りたい情報はあるかな?」
「 👇 Completed
というイベントハンドラがあるのでは?」
「 なんだこれ エンター・キー 押さないと Completed
しないじゃないか。
エンター・キー押さなかったら どうする?」
「 MAUI の不具合なんだから、エンター・キーを押せ という運用でやるしかなくない?」
「 画面の移動も、 データの編集も 同じボタンの形状なの 分かりづらいんだが」
「 画面遷移も スライドするトランジションが入るやつと、
トランジションが無いやつもあるし」
//
await contentPage.Navigation.PushAsync(new ConfigurationPage());
//
var shellNavigationState = new ShellNavigationState("//MapExplorerPage");
await Shell.Current.GoToAsync(shellNavigationState);
「 Navigation
を使うやつは、戻るボタンが勝手に付くのでは?」
「 わざわざ ShellNavigationState
を使うやり方って メリット無いの?」
「 日本語と 英語で 別々にタイトルを付けれる機能の実装を進めるぜ」
「 そろそろ アンドゥと リドゥができないことに 突っ込まれそうだぜ」
「 Command Pattern を自力実装するのか、それとも MAUI に何か便利なフレームワークがあるのかだぜ?」
「 👇 他人が作ってくれてるけど、自力実装すりゃ いーんじゃねーの?」
「 全ての操作には、逆向きの操作も用意しておく必要があるな」
「 テキストボックスの文字を1つ消したのも戻せるレベルで ヒストリーを作んの?」
「 エントリー(テキストボックス)には アンドゥ・リドゥ機能がもう付いていたぜ」
「 じゃあ MAUI に元から付いている アンドゥ・リドゥ機能と、
お父んが作る アンドゥ・リドゥ機能は ぶつからないか?」
「 いきなり バイナリで作ると バグ取りが難しいだろうから、
ヒストリーは TOMLファイルで書き出すかな」
「 削除したリソースや、 上書きで加工してしまったリソースとか 出てくると思うが
復元する方法は?」
「 ネギラーメンは リソースは編集しないぜ。
リソースを組み立てるだけだぜ」
「 インポートした素材は 削除も 上書きもされないという前提に立っているのね」
「 ネギラーメンから 素材を削除するときは、アンドゥもできません、ということにするんだぜ」
「 👆 コンピューター将棋と同じだぜ。 全ての操作には、進むと 戻るが あるんだぜ」
「 👆 履歴管理機能も 便利機能を付けなければ 芯は これだけだぜ」
「 👆 イベントハンドラの内、自動ではなく、ユーザーの操作によって呼び出されるものが
ヒストリーの記録対象になるぜ」
「 👆 ヒストリカルにしていないということは、
リドゥの方向のプログラムしか 書かれていないということだぜ」
「 画面の更新しか 書いてなくない? ズームの数字が いくつから いくつへ変更したか書いてないわよ?」
「 👆 ズームして何が起こるかは ビュー・モデルの方に書いてある」
「 👆 ズームすると、画像を作り直したり、グリッドを作り直したり、
切抜きカーソルを描き直したり、カラーマップを描き直したり、
広範囲に影響があるようだぜ」
「 ズームの変更と、ズームの変更によって起こる事象を切り分けてくれだぜ」
「 👆 IDone
インターフェースは IProcessing
インターフェースに名称変更、
Redo
メソッドも Do
に名称変更。そして実装」
「 👆 History
クラスも Done
メソッドを止め、 Do
メソッドに変更、中で Processing
クラスの Do
メソッドを呼び出している」
「 あと、コードの書き方が下手で 無駄に再帰してるから 書き直したい」
「 👇 MAUI には キーボード操作のイベントハンドラが無いじゃないか」
📖 .NET MAUIでのCtrl+Enterなどのキーイベントハンドリングの難しさ
「 [Ctrl] + [Z]
の操作をカスタマイズできないのかだぜ、クソじゃないか」
「 MAUI は 意固地にも 画面上のボタンを押せ、ということかだぜ」
「 ♪
真理は 避けられず
真実は 踏み込んだ者にしか知られず
真相は 勝手なものである」
「 Unicode には アンドゥ・リドゥに適した絵文字は無いようだぜ」
「 アンドゥ
、 リドゥ
と書かれたボタンが 画面上に置いてあるのも 邪魔ねえ」
「 日本語なのに Undo
、 Redo
というボタンを画面に配置するのも 負けた気になるしな」
「 👇 機能はあるみたいなんで、頑張って 絵 を描くか……」
「 アンドゥ中は ヒストリーのドゥを通っても そのドゥは 無視する必要があるのか」
(カタ カタ カタ カタ)
(カタ カタ カタ カタ)
「 将来リストの中身を全部捨ててるところも 状態管理の内側に入れて直した」
「 完了リストが0件のときは アンドゥ・ボタンを押せないようにしてくれだぜ。
将来リストが0件のときは リドゥ・ボタンを押せないようにしてくれだぜ」
「 アンドゥ、リドゥは 完了リスト、将来リスト間の移動が終わってから実行しないと
画面更新時に 反映が後手後手になるのか。直して 実装した」
「 お父ん、バグがあるぜ。
マップ切抜き画面、前に開いたときの内容が残ってる」
「 キャッシュされてるよな。
破棄して 新規作成してほしいんだが」
「 👇 MAUI が更新されてないという情報が よく見つかるな」
📖 Call Dispose() on Page and ViewModel when the page is popped if they implement IDisposable #7354
「 画面遷移が 進んでいるのか、 戻っているのか フレームワークに情報を与える方法が無いのだから
キャッシュを削除していいのか、 キャッシュを残すべきか、判別できないんじゃないの?」
「 グラフィック・ビューを、ビュー・モデルから Invalidate する方法が無いぜ」
「 そのトリック、偶数回 振動させると 元に戻って使えないという弱点があるのよね」
「 👆 試しに Dirty
フラグを付けてみるか。このフラグが立ったら再描画されるといいんだが」
「 あっ、 Drawing
の方でフラグを下ろしても、 ビューモデルの方のフラグは下ろされてないから
1度フラグを立てると 立ちっぱなしで プロパティ・チェンジしないぜ」
「 束縛変数に 変更通知をバンバン送っても 再描画は走らないぜ」
「 Drawable
ではなく、 GraphicsView
に変更通知を送る必要があるのでは?」
📖 Binding from view to GraphicsView property in .NET MAUI
「 👆 そのようなコードは動かない。 仕方ないので トリックコードを使う」
「 フレームワークに不具合があると、トリックコードが増えていくの わらう」
「 アンドゥによる削除は 完全削除で、
手動操作による削除は 論理削除なのな。
これは 非対称でいいのかだぜ?」
「 論理削除フラグを 下げるだけで 元データが復活するのだから、
削除のアンドゥには 向いてるのに」
「 追加のアンドゥで 論理削除されたデータを残すとして、
リドゥするのは 論理削除の解除ではなくて 上書きなんで、
追加のリドゥの役には立たないぜ」
「 そこに無いとき、選択アイテムを無しで上書きしないといけないんだが、
それをやってない気がするぜ」
「 切抜きカーソル と 選択タイル は別の概念なのに プロパティを使い回している気がするな。
これを直すのは 大がかりなので、また今度だぜ」
「 上書きボタンをクリックして保存する という操作も止めたい。
テキストボックスに入力したら そのまま保存してほしい」
「 アンドゥ・リドゥ機能は もう付いていて、
既存のプログラムを すべて それに対応していくという作業なんだぜ」
「 既存のプログラムの不出来なところを直さないと そこへ進めないぜ」
「 既存のコードを アンドゥ・リドゥ に対応するために、事前のコードの整理整頓中だぜ」
「 プログラムがヘタクソで すぐに アンドゥ・リドゥ に置き換えられないのねえ」
「 👆 1つのボタンで 追加/上書き の2つの機能を切り替えて使うの、やめたい」
「 アンドゥするには 追加をアンドゥするのか 上書きをアンドゥするのか はっきりしたいわねえ」
「 リドゥは同じ処理なのに、アンドゥは別の処理になるから 分けたいのか」
「 タイルセットに 設定ファイル3つもあって 名前が似ていてややこしいので、
CSV ファイルに対応するクラスは TilesetDatatable
に名称変更するぜ」
「 上書きボタンは 名前を変えて 復元ボタンにするぜ。
論理削除フラグを解除するのに使うんだぜ」
「 マーカーが無い所で タイル・タイトル入力できるの おかしくない?」
「 状況を調べて テキストボックスを不活性にするの わりと大変な作業」
「 見えないマーカーがあるんだが、論理削除フラグが False になってるぜ」
「 IsNone
フラグと、 LogicalDelete
フラグの2つがあるのが ダメなのでは?
どちらか片方にしたら どうだぜ?」
(カタ カタ カタ カタ)
「 論理削除済みの跡地を クリックしても 選択できなくなったぜ。
だから 復元ボタンも 活性化しなくなったぜ」
「 カーソルが無いということと、カーソルが論理削除されているということは 何が違うの?」
Zzz
「 👆 ファイルでは論理削除フラグが立ってるのに、
デバッグ出力では下りてるから、それがなぜなのかを調査するぜ」
「 ファイルから読み込んだときは 論理削除フラグは立ってるぜ」
「 途中で タイトルも消えて、論理削除フラグも下ろしてるな」
「 ログにコードの場所を付けてみた。
タイルを選んだときに タイトルと、論理削除を読み取ってないのかな?]
「 なんか エラー出るけど 無視して実行したら 動くな、不気味だな」
「 横幅を 奇数、偶数 振動させてるトリックコードを止めて、
オフセット 0, 1 を切り替える変数を別途用意したい。これは8月下旬にしよう」
「 また 範囲指定するとき Working の方を使っているが、
全部 Source の方を使うように変更したい。
Working は変数にしないようにしたい。これも8月下旬にしよう」
「 👆 ルビ振る領域も考えると ドーナツは これだけ広くなる。
4K ディスプレイ推奨だぜ」
「 👆 ホーム画面も 四方をぐるっと 青い領域で囲みたいぜ」
「 👆 タイルセット一覧画面も 四方をぐるっと 青い領域で囲みたいぜ」
「 👆 タイル切抜き画面も 四方をぐるっと 青い領域で囲みたいぜ」
「 その前に タイル切抜き画面の トリック・コードが足引っ張ってるので
それの改修だぜ」
「 ビューモデルがでかくなりすぎる。
View Inner Models
というフォルダーを作って中身を分けることにするぜ」
Zzz
「 ビュー・モデルは 透過メソッドばっかりでも 良い気がしてきたな……」
……。
「 👆 また、スクリーンショットからは見えないが、論理削除といった機能を 廃止した。
論理削除はデバッグを助けるが、論理削除自体のメンテナンスが大変だから嫌になった」
「 そういえば Unity が料金体系変更で ひと騒動やってるらしいぜ」
「 Godot への乗り換えも考えないと いけなくなるのかしらねえ?」
「 論理削除をやめたから、壊れていたアンドゥ、リドゥの動きが、マシになったぜ」
…
「 わたしは Unity からイチ抜けた をし、 Godot へ改宗するぜ」
.
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント