「 なんで お父んが そんなこと しなければならないのか?
働けだぜ」
「 思いついたら やるんでしょ。
そこらへんの 凡人と同じよ」
「 👇 それより 作業用BGM かけようぜ? 気分を盛り上げないとな」
💿 Rockman ロックマン 11 [OST] Full Album
「 それは作業用BGMではなく、違法アップロード動画なのでは?」
「 👆 サイドPCを Ubuntu に差し替えたんで Steam を久しぶりに動かしてみたら、動いたんで驚いたぜ」
「 えっ? RPG制作ツールを作るのに 作業用BGMが ロックマン11 でいいのかだぜ??」
「 👇 企画と アーキテクチャーを 長々と 説明しても あれなんで……
MAUI を調べようぜ?」
📖 .NET MAUI (環境構築からHallo, World!~Windowsアプリ編~)
「 あんたのお父ん ノー・プランよ。
ウォーター・フォール・モデルの下流工程の 実装 という狭い範囲でしか仕事できないんだから。
あと 事務と経理」
「 それって 羨ましがられるらしいぜ。 いーな、コードばかりやってて、と」
「 なんか 知らん間に Windows の 開発者モード というのが オフ にされているらしく、
これを オン にして ビルド」
「 👆 これが MAUI を実行した画面か~。 なんなんだろうな これ?」
「 Click me
を押したら ボタンを押した回数が出てくるぜ」
「 こっから ツールを組み立てていくの、気が遠くなるわね~」
「 タイトル画面なんか作らずに メニューがさっさと出てきたら 良くね?」
「 お父んがよく選ぶのは 家系二郎ラーメン、 塩ラーメン、 レモンサワー」
使用フォント: 📖 たぬき油性マジック
「 いいのか よくないのか 分かんないから さっさと作って 動かしてみましょう!」
「 わたしが やりたいことを 説明してくれるのではなく、
マイクロソフトが 説明したいことを 説明してくるので つらいぜ」
「 👇 どこを読めばいいのか 分からない、ドキュメントとの戦いだぜ」
「 👆 様々な概念が分からなかったが作った、とりあえず これで」
「 👆 AppShell.xml
というのを 何か設定しないといけない?」
「 👇 パンくずリストのような機能は用意されているのではなく、自力で作らないといけないらしいぜ」
📖 .NET Maui Tabs with Breadcrumb Navigation
「 👆 作り方 分からんので とりあえず ボタンとラベルで」
「 👆 .xaml
ファイル増えてきて 邪魔なんだけど、置き場フォルダーは どう作るのが ベストプラクティス なんだぜ?」
「 👇 Qiita の素人記事読むぐらいだったら 公式読むぜ」
📖 Microsoft > Upgrade your app with MVVM concepts
「 MainPage.xaml
も 📂 Views
フォルダーに入れろだぜ」
「 お父ん。
プレイヤー・キャラクター編集画面に入る前に、
どのプレイヤー・キャラクターを編集するのか 選択する画面が 要るんじゃないか?」
「 あっ、そうか!
ほとんどの データ編集ページで リストが要るぜ!」
「 じゃあ エクスプローラー だ。
MAUI にそんなコントロール あるかな?」
「 👇 お父ん。コレクション・ビューと、テーブル・ビューを調べてみてくれだぜ」
「 👆 なんか知らんが ListView
より かっこいいのが CollectionView
らしいぜ」
「 👆 コレクション・ビューを使うべきか、テーブル・ビューを使うべきの判断方法が分からん」
「 ページネーション(Pagination;ページ割り)の機能は付いているのかだぜ?」
「 調べたいことが 出てこないのが マイクロソフトの ドキュメントなんだぜ」
「 マイクロソフトの Bing の人工知能に利いても MAUI がなんだか分かってないし」
「 そうであるなら、クリテリア(Criteria;判断基準)が必要だぜ。
どのような規則に従って整列しているか、
表示されているデータは 何件目~何件目までのデータか、
表示されているのは 何ページ目か、
昇順か逆順か、
フィルタリングされているか」
「 無いものを 自力実装するのが プログラマーだぜ。
必要だったら 片っ端から 実装していけばいいんだぜ」
「 迷ったら テーブル・ビューと コレクション・ビューの両方実装すりゃいいのよ」
「 👆 こういう風に 途中に 検索ページを1枚 挟めばいいんだろ?」
「 デザインを分かってないエンジニアが作りがちな検索画面だぜ」
「 動いているところを見ないと よく分かんないから、
明日は 動くところを作ってみましょう」
「 ライセンスが オープンソースの 2D RPGの素材 なんも 持ってねー ぬぎぎぎぎぎ!」
「 あっ! ゲームとして利用するならフリーだが、素材集を ツールに同梱して そのまま再配布することを許可している素材が無いのか!」
「 まあ 無いものは無いで 変数x とでも置いておいて 解けるところから先に解くのが 数学だぜ」
「 👆 テンプレートは 2D を選び、プロジェクト名は Negiramen Practice にして
プロジェクトを作成しようぜ?」
「 ネギラーメンでは、それが何だか 分かんなくない?
検索のサジェスチョンも汚すし」
「 なんか最初から いろんなものが入っている……、ちょっと見てみるか……」
「 👆 多分 エディターなんだと思うんだが、この1つ1つを調べて行くか」
「 👇 お父んが作らなくても、すでに いいものがあるのでは?」
「 👇 それで……、テキスト・ボックスって どうやって作るんだ? 調べるか……」
「 👆 それで こういうページ作るだろ。
ディレクトリー・パスを入れて ボタンを押すと……」
「 👆 Unity プロジェクトへ フォルダーや ファイルを送り付けるという算段だぜ」
「 Unity の外部で ゲーム・オブジェクトを作れるの?
Unity の外部で プレファブは作れるの?」
「 Unity に そういう API があるか? ということだな。
調べてみようぜ?」
「 そんなことを しようという人が少ないのか 記事が見当たらないな」
「 毎回 📂 Assets
フォルダーへのパスを入れるのが 面倒なんだけど?」
「 ユーザー・データとして保存したいよな。
最近流行りのファイル形式は何だぜ?」
📖 Tomlyn
「 このテキスト・ボックスの初期値って どうやって 入れるんだぜ?」
「 👇 お父んが さっき読んでいた記事に書いてあるだろ。 MVVM のところを読み直せだぜ」
📖 Microsoft > Upgrade your app with MVVM concepts
「 👆 とりあえず 📂 Views
、 📂 ViewModels
、 📂 Models
の3フォルダーを揃えよう」
「 👆 なんだか分からないが Model は ただのオブジェクトだろ。イミュータブルに作ったった」
「 👇 ViewModel は なんか複雑だな 説明を よく読も」
「 👆 ViewModel は Model と 変更通知プロパティ を紐づければいいんだろ?」
「 ビュー・モデルのデフォルト・コンストラクターは public 修飾にする必要があるぜ」
「 👆 ViewModel に コマンドを取り付け。
ビュー・モデルは MVVMアーキテクチャーの中で 雑用とか、アシスタント・ディレクターの役割を担当するんだな」
「 👆 View も更新。
イベントハンドラを消し、コマンドを追加」
「 👇 いろいろなフォルダーに ユーザー・データは保存できるが、どこにするんだぜ?」
「 ユースケースによる。
2D RPG を開発するような アクターは、
いつも 同じ部屋で 同じ1台のパソコンを使って 1人で開発している、という状況設定を押し付けることにしようぜ?」
「 それ以上のものを要求するユーザーは ネギラーメン ではなく
もっと 金出して 高級な開発ツールを使えばいいのよ」
「 じゃあ LocalApplicationData だな」
C:
└── 📂 Users
└── 📂 {ユーザー名}
└── 📂 AppData
└── 📂 Local
👉 └── 📂 Muzudho
└── 📂 2D RPG Negiramen
「 👆 他人のローカルPCに わたしの名前のフォルダーが ポコポコできるの 嫌なんだが……」
「 屋号、 サークル名、 ホームページ名など いろいろあるけど」
C:
└── 📂 Users
└── 📂 {ユーザー名}
└── 📂 AppData
└── 📂 Local
👉 └── 📂 Doujin Circle Grayscale
└── 📂 2D RPG Negiramen
「 👆 Grayscale
だけだと 何だか分からないので、
かなり長いけど Doujin Circle Grayscale
にしよう」
「 👇 あれっ! MAUI には AppData 下の LocalFolder にアクセスする正式な方法が無い!」
「 マルチプラットフォームだから Windows のフォルダーにアクセスするのが ナンセンスなんじゃないか?」
「 MAUI は他の手段を用意しているのかしら? 調べなさい」
string mainDir = FileSystem.Current.AppDataDirectory;
// Example: `C:\Users\むずでょ\AppData\Local\Packages\1802ca7b-559d-489e-8a13-f02ac4d27fcc_9zz4h110yvjzm\LocalState`
「 それは ネギラーメンを何回実行しても同じディレクトリー名になるのかだぜ?」
string mainDir = FileSystem.Current.AppDataDirectory;
// Example 1回目: `C:\Users\むずでょ\AppData\Local\Packages\1802ca7b-559d-489e-8a13-f02ac4d27fcc_9zz4h110yvjzm\LocalState`
// Example 2回目: `C:\Users\むずでょ\AppData\Local\Packages\1802ca7b-559d-489e-8a13-f02ac4d27fcc_9zz4h110yvjzm\LocalState`
「 👆 設定ファイルの保存の仕方は 覚え直した……。
やり方ぜんぜん変わってた……」
「 👆 前に入力した内容も フォームが覚えてくれるようになったから 楽になったぜ」
「 絵を描くツールも Windows Paint と、 GIMP しかないしな……
少し ネットで調べる」
📖 WOLF Editor 公式 > 【マップチップ素材はどういう風に作ればいいの?】
「 👇 ゲームの特許なんか調べてられん。 ページ開いて出てくるの、こんなんだぜ?」
「
真理は 避けられず
真実は 踏み込んだ者にしか見えず
真相は 勝手なものである」
「 ゲーム業界に首を突っ込みたかったら 死にましょう。
生きて帰るな。代わりに 徹底的に世界を破壊しなさい」
「 WOLF EDITOR のドキュメントが整理されているので、それ読む」
「 ピクセル・アートを描くのに 良い無料のツールって どんなのだぜ?」
「 マップ・タイルは 16 x 16 ピクセル・サイズが単位のようだぜ」
「 それを どれぐらいのサイズのキャンバスの中に 敷き詰めんの?」
「 Nintendo Switch は、 1080 x 720 だそうだぜ。
これを 参考に試算しよう」
📖 Nintendo > Switch > Technical Specs
「 タイル数で言うと、 67.5 x 45 枚だな。スクリーン上に 3037.5 枚のタイルがあるぜ」
「 オブジェクト数が 多い気がするけど、 RPG なら大丈夫なのかなあ?」
「 👆 マップ・エディターって こんなレイアウトで いいのかだぜ?」
「 👆 お父んに 分かりやすいように まとめたぜ。
空間充填問題も セル・オートマトンも 好きだろ」
「 👆 あれっ? 64 x 64 pixel のつもりで描いたのに でかいな……」
「 👆 Save じゃなく、 Download すればいいのか」
「 じゃあ これを最初のマップタイルとして MAUI の画面に表示してみようぜ?」
「 👆 初期設定として これだけ入力してくれないと つらい」
「 ほとんどの人は ディレクトリー・パスも入力できず 投げるんじゃないの?」
「 ファースト・ビューで ユーザーが入力できるのは、
自分の名前と メール・アドレスぐらいだぜ」
「 じゃあ ファースト・ビューのボタンを クリックするところまでは
設定無しで 行けるようにしよう。
そこで何か ボタンを押したら 初期設定画面へ」
「 サイト・マップのツリー構造を崩したくないから、
モーダル・ダイアログ・ボックス的に ページ遷移せず出てくる サブ・ウィンドウみたいにできないの?」
「 👇 マイクロソフトのドキュメントって、スクリーンショットを使った説明が 圧倒的に足りないよな」
「 👇 じゃあ まず 普通のサブ・ウィンドウを出して、閉じるところまで やってくれだぜ」
📖 Opening a new window in MAUI
/// <summary>
/// [ウィンドウ表示テスト]ボタン押下時
/// </summary>
/// <param name="sender">このイベントを呼び出したコントロール</param>
/// <param name="e">この発生イベントの制御変数</param>
private void TestShowWindowButton_Clicked(object sender, EventArgs e)
{
var secondWindow = new Window
{
Page = new StartupConfigurationPage
{
// ...
},
Width = 1200,
Height = 400,
};
Application.Current.OpenWindow(secondWindow);
}
「 👆 これで ウィンドウは ポップアップされるけど、もっと簡単な書き方無いかなあ?」
/// <summary>
/// [ウィンドウ表示テスト]ボタン押下時
/// </summary>
/// <param name="sender">このイベントを呼び出したコントロール</param>
/// <param name="e">この発生イベントの制御変数</param>
async void TestShowWindowButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new StartupConfigurationPage());
}
「 👆 こう書くと 同じウィンドウで ページだけ スライドする演出付きで 切り替わるぜ」
「 戻るボタンも 付いてるし、こっちの方が かっこいいわよ」
「 👇 Navigation.PopAsync();
じゃないのかだぜ?」
「 ContentPage クラスを継承したコード・ビハインド には Navigation 書けるけど、
ViewModel のプロパティーである Command にはどこから Navigation 持ってくるんだぜ?」
「 👇 Backwards Navigation があるんじゃないか?
await Shell.Current.GoToAsync("..");
を試せだぜ」
「 👆 この画像を Unity の Assets フォルダーへコピーしたいけど……」
「 この画像のファイル・パスって どうやって指すんだぜ?
カレント・ディレクトリー どこよ?」
「 指せるからこその Resources ディレクトリーなんじゃないの?
Special Directory に無いの?」
「 Application.ImageDirectory
を調べてみるかだぜ」
「 👇 ワーキング・ディレクトリーの設定方法を調べた方がいいのでは?」
📖 How to change the Current Working Directory of Maui Blazor App
「 まず AppDomain.CurrentDomain.BaseDirectory
が何を出力するか 調べてみてくれだぜ」
「 👆 bin ディレクトリーの方を向いていて、 Resources フォルダーなんか見にこないぜ」
「 じゃあ マップ・タイル画像は ネギラーメンの実行ファイルとは別に 配ったらいいんじゃないの?」
「 ツクラーは こんな設定 できないから RMUは 全部入りのパッケージで売るのよ」
「 ソフトを自分で選んで インストールして 設定するような
パソコンの正しい使い方をしたくないのが、 ツクラーだぜ」
「 ネギラーメンは ツクラーの思想とは おさらばだぜ。
ネギラーメンは ただの食材を ユニティーにコピーする トッピング・ソフト だぜ」
「 👆 さらに調整。
ハードコーディングしてしまったが、ただの Copy
だぜ」
「 タイル・パレットを作ってみましょう。
この画像を MAUI に表示して、草原、砂漠、岩、海を クリックできるようにしましょう」
「 👇 このポップアップというやつは 画面上に 画像が浮くやつじゃないか?
試してみようぜ?」
📖 マイクロソフト > ポップアップ
📖 Microsoft > Popup
📖 .NET MAUI Community Toolkit Popup PopupHandler is incompatible
「 👆 思ってるのと違って モーダル・ダイアログボックスみたいなのが 出てきたぜ」
「 画面の真ん中じゃなくて、ボタンをクリックしたマウスの近くに 出せないの?」
「 👆 ウィトス(WIdth;横幅)と ハイト(Height;縦幅)を 指定すればいいのか」
「 なんか変なデバッグ用のツールバーと被ってるから 真ん中らへんに表示しましょう!」
「 画像にマウスカーソルを合わせたら、枠が出るようにしてみてくれだぜ」
「 オン・マウス・オーバー みたいなイベントハンドラが無いか 探してみるかだぜ」
📖 Mouse hover detection in .NET MAUI
「 MAUI は、スマホのようなタッチ・デバイスで使われることを想定していて、
デスクトップのようなマウス操作は 想定していないそうよ」
「 マウス・カーソルの位置を取得できないか、調べてみてくれだぜ」
📖 How to capture global touch events in MAUI app
「 👆 なんか いっぱいある。
この PointerGestureRecognizer
とか それっぽくないかだぜ?
👇 調べるか」
「 👆 反応はしてるから、後は 引数を取りたいぜ。
もう少し調べるか」
📖 Microsoft > PointerGestureRecognizer.PointerMovedCommandParameter Property
📖 .NET MAUI Custom control with command and parameters
「 👆 コード・ビハインド を使うと 画像上の座標を取ることはできるが……」
「 コード・ビハインドと ビュー・モデルと 何の関係があるんだぜ?」
「 👇 WPFの依存関係プロパティみたいなものがないか 調べてみるぜ」
📖 How to use DependencyProperty in Maui?
📖 Microsoft > Bindable properties
📖 How can I bind a property to a view model in MAUI?
「 👆 コード・ビハインドから、ビューにバインドされてる ビュー・モデル 取れた……。
WPF の DataContext と同じ考えだ」
「 それを いったん ウィンドウ上の座標に変換して、
32 x 32 pixels サイズのタイルを囲む枠を 置いてくれだぜ」
📖 How to get MAUI UI element absolute coordinates
「 👆 画像の座標は取れるのか。だったら それ使えばいいんじゃ……」
「 じゃあ 次は 枠を描こうぜ。
MAUI の画面上に 矩形を描く命令 あるかだぜ?」
「 👆 canvas
って どっから出てきたんだぜ?
👇 調べるか」
📖 .net MAUI: how to draw on canvas
「 マウスカーソルは 1ピクセルずつ動いて欲しいけど、
赤い枠は 32ピクセルずつ 動いて欲しい」
「 👇 なんで Alignment
じゃなくて Options
って名前なんだ?」
「 タイル・マップを クリックしたいのに、
その上のカーソルが 邪魔なんだが」
📖 VisualElement.InputTransparent プロパティ
「 隣の砂漠をクリックしたら、タイル・カーソルがそこを指すようにしなさいよ」
「 👆 グリッドが 32ピクセル固定で ハードコーディングして いけたぜ」
「 じゃあ そのタイルを マップ・エディター で使いたいけど、
マップ・エディターが まだ無いのよねえ」
「 👆 pixilart.com のツールに グリッドのサイズ指定が無いから 枠線 引いてしまう」
「 どの画像の 何個目のタイルを表示するのか 名前で指定できるようにしたくない?」
tool_box.pen
「 👆 カテゴリー名と、そのメンバーを ドット・シンタックスで指定して」
crop_image("tool_box.png", 0, 0, 32, 32)
code , file , x, y, width, height, comment
tool_box.pen , tool_box.png, 0, 0, 32, 32, ペン
tool_box.eraser, tool_box.png, 32, 0, 32, 32, 消しゴム
tool_box.bucket, tool_box.png, 0, 32, 32, 32, 塗りつぶし
code , file , x, y, width, height, comment
adventure_field.grass , adventure_field.png, 0, 0, 32, 32, 草原
adventure_field.desert, adventure_field.png, 32, 0, 32, 32, 砂漠
adventure_field.rock , adventure_field.png, 0, 32, 32, 32, 岩
adventure_field.sea , adventure_field.png, 32, 32, 32, 32, 海
例: C:\Users\むずでょ\Documents\GitHub\2D-RPG-Negiramen
└── 📂 Workspace
├── 📂 Assets
│ ├── 📂 CSV
👉 │ │ └── 📄 tiles.csv
│ └── 📂 Image
│ └── 📄 tool_box.png
└── 📂 For Unity Assets
├── 📂 CSV
👉 │ └── 📄 tiles.csv
└── 📂 Images
└── 📂 Tile Set
└── 📄 adventure_field.png
「 👆 ツール・ボックスのアイコン・ファイルの置き場所は
ネギラーメンの 📄 Workspace/Assets/CSV/tiles.csv
でいいかな?」
「 その tiles.csv
を作るエディターが欲しいわよね」
「 👆 コメントを入力することを考えると、横に広く使いたいしな」
「 グリッド引くなら ループ文を使うのが鉄板だと思うが
MAUIで ループ文があるのか 分からん……」
📖 MAUI equivalent to CSS background-repeat: repeat;
「 👆 Webみたいに 背景画像をタイルにしてリピートできるなら グリッドが作れるかと思ったが
それも無いみたいだし」
「 動的に XAML の要素を追加・削除する方法も分からん」
「 透明なキャンバスに 罫線引いて タイル・マップの上に 覆い被せりゃいいんじゃないの?」
「 👆 パンくずリスト付けたら もっさり したんだが、
ヘッダーと パンくずリスト を1つに まとめる方法 無い?」
「 👇 ソースの中には いじれそうなものは無かった。もっと探そう」
📖 How to customize the navigation title view of MAUI ContentPage
「 👆 初期状態は こう書いてるのと 多分 おんなじらしい。
これをベースに改造してみようぜ?」
「 👆 パンくずリストと ページ・タイトルを 同じ行に まとめたぜ」
「 スマホの画面には タイトル・バーが無いから、ページの中に作るのね」
※クローム … ウィンドウの枠。タイトルバーなどを含む
「 👆 ひとまず ハードコーディングで 半透明の赤い色で グリッドを置いてみたが どうだろうか?」
「 この画面は ハリボテ なんで……。
動きを付ける段階になったら 調べるぜ」
「 その前に 1枚の画像にタイルが 100枚ぐらいなのか、 1000枚ぐらいなのか、 10000枚ぐらいなのか、
規模を知りたいぜ」
📖 WOLF RPG エディター(ウディタ)でゲームを作る3 ~マップチップとオートタイルを増やす~
「 👆 あれっ、思ってたより小さい……
横 332 × 縦 772 かだぜ?」
「 あれっ? 枠が付いてある…… これは使用例のスクリーンショットか。
横 256 x 縦 608 のものもあるぜ」
「 1つのタイル・サイズは 32x32 を最小として、
64x64 とか 96x64 とか 自由気ままに配置されているぜ!」
「 じゃあ分かった、 256 × 608 を画面に表示してみようぜ?
👇 画像を作るぜ」
「 お父んの思想と ウルフエディターの素材作成勢とで 相性が悪いの わらう」
「 意匠は 相容れない方が 著作権を差別化できて有利なのよ」
[http://www.example.com/index.html?name=banana](http://www.example.com/index.html?name=banana)
^^^^^^^^^^^^
「 👆 Web系なら URLの後ろに クエリー文字列を付けることができるが、
MAUI で画面遷移時に何かデータを渡すには どうやるんだぜ?」
「 👆 parameters
で オブジェクトを渡せそうだな。
渡した先での 受け取り方も 調べるかだぜ」
「 👆 フィールド名を使って、オブジェクトを渡せるみたいだな」
📖 [C#]PNGの幅,高さのみをファイルから高速に取得する方法について
📖 【C#】PNG画像サイズの取得方法
「 👆 渡せるものと、渡せないものとがある。
調べるか……」
「 👆 QueryParameter
アトリビュート(属性)をクラスに1個1個すべて書くのが確実らしい」
「 画面は 手戻り無しで 1発で作らないと 時間が 厳しそうねえ」
「 👆 変更通知プロパティの手順の順序を 間違えると 画面に出てこないから 正確にやるぜ」
「 👆 グリッドは GraphicsView
を使ってプログラム的に描画しているが、
これは どうやって再描画するんだぜ?」
「 👆 graphicsView.Invalidate();
を呼び出せばいいのか。やってみるかだぜ」
「 ビューモデルから どうやって graphicsView
にアクセスするんだぜ?」
📖 Binding from view to GraphicsView property in .NET MAUI
📖 How to databind imageview with drawable in viewmodel?
📖 .NET MAUI: Dynamic behaviour of graphic elements when using MVVM
「 Drawable
に変更通知プロパティを 仕掛けられないの?」
「 👆 ビュー・モデルから IDrawable 実装インスタンスの Draw をコールする方法も分からん」
「 ビュー・モデルから ビューに アクセスできないのかだぜ?」
「 ビュー・モデルは 変更通知プロパティ をビューに仕込んで連携できるだけだな」
「 ビュー・モデルの方から ビューのキャンバスを変更できないと グリッド・サイズを更新できなくない?」
「 じゃあ GraphicsView
を使って グリッドを描画するのが そもそも間違ってる?」
「 伸縮自在なラインを 固定長で1000本ぐらい 画面に持たせた方がいいのかだぜ?」
「 👆 GraphicsView
の横幅、縦幅は バインディングできるから、
ビューモデルから 縦幅、横幅の変更通知を送ったら IDrawing
実装インスタンスの Draw
メソッドを呼び出してくれないかな?」
「 👆 なんらかのタイミングで Draw
メソッドの dirtyRect
引数に矩形が入っているから、
あとは ビュー・モデルから 任意のタイミングで Draw
をコールできれば うまくいきそうだが」
「 👆 画面を出した初回は 画像サイズが分かってるんで グリッドも合わせられるぜ」
「 👆 グリッドの横幅が変更されたタイミングで
グリッドの画像サイズの横幅を 1px 伸ばしたったら、再描画が呼び出されるけど」
「 再描画が呼び出されるトリガーのうち、もっとも無難なものって何だぜ? タグの変更トリガーとか無いのかだぜ?」
「 👆 今回のプログラミングで最初のトリック・コードだぜ。
画像の横幅が偶数のときは 横幅を1伸ばし、
画像の横幅が奇数のときは 横幅を1縮めて 無限に振動させるぜ」
「 グリッドのサイズを変えるたびに
グリッドの右端が 1px飛び出たり 元に戻ったりするんだけど」
「 👆 トリック・コードのせいで 割り食って おかしくなっている箇所は
キャンセル・コードを書いて おかしなものを 隠すぜ」
「 グリッド・サイズを変えれるようになった。今日はここまでだぜ」
「 MVVMの実装 時間かかるんだな、作る画面は 最低限の数で済むように 全体を考えないとな」
「 👆 グリッドの全体も ずらせるようにしたので、 位相 にも対応だぜ」
「 これだけで 日曜日が終わろうとしている。時間がかかるな」
「 下側の タイル 0 0 0 0 のとこに カーソルの位置を表示しなさいよ」
「 お父ん。グリッド1ます の たて に 50000 と入れたら強制終了したぜ?」
「 👆 確かに。あのグリッド、ただの縦線じゃなくて 画像だからな」
「 テキスト・ボックスに バリデーション(妥当性検証)を入れなさいよ」
「 👇 何もかも やり方が分からん。覚え直しだ。 調べるか……」
「 👆 説明の通りコードを書いても IValidity
のところで コンパイル・エラーになるの 分けわからんな」
📖 RequiredStringValidationBehavior
📖 NumericValidationBehavior
「 👆 NumericValidationBehavior
を調べてみるか」
「 1辺の上限が 16384 じゃなくて、
面積の上限が 16384 か?
ルート2 したら 128 で小さすぎないか?」
「 なんか限界 攻めたくないな。
グラフィック・カードの性能に依存するのかも知らん。
とりあえず 上限は 8192 にしよう!」
「 👆 じゃあ 無難な 上限 2048 にして、
文句があるやつがでてきたときに 拡張用の設定ファイルを用意しようぜ?」
「 ビュー・モデルの方で 2048 以上を入力したら 2048 として扱うような処理がいるかもしれないな」
C:
└── 📂 Users
└── 📂 むずでょ
└── 📂 AppData
└── 📂 Local
└── 📂 Packages
└── 📂 1802ca7b-559d-489e-8a13-f02ac4d27fcc_9zz4h110yvjzm
└── 📂 LocalState
├── 📄 configuration.toml
👉 └── 📄 settings.toml
「 👆 settings.toml
ファイルを作って、そこに グリッドのタイル・サイズの一辺の上限は 2048 だという設定を
書いておこうぜ?」
「 両端に太さが 2px のグリッドの線があるから、1px ずつ食み出るとして 2048 から 2px 引いて 2046 にすべきだと思うが」
[tile]
# 一辺が 2048 ピクセルのキャンバスを想定し、両端に太さが 2px のグリッドの線があって 1px ずつ食み出るから 2px 引いて 2046
max_width = 2_046
max_height = 2_046
「 👆 1辺が 2048 ピクセルのタイルを作りたいやつは 設定を変えろ」
「 👆 バリデーションというのは ビューの色を変えるぐらいしか機能がないので、
ビューモデルでは 単に リミット・チェックを入れた」
「 入力された値は(弾かず)とりあえず受け取る、という思想なのね」
「 入力を受け取らないフラグ 無いのかなあ? もっと探してみろだぜ」
「 お父ん。マウス・ドラッグして タイルを連続選択できないのかだぜ?」
「 ドラッグ アンド ドロップなのかな?
プレス アンド リリースじゃないかな?」
「 ドラッグ アンド ドロップって、
ファイルをつかんで アプリケーションの領域に落とすやつを言うのよ。
タイルの連続選択は プレス・アンド・リリース じゃないかなあ?」
「 今は タップした時点で カーソルの位置が決まっているが、
リリースした時点で カーソルの位置を決めた方がいいのかだぜ?」
「 タップしたジェスチャーは 捕捉できるが、
リリースしたというジェスチャーは どうやって捕捉するのかな?
調べるか」
「 👆 ドラッグ アンド ドロップは ファイルを落とすやつだしな」
「 👆 ポインター・ジェスチャーには 入りと 出があるのか。これを調べてみるか」
「 エンターは 画像と重なったところで、
エグジットは 画像の外と重なったところか。
マウス・ボタン押下とか、マウス・ボタン・リリースではないんだ」
「 タップは マウス・ボタン押下と マウス・ボタン・リリースのペアか?」
「 そうだぜ。
それも マウスを移動させずに マウス・ボタン押下と マウス・ボタン・リリースを
行わなければいけないぜ」
「 マウス・ボタン押下と、 マウス・ボタン・リリースを 別々に捕捉する方法を探せだぜ」
「 マウス・ダウンと マウス・アップを検知する 何らかの方法はあるのかしら?」
「 ペン・タブレットで 必要になりそうな機能が MAUI に無いなんて
理解できないが お父んは そのような 理不尽や 意味不明を 越えていくのに長けているのだろう」
「 👆 カンペキは無いものだぜ。タイルの連続選択できあがり」
「 現代は UUID が流行ってて URL は わけの分からない数字、文字の羅列になってしまったが、
UUID が真のIdなら URL 無くして UUID にすればいいものの、
そうならないのは 名前 からイメージできる姿があるからだぜ」
「 こんな ネギラーメンで ユニコードが使えない環境のことを 本気で考えなくていいだろ。
ユニコードでいいんじゃないか?」
「 本気で考えなくていいだろ。 64文字でいいんじゃないか?」
「 1~4バイトのユニコード文字が 64個であることの意義は?」
「 ツクラーが タイルに名前を付けようと思ったときの 文字数の分布は?」
📖 Tiledによるタイルマップの作成とオブジェクトデータの取得を行う!:Cocos Creator
Tiled Cocos Creator
📖 KRAFT Macaroni & Cheese Easy Mac Cups (12 x 2.05 oz cups)クラフト マカロニチーズ イージーカップ 12パック 海外直送
「 👆 適当にクリックしたが 英語表記、日本語表記をつなげて 重さや個数、海外直送まで入っているぜ」
「 すると みんな タイル名に 検索のための情報を 詰め込みだすのでは?」
「 1つのタイルに タグは何個付けれて、1つのタグの長さは何文字までなの?」
「 これは再帰的な話しで、1つのタグに たくさんの検索の情報を詰め込みだす……」
「 タイル名を付けるの めんどくさくなって タイル1
、 タイル2
、タイル3
みたいな名前を付けるやつが
蔓延したらどうする?」
「 連番付けられるぐらいだったら、 Id は こっちで採番してやるかだぜ」
「 Id だけ必須にして、それは自動で採番して、
100文字のテキスト・フィールドに 半角カンマ区切りで タグを自由入力させたらどうだぜ?」
「 49個のアルファベットを タグで登録するやつがいて うざいかもしれない」
「 タグも 日本語で入力されたり、英語で入力されたり、表記揺れしたりするんじゃない?」
「 じゃあ カテゴリー・ツリーを予め こっちで定義して そのノードを選ばせる方式にして、
それ以外に 固有名詞 を自由に入れれる欄を置けばどうか?」
「 Id欄と、100文字のコメント欄の2つにしましょう!
検索のための仕組みは 後回しで」
「 動かしてみないと分かんないから それで 作ってみましょう」
C:\Users\むずでょ\Documents\Unity Projects\Negiramen Practice\
└── 📂 Assets
└── 📂 Doujin Circle Negiramen
└── 📂 Negiramen Quest
└── 📂 Auto Generated
└── 📂 Data
└── 📂 CSV
└── 📂 Tile Set
👉 └── 📄 map-tile-format-8x19.csv
Id, Left, Top, Width, Height, Comment
1, 96, 64, 64, 64, 草原
「 クエリー・パラーメーターで ビュー・モデルへ CSVファイルへのパスを渡したつもりなんだが、
コード・ビハインドの Load 時には ビュー・モデルに CSVファイルへのパスが入ってないな」
「 👆 書かれてる通りにやってるんだが……、あっ、スペルミスだ!」
「 👆 とりあえず ハリボテ で、1行追加 できるようにしたぜ」
「 コメントの文字数のバリデーション、
Idの自動採番、
既に追加されているタイルは コメントを表示、ボタンのラベルも[追加]から[上書]へ変更、
削除ボタンの実装、
CSVを固定レイアウトではなく、列名を見るように変更、
CSVのダブルクォーテーションのエスケープ、
少なくとも これらは 残っているな」
「 MAUI がタップ操作にUIを寄せていて、マウスダウン、マウスアップ が検知できないの クソなんだが」
「 最初の1回目のタップで あんたが枠を出すから、これでいいんだと思っちゃって
そしたら もう1回 タップしなくちゃいけないって分かって 分かりづらってなるのよ」
「 選択が未確定の間は カーソルが アニメーションしたらどうだぜ?」
「 画面が チカチカ 動くのは オフィス・ソフトの分野では 非常識だぜ」
「 👆 これぐらい はっきりと分かるぐらい 半透明にしてみるか」
「 CSVファイルの中で1番大きなIdより 1 だけ大きいのが 新しいIdだろ。
お父ん。実装しろ」
「 スキャンするのは カッコ悪いから 行が追加されたタイミングで 最大のIdを常に更新するかだぜ」
「 タイルが 1000個 登録されていたら、
画面上には タイル 1000個 のスプライトを 表示しないと いけないのかだぜ?」
「 画面を開いたときと、
タイルを追加・削除したときに キャンバスを描きなおせばよくない?」
「 じゃあ 実際のタイル画像とは別に、
ワーキング(Working;作業中)なタイル画像を 内部的に持っておけばいいかな」
「 👆 Source
に内部メモリーを指定できるのかだぜ?」
「 作業中の画像は、
ネギラーメンのワーキング・スペース、
ユーザーの AppData、
ユニティの Assetsフォルダー、
どこに置くのがベスト・プラクティスだぜ?」
「 ユニティの Assets フォルダーには 資産じゃないものは 置きたくないわねえ」
「 ネギラーメンを2重起動しなければ 問題ないんじゃない?」
例: C:\Users\むずでょ\Documents\GitHub\2D-RPG-Negiramen
└── 📂 Workspace
├── 📂 Assets
├── 📂 For Unity Assets
└── 📂 Temporary
└── 📂 Images
👉 └── 📄 working_tile_set_canvas.png
「 👆 こんな感じで テンポラリー(Temporary;一時的な)フォルダーを切ろうかな?」
「 いーや。ソース・コードの中に設定が書かれているのは メンテナンス 時の選択肢を狭めて良くない。
外部ファイルで全部 指定できるようにしたいぜ」
例: C:\Users\むずでょ\Documents\GitHub\2D-RPG-Negiramen
└── 📂 Workspace
👉 └── 📄 configuration_2nd.toml
[paths]
# ネギラーメン・ワークスペースの作業中のタイル・セット・キャンバスPNG画像ファイルへのパス
working_tile_set_canvas = "{negiramen_workspace_folder}/Temporary/Images/working_tile_set_canvas.png"
「 👆 厳密に行くなら 構成ファイルに この1行を書き足しておきたいぜ」
「 そのファイル・パスは MAUI から編集できるようにするのかだぜ?」
「 じゃあ ネギラーメンを開発する お父ん や、
ネギラーメンを改造する人だけが 知ってればいいんだ」
「 configuration_2nd.toml
は 隠さなくていいの?」
「 configuration_2nd.toml
って名前だったら、じゃあ configuration_1st.toml
は どこにあるんだ?
って探さない?」
例: C:\Users\むずでょ\Documents\GitHub\2D-RPG-Negiramen
└── 📂 Workspace
👉 └── 📄 user_configuration.toml
「 👆 user_configuration.toml
にしよう!」
「 👆 設定ファイルの中で変数が使える仕組みを 簡易実装したところで 今日はここまでだぜ」
「 最初に作っとかないと あとで困るやつだし、
本筋の作業が進んでないけど いっか~」
「 お父ん、タイル・セット画像へのファイル・パスをハードコーディングしてるだろ。
まず これを クエリー・パラメーターに置き換えて、
画面遷移元から タイル・セット画像へのファイル・パスを受け取る形にしろだぜ」
(カタ カタ カタ カタ ……)
「 次はその タイル・セット画像へのファイル・パスを読込んで、
そして
C:/Users/むずでょ/Documents/GitHub/2D-RPG-Negiramen/Workspace/Temporary/Images/working_tile_set_canvas.png
へ出力しろだぜ」
(カタ カタ カタ カタ ……)
📖 画像ファイルを読み込み、Imageオブジェクトを作成する
using System.Drawing.Image;
「 👆 MAUI に System.Drawing.Image
無い」
「 マルチプラットフォーム化で 使えなくしているパッケージなのでしょう」
「 👇 現代では こっち読むのか。インターネット上の大量の記事が 無駄になったかも知れないなあ」
📖 Microsoft > Image(コントロール)
📖 Microsoft > イメージ(メモリー)
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
image = new W2DImageLoadingService().FromStream(stream);
}
「 👆 リソース・フォルダーの中の画像を取得する方法が 書いてるな。
今回は逆に 任意のディレクトリーの画像のメモリー・ストリームを取らないといけない」
public async Task<string> ReadTextFile(string filePath)
{
using Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync(filePath);
using StreamReader reader = new StreamReader(fileStream);
return await reader.ReadToEndAsync();
}
「 👆 このテキスト・ファイルの例を、画像に書き換えられないか?」
using Microsoft.Maui.Graphics.Win2D;
「 プリプロセッサ・ディレクティブを使って Windowsと Macを分ける必要があるんじゃないの?」
#if IOS || ANDROID || MACCATALYST
using Microsoft.Maui.Graphics.Platform;
#elif WINDOWS
using Microsoft.Maui.Graphics.Win2D;
#endif
// ... 中略 ...
#if IOS || ANDROID || MACCATALYST
// PlatformImage isn't currently supported on Windows.
TheGraphics.IImage image = PlatformImage.FromStream(inputFileStream);
#elif WINDOWS
TheGraphics.IImage image = new W2DImageLoadingService().FromStream(inputFileStream);
#endif
「 👆 プラットフォームの違いを メソッドが隠蔽してくれたらいいのに……」
//
// タイル・セット・キャンバス画像の読込
//
var task = Task.Run(() =>
{
try
{
// 読込元(ウィンドウズ・ローカルPC)
using (Stream inputFileStream = System.IO.File.OpenRead(tileSetImageFilePathAsStr))
{
#if IOS || ANDROID || MACCATALYST
// PlatformImage isn't currently supported on Windows.
TheGraphics.IImage image = PlatformImage.FromStream(inputFileStream);
#elif WINDOWS
TheGraphics.IImage image = new W2DImageLoadingService().FromStream(inputFileStream);
#endif
//
// 作業中のタイル・セット・キャンバス画像の保存
//
if (image != null)
{
// 書出先(ウィンドウズ・ローカルPC)
using (Stream outputFileStream = System.IO.File.Open(workingTileSetCanvasImagefilePathAsStr, FileMode.OpenOrCreate))
{
image.Save(outputFileStream);
}
}
}
}
catch (Exception ex)
{
// TODO エラー対応どうする?
}
});
「 👆 MAUI から Windowsのフォルダーにアクセスできるけど、
マルチ・プラットフォームというメリットが無くなってしまうよな」
「 ユーザーに AppData
フォルダー下を いじらせるのは
隠しフォルダーの意義を失うわよ」
「 今は タイル・セット画像の方を MAUI に表示してるだろ。
それを止めて、こっちの 作業中のタイル・セット画像の方を MAUI に表示してくれだぜ」
(カタ カタ カタ カタ ……)
「 すでに登録済みのタイルは 半透明の青色で塗りつぶしておくと、
残作業が見えやすくなるんじゃない?」
Microsoft.Maui.Graphics.IImage
Microsoft.Maui.Graphics.ICanvas
「 👆 あれっ? 描画ができるのは IImage
じゃなくて、 ICanvas
の方だぜ?」
「 IImage
を ICanvas
に変換する方法がないか、調べろだぜ」
「 ググっても出てこないから デバッグライトするぜ。
IImage の実体は W2DImage
らしいんだが、これも探しても出てこない」
「 タイル・カーソルと同じように IDrawable
を実装したクラスを作って、
XAML の GraphicView 要素の Drawable 属性で指定したらどうだぜ?」
「 IDrawable
を実装したクラスと、ビュー・モデルが連携できないの
グローバル変数での連携が大変だぜ、どうにかならないの?」
「 👇 BindableObject というのが あるそうだぜ」
📖 In .NET MAUI, how do I pass variables to a GraphicsView.Drawable in a ContentView
📖 How to pass variable data into .NET MAUI GraphicsView canvas
「 人は 整数を見ると 1から順にソートしたくなる。
BASE64 なら ソートしようとは思わない。
Idをソートしようとする悲しい人類を救うことができる」
📖 Convert.ToBase64String メソッド
📖 Converting string to byte array in C#
「 👇 桁数が気になるわね。
1
は MQ==
だけど、 1000
は MTAwMA==
よ?」
📖 データ変換ツール
「 3桁ぐらいの文字なら 4桁ぐらいに変換されて気にならないが、
4桁ぐらいの文字は 8桁ぐらいに変換されて メモリ効率は 2倍になって 悪くなるな。
だいたい、 1.3倍ぐらい メモリ効率は悪くなるぜ」
「 メモリ効率が1.3倍に悪くなることと引き換えに
悲しい人類がIdをソートしようとすることを防ごうと言うのね」
「 👆 Idが1000に達したとき、BASE64にすると 改行されてしまうのは 悪しと見るか、
コーナーケースに合わせるとキリがないので これで良しと見るか」
「 1000枚もタイルが含まれる タイル・セット画像を 利用するやつ、
そんなとこ 気にするでもなく ハードワーカーだろ」
「 999枚もタイルが含まれない タイル・セット画像を 利用する人にとっては、
無駄なスペースが 空いていることの方が きにするでしょう」
「 あっ、Id をマウスで選択できね。気になるな……。
選択可能なテキストで、かつ入力ができない リード・オンリーな設定できるかな……?」
「 👆 でけた。
フォント・サイズは きっかり スタイルで決まっているようで 任意サイズにはできなかったので
Entry
要素を広げたぜ」
「 ラベルの Id
と コメント
の間に パディング 入れられないの?」
「 良くなった。ラベルの文字は 隣のセルと くっつかないようにしましょう」
「 👆 Id
だけだと オーラル・コミュニケーションで困るだろうから
タイルId
と書いといてやろう」
「 そのIdって、 ひだり、うえ、よこ、たて から一意に決まらないの?」
「 例えば 128,32,96,32
を BASE64に変換擦ると MTI4LDMyLDk2LDMy
だぜ。
これだったら ベクターを2つ 覚えておいた方がマシかも」
「 情報で構成されている Id なんて、イケてないぜ。それは暗号だぜ」
「 あくまで 表示に BASE64 を使っているだけで、内部的には 整数なんで」
「 このタイルIdは、プロジェクトの中で一意なの?
それとも タイル・セット画像ファイルの中で一意なの?」
「 凝り出すと UUID に行きついてしまう。
タイル・セット画像ファイルの中での ローカルなIdだぜ」
「 じゃあ タイルIdには タイル・セット画像ファイルのIdも 表示しないと
本当のタイルIdにはならないんじゃない?」
「 タイル・セット画像ファイルに Idを振ることが難しい。
プロジェクトの中での ローカルなIdなら 振れるだろうけど、
そのIdは 別のプロジェクトへ使い回せない」
「 ブランチして 開発が平行したら ノー・コントロール になってしまう」
「 じゃあ なんで タイルId なんか採番するんだぜ?
ファイル名、ひだり、うえ、よこ、たて が本当のIdなんじゃないのかだぜ?」
「 あるファイル内の中だけで使える、
ひだり、うえ、よこ、たて の情報を 3桁程度の整数に集約するのが タイルId だぜ」
「 タイル1
、 タイル2
のような タイル名 を ユーザーに入力させないこと、
1
、 2
のような 連番 を ユーザーに入力させないこと、
これらの代替が タイルId だぜ」
「 しかし それは Id の働きを持っていないのでは?
何かと言えば コード なのでは?」
「 内部的には Id なんだが、表示上は コード だな。
じゃあ オーラル・コミュニケーションを考えて ラベルを変えるかだぜ」
「 しかし そもそも BASE64 は、 オーラル・コミュニケーション には向いていないのでは?」
「 じゃあ BASE64の 64文字に対して Apple
とか、 Banana
とか
プロナウンサブル(Pronounceable;発音可能)な単語を当てたらどう?」
「 インターナショナル(International;国際的)に通用するフォニックス(Phonics;発音)ってある?」
「 軍事か通信、航空あたりを調べれば 何か規格があるのでは?」
「 英語のフォネティックコードが人名なんで、大文字にかこつけて 大文字は英語にしたらいいんじゃないの?」
BASE64 英単語 読み
------ --------- -------
A Alice アリス
B Boart ボート
C Castle キャッスル
D Drink ドリンク
E Elf エルフ
F Forest フォレスト
G Gold ゴールド
H Hotel ホテル
I Island アイランド
J John ジョン
K King キング
L Level レベル
M Madam マダム
N News ニュース
O Ork オーク
P Pond ポンド
Q Queen クイーン
R Room ルーム
S Soup スープ
T Talk トーク
U Uncle アンクル
V Video ビデオ
W Wolf ウルフ
X Xmas クリスマス
Y Yogurt ヨーグルト
Z Zebra ゼブラ
a and アンド
b big ビッグ
c clean クリーン
d damage ダメージ
e evil イーブル
f fake フェイク
g get ゲット
h hit ヒット
i ice アイス
j jump ジャンプ
k kick キック
l love ラブ
m magic マジック
n no ノー
o oily オイリー
p poison ポイズン
q quiz クイズ
r rain レイン
s solty ソルティー
t trendy トレンディー
u up アップ
v victory ビクトリー
w wind ウィンド
x xrated エックスレーテド
y your ユワ
z zenith ゼニス
0 0 (数字)
1 1 (数字)
2 2 (数字)
3 3 (数字)
4 4 (数字)
5 5 (数字)
6 6 (数字)
7 7 (数字)
8 8 (数字)
9 9 (数字)
+ + (プラス)
/ / (スラッシュ)
= = (イコール)
「 あと、 BASE64 は、文字列連結しないのであれば 桁のプレースホルダーの =
は省いていいそうよ」
「 それがタイルの名前としての働きを持つと 認識できないのよね」
「 👆 どうしても 名前を書きたいやつは コメント欄に書け、と親切に 書いておくぜ」
「 タイル・パレット上で 範囲選択したら、
そのタイルは すでに登録済みか、まだ登録されていないか、判定しろだぜ」
(カタ カタ カタ カタ)
「 前のトリック・コードは 奇数回実行すると働くが、偶数回実行すると働かないというバグを踏んだので
対応中」
「 👆 登録したタイルの情報を 読み込むようにしたが、
全体的に動きが 怪しいので 調整中だぜ」
「 👆 タイル・コードと コメントの入出力は 動きが付いたぜ」
「 タイルが登録されているとき、ボタンのラベルを追加から上書へ変更してくれだぜ」
「 👆 タイルが登録されているときは、ボタンのラベルを 上書
に変えるようにしたぜ」
「 その 上書
を、 Overwrite
にしても ボタンの中に収まるの?」
「 横幅が長い……。 海外人、どんな UI 使ってるんだぜ?」
「 日本人が ビューにたくさん項目を詰めるの こだわるの、
チャイニーズ・キャラクター文化だからなのかな?」
「 じゃあ、アイコンか 漢字かを選べるようにしたらいいのか……?」
📖 Microsoft > Visual Studio のイメージとアイコン
「 👆 欲しいアイコンが無いし、似たようなアイコンばっかり いっぱいある」
「 👆 しかし MAUI にも最初から ちょびっとだけ リソースが入ってるな」
「 MAUI 用のリソース集も どっかで配られてんじゃないの?」
📖 .NET MAUIで、Emoji(絵文字)やIcon(アイコン)を打とう
📖 Google Fonts > Material Symbols And Icons
「 👇 ボタンに アイコン・フォントを表示させるのは どうやるんだぜ? 画像の方法はあるが」
📖 [.NET MAUI][Button] ボタンにイメージを表示する
「 👇 画像も 文字も アイコン・フォントも 方法を統一してくれたらいいのに……」
📖 Font Icons in Shell Tabs not showing
「 👆 あれっ? 文字化けだ。 Windows 11 には Segoe Fluent Icons
フォントは最初から入ってると 読んだが……」
「 マルチ・デバイスのプログラミングが ホストOSに依存したら 悪いんじゃない?」
「 👇 どこから Segoe Fluent Icons
フォントを入手するんだぜ?」
📖 Microsoft > fluentui-system-icons
📖 Microsoft > Segoe UI font family
「 👇 ずっと前から シーゴーは Windows に入っているみたいだけど」
「 👇 シーゴーは 日本語フォントではない、という話しもあるわね」
「 👆 フォントのような不確定要素を使うのは諦め、ビジュアル・スタジオ2022イメージ・ライブラリーから
ザムルを取ってくることにするぜ」
📖 Microsoft > リソース ディクショナリのマージ
「 👆 リソース・ディクショナリーとして マージすれば 使えるか?」
「 👆 リソース・ディクショナリーの書き方を忘れたので 調べるぜ」
「 👆 ビジュアル・スタジオ2022イメージ・ライブラリーの Edit.xaml
の Viewbox
を、貼り付けるぜ」
📖 I can't set a .svg file as a maui app icon
「 📂 Resources/Images
フォルダーは 何か特別な管理下にあるみたいで ふつうのフォルダーのようには扱えなさそうだぜ」
「 MAUI は、ファイル・パスという概念を 無くしたいのでは?」
「 👆 MAUI の最初に出てきた ロボット、あいつ SVG画像らしい。
あいつは 表示されて、
ペンを ボタンの上に表示できないの、何故だぜ?」
「 👆 なんで ペン がでかい?
そして SVG なのにラスタライズされてる?」
「 👆 さらに なぜ .svg
ではなく .png
にしないと画像が出てこない?」
「 👆 フォルダーを切って その中に入れると、パスでアクセスできなくなるのは 何故だぜ?」
📖 Add images to a .NET MAUI app project
「 Android アプリがこうしてるから、 MAUI もこうしてる、というような 腰抜けの話しが 書かれているぜ。
クソじゃないか」
📖 Images in subfolders not showing up on Windows but do on Android #8628
<ItemGroup>
<MauiImage Include="Resources/Images/XXXXXXXX/*" />
</ItemGroup>
「 👆 なんか こういうタグをどこかに書くと そのフォルダーがプロジェクトに認識されるらしいぜ」
「 ファイルを開けまくって 消去法をしている途中で 2D RPG Negiramen.csproj
に それっぽいコードを発見したぜ」
「 👆 \*
を、 \**\*
に変えると、
直下フォルダーの任意のファイルではなく、
サブフォルダ―も下りて行って任意のファイルを 見てくれるようだぜ」
「 👆 また、 Android に迎合して、ディレクトリー名、ファイル名に 大文字や空白は使えず、
小文字英字、ハイフン、数字だけを使うようにするぜ」
「 👆 そして ディレクトリーは無視されるという クソ仕様により、
ファイル名だけで指定することになるぜ。拡張子は、ファイル名に .png
と付いていても XAML には .png
と指定するクソ仕様」
「 MAUI は、プラットフォームごとに 異なる画像ファイル形式で用意されても、
XAML には .png
と書こうという MAUI の開発者の頭が残念なことに なってるのね~」
「 ラスター画像と ベクター画像の特性の違いも理解できないぐらい 知能が下がってるのかだぜ?」
「 👆 ファイル名が被りたくないので プレフィックス(Prefix;接頭辞)を付けるぜ」
「 インフラの出来が悪いと 要らんテクニックが使われてしまうな」
「 フォルダー下に、別の命名システムを持ったフォルダーを ぶらさげたいこともあるのに、
それが考慮されていないのは
MAUI の開発者が 開発の現場経験が未熟だろうから 全員クビになっても 不思議ではないわねえ」
「 👆 SVG形式画像を PNG形式画像に変換した後のものが ボタンの上に描かれるから、
テキスト・カラーを 白 にしても 白くならないぜ!」
「 MAUI お前 コンピューター・グラフィックスという 自覚 無いのかだぜ?」
📖 Specify the color of a SVG image in .NET Maui
「 MAUI の出来が悪いんじゃないの? あんまり 期待できないんだから
トリックを使っていくことに なるんじゃない?」
「 👆 ボタンは 横に広く使うように 妥協。
ツールチップ・ヒントも 組み合わせたので、
国際化は 少々横長のボタンと ツールチップ・ヒントで なんとかしてくれだぜ」
「 タイル名、または コメントを自由入力
というメッセージが ダサいから タイル名
だけでよくない?」
📖 .NET MAUI : Write multilingual apps easily
「 👆 Xamarine の方法がそのまま使えるらしいが、それでいいのか もっと調べるか……」
「 👇 昔の Windows のやり方も 冴えてないしなあ」
📖 How to implement .NET MAUI localization
📺 Localization in .NET MAUI - Adding Multi-Language to Your Apps
📖 .NET MAUI Localization Sample
「 👆 サンプルは動くが、どうやって 📂 Languages
フォルダーを作ったのか
手順が分からんな」
「 👇 ResXManager は 多言語化の鉄板ソフトなんで インストールしておこうぜ」
「 しかし ResXManager の画面は どこから出すんだぜ?」
「 👆 昔ながらの場所に作られたが、そこじゃなくて
📂 Resources/Languages
フォルダ―の下に置いてほしいんだぜ」
「 👆 サンプル見ると、単に ファイルを移動しただけに見えるな……
やってみるか」
「 👆 ファイル名も Resources
から AppResources
に変えたろ」
「 👆 LocalizationResourceManager
クラスを真似して 自分用に設定」
「 👆 アプリケーション起動直後に 文化を日本に設定。
わたしが日本語しか確認しないから 日本がデフォルトでいいだろ」
「 👆 TranslateExtension
も真似て作り、自分に合わせて編集」
「 👆 ビューのコード・ビハインド、つまり ContentPage
の継承クラスには
ローカライゼーション資源管理へのアクセッサ―を書いておくぜ」
「 👆 とりあえず AppResources.resx
へ、 1件だけ 文字列リソースを 記入するぜ」
「 👆 MainPage.xaml
に xmlns:app="~"
を追加し、
Text プロパティへ {app:Translate LayoutTheScreen}
と書いてみるぜ」
「 カルチャーを変更するドロップダウン・リストみたいなものが欲しいわねえ」
「 試しに 英語と 中国語のリソースも 作ってみてくれだぜ」
「 👆 ResXManager
がインストール済みなのは確認できるんだが、
どこから実行するんだぜ?」
「 👆 ニュートラル・リソース・ランゲージを 英語から 日本語へ 変えるぜ。
理由は 開発者が わたし1人だから」
「 👆 とりあえず イーエヌ・ユーエス(en-US;英語(米国))は 必須だろ」
「 👆 ばばばっと 翻訳して 空いてるとこ 埋めてくれる機能があるから
使おうぜ?」
「 👆 なぜか 英語しか 対応してくれなかった。 寄付が足りないとか?」
「 👆 AppResources.en-US.resx
ファイルが増えてるぜ」
「 📂 Languages
フォルダーの中をサーチして、翻訳できる言語を調べて
言語を選べるリスト・ボックスが欲しいわねえ」
「 そのフォルダーに アクセスする方法が 見当つかないんだが」
「 👆 実行環境を見にいってしまうので、開発環境のプロジェクト・フォルダーの中とか見れないのでは?」
「 👇 じゃあ ビルド前のイベント を書いたらいいんじゃない?」
📖 Pre build Events in Visual Studio
📖 Is there a way to get a flag image from a C# CultureInfo?
「 こういう センシティブな素材は デジタル庁 が作って欲しいものだぜ」
「 国旗は後回しにし、ISO言語コードのドロップダウン・リストを
ハードコーディングする方向で 次回 考えようぜ?」
「 日本語か、英語かを選べるドロップダウン・リストボックスを置いてくれだぜ」
「 まず MAUI に ドロップダウン・リストボックスがあるのかどうかも 分からないしな。
調べないとな」
「 👇 マイクロソフトの記事、映像がなくて どんな画面が出てくるのか 分からん」
📺 How To Create Custom Picker Control (Dropdown Button) In .NET MAUI
📖 ピッカー
「 👆 ピッカーに 最初に選択されている値を表示することは できないぜ」
「 じゃあ、値が最初に選択されていることを ピッカーでどう表現するんだぜ?」
「 マイクロソフトの社員の知能が欠如していて それはできないぜ」
「 👇 初期値を表示するのって HTML の Select タグの Option 要素の Selected
属性でも指定できるのに、
MAUI で 何でできないの?」
「 マイクロソフトの社員が欠陥だというところまでは 事実と認めましょう。
それは置いておいて わたしたちは ピッカーに初期値を表示したいのよ。どうしたらいいのよ?」
「 👆 ContentPage
要素に Loaded
属性を追加して……」
「 👆 コード・ビハインドで picker.SelectedItem = "ja-JP";
とでも書いておけば……」
「 👆 ページの表示完了時には ピッカーで ja-JP
が選択されている、という状態にできるぜ」
「 トップページは MVVM にまだ対応してないんで、これから対応させるぜ」
(カタ カタ カタ カタ)
「 でけた。 さっきのイベントハンドラも コード・ビハインドも要らんかった。
MVVM だけでいけるぜ」
「 あとから 多言語化しようとして レイアウトの変更が必要になったりしたら 大変だしな」
ramen-tabero-futsu2.png
「 👆 この アイテム・リソースも ハード・コーディングじゃなくて
どこかのファイルにまとめて、一元化したいんだが」
「 App.xaml
に リソース・ディクショナリーを作って、それを読み込んだらどうだぜ?」
「 👇 テンプレートは リソースにできるが、 データは リソースにできないんじゃないか?」
「 ItemSource
の使い方が分からん。調べても出てこないぜ」
「 この前の多言語化のときの おっさんの You Tube に なんか まとまってんじゃないの?」
「 👇 このおっさんは MAUI を作ったチームの中の人らしいぜ」
Zzz
「 猿を1匹記録するクラスを作るんだけど、例が JSON なのよ」
「 じゃあ 真似して 言語を1つ記録するクラスを作るかだぜ」
Name,
ja-JP,
en-US,
「 👆 📂 Releases/Raw
フォルダーの中に 📄 languages.csv
ファイルを作ろうぜ?」
「 その1行に対応する Language
クラスを 📂 Models
フォルダーの下に作りましょう」
「 C# に、 CSV をシリアライズする仕組みなんか あるのかだぜ?」
「 Resources/Raw
フォルダーの中にあるファイルって どうやって読取るんだぜ?」
📖 Microsoft > Bundled Files
📖 Get Resources\Raw Path in .NET Maui
「 👆 None
とか Remove
って何だぜ? 無いことになってんの?」
「 👆 出力ディレクトリーにコピー
を 新しい場合はコピーする
に変えたら、何が起こるんだぜ?」
「 コピーをしなければ bin
フォルダーの下に languages.csv
はなく、コピーすると2つある」
「 Visual Studio のコンフィグ・ファイルは無いの?
インストール時に 作業ディレクトリーを設定できなければ、
設定ファイルの置き場所も 定まらなくない?」
「 アプリケーション・フォルダーに置いてくれないのかだぜ?」
「 じゃあ いったん、言語のリストは ハード・コーディングするものとしようぜ?」
「 恐らく、ビューモデルの 変更通知プロパティじゃないか?」
「 👆 ItemSource
に バインドすれば うまくいくぜ」
「 どのページにも 言語ピッカー を置きたいときは、
どのページにも LanguageCollection
変更通知プロパティーを作ることになるの?」
「 👆 ロケールIdのリストなんか 途中で変わらないだろ。
セッターのないプロパティとして、グローバル変数のように利用できるように App
クラスに定義しようぜ?」
「 👆 変更できない変更通知プロパティなら、グローバル変数を取得してても 不整合は起こらないだろ」
「 👆 テキストボックスの白が目立つように 配色を変更し、
ロケールIdのピッカーを 右上に置いたぜ」
「 👆 📂 Resources/Styles
フォルダーの下にあるぜ」
「 👆 ダーク・モードのことを考えずに いじってしまった。
どこか 壊してしまったかも知れないが 分からない。
勘弁しろだぜ」
「 👆 トップページの配色も 白が無くなるように ゲインズボロ という色をベースに敷いたが
ダークモードにしたら どうなるのか 知らん」
「 登録済みタイルがどれか分かるようにしてくれだぜ。 例えば……」
「 タイルは 重なって登録されることは あるのかだぜ? 例えば……」
「 あるある。 タイル・セットは それ自体が パレットのような配置をしているのよ」
「 ゲリマンダー(Gerrymander)みたいな形や、飛び地が必要かどうかも 洗い出したいぜ」
「 ファイルのバリデーターを作るべきだが、とりあえず 手作業で直せだぜ」
「 👆 Excel で CSVファイルを開いたら 文字化けしてるんだが……」
「 BOM付きの UTF-8 じゃないと Shift-JIS と判定されてるわよ」
「 👆 BOM 付きの UTF-8 にしたら 文字化けが直った!」
「 👆 こういう感じのデータを、エディター上で どう表示するかだよな」
「 👆 IDrawableインターフェース実装クラスの Draw関数にビューモデルからどうやって値を渡すんだぜ?」
📖 In .NET MAUI, how do I pass variables to a GraphicsView.Drawable in a ContentView
📖 How to pass variable data into .NET MAUI GraphicsView canvas
「 👆 なんだか分からないが 束縛可能プロパティ の書き方のパターンを なぞろうぜ」
「 👆 さっきの IDrawing 実装クラスは、リソースとして ContentPage に読み込まれて……」
「 これでいいなら ビュー・モデルの変更通知プロパティを、
IDrawable クラスの束縛可能プロパティに 渡すことができそうねえ」
「 👆 IDrawing 実装クラスは 束縛可能プロパティに値が入ってくるんで
グローバル変数を1つ 取り除くことに成功だぜ」
「 IDrawing 実装クラスから グローバル変数を取り除いていきましょう!」
(カタ カタ カタ カタ)
「 さらに まずいことに 線の太さが 0 になり、
for 文で 横に 0 移動しながら 縦線を引く 無限ループに陥った……」
「 束縛プロパティの初期化が始まる前に Draw メソッドが呼び出されているのでは?」
「 どっちが頭で どっちが尻尾問題で、
IDrawable 実装クラスのプロパティが、ビュー・モデルのプロパティーを上書きしてくることはないだろうか?」
「 グローバル変数が いつ変更されたとか、ビューモデルには 分からないからな」
「 グローバル変数を 使わないようにしたが、束縛プロパティが 渡せていないぜ」
「 👇 GraphicsView
の Drawable
クラスの Draw
変数へ、プロパティを渡すだけのサンプル・プログラムを組んでみようぜ?」
📖 Git Hub > Muzudho > MAUI-BindableProperty-Practice
「 Bindable
なプロパティなのに Binding
したら動かないなんてことが あるのかしら?」
「 👆 確かに 変更前と、変更後の値を取れて 気が利くが、
そういうの 自動でやってくれるわけじゃないのかだぜ?」
「 いや、取れてないぜ。 Binding
したものは 取れてない」
📖 Binding not working on custom BindableProperty
「 GraphicsView
はビュー・モデルを持ってないから Simple30
を Binding
しようとしても持ってないんで、
親コンポーネントのビュー・モデルを指せ、ということらしい」
📖 .NET Maui Binding a contentview to parent ViewModel MVVM
📖 ContentView
「 ContentPage
使ってるのに、 ContentView
の説明されても 利用できないぜ」
(カタ カタ カタ カタ)
「 👆 ビューの コード・ビハインドに、
バインディング・コンテキストを公開する パブリック・メソッドを書くぜ」
「 👆 ビューの ContentPage
要素に、 x:Name
属性を付けて、その値を thisContentPage
とでもしておくぜ」
「 👆 これで MainPageVM
プロパティが公開されてるんで、そのメンバーにアクセスできるぜ」
「 バインディング・コンテキストを公開しているのが 気になるなあ」
「 👆 グローバル変数を減らし、 BindableProperty を使って GraphicsCanvas の Drawable 属性のクラスの
Draw メソッドに プロパティ値を渡すことに成功したぜ」
「 ViewModel を public にするのは心理的抵抗がある。
公開するのは interface にして、プロパティだけ公開しろだぜ」
「 キャンバスで描画するのか、画像オブジェクトでピクセルを直接描画するのがいいのか」
「 インプレース(In-place)で クロップ(Crop)と コピー(Copy)を1ステップでできないと
実行速度が遅くなるんじゃないの?」
「 画像は Microsoft.Maui.Graphics.IImage
というインターフェースに抽象化されていて、
とても 高速に画像を描画することを指向しておらず、
マルチ・プラットフォームで一番性能の低いマシンで動くように考慮されてる護送船団方式の雰囲気を感じるぜ」
「 この IImage インスタンスは あくまで 画像ファイルを指さしているだけで、
描画をするときは GraphicsCanvas を使えという思想なのかもしれない」
「 画面の描画前に 画像の前処理を 終わらせておきたいことなんて
いっぱいあると思うのに
キャンバスを用意してから 画像処理しなくちゃいけないのね」
「 じゃあ 画面上に キャンバスを2層に重ねて表示することにして、
元画像を下レイヤーに、作業用レイヤーを上レイヤーに置いて
作業用レイヤーに いろんな加工をしていくことにしようぜ?」
「 キャンバスの練習を先にしておいた方が いいんじゃない?」
📖 GitHub > Muzudho > MAUI-IDrawable-Practice
「 変更通知プロパティを バインディング しただけでは
Draw
は呼び出されないのか」
「 GraphicsView に Draw
メソッドの呼び出しを要求するの、どうやればいい?」
「 👆 GraphicsView 要素に x:Name
属性を使って名前を付けて……」
「 👆 コード・ビハインドで GraphicsView 要素の Invalidate( )
メソッドを呼び出せば いいわけかだぜ」
「 ビューモデルで Invalidate( )
メソッドは呼び出せないのかだぜ?」
「 お父んは タイル・カーソルと グリッドと タイル・セット画像を 分けて 置いているが、
そのようにするのと、1つのキャンバスの上に全部描くのとでは、どっちが得なんだぜ?」
「 何も考えなければ 1つのキャンバスの上に全部描くのが お得だぜ」
「 グリッドは ヴィジブル(Visible;可視性)を オン・オフ したいときがあるだろ。
フラグ1個だけで できるというメリットを生かしたい」
「 じゃあ カラード・マップ(Colored Map)のキャンバスも 1つ用意したいわねえ」
「 GraphicsView
の初期設定をする前に Draw
が呼び出されているぜ」
「 設定ミスは エラー・メッセージを出してくれないからなあ」
「 👆 薄いグレーを被せてみたが、
どうやっても 視認性が悪くならないか?」
「 👇 スキア・シャープ(Skia Sharp)というものが あるそうだぜ?」
📖 .Net Maui - Create ImageSource from pixel data
📖 GitHub > Muzudho > MAUI-SkiaSharp-Practice
「 👆 とりあえず SkiaSharp
をインストールするか」
📖 SkiaSharp/samples/Basic/Maui/SkiaSharpSample/MainPage.xaml.cs
📖 Understanding SkiaSharp and Maui Coordinates
「 👇 ここに ピクセル単位でのアクセスの仕方が書いてあるのでは?」
📖 SkiaSharp ビットマップ ピクセル ビットへのアクセス
「 まあ、午前中に独習してきた GraphicsView は使わないというのは 分かったが……」
「 将来的にやろうとしていることを考えると、 GraphicsView
では限界がくるんじゃない?」
「 👆 SkiaSharp.Views.Maui.Controls
をインストールしてみようぜ?」
「 👆 xml の namespace として
xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"
を書き足すぜ」
「 👆 これで skia:SKCanvasView
を使えるようになったはずだぜ」
「 👇 SkiaSharp.Views.Maui.Core
はインストールしてんの?」
dotnet add package SkiaSharp.Views.Maui.Core --version 2.88.3
// NuGET のパッケージ・マネージャー・コンソールの文字化けに対応を期待したが効果なし
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
📖 パッケージマネジャコンソールにおける dotnet コマンド出力の文字化け
dotnet --help
PM> chcp
現在のコード ページ: 932
PM> chcp 65001
Active code page: 65001
「 👆 コードページを 65001 に変えたったが ダメだ」
「 👆 方法も無いし、 Microsoft.Maui.Graphics.Skia
でもインストールしてみようぜ?」
📖 .NET MAUIによるクロスプラットフォーム2D描画(その1)
「 👇 MAUI は SkiaSharp をベースに使ってるんだろう」
「 👇 キャンバスではなく、イメージの方に ピクセルにアクセスするメソッドがあるんじゃないの?」
📖 ByteArrayToImageSourceConverter
📖 MAUI: How to draw image from byte[] on MAUI
「 画像は バイトに分解できるけど、 バイトは 画像に構成できないのか」
「 👇 じゃあ バイト配列を 直に PNG画像として出力すればいいんじゃないの?」
📖 Create a PNG from an array of bytes
「 ファイルの書出しと 読込みたいな 重い処理やるんだったら なんのための画像処理か 分からんが……」
「 MAUI で画像処理しようとするのが 間違いなのでは?」
「 👇 公式の記事を なぞってるだけじゃないか。アマチュアのコピー記事が」
📖 Handling Images With .NET MAUI Graphics
「 明度を下げたいとか、特定の色だけを抽出したいとか、そういう知能はないのかだぜ、記事を書くやつは?
知能だけではない 意志も無い。
インターネット上のアマチュア記事は 公式のダウングレードばかり 情報を増やさない。
筆を折れだぜ」
「 👆 とりあえず、NuGET で 正しい SkiaSharp をパッケージ単位でダウンロードして
アセンブリを使えるようにするところからだぜ」
「 👆 だから この SkiaSharp
パッケージを入れておくだけで 充分なはずなんだが」
「 👆 だから この SkiaSharp
に SkiaSharp.Views
は入ってないようだぜ」
「 👆 SkiaSharp.Views
パッケージもインストールしてみるかだぜ」
「 👆 だから この SkiaSharp.Views
には SkiaSharp.Views.Maui
は入ってないようだぜ」
「 👆 似たような名前のパッケージが並んでいて困るが
とりあえず SkiaSharp.Views.Maui.Core
パッケージを インストールしてみるかだぜ」
「 👆 SkiaSharp.Views.Maui.Core
には SkiaSharp.Views.Maui.Controls
は入ってないようだぜ」
「 👆 じゃあ SkiaSharp.Views.Maui.Controls
パッケージを インストールしてみるかだぜ」
「 UnhandledException
というのは ライブラリが 例外が起こっているのにキャッチせずに終了したということだぜ」
「 カタストロフィック(Catastrophic;大災害的)な フェイラー(Failure;失敗)だぜ」
📖 [BUG] MAUI: SKCanvasView crash, unable to display SKBitmap directly #2139
「 じゃあ SkiaSharp のサンプル・プログラムを真似たろ」
(カタ カタ カタ カタ)
「 👇 SKBitmap なら、ストリームから作れるんじゃないか?
SKImage は、SKBitmap を読込むメソッドを持っているだろ」
「 👇 Xamarine の利用者は SkiaSharp に詳しいらしいぜ」
「 👆 おー、描画できたぜ。
基本的にラスター画像は SKBitmap で記憶しておいて、
表示するときは SKBitmap を SKImage でラッピングすればいいわけだぜ」
「 じゃあ あとは ラスター画像の容量で 画像処理できるな」
「 愚直に 1ピクセルずつ 変更するコードなら すぐ書けそうだが、
並列処理に有利な コードの書き方が 分からんな……」
📖 SkiaSharp ビットマップ ピクセル ビットへのアクセス
「 マイクロソフトの記事の コード・サンプルのリンク クリックしても
コード・サンプルが置いてないの 何でなんだろな?」
「 実行速度の検証は iOS、 Android、 UWP で行われていて、 Windows が含まれていないのは何でだぜ?」
「 Windows 上で Windows 向けのプログラム書いたら有利なの明らかだからよ。
UWP というのが Windows なのよ」
「 unsafe 使って uint のポインター型を使うのが そら早そうだが、
unsafe を使いだしたら C# というより C言語だしな」
SKBitmap FillBitmapUintPtrColor(out string description, out int milliseconds)
{
description = "GetPixels SKColor";
SKBitmap bitmap = new SKBitmap(256, 256);
stopwatch.Restart();
IntPtr pixelsAddr = bitmap.GetPixels();
unsafe
{
for (int rep = 0; rep < REPS; rep++)
{
uint* ptr = (uint*)pixelsAddr.ToPointer();
for (int row = 0; row < 256; row++)
for (int col = 0; col < 256; col++)
{
*ptr++ = (uint)new SKColor((byte)col, 0, (byte)row);
}
}
}
milliseconds = (int)stopwatch.ElapsedMilliseconds;
return bitmap;
}
「 👆 先頭から末尾まで 色を置き換えながら シーケンスしてるだけだ やってることは簡単だ
真似るか」
「 👆 じゃあ今後、ネギラーメンでは アンセーフなものも 使っていくことにするぜ」
「 int 型のメモリ・レイアウトは red, green, blue, alpha で確定かだぜ?
環境により異なるかだぜ?」
「 ビッグ・エンディアン、リトル・エンディアンが関わってくるかどうか 知らないぜ?」
ここでのコードでは、バイトが赤、緑、青、アルファの順序であり、色の SKColorType.Rgba8888 種類と一致していることを前提としています。 これは iOS と Android の既定の色の種類ですが、ユニバーサル Windows プラットフォームでは使用されないことを思い出してください。
namespace MauiApp1;
using SkiaSharp;
/// <summary>
/// 明度を下げる
/// </summary>
internal static class ReduceBrightness
{
// - インターナル静的メソッド
/// <summary>
/// そうする
/// </summary>
internal static SKBitmap DoItInPlace(SKBitmap bitmapInPlace)
{
IntPtr pixelsAddr = bitmapInPlace.GetPixels();
int width = bitmapInPlace.Width;
int height = bitmapInPlace.Height;
unsafe
{
uint* ptr = (uint*)pixelsAddr.ToPointer();
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++)
{
// ビッグ・エンディアンか、リトル・エンディアンかの違いを吸収してくれることを期待して
SKColor color = new SKColor(*ptr);
// RGB値が減れば、暗くなるだろ
*ptr++ = (uint)new SKColor(
red: (byte)(color.Red * 0.7),
green: (byte)(color.Green * 0.7),
blue: (byte)(color.Blue * 0.7));
}
}
return bitmapInPlace;
}
}
「 👆 なるほど、
ビッグ・エンディアンか、リトル・エンディアンか、環境の違いを吸収しようとすると
ちょっと 遅くなるのか、まあいいか」
「 👆 明度を下げることはできたが、再描画のたびに どんどん暗くなって 真っ黒になる。
コードを書く位置を ミスった」
「 タイル・パレット編集画面のタイル・セット画像の明度も下げてくれだぜ」
「 GraphicsView
と、 SKCanvasView
の結局どちらも 用途に応じて使い分けることになるの
学習コストと、脳への負荷が高いな」
「 フォルダーの整理も大変だ。
MAUI のフレームワークに従うものと、
SkiaSharp という異質なものを どう配置しよう」
「 FeatSkia
みたいなフォルダーを作って
そこに放り込めば?」
「 ミュージシャンみたいだな。
そもそも Skia
って何だ? 調べるか」
「 👆 SkiaSharp.Views.Maui.Controls
パッケージをインストールすれば、必要なものは 自動的にインストールしてくれるようだぜ」
「 👆 MauiProgram.cs
に builder.UseMauiApp<App>().UseSkiaSharp();
って書く必要があるの、
探さないと見つからない 分からんと思う」
「 👆 画像処理は とりあえずお試しに FeatSkia
フォルダーを作って そこへ入れよう」
「 👆 ビューに ネームスペース
xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"
を追加して」
「 👆 ビューモデルには、 SKBitmap
を格納できるようにしておくぜ」
「 👆 描画処理は、ただビットマップを描画するだけにするぜ」
「 コード・ビハインドに 画像処理をベタ書きするのが、 MVVM の思想では イケてないんだろうな」
「 👆 ページの読込完了時に、画像読込と、明度を下げるのを 済ませてしまおう」
「 明度を下げたタイル・セット画像の上に、
半透明のグレーの画像を置いているけど、
その2枚は MAUI によって どうブレンドされているの?
加算合成なのか、乗算合成なのか」
📖 ブレンド モード
「 選択領域か、そうでないかが 明確に見分けられなければ いけなくない?」
「 暗い場所が残ってると 残差業が残ってるみたいに感じるじゃない。
選択されていないところを 暗くして、 選択されているところは 元の色調にしたら いいんじゃないの?」
「 じゃあ 元画像 は どこかに残しておかないといけないのかだぜ」
「 カラード・マップ(Colored Map) は色分けだろ。
色で分けろよ」
「 登録タイルが こんな ぐちゃぐちゃ しているのが悪いんだ。 削除できるようにしよう」
「 追加ボタンと 削除ボタンが こんなに近くにあるのは 悪いデザインでは?」
「 MAUI のボタンって 反応しないから 押した感じがしないけど
ボタンを押したという演出を 付けれないの?」
「 そんなものは ボタンには最初から付いているものと 思っていたぜ」
📖 【.NET MAUI】Buttonにアニメーションを付けて表示をカスタマイズする
📖 Microsoft > AnimationBehavior
「 👆 ボタンに ビヘイビアを書いたが、アニメーションする様子は無いぜ」
「 👆 XAML をどう書いても ボタンはアニメーションせず クソだったので
コード・ビハインドに コードを書くことにしたら アニメーションしたぜ」
「 なぜ XAML の設定では アニメーションしなかったのか 謎のままだぜ」
「 ボタンの上をマウスがホバーしたときに、ボタンの色を変えれないの?」
「 👇 ご存じの通り MAUI は マウス操作ではなく タップ操作を前提とした設計なんで マウス・ホバーなんて無いぜ」
📖 Mouse hover detection in .NET MAUI
📖 Implement PointerGestureRecognizer #9592
「 ボタン毎に ジェスチャー付けるの クソ めんどくさいが やってみるかだぜ」
「 Colors.xaml に定義されている primary という名前の Color インスタンスはどうやって取得できるんだぜ?」
📖 How to find a resource with key in code behind? [MAUI]
「 いくつかあるディクショナリーのどこかにあるから 探索しろということかだぜ」
namespace _2D_RPG_Negiramen.Models
{
/// <summary>
/// リソース・ヘルパー
/// </summary>
internal static class ResourcesHelper
{
/// <summary>
/// 探す
///
/// <list type="bullet">
/// <item>TODO ★ 実装をもっと書いてほしい</item>
/// </list>
/// </summary>
/// <param name="key">リソースのキー</param>
/// <param name="resource">見つけたもの</param>
/// <returns>見つかった</returns>
internal static bool TryFind(string key, out object resource)
{
if (App.Current==null)
{
resource = 0;
return false;
}
// 愚直な探索
foreach (var resourceDictionary in App.Current.Resources.MergedDictionaries)
{
if (resourceDictionary.TryGetValue(key, out resource))
{
return true;
}
}
resource = 0;
return false;
}
}
}
「 👆 マウスのホバーで色変えるぐらいのことで ジェスチャーのコード 書くことになるの うんざりするが
仕方ない」
「 そういえば ボタンにマウスカーソルが被さった時の カーソルの見た目の変更も やり方が分からないな」
「 👆 マウス・ホバー時の色味を落ち着かせて、 画面の配色のコントラストも落とすように 変更したぜ」
「 ネギラーメンという名前なのに 青が基調でいいのかだぜ?」
「 青は 集中力を高める色という俗説もあるし、 まあいいだろ」
「 👆 マウスカーソルが ボタンに被さると ボタンが少し 明るく見えるとか 1個1個 設定しないといけない
これは大変だ」
「 Tileset
は 1単語として よく使われているようだぜ」
「 タイル・パレット編集画面
という名前は 実態に合ってんの?」
「 英語にすると Tile Crop Page
ぐらいかだぜ?」
「 まだ タイル・パレット本体も無いし、
タイル・パレット編集 と言われても 分かんなくない?」
「 とりあえず 文字数が減るのは嬉しいから リネームするか」
TilePaletteEditPage ----> TileCropPage
「 あれっ? C# スクリプトから 文字列リソースを取得するには どうやったらいいんだぜ?」
「 LocalizationResourceManager
を直で呼び出したらどう?」
var text = (string)LocalizationResourceManager.Instance["Add"];
「 👆 ローカライズしたら
という改行コードが そのまま表示されてしまった!」
「 MAUI って デスクトップ上のウィンドウの位置 という概念も無いよな」
📖 .NET MAUI でウインドウサイズを指定する方法(Windowsで実行した場合)
(カタ カタ カタ カタ)
「 Loaded
は最初の1回しか呼び出されないらしい。
画面遷移で 戻ってきたときに呼び出されたいときは どうすんだぜ?」
「 とりあえず いろんなイベントハンドラ実行したら NavigatedTo
が それっぽいかな」
(カタ カタ カタ カタ)
「 ブログの編集画面が なんか重くなってきたので 次の記事へ飛ぶかだぜ」
📖 次の記事: 2D RPG 制作ツールを作ろうぜ(^~^)? <その2>
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント