- 投稿日:2021-04-03T21:26:23+09:00
RPG
Zophar
RPGツクール
MOTHERシリーズ
クロノ・トリガー
ウィザードリィ
ファンタシースター
ウルティマオンライン
暗い太陽:粉々になった土地
ウィキペディアから、無料の百科事典
ナビゲーションに
ジャンプ検索にジャンプ
暗い太陽:粉々になった土地
Dark Sun-Shattered Lands Coverart.png
開発者 戦略的シミュレーション
出版社 戦略的シミュレーション
プロデューサー ブレットベリー
プログラム ラッセルブラウン
作曲家 ラルフ・トーマス
シリーズ ダークサン
プラットフォーム MS-DOS
リリース 4 1993年 月 [1]
ジャンル ロール・プレイング
モード シングルプレーヤーダーク日:シャッタードランズ ある ターンベースの コンピュータRPG で行わ ダンジョンズ&ドラゴンズ 「 キャンペーン設定 の 暗い日 。 それがために発売された MS-DOS によって1993年に幾分未完成状態で 戦略シミュレーション 、 [2] 以降より実行可能なバージョンにパッチ。 両方で利用できました フロッピーディスク と CD-ROMの が、CD-ROMには追加のコンテンツは含まれておらず、コンピュータのハードドライブにゲームをインストールするためにのみ使用されていました。
その後 一部として再リリースされ AD&Dマスターピースコレクションの 、1996年に ました。さらに、 Data East は ために コンソールポートを開発し セガサターン と ソニープレイステーション 1996年にリリースする 用の [3] ていましたが 、キャンセルされました。
ゲームには、 続編の Dark Sun:Wake of the Ravagerが 1994年に ありました。オンライン MMORPG 、 Dark Sun Online:Crimson Sands は、1996年にリリースされ、 ホストされました TENネットワークで 。
このゲームは で2015年に再リリースされました Gog.com 、 をサポートする Windows 、 macOS 、および Linux 。
内容1 プロット 2 ゲームプレイ 3 発行履歴 4 レセプション 4.1 販売 4.2 批評的レビュー 5 参考文献 6 外部リンクプロット
Dark Sun:Shattered Lands は である の架空の土地で行われ Athas 、死にゆく敵対的な砂漠の世界 ます。 ロケールは ドラージ 、強力な魔術師王によって統治されている都市国家である です。 [4] 近くにはいくつかの「自由都市」があり、市民の努力のおかげで砂漠で生き残っています。 ドラージのピラミッドが完成すると、魔術師王は砂漠を一掃し、彼の支配下にない都市の住民を破壊することによって、血を大いに犠牲にすることを望んでいます。 プレイヤーは最大4人の パーティを支配し、 剣闘士の 彼らが死ぬまでドラージのアリーナで戦うことを非難しているので、当然のことながら最初の仕事は脱出です。 脱出すると、党はドラージの軍隊に抵抗するために自由都市を団結させなければなりません。 [4]
ゲームプレイDark Sun は、SSIの古い 使用していません ゴールドボックス エンジンを 。 ゲームは 使用 トップダウン し 、 と同様の世界の ビューを ます ウルティマ シリーズ 。 ゲームの多くは と 相互作用を伴い 他のキャラクター の 、 ダークサン シリーズ ロールプレイングに重点を置き、 重点を置いていません ダンジョンクロールに はゴールドボックスゲームよりも は 。 [5]
ゲームは、 バリアントを使用し Advanced Dungeons&Dragons 2ndEdition ルールの ます。 [5] 他の ダンジョンズ&ドラゴンズの コンピュータゲームと同様に、戦闘はゲームプレイで際立って機能します。 Shattered Lands は、2次元のターンベースの戦闘システムのおかげで、非常に戦略的な戦闘で有名です。 2つの戦いは同じではなく、「ボスの戦い」の多くは、少数の強力な魔術師や戦闘機ではなく、大きな軍隊を巻き込みます。 特に複数の方向から攻撃された場合、適切なフォーメーションとスペルの使用は必須です。 ダークサンでは、キャラクターは通常の よりもはるかに強力です ダンジョンズ&ドラゴンズの キャンペーン設定 。基本ステータスは3d6ではなく4d4 + 4であり、ハーフジャイアントの1つの種族のメンバーは 2倍を受け取り ヒットダイス ロールの ます。 粉々になった土地は またに固有の要素を取り入れ 暗い日の ユニークなキャラクターの種族(含むキャンペーンの設定、 ムル とinsectoid スリクリーン )との広範な使用 psionicsを 。
出版履歴このゲームは後に1996年のコンピレーションセットである に含まれました AD&Dマスターピースコレクション 。 [6]
レセプション
販売Shattered Lands は、 17位でデビューし PCデータ 1993年9月の のコンピュータゲーム販売チャートで ました。 [7] 10月に3位に上昇しました。 [8]
重要なレビュー
受信
スコアを確認する 出版物 スコア
ドラゴン 3/5つ星[9]
今日のCD-ROM 4/5つ星[10]
電子エンターテインメント 10点中8点 [11]執筆で、 今日のCD-ROMの と呼び T。LiamMcDonaldは、Shattered Landsを 「おなじみの さわやかな新しいひねり」 AD&D ゲームの 、SSIの以前の製品と比較して「大幅に改善されたインターフェイス」に言及しました。 [10] PeterOlafsonは、 ElectronicEntertainmentの 発見しまし Shattered Lands に欠陥があることを たが、それでも「非常に優れたゲーム」であると結論付けました。 彼は、「これは金メッキではありません。これは錫ではありません。少し変色した本物です」と要約しました。 [11]
Scorpia の コンピュータゲームの世界 という1993保証読者で 暗い日は 「程度まで[ゴールドボックスシリーズ]からであるとして、あなたが得ることができます... SSIは、参照するには良いですが、新しい方向のロールプレイングラインを取っています」 。 AD&D第2版のルールの「無邪気さ」と不十分なドキュメントを批判しながら、彼女は「 私の印象 ダークサンの は良好です。SSIはCRPGのより成熟した形式に移行しており、将来に大きな期待が寄せられています。今すぐプレイするのに良いゲームです。」 [5] このゲームは、1994年に でレビューさ ドラゴン によって #205 Sandy Petersen 「Eyeofthe Monitor」列の れ、5つ星のうち3つを与えました。 [9] のジョン・テラ コンピュータショッパーは 、主にゲームを賞賛しました。 [4] 彼は、コントロールを「本能的」かつ「習得しやすい」と呼んだ。 [4] 彼はさらに、グラフィックは「非常に詳細」であり、効果音は「近接攻撃中の雰囲気を高めるさまざまな戦闘ノイズで際立っている」と述べ、オーディオとビジュアルを補完しました。 [4] 彼はマップ機能について否定的な発言をしました。それは自動マップではなく、敵の位置を表示し、サスペンスの一部を排除していると述べました。 [4]
暗い日は、 次点のためだった コンピュータゲームの世界 " イヤー賞のSロールプレイングゲーム1994年6月で、最終的に行ってきました Krondorで裏切り 。 編集者は、 書いてい ダークサンが 「トロイデニングの の魔法のシステムと「焦土作戦」の外観の独自性を 捉えることができた」と プリズムペンタッド うまく シリーズの小説 ます。 [12]
GameSpyによると、「ダークサンはTSRの「魔法の黙示録」の残忍さ、血、そして信じられないほど暴力的な死の世界でした。 ダークサン:シャッタードランドの 一方、 グラフィックはかなりかわいくて、暴力的で成熟した事件のファンではありませんでした。期待していた」 [13]
参考文献「PCゾーンマガジン」 。 PCゾーン 。 No. 1. 1993年4月。p。 11 。 取得 7月5日 2017年 。
シャノンアッペルクライン(2011)。 デザイナー&ドラゴンズ 。 マングースパブリッシング。 p。 21. ISBN 978-1-907702-58-7 。
「ロールプレイヤーズレルム」 。 GamePro 。 No. 73.IDG 。 1995年8月。p。 85 。
テラ、ジョン(1994年3月1日)。 「AD&Dダークサン:シャッタードランドレビュー」 。 コンピューターショッパー 。 からアーカイブされまし オリジナル た 2016年4月13日に 。 取得 9月21日 2012年 。 – 経由 HighBeam Research (サブスクリプションが必要)
蠍座(1993年12月)。 「さようならゴールドボックス!」 。 コンピューターゲーミングワールド 。 pp。124–126 。 取得した 3月29日に 2016 。
ブッチャー、アンディ(1996年1月)。 「ゲームレビュー」。 秘儀 。 フューチャーパブリッシング (2):80。
スタッフ(1994年1月)。 「最新情報; PCデータが売れ筋ソフトウェアのリストにヒット」。 Computer Gaming World (114):240。
スタッフ(1994年3月)。 「リーダーボード」。 電子エンターテインメント (3):20。
ピーターセン、サンディ (1994年5月)。 「モニターの目」。 ドラゴン (205):59–62。
マクドナルド、T。リアム(1995年1月)。 「 暗い太陽:粉々になった土地 」。 今日のCD-ROM (11):106。
オラフソン、ピーター(1994年1月)。 「 暗い太陽:粉々になった太陽 」。 電子エンターテインメント (1):94、95。
「新しいプレミアアワードの発表」 。 コンピューターゲーミングワールド 。 1994年6月。51〜58ページ。ラウシュ、アレン(2004-08-17)。 「 歴史 D&D ビデオゲームの -パートIII」 。 ゲームスパイ 。 取得 11月17日 2012年 。外部リンク
ダーク日:シャッタードランドの で MobyGames v t eダンジョンズ&ドラゴンズの ビデオゲーム
初期のゲームdnd (1975) ダンジョン (1975) DND (1977) Dungeons&Dragonsコンピューターファンタジーゲーム Advanced Dungeons&Dragons:Cloudy Mountain ダンジョン! (1982) Advanced Dungeons&Dragons:Treasure of Tarminゴールドボックス ゲーム
プールオブレイディアンス シリーズ 輝きのプール 紺碧の絆の呪い シルバーブレードの秘密 闇のプール サベージフロンティア シリーズ サベージフロンティアへの玄関口 野蛮なフロンティアの宝物 ドラゴンランス シリーズ クリンのチャンピオン クリンの死の騎士 クリンの闇の女王 スペルジャマー:レルムスペースの海賊 忘れられた領域:無制限の冒険バルダーズゲート
バルダーズゲート テイルズオブソードコースト エンハンスドエディション ドラゴンスピアの包囲 Baldur's Gate II:Shadows of Amn バールの王位 エンハンスドエディション バルダーズゲート:ダークアライアンス Baldur's Gate:Dark Alliance II バルダーズゲートIII:ブラックハウンド バルダーズゲートIII ダンジョンズ&ドラゴンズ:ダークアライアンスアイスウィンドデール シリーズ
アイスウィンドデール 冬の心 エンハンスドエディション Icewind Dale IIネヴァーウィンターナイト シリーズ
ネヴァーウィンターナイト (2002) Undrentideの影 暗闇の大群 キングメーカー ダガーフォードの闇 Neverwinter Nights 2 裏切り者のマスク Zehirの嵐 ウェストゲートの謎大規模マルチプレイヤーオンライン
ネバーウィンターナイト (1991) 滞在/ TorilMUD ダークサンオンライン:クリムゾンサンド ダンジョンズ&ドラゴンズオンライン Neverwinterインタラクティブフィルム
スカージオブワールド:ダンジョンズ&ドラゴンズアドベンチャーその他
シルバーボックスゲーム Advanced Dungeons&Dragons:Heroes of the Lance ドラゴンオブフレイム シャドウソーサラー ヒルズファー ランスの戦争 見る人の目 シリーズ 見る人の目 (1991) ダークムーンの伝説 神話ドラナーへの攻撃 DragonStrike グリフォンの注文 ダンジョンとドラゴン:永遠の太陽の戦士 ダークサン シリーズ 粉々になった土地 荒廃者の目覚め ダンジョンハック ファンタジー帝国 拠点 ミスタラシリーズ 運命の塔 ミスタラの影 コレクション ミスタラのクロニクル スレイヤー シリーズ スレイヤー DeathKeep Ravenloft シリーズ ストラッドの所持 石の預言者 アルカディム:魔神の呪い メンゾベランザン ブラッド&マジック 生得権:ゴーゴンの同盟 アイアン&ブラッド:レイヴンロフトの戦士たち アンダーマウンテンへの降下 プレーンスケープ:トーメント 輝きのプール:神話の廃墟ドラナー Dungeons&Dragons:Eye of the Beholder (2002) ダンジョンズ&ドラゴンズ:ヒーローズ エレメンタルイービルの神殿 忘れられた領域:悪魔の石 Dungeons&Dragons:Dragonshard Dungeons&Dragons Tactics Dungeons&Dragons:ダガーデール ウォーターディープの領主 ソードコーストレジェンド 忘れられた領域のアイドルチャンピオンカテゴリ :
1993年のビデオゲーム キャンセルされたプレイステーション(コンソール)ゲーム セガサターンのゲームをキャンセルしました ダークサン DOSゲーム Linuxゲーム MacOSゲーム ダンジョンズ&ドラゴンズのビデオゲーム DOSBoxで市販されているゲーム ロールプレイングビデオゲーム 戦略的シミュレーションゲーム 米国で開発されたビデオゲーム 選択可能な性別の主人公をフィーチャーしたビデオゲーム WindowsゲームNavigation menu
ログインしていない トーク 貢献 アカウントを作成する ログイン 論文 トーク 読んだ 編集 履歴を表示探す
メインページ 内容 時事問題 ランダム記事 ウィキペディアについて お問い合わせ 寄付助ける
助けて 編集することを学ぶ コミュニティポータル 最近の変化 ファイルをアップロードするツール
ここにリンクするもの 関連する変更 特別ページ パーマリンク ページ情報 このページを引用する ウィキデータアイテム印刷/エクスポート
PDFとしてダウンロード 印刷可能バージョン言語
ドイツ語 フランス語 ロシアリンクを編集する
このページの最終編集日は2021年1月27日10:30 (UTC) です。 テキストは 下で利用可能です クリエイティブ・コモンズの帰属-継承ライセンスの 。 追加の条件が適用される場合があります。 このサイトを使用することにより、 同意したことになります 利用規約 と プライバシーポリシーに 。 Wikipedia®は、 である 登録商標 Wikimedia Foundation、Inc。の 非営利団体 です。 個人情報保護方針 ウィキペディアについて 免責事項 ウィキペディアに連絡する モバイルビュー 開発者 統計学 クッキーステートメント ウィキメディア財団 MediaWikiを搭載
- 投稿日:2021-04-03T20:23:22+09:00
【Unity】InputSystemのInputControlから「押されているか」を簡単に検知したい
※ InputSystemのPreview版を使用している記事です。 正式バージョンでは使用方法が変更されている可能性があります まず、UnityのInputSystemとは キーボードの左キーを押したとき ゲームパッドの左キーを押したとき uGUIで作った左移動ボタンを押したとき などなど.. 入力側が異なってもコード側で意識せずに処理できる新しい入力システムです InputSystem自体は素晴らしい記事が沢山あるのでカット 今回は InputSystem を使用している場合の 「キー(orボタン)が押されているか」 判定取得の記事になります。 しかも従来のInput.GetKeyライクに扱いたい。(つまりinterfaceを継承したりせずに簡単に扱いたい) 単純に「Updateメソッドで"押され続けているか"」を取りたいときあると思うんですよね // Input時代の書き方 void Update() { if (Input.GetKey(KeyCode.LeftArrow)) { // 左キー(orボタン)が押されている } } InputSystemのInputControlを使用している場合、該当するものがどれかわからずに悩みました... まず、以下のような移動に関するaInputControlを作成しました。 「キーボードの左を押したとき」と「画面上の左ボタンを押した時」に同じ処理をさせたいので ActionType は Button にしています。 結果として、InputSystem v1.0.x の時は以下の形で取得することができました void Update() { // moveはInputControl if (move.Left.ReadValue<float>() > 0f) { // 左キー(orボタン)が押されている } } ActionTypeがボタンの場合floatでReadValueができるようなクラス設計になっていました。 なので、キーやボタンが押されている場合 ReadValue<float>() で値が返ってくるので判定ができます。 しかし、 直感的でもなく気持ち悪さが残る.. (float... 現在の最新である InputSystem v1.1.0-preview3 を入れた所、以下のような形で取得できるようになっていました。 (リファレンス) https://docs.unity3d.com/Packages/com.unity.inputsystem@1.1/manual/Actions.html void Update() { // moveはInputControl if (move.Left.IsPressed()) { // 左キー(orボタン)が押されている } } IsPressed!! すごくスマート、そして直感的な形でとれるようになりました。 終わり 以前も isPressed 変数自体はあり外からはアクセスできないようになっていましたが IsPressed() メソッドとして公開されるようになっていました。 リポジトリ覗いてみても v1.1.0 で中身大幅に変わってますね。 これ以外にも沢山機能が追加されていそうです。 今回の内容はブログにも書いてあります https://toshizabeth.hatenablog.com/
- 投稿日:2021-04-03T19:25:31+09:00
【Unity(C#)】Microsoft Translatorの使い方
はじめに VRヘビーユーザーからすれば当たり前のことかもしれませんが、 OculusQuestにマイクが搭載されているのはご存じでしたでしょうか。 ボイスチャットに利用されることがほとんどで、 その他の用途で使われている事例をあまり見たことがありませんでした。 (たぶん世の中にはたくさんある) しかし、最近目にした記事にボイスチャット以外の用途でマイクを使った事例がありました。 【参考リンク】:Synamon、ロゼッタと「リアルタイム多言語翻訳システム装備のVRオフィス」を共同開発 一言で説明すると、翻訳VRアプリです。 マイクを音声認識の受け口として利用しています。 そこで、私も勉強がてら"OculusQuestのマイクを利用したVRアプリ作りに挑戦してみたい"と思い、実際に作りました。 勉強がてら作成していた翻訳VRできました!?OculusQuestのマイクで拾った音声を音声認識APIに渡して、認識結果を翻訳APIに渡す、、、というやり方です?次はマルチ対応していきます?#OculusQuest #Unity pic.twitter.com/k95D73gEnh— KENTO⚽️XRエンジニア?Zenn100記事マラソン挑戦中29/100 (@okprogramming) April 3, 2021 Microsoft Translator 先ほどのアプリで利用した翻訳の部分はMicrosoft Translatorを利用しています。 登録の手順を覚えている範囲でメモします。 この手順に関してですが、2021/04/03時点の情報となります。 私も使い方がわからなかったため、過去に執筆された記事等手掛かりに進めてましたが、UIや手順に変更があり、そこそこ苦労しました。 この記事もそうなる可能性が高いのでご注意ください。 まずは下記からMicrosoft AzureのHome画面を開きます。 ログインしたらHome画面上部からTranslatorを検索し、 Marketplaceの欄のTranslatorを選び、登録に進みます。 登録画面で必要な項目を選択します。ここでPricing tierの欄をfreeにしておけば無料で使えるはずです。(たぶん) 設定完了したらAPIを利用する際に必要な値を下記画面から確認できます。 翻訳デモ 翻訳VRアプリ内のコードは他のAPIに置換できるよう、モジュール化しているためわかりにくいかなと思い、シンプルな翻訳デモを作りました。 英語→日本語、日本語→英語が翻訳可能です。 もちろんMicrosoft Translatorが対応している言語であれば他の言語でも翻訳可能です。 【参考リンク】:Language and region support for text and speech translation バージョン 念のため使ったライブラリ等のバージョンも書いときます。 Unity 2019.4.8f1 UniTask.2.2.4 UniRx 7.1.0 コード 翻訳デモのコードです。 using System; using System.Linq; using System.Text; using System.Threading; using Cysharp.Threading.Tasks; using UniRx; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; /// <summary> /// MSTranslatorの最小構成サンプル /// </summary> public class SimpleTranslation : MonoBehaviour { [SerializeField] private Dropdown fromLanguageDd; [SerializeField] private Dropdown toLanguageDd; [SerializeField] private Button translateButton; [SerializeField] private InputField inputField; [SerializeField] private Text translationText; /// <summary> /// レスポンスを格納する構造体 /// </summary> [Serializable] public struct TranslateData { public Translations[] translations; [Serializable] public struct Translations { public string text; public string to; } } /// <summary> /// リクエストを格納する構造体 /// </summary> [Serializable] public struct SpeechData { public string Text; } private const string SUBSCRIPTION_KEY = "登録キー"; private const string ENDPOINT = "https://api.cognitive.microsofttranslator.com/"; private const string LOCATION = "登録時に設定したLocation"; /// <summary> /// 設定言語 /// </summary> private enum Language { ja, en } private Language fromLanguage = Language.ja; private Language toLanguage = Language.en; private void Start() { var token = this.GetCancellationTokenOnDestroy(); //ドロップダウンメニュー作成 var languages = Enum.GetNames(typeof(Language)); fromLanguageDd.ClearOptions(); fromLanguageDd.AddOptions(languages.ToList()); toLanguageDd.ClearOptions(); toLanguageDd.AddOptions(languages.ToList()); fromLanguageDd.value = (int) fromLanguage; toLanguageDd.value = (int) toLanguage; //翻訳元言語 fromLanguageDd.OnValueChangedAsObservable() .Subscribe(value => { fromLanguage = (Language) value; }) .AddTo(this); //翻訳後言語 toLanguageDd.OnValueChangedAsObservable() .Subscribe(value => { toLanguage = (Language) value; }) .AddTo(this); //翻訳ボタン押下 translateButton.OnClickAsObservable() .Subscribe(async _ => { //結果が送られてくるまで待ってから表示 var result = GetTranslation(fromLanguage, toLanguage, inputField.text, token); translationText.text = await result; }) .AddTo(this); } /// <summary> /// 翻訳結果を返す /// </summary> /// <param name="from">翻訳前の言語設定</param> /// <param name="to">翻訳語の言語設定</param> /// <param name="speechText">翻訳したい文字列</param> /// <param name="ct">CancellationToken</param> /// <returns>翻訳結果</returns> private async UniTask<string> GetTranslation(Language from,Language to,string speechText, CancellationToken ct) { //POSTメソッドのリクエストを作成 var requestInfo = "translate?api-version=3.0"; requestInfo += $"&from={from}&to={to}"; var request = UnityWebRequest.Post(ENDPOINT + requestInfo, "Post"); //リクエストに使用するJSON作成 var speechData = new SpeechData {Text = speechText}; var jsonData = "[" + JsonUtility.ToJson(speechData) + "]"; var bodyRaw = Encoding.UTF8.GetBytes(jsonData); request.uploadHandler = new UploadHandlerRaw(bodyRaw); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); //ヘッダーに必要な情報を追加 request.SetRequestHeader("Ocp-Apim-Subscription-Region", LOCATION); request.SetRequestHeader("Ocp-Apim-Subscription-Key", SUBSCRIPTION_KEY); //結果受け取り var second = TimeSpan.FromSeconds(3); var result = await request.SendWebRequest().ToUniTask(cancellationToken: ct).Timeout(second); var rawJson = result.downloadHandler.text; var json = rawJson.Substring(1, rawJson.Length - 2); var data = JsonUtility.FromJson<TranslateData>(json); return data.translations[0].text; } } APIのリクエスト用のJson、APIのレスポンス用のJsonのそれぞれをシリアライズ、デシリアライズするための構造体を用意する必要があります。 リクエスト用のJsonはルートが配列でないとAPIの都合上だめだったのでごり押ししています。 var jsonData = "[" + JsonUtility.ToJson(speechData) + "]"; 【参考リンク】:【Unity(C#)】WebAPIで返ってきたJSONデータの扱いでつまったところ EnumをDropDownに反映 全然関係ない内容ですが、知らなかったのでメモします。(下記参考リンクのまんまですが...) 【参考リンク】:UnityでDropDownのOptionリストに、enumの定義値をラベルとしてスクリプトからセットする /// <summary> /// 設定言語 /// </summary> private enum Language { ja, en } private Language fromLanguage = Language.ja; private Language toLanguage = Language.en; private void Start() { //ドロップダウンメニュー作成 var languages = Enum.GetNames(typeof(Language)); fromLanguageDd.ClearOptions(); fromLanguageDd.AddOptions(languages.ToList()); toLanguageDd.ClearOptions(); toLanguageDd.AddOptions(languages.ToList()); fromLanguageDd.value = (int) fromLanguage; toLanguageDd.value = (int) toLanguage; //翻訳元言語 fromLanguageDd.OnValueChangedAsObservable() .Subscribe(value => { fromLanguage = (Language) value; }) .AddTo(this); //翻訳後言語 toLanguageDd.OnValueChangedAsObservable() .Subscribe(value => { toLanguage = (Language) value; }) .AddTo(this); } Enumの値をvar languages = Enum.GetNames(typeof(Language));で配列化してDropDownの値に追加します。 あとはDropDownの値変更を監視して、変更時にenumに値を設定してあげればOKです。 おわりに 記事内にも書いた通り、翻訳APIの箇所はモジュール化しているので他の無料で使えるAPIと比較して精度など試してみようと思います。 次は音声認識機能について書きます。 参考リンク Microsoft Translator テキスト API で、日本語を英語に翻訳するサンプル UniTaskの使い方2020 / UniTask2020 Quickstart: Get started with Translator
- 投稿日:2021-04-03T19:05:38+09:00
VSCodeでSvelte+ASP.Net CoreでJWTを利用したSPAの認証(実装)
前回の記事で環境構築をしましたが、実装しか必要のない方も多いと思いますので、分けておきました。 データベースの設定と実装 ユーザー管理用のDBの実装です。以下の3つのパッケージをNuGetで取り込んでください。DBのパッケージは使うデータベースに合わせて変えてください。私はPostgreSQLを使っています。3つ目のパッケージは、後述するマイグレーションの実行時に必要なようです。インストールしてないと、マイグレーション時にインストールしろとメッセージが出ます。 Npgsql.EntityFrameworkCore.PostgreSQL(使うデータベースに合わせて変更します) Microsoft.AspNetCore.Identity.EntityFrameworkCore Microsoft.EntityFrameworkCore.Design 「appsettings.json」にDBのエントリーを追加します。これも使うDBに合わせてください。 appsettings.json { "ConnectionStrings": { "DefaultConnection": "Server=localhost;Port=5432;Database=<DBNAME>;User ID=<USERID>;Password=<PASSWORD>;Enlist=true" }, .... 基本のフォルダの直下に「Data」フォルダを作成し、「ApplicationDbContext.cs」を作成し、以下の様にします。 ApplicationDbContext.cs using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace svelteCsAsp.Data { public class ApplicationDbContext: IdentityDbContext<IdentityUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } } } 「Startup.cs」に以下の2つのネームスペース参照を追加します。 Startup.cs using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Identity; さらに「ConfigureServices」メソッドに以下の行を追加します。 Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(options => options.UseNpgsql( // <= この部分は使うDBに合わせてください Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>(); ... これでデータベースを使ってDefaultIdentityユーザーを利用する準備ができました。 データベースのマイグレーション 作成た状態で、データベースのマイグレーションを実行します。マイグレーションは「dotnet ef」コマンドで実行するのですが、インストールする必要があります(Visual Studioならインストールの必要もないのですが)。コマンドラインで「dotnet tool install --global dotnet-ef」でインストールしてください。 インストールしたら、VSCodeのターミナルで「dotnet ef migrations add initdb」を実行してEntityFrameworkを使えるようにします。(initdbの部分は管理用の名称ですので変更してもいいです) 手動でDBを初期化する場合は、同じく「dotnet ef」コマンドをつかうのですが、私は基本的に自動アップデートにしていますので、今回もそうします。 (※わかっているとは思いますが、データベースは既にインストールして使える状態です。テーブルが何も入っていない状態にしておいてください) ユーザー初期化のクラスは以下の通りです。(好きなフォルダに作ってください。私は「Models」フォルダに作りました。) UserRollInitialize.cs using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using System; using System.Threading.Tasks; namespace svelteCsAsp.Models { public class UserRollInitialize { // 初期化時のロール public static readonly string SystemManagerRole = "SystemManager"; // システム管理権限 public static readonly string GroupManagerRole = "GroupManager"; // グループ管理権限 // 初期化時のシステム管理ユーザーID public static readonly string SystemUserName = "system"; // 最初のシステム管理ユーザーのメールアドレス public static readonly string SystemManageEmail = "system@test.com"; // 最初のシステム管理ユーザーのメールアドレス public static readonly string SystemManagePassword = "!initialPassword01"; // 最初のシステム管理ユーザーの初期パスワード // 初期化時のグループ管理ユーザーID public static readonly string GroupUserName = "groupuser"; // 最初のグループ管理ユーザーのメールアドレス public static readonly string GroupUserEmail = "groupuser@test.com"; // 最初のグループ管理ユーザーのメールアドレス public static readonly string GroupUserPassword = "!initialPassword02"; // 最初のグループ管理ユーザーの初期パスワード /// <summary> /// ユーザーとロールの初期化 /// 初期のシステムユーザーあが存在しない場合のみ内容が実行される。存在する場合は何もせずに終了 /// </summary> /// <param name="serviceProvider"></param> public static async Task Initialize(IServiceProvider serviceProvider) { // ユーザー管理を取得(using Microsoft.Extensions.DependencyInjectionがないとエラーになる) var userManager = serviceProvider.GetService<UserManager<IdentityUser>>(); // 初期のユーザーマネージャーが存在しなければロールの作成と初期システムユーザーを作成する var systemManager = await userManager.FindByNameAsync(SystemUserName); if (systemManager == null) { // ロール管理を取得 var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>(); // ロールの追加 await roleManager.CreateAsync(new IdentityRole(SystemManagerRole)); // システム管理ロール await roleManager.CreateAsync(new IdentityRole(GroupManagerRole)); // グループ管理ロール // 初期システム管理者の作成 systemManager = new IdentityUser { UserName = SystemUserName, Email = SystemManageEmail }; await userManager.CreateAsync(systemManager, SystemManagePassword); // システム管理ユーザーにシステム管理ロールを追加 systemManager = await userManager.FindByNameAsync(SystemUserName); await userManager.AddToRoleAsync(systemManager, SystemManagerRole); // グループユーザーの作成 var groupUser = new IdentityUser { UserName = GroupUserName, Email = GroupUserEmail }; await userManager.CreateAsync(groupUser, GroupUserPassword); // グループユーザーにグループユーザーロールを追加 groupUser = await userManager.FindByNameAsync(GroupUserName); await userManager.AddToRoleAsync(groupUser, GroupManagerRole); } } } } 「Program.cs」の「Main」を次のように変更します。 Program.cs public static void Main(string[] args) { // CreateHostBuilder(args).Build().Run(); <= もともとこの1行のみ var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { // サービスプロバイダーの取得 var services = scope.ServiceProvider; // データベースの自動マイグレーション var context = services.GetRequiredService<ApplicationDbContext>(); context.Database.Migrate(); // 初期のユーザーとロールの作成 UserRollInitialize.Initialize(services).Wait(); } host.Run(); } これでサービスを起動すると、DBに接続して必要なテーブルは勝手に作って初期ユーザーの登録までできてしまいます。 JWTのバックエンド実装 まずJWTを使うために「Microsoft.AspNetCore.Authentication.JwtBearer」パッケージを追加します。 「appsettings.json」にJWT認証のパラメータを追加します。Kyeは下記の通りにするのではなく、ランダムな文字列で長いものを設定します。重要なキーになりますのでしっかり作ってください。 appsettings.json "Jwt": { "Key": "abcdefghijklmnopqrstuvwxyz", "Issuer": "https://virtual_office.com" } JWTによる認証はDBと同じように「Startup.cs」を変更します。まず必要な以下の2つのネームスペースをusingで追加してください。 - System.Text; - Microsoft.IdentityModel.Tokens; 次に「ConfigureServices」に以下の内容を追加してください。 cs:Startup.cs // 認証にJWTベアラトークンを利用 services.AddAuthentication().AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Issuer"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); ログイン用のコントローラーを作ります。 「Controllers」フォルダに「AuthController.cs」を作って以下の様にします。 AuthController.cs using System; using System.IdentityModel.Tokens.Jwt; using System.Text; using System.Threading.Tasks; using System.Collections.Generic; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Authentication.JwtBearer; namespace svelteCsAsp.Controllers { [AuthorizeJwt] // [Authorize] // [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ApiController] [Route("[controller]/[action]")] public class AuthController : ControllerBase { // 設定管理オブジェクト IConfiguration _config; // サインインマネージャー(DefaultIdenityを利用している) SignInManager<IdentityUser> _signInManager =null; UserManager<IdentityUser> _userManage = null; public class LoginRequest { public string userId { get; set; } public string password { get; set; } } // コンストラクタ // サインインマネージャーとコンフィグ管理のオブジェクトをDI public AuthController(SignInManager<IdentityUser> signInManager, IConfiguration config, UserManager<IdentityUser> userManage) { _signInManager = signInManager; _config = config; _userManage = userManage; } // ログイン処理 [HttpPost] [AllowAnonymous] public async Task<IActionResult> Login(LoginRequest request) { // ASP.Net core のdDefaultIdentityを利用してIDとパスワードの確認 var result = await _signInManager.PasswordSignInAsync(request.userId, request.password, false, false); if (result.Succeeded == false) { return BadRequest("ユーザー名またはパスワードが違います。"); } // ログイン成功でおJWTトークンを返す return Ok(new { token = await BuildToken(request) }); } // ログアウト処理 [HttpPost] public IActionResult Logout() { _signInManager.SignOutAsync(); <= これは不要な気がする return Ok(); } // JWTトークンの作成 private async Task<string> BuildToken(LoginRequest request) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var user = await _userManage.FindByNameAsync(request.userId); var principal = await _signInManager.CreateUserPrincipalAsync(user); var roles = await _userManage.GetRolesAsync(user); var claims = new List<Claim>(principal.Claims); claims.Add(new Claim(JwtRegisteredClaimNames.Sub, user.UserName)); claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); foreach(var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"], audience: _config["Jwt:Issuer"], expires: DateTime.Now.AddMinutes(30), signingCredentials: creds, claims: claims); var tmp = new JwtSecurityTokenHandler().WriteToken(token); return tmp; } // テスト用 [HttpPost] [AuthorizeJwt(Roles = "SystemManager")] public IActionResult IsSystemManager() { return new JsonResult(new {status="OK", message="You are SystemManager!"}); } [HttpPost] [AuthorizeJwt(Roles = "GroupManager")] public IActionResult IsGroupManager() { return new JsonResult(new {status="OK", message="You are GroupManager!"}); } } } ログイン、ログアウトと、ロールの確認用の2つのメソッドが公開されています。 JWTのフロントエンド実装 フロントエンドはSPAですので、SPAのルーティングの為に「svelte-spa-router」をインストールします。VSCodeのターミナルでClientAppフォルダに移動して「npm install --save-dev svelte-spa-router」を実行します。この辺りについてはSvelteで始める頑張らないフロントエンド生活 後編を参考にしました。あと、Ajaxを使うために「npm install --save-dev rxjs」で「rxjs」をインストールしました。 ログイン画面として「ClientApp/src」フォルダに「Login.svelte」を以下のようにつくりました。 login.svelte <script lang="ts"> import { push } from 'svelte-spa-router' import { ajax } from 'rxjs/ajax' import {sharedData} from './sharedData' export let userId = ""; export let pass = ""; let errorMessage = ""; function Login(){ ajax({ url: '/Auth/Login', method: 'POST', headers : { 'content-type': 'application/json;charset=UTF-8' }, body: { userId: userId, password: pass } }).subscribe( res => { sharedData.jwtBearerToken = res.response.token; push(`/Main/${userId}`); } ); } </script> <main> <h1>Svelte C# SPA login</h1> <div class="login-frame"> <table> <tr> <th>ID:</th> <td> <input type="text" id="user_id" bind:value={userId} ></td> </tr> <tr> <th>PASSWORD:</th> <td><input type="password" id="password" bind:value={pass} ></td> </tr> <tr> <td colspan="2"><button on:click={Login}>ログイン</button></td> </tr> {#if errorMessage.length > 0} <tr> <td colspan=2>{errorMessage}</td> </tr> {/if} </table> </div> </main> さらに、ログイン後のロールの確認画面として以下のファイルを作りました。システム管理のロールとグループ管理のロールの確認ボタンを配置してます。 Main.svelte <script lang="ts"> import { push } from 'svelte-spa-router' import { ajax } from "rxjs/ajax" import { sharedData } from './sharedData' export let params: {userId:string} let userId = params.userId let isSystemManager = ""; let isGroupManager = ""; function checkSystemManager(){ ajax({ url: '/Auth/IsSystemManager', method: 'POST', headers: { 'Authorization': 'Bearer ' + sharedData.jwtBearerToken, 'rxjs-custom-header': 'Rxjs' } }).subscribe( res => { isSystemManager = res.response.status + ' '+ res.response.message; }, () => { isSystemManager = "Not authenticated!" } ); } function checkGroupManager(){ ajax({ url: '/Auth/IsGroupManager', method: 'POST', headers: { 'Authorization': 'Bearer ' + sharedData.jwtBearerToken, 'rxjs-custom-header': 'Rxjs' } }).subscribe( res => { isGroupManager = res.response.status + ' '+ res.response.message; }, () => { isGroupManager = "Not authenticated!" } ); } function logout(){ ajax({ url: '/Auth/Logout', method: 'POST', headers: { "Authorization": "Bearer " + sharedData.jwtBearerToken } }).subscribe( () => { sharedData.jwtBearerToken = ""; push('/') } ); } </script> <main> <h1>Svelte C# SPA Main</h1> ようこそ{userId}さん<br /> <button on:click={checkSystemManager}>Is System Manager</button>{isSystemManager} <button on:click={checkGroupManager}>Is GroupManager</button>{isGroupManager} <!-- PASSWORD: {password} --> <button on:click={logout}>ログアウト</button> </main> また、ログインのトークン保持用に以下のファイルを作成します。 sharedData.ts class SharedData{ public jwtBearerToken = ""; } export const sharedData = new SharedData() 最後にspaルーターの動作にするために「App.svelte」を以下の様に変えます。(styleは全部消してます) App.svelte <script lang="ts"> import Router from 'svelte-spa-router' import Login from './Login.svelte'; import Main from './Main.svelte'; const routes = { '/': Login, '/Main/:userId': Main, '*': Login }; </script> <Router routes={routes}></Router> 承認用アトリビュートの作成 さて、承認について「AuthController.cs」で気づいたかもしれませんが、承認のアトリビュートが[Authorize]ではなく、[AuthorizeJwt]になっています。これはコントローラーと同じフォルダに以下のファイルを作っています。 AuthorizeJwtAttribute.cs using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authentication.JwtBearer; namespace svelteCsAsp.Controllers { public class AuthorizeJwtAttribute : AuthorizeAttribute { public AuthorizeJwtAttribute(): base() { AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme; } } } これは[AuthorizeJwt(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]と書くのが長ったらしいので、作りました。 よく知っている方なら、それなら「Startup.cs」で「services.AddAuthentication()」の時に、以下の様に書けばとなりますが、なぜかこれが機能しません。どうすればいいか知っている方がおられたら教えてください。(ネットで探していると。 app.UseAuthorizationをapp.UseMVCの前に書けばいけたとの書き込みもありましたが、MVCは使わないので...。あとでも記載していますが、どうもMVCの何かが必要な状態になっている様に思います。) Startup.cs services.AddAuthentication(options => { // JWT Bearer をデフォルトにする options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Issuer"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); 承認とロールの確認 これで全てそろったので、Launch.jsonに作成した「Compound」でデバッグ実行します。 問題が無ければログイン画面が表示されます。データベースの自動マイグレーションで作成しておいた「system」でログインすると、ログイン後の「Is System Manager」は成功し、「Is GroupManager」は失敗します。「groupuser」でログインすればその反対になります。 ほかに検討してみた実装 今回、SPAの認証の実装にはJWTを使っているのですが、そこに行く前の段階で2種類ほど検討しました。 一つ目は、dotnetコマンドでAngularの環境を作り、AngularをSvelteに入れ替えようとしました。AngularのテンプレートではIdentityServerで認証サーバーを立ててoidcで認証しているみたいです。認証のIdentityは同じものを使っているようでしたし、独自の認証サーバーが必要かどうか疑問でした。ソースがAngular用で少し複雑だったのもあってパスしました。他の認証局を利用する場合、例えばGoogleなんかを使う場合のライブラリは別途あるみたいでしたし、それはその時調べて使おうかなと思います。どうもoidcでstateを使ってCSRF対策してるようです。oicdはまだよく知らないので今後の私の課題でしょうか。 次にMVCとかで使っていた方法で、通常の認証を使った上にCSRF対策のアンチリフォージェンシーを利用する方法を使ってみました。トークンの作成と引き渡しできましたが、なぜか[ValidateAntiForgeryToken]がうまく機能しませんでした。オプションでトークンをヘッダーに置く方法もあったのですが、どうやらこの属性はMVC関連のライブラリにいることから、MVCでないとうまく動作しない感じです。まあ、ページの隠し入力エレメントを利用して、開発者が意識しなくても使えるように作っているので当然かもしれませんが。Angularでoidcを使っているのもこの辺りが原因かなと考えています。
- 投稿日:2021-04-03T17:56:21+09:00
VSCodeでSvelte+ASP.Net CoreでJWTを利用したSPAの認証(環境構築)
表題の通りの認証を実行してみました。結構苦心したのでまた作るときのメモ、兼今後これをやろうとしている人への助けにでもと思っています。 目標 VSCodeで環境を作る ASP.Net coreのDefaultIdentityを利用してユーザー認証をする 承認の属性が使えるようにする ロールの属性が利用できるようにする CSRFの対策ができている 環境 dotnetコマンドで作る場合のAngularやReactのテンプレートを参考に、ASP.Net coreの環境を基本に作り、その中にSvelteのクライアント環境を作ることにしました。ASP.Net coreのテンプレートはWebAPIで作成します。 開発フォルダでコマンド「dotnet new webapi」を実行してまずASP.Net coreのWebApi環境を作る。 次に「npx degit sveltejs/template ClientApp」を実行。フロントエンドのために作成するフォルダ名はangularなどSPAのテンプレートに倣って「ClientApp」にしています。 「ClientApp」でVSCodeを開いて、前回書いた記事VSCodeでSvelteをTypescriptにしてESLintの環境を整えるを参考にsvelte+typescript+ESLintの環境を整えます。 VSCodeには以下のパッケージを入れておきます。 - Japanese Language Pack まあ、日本人なら入れますね - C# これは必須ですね - ESLint これも基本入れておいた方がいいかと。前回の記事を参考にしているなら入ってますよね。 - Svelte for VSCode Svelte使うので入れておいてください。 - Svelte Interisense これも入れておいた方がいいですね。 後は、必要に応じて入れて下さい。Nuget使うのに私が作ったNuget GUI Managerなんか入れてくれると嬉しいです。ちょっとダイアログがバグってますが、そのうち調べます。 作ったフォルダをVSCodeで開くと、しばらくして右下に次のようなダイアログが出ます(csのファイルを開くか、projectのファイルを開いた時かもしれません。実はタイミングがよく分かってないんです)。ここで「yes」をクリックしておくと、ASP.Net用のタスクとデバッグ設定が作られます。(出ない場合はタスクバーの一番右の鐘のマークをクリックしてみてください。) rollup.config.jsの変更 開発環境ですが、WebサーバーはASP.Net coreを利用します。Svelteのトランスパイルにはコマンドオプションに-wを付けてファイル更新時に自動的にトランスパイルする方法もありますが、今回はASP.Net coreのデバッグ時に同時にトランスパイルする様にします。また、WebサーバーはASP.Net coreのものを使うので、Svelteの「dev」タスクは使わず「build」タスクを使うのですが、そのまま使うとTypeScriptやSvelteのmapファイルができないので少し設定を変更します。 「rollup.config.js」を開いて出力フォルダをASP.Net coreのwwwrootフォルダに向けます。「!production」を4か所「true」に変更します。最後の3つは変更しません。 rollup.config.js input: 'src/main.ts', output: { sourcemap: true, format: 'iife', name: 'app', file: '../wwwroot/build/bundle.js' <= この部分を変更しています。 }, plugins: [ svelte({ preprocess: sveltePreprocess({ sourceMap: true }), <= この部分を「!production」から変更する compilerOptions: { // enable run-time checks when not in production dev: true <= この部分も「!production」から変更します } }), // we'll extract any component CSS out into // a separate file - better for performance css({ output: 'bundle.css' }), // If you have external dependencies installed from // npm, you'll most likely need these plugins. In // some cases you'll need additional configuration - // consult the documentation for details: // https://github.com/rollup/plugins/tree/master/packages/commonjs resolve({ browser: true, dedupe: ['svelte'] }), commonjs(), typescript({ sourceMap: true, <= この部分も「!production」から変更します inlineSources: true <= この部分も「!production」から変更します }), task.jsonの変更 次に親のASP.Net coreのフォルダをVSCodeで開いて設定します。以後はこのフォルダで作業をします。 まず、.vscodeフォルダのtask.jsonですが、「build」「publish」「watch」の3つのASP.Net用のタスクが作られていると思います(前にのほうで説明していたダイアログで「yes」とするとできます)。これにsvelteのbuildを追加します。もちろん設定を直接書いてもいいのですが、メニューの「ターミナル」->「タスクの構成」とすると構成するタスクを選択して下さいと出ます(このパターンの処理が多いのですが、最初選択肢が出ているのがわかりませんでした)。ここで、基本的に使えるタスクが選択できるのでここでは「npm: build - ClientApp」を選択して追加します。npmのビルドでClientAppの中にあるのでこういう名前になるようです。さらに2つのタスクを同時に実行するタスクを以下の様に手入力で追加します。2つを同時に実行させるタスクは、あとでlaunchで参照させます。 task.json ... // ここからがタスクの構成で追加されたもの { "type": "npm", "script": "build", "path": "ClientApp/", "group": "build", "problemMatcher": [], "label": "npm: build - ClientApp", "detail": "rollup -c", }, // 以下を手作業で追加します。ASP.Netのコンパイルと、Svelteのトランスパイルが実行されます { "label": "AllBuild", "dependsOn": [ "npm: build - ClientApp", "build" ] } launch.jsonの変更 次にlaunch.jsonを変更します。まず、メニューの「実行」->「構成の追加」で「Chrome: Launch」を選択します(ブラウザは好きなものをえらんでください)。で、追加されたものですが、URLのポートが8080になっているのを5001に変更します。さらに、以下の様に「compounds」を追加して、デバッガを一度に起動する設定をします。この時、設定されている「.NET Core Launch (web)」のpreLaunchTaskは消しておきます。 launch.json { "version": "0.2.0", "configurations": [ // これが追加された部分です { "name": "Launch Chrome", "request": "launch", "type": "pwa-chrome", "url": "https://localhost:5001", <= ポート番号を変更します "webRoot": "${workspaceFolder}" }, { // Use IntelliSense to find out which attributes exist for C# debugging // Use hover for the description of the existing attributes // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", // "preLaunchTask": "build", <= ここをコメントアウトしています。 // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/bin/Debug/net5.0/svelteCsAsp.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser "serverReadyAction": { "action": "openExternally", "pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)" }, "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" } }, { "name": ".NET Core Attach", "type": "coreclr", "request": "attach", "processId": "${command:pickProcess}" } ], // 以下を追加しました。 "compounds": [ { "name": "Compound", "configurations": [".NET Core Launch (web)", "Launch Chrome"], "preLaunchTask": "AllBuild", "stopAll": true } ] } 追加した「compounds」は複数のデバッグを実行させる設定で、「configurations」に実行させる複数のデバッグを書き込みます。preLaunchTaskにtask.jsonで作った物を入れて、デバッグ前にASP.Net coreとsvelteの両方をbuildしています。 wwwrootにsvelteの静的ファイルを移動 webサーバはASP.Netを利用しますので、CliantAppのpublicフォルダ内のファイルを、ASP.Netのwwwrootにコピー(移動でもいい)します。svelteで一度実行なりトランスパイルをしているとbindフォルダができていますが、このフォルダは移動しなくてもいいです。rollupの設定を変更しているので、svelteでトランスパイルするとwwwrootの下に作成されます。 ASP.Net coreとsvelteの実装は次の記事に書きます 長くなったので、とりあえずこの記事はここまでです。次の記事でコードを実装します。
- 投稿日:2021-04-03T17:00:36+09:00
C# アセンブリのバージョンの謎を調べる(1)
C# の Assembly 周りを職業柄深く理解しないといけないのだけど、自分の理解はとっても浅いと最近感じてきたので、勉強することにした。 特に、次の分野を勉強したい Assembly Versioning Resolve assembly loads Strong Name and Side by side execution Load and unload Assembly あたりは抑えておいた方がよさそう Assembly Version まずは基本的なバージョニングについてみておきたい。 自分のライブラリが参照するライブラリがあり、それが自分と同じライブラリを使っている場合、バージョンを参照先より高くする 自分が実験してみたのですが、下記のように、自分のライブラリがあり、それが、自分のライブラリの内部で使用している別のライブラリの一部として、Newtonsoft.Json が あり、自分のライブラリが参照しているプロジェクトでも、Newtonsoft.Json が存在するとします。自分のライブラリの中で、Newtonsoft.Json を使いたい場合は、 必ずそれら2つよりもバージョンを高くします。 例えば、内部参照しているライブラリのバージョンより低いバージョンを使った場合、内部参照をしているパッケージが、11.0.2 を使っているのに、 自分のライブラリで、9.0.1 を使うと、両方のライブラリが 9.0.1 になります。ただし、これは使えなさそうです。Detected package downgrade: Newtonsoft.Json from 11.0.2 to 9.0.1 というエラーが出てしまいます。 これは、外部参照先であっても同じです。 エラーがダブルになりました。外部参照先は、12.0.2 です。安全のためには、自分のライブラリで参照を持たないか、それらより高いバージョンを使えばよくなります。13.0.1 を参照してみると、 自分のライブラリの参照が13.0.1 になるのがわかるでしょう。もちろん依存先のライブラリにはバージョンの変化はありません。 削除すると、高いバージョンに合わせる感じになります。内部参照のバージョンと比べると、外部参照の方が高いバージョンなので、内部参照の ライブラリのバージョンが高くなりました。 バージョンの数字の決定 今まで実験した通り、cscprojファイルにバージョンを書いていても、同じライブラリのバージョン上のものがあれば、バージョンが上がったりしているのが わかったと思います。バージョン指定をしているのに何でとおもうかもしれません。みんながよく使っている指定は、最小バージョンを指定しているので、上がるのはありなのです。 <PackageReference Include="Newtonsoft.Json" Version="11.0.2"/> Version ranges 試しに、最大で、9.0.3 の指定をしてみました。 <PackageReference Include="Newtonsoft.Json" Version="(9.0.3]"/> 元々がルールに外れるのでさんざんな結果です。折角なので適切だけど最大値あるとかやってみます。 バージョン指定を試してみる バージョン指定を試してみます。csproj ファイルを、次のように指定してみます。最小バージョンが9.0.1 11.0.01未満です。 Reference.csproj <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <Version>1.0.2</Version> </PropertyGroup> <ItemGroup> <PackageReference Include="Newtonsoft.Json" version="[9.0.1, 11)" /> </ItemGroup> </Project> このプロジェクトを pack して、LocalNuGet に配置した後、11以上のバージョンを指定してみます。 AssemblyVersion.csproj <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <AzureFunctionsVersion>v3</AzureFunctionsVersion> <Version>1.0.1</Version> </PropertyGroup> <ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Referenced" Version="1.0.2" /> </ItemGroup> <ItemGroup> <None Update="host.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> <None Update="local.settings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToPublishDirectory>Never</CopyToPublishDirectory> </None> </ItemGroup> </Project> NU1608 のワーニングなので、サプレスは出来そうですが、しっかり必要以上のバージョンアップを止めてくれました。 Severity Code Description Project File Line Suppression State Warning NU1608 Detected package version outside of dependency constraint: Referenced 1.0.2 requires Newtonsoft.Json (>= 9.0.1 && < 11.0.0) but version Newtonsoft.Json 12.0.2 was resolved. AssemblyVersioning C:\Users\tsushi\source\repos\AssemblyVersioning\AssemblyVersioning\AssemblyVersioning.csproj 1 次回 次回はより高度な内容にする予定です。DLLのマニフェストの見かた、LoadAssemblyでロードしたときのバージョンコンフリクトの発生、 そして、LoadAssemblyしたときにDIコンテナを共有するとどのような挙動になるのか、というところをサンプルプログラムを作って 試してみたいと思います。
- 投稿日:2021-04-03T15:01:11+09:00
Visual Studio Tips
- 投稿日:2021-04-03T12:46:48+09:00
ASP.NET Core で Razor ページ アプリにモデルを追加する(EFについて)
環境や前提条件 クライアントOS : MacOS 10.15.7 ターミナル : 2.10 コマンドラインシェル : zsh 統合開発環境(IDE) : VisualStudio for Mac 8.8.8 ツールを使用 ツールをインストール ツールを更新 を行った後、 ターミナルで次のコマンドを入力 ターミナル dotnet ef --project RazorPagesMovie 出力結果 参照
- 投稿日:2021-04-03T08:35:29+09:00
RenderTexture間のRay変換
メインカメラ(Persepective)からのオブジェクトへのRayによる当たり判定 をRenderTextureを描画しているSubCamera(Orthographic)のRay へと変換するものです
public ray TransformRay(Ray ray,GameObject TargetScreen) { Plane QuadPlane = new Plane(-TargetScreen.transform.forward, TargetScreen.transform.position); float enter = 0; TargetPlane.Raycast(ray, out enter); Vector3 HitPoint = ray.GetPoint(enter); Vector3 shift=transform.position-TargetScreen.transform.position; float scale=transform.lossyScale.magnitude/TargetScreen.lossyScale.magnitude; Hitpoint=(HitPoint-shift)*scale; return new Ray(HitPoint,transform.forward); }・なんの意味があるのか?
主にLive2Dを描画するために使います。Live2DはOrthographicのカメラにしか写りません。
そこでPersepectiveのカメラに写したい場合は一度Live2DモデルをRenderTextureに描画して、それをマテリアルにして3D空間のオブジェクトに反映させます。ですがこの方法だと基準点が違うため当たり判定用のRayの変換が必要になります。そのためのスクリプトです。
RenderTextureに描画してPersepectiveカメラに写すことでズーム、スクロール、半透明化が可能になります




