- 投稿日:2019-12-18T17:23:10+09:00
C#でXMLをシリアライズ出力するためのハックいろいろ
指定された形式でXMLを出力しようとした時に毎回検索してはあっちこっちから情報拾ってる気がするのでまとめていく。
ちまちま増やしていくつもりです。・環境
・C#7.0~8.0
・.Net Framework 4.7
・.Net Core 3.1
Nullable<T>=null
の項目のタグを出力しないこんな場合。
モデル[XmlRoot] public class Library { [XmlElement] public User[] Users { get; set; } } public class User { [XmlElement] public int? Age { get; set; } [XmlElement] public string Name { get; set; } }出力<?xml version="1.0" encoding="utf-8"?> <Library xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Users> <Age xsi:nil="true" /> <!--これを出力したくない--> <Name>user1</Name> </Users> </Library>bool型で制御したいプロパティ名+Specifiedのプロパティを定義することでタグの出力/非出力を制御できるのでそれを利用します。
コード[XmlRoot] public class Library { [XmlElement] public User[] Users { get; set; } } public class User { [XmlElement] public int? Age { get; set; } // Age == null の場合falseが返ってタグが出力されなくなる // 完全にシリアライズ制御用のプロパティなのでBrowsable=falseにしてインテリセンスで表示されないようにもしています [XmlIgnore, Browsable(false)] public bool AgeSpecified => Age.HasValue; [XmlElement] public string Name { get; set; } }出力<?xml version="1.0" encoding="utf-8"?> <Library xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Users> <Name>user1</Name> </Users> </Library>構造体のシリアライズ
変換処理を書くだけといえばそれだけ
[XmlRoot] public class Library { [XmlIgnore] public Color SomeColor { get; set; } [XmlElement("FontColor"), Browsable(false)] public string SomeColorValue { get { return $"#{SomeColor.A}{SomeColor.R}{SomeColor.G}{SomeColor.B}"; } set { SomeColor = ColorTranslator.FromHtml(value); } } }出力<?xml version="1.0" encoding="utf-8"?> <Library xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <FontColor>#12345678</FontColor> </Library>DateTimeのフォーマットなんかもこれでやらざるをえない気がする。
xmlnsを外す
ちょっと古いアプリとの連携とかで要求されがちな気がする。
xmlをプレーンテキストとして処理する場合とかにもコードclass Program { static void Main(string[] args) { var app = new Library() { Names = new string[] { } } using (var writer = new StreamWriter("application.xml")) { // 空白のnamespaceを用意する var ns = new XmlSerializerNamespaces(); ns.Add(string.Empty, string.Empty); var serializer = new XmlSerializer(typeof(Library)); serializer.Serialize(writer, app, ns); // 空白のnamespaceを使ってシリアライズ } } } [XmlRoot] public class Library { public string[] Names { get; set; } }出力<?xml version="1.0" encoding="utf-8"?> <Library> <Names /> </Library>後書き
なんかもっといい方法あったら教えてほしいなって。
- 投稿日:2019-12-18T17:09:00+09:00
Unityでシューティングゲームを作る(2)
ここまでの進捗
- 背景がループするようにした。
- 普通の敵の動作を作成し、その敵が3秒ごとに生成される。
- 瞬間移動する敵の動作を作成し、5秒ごとに生成する。
- プレイヤーが画面の範囲外に行かないようにした。
- 敵とプレイヤーが衝突したらプレイヤーが消滅する。
今後やること
- オープニングシーンとエンディングシーンを追加する。
- ボスキャラの動作を実装する。
- 分散攻撃の敵を実装する。
- エフェクトとBGMを追加する。
- 様々な敵の出現方法を考える。
この記事で書くのは赤文字の部分
分散攻撃の敵を実装する
敵キャラの親クラスに分散攻撃をするための関数を以下のように作った
public void NwayShot(Transform enemy,float angle) { Instantiate(EnemyProjectilePrefab, enemy.position, Quaternion.Euler(new Vector3(0.0f,0.0f,angle))); }これのEnemyProjectilePrefabは敵が撃つ弾のPrefabで、enemy.positionは敵の位置、Quaternion.Eulerで弾の向きを設定している。
弾の向きの計算は以下のようにしたpublic int NwayCount = 3;//何方向に攻撃するか public int NwayAngle = 10;//弾の角度 . . . for(int i=1; i <= NwayCount; i++) { float angle = -(NwayCount + 1) * NwayAngle / 2 + i * NwayAngle; base.NwayShot(gameObject.transform, angle); }この計算は以下のサイトを参考にさせていただきました。
計算が苦手だったので非常に助かりました。
参考:【第2回】n-way弾実装してみるよ!ちゃんと3-Way攻撃になりました!
今後は中心の弾がプレイヤーに向けて撃たれるようにしたいです。
結構調べたりしたのでめっちゃ時間かかった(泣)
次はボスだぁー
- 投稿日:2019-12-18T16:01:20+09:00
Unityカスタムパッケージ作成
最近のUnityが機能を分別してPackageManagerからほしいものだけをインストールする形になりました。自分もこういうパッケージをどうやって作るか検討しました。
パッケージ用プロジェクトのレイアウト
まずはパッケージになる新しいUnityプロジェクトを作成。プロジェクト内のフォルダーなどを下記のレイアウトと合わせる
参考:Unityマニュアル https://docs.unity3d.com/Manual/cus-layout.htmlUnityマニュアル通りにしたら通常Unityプロジェクトではないので作る時は別のプロジェクトにインポートしないといけない。これは使いにくいのかなと思って通常のプロジェクトの中に突っ込みました。
<root> └── Assets ├── PackageContents │ ├── package.json │ ├── README.md │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── Editor │ │ ├── [YourPackageName].Editor.asmdef │ │ └── EditorScript.cs │ ├── Runtime │ │ ├── [YourPackageName].asmdef │ │ └── RuntimeScript.cs │ └── Documentation~ │ │ └── Tests ├── Editor │ ├── [YourPackageName].EditorTests.asmdef │ └── EditorTests.cs └── Runtime ├── [YourPackageName].RuntimeTests.asmdef └── RuntimeTests.cspackage.jsonを作成
まずはパッケージの情報をpackage.jsonっていうファイルに書き込もう
参考:https://docs.unity3d.com/Manual/upm-manifestPkg.html例
{ "name": "com.company.package", "displayName": "Package Name", "version": "1.0.0", "unity": "2019.2", "description": "A Test Package", "keywords": [ "package" ], "category": "Utility" }もっと設定あるけど基本的にこのくらいあると大丈夫かな?
name = reverse url形式のパッケージ名
displayName = 人間が読みやすい名前
version = パッケージのバージョン(major.minor.patch)
unity = unityの必要バージョン
description = 説明文
keywords = 検索用言葉
category = パッケージグループ名C#スクリプトを作成
これはもちろん自由なところです。作りたいパッケージを実装する。例えばAssetBundleの作成とロード周りのシステムとか?
Editor用ならAssets/PackageContents/Editorのフォルダーを利用してゲーム用のスクリプトならAssets/PackageContents/Runtime。Assembly Definitionを作成
Assembly Definitionを追加するとパッケージのDLLを作成してくれます。これはメインプロジェクトのコンパイル時間が早くなったり、誰かにDLLだけ渡したり、便利なものです!追加するだけで終わるので入れましょう。
EditorとRuntimeフォルダーに別々に追加してEditorのdefinitionでは参照するdllのところにruntime用dllを付けるTestファイルを作成
必要かどうか個人判断だけど、せっかくいい機能があるのでテスト用スクリプト追加してもいいよね。
- Testフォルダーの中にEditor用とランタイム用分かれてる
- テスト用スクリプトなどがまだ作られてない場合、Testフォルダーに右クリック→Create→Testing→Tests Assembly Folderを選択
- 作られたフォルダーをEditorかRuntimeの名前に変更して中にあるassembly definitionの名前を好きに変更
- そのフォルダーにいって右クリック→Create→Testing→C# Test Scriptを選択してスクリプトを追加
- 出来上がったテストスクリプトはだいたいこの感じになります
using UnityEngine; using UnityEngine.TestTools; using NUnit.Framework; using System.Collections; public class EditorTests { [Test] public void RunTests() { Asset.Pass(); } [UnityTest] public IEnumerator RunTestsWithEnumerator() { yield return null; } }
- 確認方法: UnityのメニューからWindow→General→Test Runnerにいって出てくるwindowでテストが正常に動くか
Verdaccioっていうnpm repositoryにパブリッシュ
これから作るパッケージがどこかに管理したいのでどこかに入れましょう。 UnityのPackage Managerがnpm利用してるので自分が作ったものをPackage Managerに出したいので合わせる必要がある。Unityさんが推奨したのはVerdaccioっていうもの。(多分インストールと準備が楽だから、unityが内部的に違うもの使ってるらしい)
ここからダウンロードとインストール:https://github.com/verdaccio/verdaccioインストール後command lineでpackage.jsonがあるフォルダーにいって
npm publish --registry http://localhost:4873を叩くと登録されます!(もちろん別のマシンにnpmホストしてるならurlをあわせる必要ある)
本番プロジェクトに利用
まずは私達作ったrepositoryはどこにあるかUnityに教えないといけない。
Packagesっていうフォルダーの中にあるmanifest.jsonを開いてscopedRegistriesっていうjsonオブジェクトを追加する。
単純にurl、名前、とパッケージの逆urlを追加する{ "scopedRegistries": [ { "name": "My Packages", "url": "http://localhost:4873", "scopes": [ "com.company" ] } ], "dependencies": { "com.unity.ads": "2.0.8", "com.unity.analytics": "2.0.16", ..hogehoge.. } }後は"dependencies"のところに自分のパブリッシュされたパッケージ名とバージョンを定義するだけ
"dependencies": { "com.unity.ads": "2.0.8", "com.unity.analytics": "2.0.16", ..hogehoge.., "com.company.package": "1.0.0" }Unityに戻ったらパッケージがダウンロードされて使えるようになります!
さらに便利に
まずはコード管理するにはgit repositoryがもちろんやったほうがいいよね。さらにGitLabのCI機能を利用してテストクラスを自動的に走らせてOKが出ればnpmにpublishするような流れるを作る!
・Git repoを作る
・git runnerを作る(やり方がCIのところに書いてる)
・git runner用のスクリプトを追加 (ルートフォルダーに.gitlab-ci.ymlを追加)自分が作ったスクリプトはこんな感じになりました
stages: - runTests - publish variables: version: "awk '$$1==\"m_EditorVersion:\"{print $$2}' ./ProjectSettings/ProjectVersion.txt" editor-tests: script: - "echo 'Running Editor Tests...'" - "unityVersion=$(eval $version)" - "unity='/Applications/Unity/Hub/Editor/'$unityVersion'/Unity.app/Contents/MacOS/Unity'" - $unity -batchmode -runTests -projectPath . -testResults ./editmodeResults.xml -testPlatform editmode stage: runTests tags: - unity only: - master runtime-tests: script: - "echo 'Running Runtime Tests...'" - "unityVersion=$(eval $version)" - "unity='/Applications/Unity/Hub/Editor/'$unityVersion'/Unity.app/Contents/MacOS/Unity'" - $unity -batchmode -runTests -projectPath . -testResults ./playmodeResults.xml -testPlatform playmode stage: runTests tags: - unity only: - master publish-npm: script: - "cd ./Assets/PackageContents" - "npm publish --registry http://localhost:4873/" stage: publish tags: - unity only: - master・2つのステージに分ける(テストとパブリッシュ)
・editor用とランタイム用のテストスクリプトを実行する(起動するunityバージョンをProjectVersionsのテキストから引っ張る)
・テストがOKならnpm publishかける
・メインブランチしか動かない(only: master)おわりに
パッケージ分けるとちゃんとしたフレームワークができて各プロジェクト使い回すことが本当に便利!後から分別するのも大変かもしれないので早い段階でパッケージ化するのはおすすめ!
- 投稿日:2019-12-18T15:56:15+09:00
workgroup名の取得方法
.Net C#でworkgroup名を取得する方法です。
日本語でググると Marshal を利用した方法がトップに出るのですが、Windows10では保護されたメモリ領域へのアクセスとして例外エラーが生じます。以下からの引用の一部改変(usingなしでOKな記述に変更)ですが、載せておきます。
https://stackoverflow.com/questions/2708610/how-can-i-get-the-workgroup-name-of-a-computer-using-cpublic static string GetWorkGroupName() { System.Management.ManagementObject computer_system = new System.Management.ManagementObject( string.Format( "Win32_ComputerSystem.Name='{0}'", Environment.MachineName)); object result = computer_system["Workgroup"]; return result.ToString(); }
- 投稿日:2019-12-18T13:09:35+09:00
ベクトルの内積によるサイコロの出目判定
PONOS Advent Calendar 2019の19日目の記事です。
昨日は@loveRiceさんの「[Unity] DOTweenを使ってみよう」でした。はじめに
今日はUnityでサイコロを作ったお話をさせていただきます。
上の画像が完成形ですが、物理演算で転がり終えたサイコロの出目をテキストで表示しています。とあるゲーム企画でサイコロを出してみたいという要望があり試作したものです。
要件
サイコロの要件は以下の通り。
① 3D空間上にサイコロを出現させたい
② 物理演算でリアルに転がしたい
③ 転がり終わったときの出目を判定したい技術的に可能かどうか聞かれ、もちろん実装できますよ!と即答したものの...
Unityなら①と②はお茶の子さいさいなのですが、さて③の出目判定はどうしたものか...?ここはエンジニアの腕の見せ所!と、ちょっぴり本気出して考えてみました。
運や偶然
いきなりですが、ちょっと脱線します。
サイコロやルーレットなど、運や偶然の要素は楽しさを演出しプレイヤーをワクワクさせてくれます。
ただ、そういったゲームの殆どは、実は表示される前から結果が決まっているもので、
私が知る限り、大半は「予め結果を確定させた上でビジュアルをその結果に合わせる」処理をしています。いかにも今この場でリアルタイムに確定したように見えても、実はそう思わされているだけなのです。
例えば、サイコロをふるボタンを押して3秒後に出目が確定するゲームの場合
1.サイコロをふるボタンが押された
2.乱数により「5」が選ばれる(※この時点で確定)
3.サイコロのアニメーション再生開始(再生開始ポイントは5が出る3秒前に設定)
4.出目が5の状態でサイコロのアニメーションが停止する
5.プレイヤーが結果が5であることを認識するこういう話をすると、そんなのインチキだと暴れ出す人もいるかもしれませんが、これはユーザーの当選権利を守ったり、確率を適正にコントロールするための仕様だったりします。
(少なくとも自分がこれまで関わったゲームについてはインチキしていませんので。^^;)理由はそれぞれとしても、大概のゲームはルーレットが回り始める前に結果を確定させています。
なので、今回やろうとしている出目判定というのは、サイコロが登場するゲームの中でも特殊と言えます。
いくつかの案
さて、どうやってサイコロの出目をリアルタイムに判定するか?に戻しますが、
軽く考えを巡らせてみただけでもアイデアはいくつか出てきます。・6面それぞれにCollider(当たり判定)を配置し、地面と衝突している面の対面を出目とする。
・6面それぞれの面の中心座標を比較し、最も高い位置にある面を出目とする。
・立方体モデルの角の8頂点のうち高さの上位4頂点の組み合わせから出目を求める。
・立方体モデルの向き(回転角度)から出目を求める。
などなど今回は「立方体モデルの向きから出目を求める」方法をチョイスしました。
回転角度から出目を求める
オブジェクトの回転角度ですが、UnityのGameObjectならtransform.rotationで取得できます。
ただ、角度をオイラー角で判定するというのは経験上ろくなことがなかったので(0度や360度を跨いだ時の処理とか、ジンバルロックとか...)、迷わず「ベクトルの内積」を使って判定することにしました。測定したことはないですが、おそらく計算処理も軽いはずです。
ベクトルの内積
ベクトルの内積を使うと以下のことがわかります。
・2つのベクトルの射影
・2つのベクトルが平行かどうかの判定
・2つのベクトルが垂直かどうかの判定
・2つのベクトルの角度差UnityにはVector3.Dot()という便利なものが用意されています。
具合案
最初に考えた案は、サイコロの6面の法線ベクトル(面に垂直なベクトル)の中で、ワールド空間上の上向きベクトル(アップベクトル)に最も近いものを探す、というものでした。
でも面と裏面の法線ベクトルは符号の違いでしかないので、6面分の法線ベクトルでなくても3面分で良いことに途中で気付きました。更に突き詰めると法線も必要なく、transformの3つの回転ベクトル成分で十分ということになりました。
整理すると...
ワールド空間のアップベクトルとサイコロの回転ベクトルXYZを比較して、最もアップベクトルに近いベクトルをみつけ、且つそのベクトルがプラス方向かマイナス方向なのかで出目を判定します。実装してみた
UnityエディタのGizmoでは、Xが赤、Yが緑、Zが青で色付けされています。
まず、基準となるワールド空間の座標は以下のようになっています。
グレーの原点から上方向に伸びる緑色の線をアップベクトル(=Vector3.up)とし、基準にします。サイコロの赤青黄の線のうち、上記ワールド空間のアップベクトルと方向が一致しているのは青い線ですね。青い線はZ方向ベクトル(.foward)、且つ正方向なので、出目は1だと判定できます。
スクリプトは以下の通り。
(Unity c# MonoBehaviour) // 出目チェック int GetNumber (Transform diceTransform) { int result = 0; float innerProductX = Vector3.Dot (diceTransform.right, Vector3.up); float innerProductY = Vector3.Dot (diceTransform.up, Vector3.up); float innerProductZ = Vector3.Dot (diceTransform.forward, Vector3.up); if ((Mathf.Abs (innerProductX) > Mathf.Abs (innerProductY)) && (Mathf.Abs (innerProductX) > Mathf.Abs (innerProductZ))) { // X軸が一番近い if (innerProductX > 0f) { result = 4; } else { result = 3; } } else if ((Mathf.Abs (innerProductY) > Mathf.Abs (innerProductX)) && (Mathf.Abs (innerProductY) > Mathf.Abs (innerProductZ))) { // Y軸が一番近い if (innerProductY > 0f) { result = 5; } else { result = 2; } } else { // Z軸が一番近い if (innerProductZ > 0f) { result = 1; } else { result = 6; } } return result; }少しだけ解説すると、以下がベクトルの内積を求めている部分であり、サイコロのX方向ベクトル(.right)とワールド空間のアップベクトル(.up)の内積、つまり角度差を求めています。
float innerProductX = Vector3.Dot (diceTransform.right, Vector3.up);XYZのベクトルのうち最もワールド空間のアップベクトルと角度差が小さいものを判定し、更に正方向か否か場合分けして出てきた答えをresultとして返します。
実際動かしてみると思った通りの判定結果を得ることができました!
補足
サイコロの目の配置は世界標準で決まっているそうです。
裏表の目を足して7になるというルールは有名かと思いますが、それ以外の配置も決まっています。
「天一地六東五西二南三北四」
1は天の方向、6は地の方向、4は北の方向...という意味で、その配置が世界基準です。しかしながらあまり知られていないので、そもそもサイコロ3Dモデルがそのルールに法って作られているか確認も必要です。上記サンプルのサイコロ3DモデルはZ方向が「1」となっていたので、スクリプトもそれに合わせています。
おわりに
今回の判定方法はあくまでもひとつの手段に過ぎません。
もっとスマートな方法もあるでしょうし、どの方法が最適かは要件によっても変わってくるでしょう。結果ではなく、自分が試行錯誤した過程をみていただければと思い記事にしました。
そもそもサイコロ作らなきゃいけない人ってそんなに居ないでしょうしw明日は@e73ryoさんですー!
- 投稿日:2019-12-18T06:44:12+09:00
C#でもIoTできるんだって!!RaspberryPiとC#使ってラジコン?的なの作る。
突然ですが皆さん「ロボット」好きですよね?
はい!すきです!
良い返事ありがとうございます。
C#好きですよね?
...ハイ好きです。
すみません....
嘘です。
まだC#書き始めて一月ちょいしかたっていないので
すげぇとは思いつつまだ愛せていないです。。。とはいえC#書けるようになると色んな事が出来るようになるよ!!ってのはお伝えしたくなったので
僕が愛してるIoT的なハードウェア的なものと絡めてふーんなんかこんな感じなんだ?
くらいをふわっと伝えられればいいなーと思います。ちなみにC#勉強してみたいんだよねーって人にはまず。
オブジェクト指向でなぜつくるのか
をまず読んでオブジェクト指向の変な先入観を取り払って
スラスラわかるC#
を読んで弄り始めてみると楽しくなると思います!!取っ付き難いと苦手意識持っている人も多いかもですけど意外にやってみると
やっぱりプログラミングって楽しいゾこういう人も居ますしある日いきなりホルス神が送られてくればやってみたくなるかもしれない。
https://qiita.com/sngazm/items/c27b7745fb68139213c2ということで
世に居るロボット好きでC#好きで物作りが好きな人が増えて行くそんな記事を書きたい(願望)
まずは環境構築から。
とりあえずRaspberry Piの初期設定は終わっていてリモート操作はできる状態までいった前提で話していく。
その辺はここを見て貰うのが早い
Raspberry Pi 初期設定基本的に下記コマンドはRaspberry Pi上のターミナルに打っている。
コードはWindows10にインストールしているVisual Studio2019で書いてbuildしている。samba導入するとWindowsの共有フォルダが使えるのでファイル共有が少し楽。
sudo apt-get install sambaapt-getをアップデートしないと404エラーでインストールできないことがある
sudo apt-get update「Windowsの機能の有効化または無効化」でSMB 1.0/CIFSのClientとServerをチャックマーク付けて
有効にしないとWindows10だとネットワーク共有に表示されない。smb.conf[Raspberry-Pi] path = /home/pi/Public read only = No browsable = no guest ok = yes force user = pisudo /etc/init.d/samba restartSSH接続でファイル転送するのもあり。
遠隔ソフトにはこれ
RealVNCをダウンロードインストール
Download VNC Viewer for Windows | VNC® Connect※日本語版とかいうのはくそ古いので注意
デフォルトのログインパスワードは
pi
raspberryここから今回のメインインストール作業
.NET Coreのインストール
GitHub - dotnet/core: Home repository for .NET Core
ここのARM/Linux用のSDKをダウンロード
直接DLするならRasberryPiでwgetするといい
100M位容量がある
俺のどんなときもWi-Fiだとくそ遅い。wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gzSDKはホームディレクトリ ~/ に入れておくこと
後の作業でそこから解凍する。
あとSDKインストールの前に以下のコマンドでアップデートとライブラリのインストールを行っておく。sudo apt-get update sudo apt-get install libunwind8 gettext apt-transport-httpsホームディレクトリにdotnetフォルダを作りそこにさっきwgetでダウンロードしてきたファイルを解凍する
※もしDLしてきたファイルがバージョン違ったりするならファイル名を変えてくださいmkdir -p $HOME/dotnet && tar zxf dotnet-sdk-3.1.100-linux-arm.tar.gz -C $HOME/dotnet次にPATHを通す必要があるので
sudo nano ~/.bashrcnanoで.bashrc開いて最後の行に以下を挿入
.bashrcDOTNET_ROOT=$HOME/dotnet PATH=$PATH:$HOME/dotnet保存したら下記コマンドで再読込
. ~/.bashrcこれでバージョンが出ればインストール成功
dotnet --version 3.1.100とりあえずあるあるなLチカ(LEDをちかちかさせる。)をやってみる。
Raspberry PiはLEDを付けるときに抵抗が必要なのでプラス側でもマイナス側でもどっちでもいいので
200Ωくらいの抵抗を適当にかますとLEDが燃えない。
いきなり燃えるわけではないけどそのまま付けると負荷が大きくて点かなくなる!マイナス側はRaspberry PiのGNDのどこでもいいので付ける
プラス側は今回はコードでGPIO4を使うのでそこに付ける。ちなみにこの回路図は僕が今作ってるIoTHubToolsというサービスで自動生成した(まだ非常に見にくい....)
コードはこんな感じGPIOピンの初期化を行い
1秒ごとにON/OFFを繰り返す感じになっている。Program.csusing System; using System.IO; using System.Threading; namespace RasPiCtrlLED { class Program { // GPIOピンの初期設定を行う. public static void initGPIOPin(int gpio) { if (Directory.Exists("/sys/class/gpio/gpio" + gpio) == false) { ///sys/class/gpio/exportにGPIOピン番号を書き込む. using (var writer = new StreamWriter("/sys/class/gpio/export")) { writer.WriteLine(gpio.ToString()); } } System.Threading.Thread.Sleep(100); // /sys/class/gpio/gpioX/direction using (var writer = new StreamWriter("/sys/class/gpio/gpio" + gpio + "/direction")) { writer.WriteLine("out"); writer.Close(); } } // GPIOのON/OFFを制御する. public static void switchGPIO(int gpio, int level) { // /sys/class/gpio/gpioX/valueの内容が0/1でOFF/ONが求まる. using (var writer = new StreamWriter("/sys/class/gpio/gpio" + gpio + "/value")) { writer.WriteLine(level.ToString()); writer.Close(); } } static void Main(string[] args) { // GPIO4を初期化する. initGPIOPin(4); while (true) { // LEDを点灯する.(GPIO4をONにする) switchGPIO(4, 1); // 1秒待機. Thread.Sleep(1000); // LEDを消灯する.(GPIO4をOFFにする) switchGPIO(4, 0); // 1秒待機. Thread.Sleep(1000); } } } }動画はこんな感じ
めっちゃごちゃごちゃしているがこれは作ってみたラジコン的な奴に色々ついてるのでケーブルがごちゃついている。
作った奴に使っているのは
タイヤとかキャタピラとかをこいつで
タミヤ 楽しい工作シリーズ No.100 トラック&ホイールセット
車体をこれで作ってRaspberry Piもこの上に乗っける
タミヤ 楽しい工作シリーズ No.157 ユニバーサルプレート 2枚セット
前後と左右回転させるためにモーターが二つ入っている奴をチョイス
楽しい工作シリーズ No.168 ダブルギヤボックス 左右独立4速タイプ
こいつを組み上げて作って行くぅ!
んでできたのがこいつ。(ごちゃぁ)
モーターを動かすためにはRaspberry Piに直接繋げることはできなくて
モータードライバーという物と電源が別に必要になってくる。ほんとは色々遠隔操作できるようにすると面白いけど多くなりすぎるのでとりあえずモーターを動かすって所までやってみようと思う。
まずモータードライバーというのがこれ
TA7291Pデータ
こいつを使ってモーター制御を行う。こいつを使うとモーターを右回り、左回り、ブレーキ、回転速度の制御ができる。
詳しくはこの素晴らしい記事をご覧頂くのが良い
端子記号 ピン番号 Vcc 7 ロジック側電源端子 マイコンや回路の最大出力電圧を接続 Vs 8 出力側電源端子 モーターで使用する電源のプラスを接続 Vref 4 端子電源端子 モーターに流す電圧を決定する電圧を接続 GND 1 GND マイコンのGND端子とモータで使用する電源のマイナスを接続 IN1 5 入力端子 命令表にしたがって操作を選択するHighまたはLowを接続 IN2 6 入力端子 命令表にしたがって操作を選択するHighまたはLowを接続 OUT1 2 出力端子 モーターの片側を接続 OUT2 10 出力端子 モーターのもう片側を接続 上記のようなピンの仕様になっている。
今回は
TA7291P RaspberryPi RaspberryPiのピン番号 GND GND 6 Vref GPIO18 12 IN1 GPIO14 8 IN2 GPIO15 10 VCC 5V 2 Vs none 電源のプラス端子 こんな感じで配線を行った。
Program.csusing System; using System.Runtime.InteropServices; using System.Threading; namespace DCMotorPWMCtrl { class Program { public const int PWM_MODE_MS = 0; public const int INPUT = 0; public const int OUTPUT = 1; public const int PWM_OUTPUT = 2; [DllImport("wiringPi")] extern static int wiringPiSetupGpio(); [DllImport("wiringPi")] extern static void pinMode(int pin, int mode); [DllImport("wiringPi")] extern static void digitalWrite(int pin, int mode); [DllImport("wiringPi")] extern static void pwmWrite(int pin, int value); static void Main(string[] args) { // wiringPiのセットアップ wiringPiSetupGpio(); // GPIO 14をOUTPUT(1)に設定する. pinMode(14, OUTPUT); // GPIO 15をOUTPUT(1)に設定する. pinMode(15, OUTPUT); // GPIO 18をPWM_OUTPUT(2)に設定する. pinMode(18, PWM_OUTPUT); // 初期状態としてモーターを停止する. digitalWrite(14, 0); digitalWrite(15, 0); // モーターを正転する. digitalWrite(14, 1); digitalWrite(15, 0); // 回転数を1/4に設定する pwmWrite(18, 256); // 3秒待機する. Thread.Sleep(3000); // 回転数を1/2に設定する pwmWrite(18, 512); // 3秒待機する. Thread.Sleep(3000); // 回転数を最大に設定する pwmWrite(18, 1024); // 3秒待機する. Thread.Sleep(3000); // モーターをブレーキする. digitalWrite(14, 1); digitalWrite(15, 1); } } }このコードでモーターを一つ制御できる。
二つ制御する場合はこれを二つ書けば良い。この先でやることは
二つのモーターをコントロールさせるために
TA7291Pを二つ繋げてそれをコントロールするコードを
さっきのコードを元に書く。
例えば左側のモーターを正転させて右側のモーターを逆転させれば車体を右に回転させることができたりする。C#ならこのコードを一つの言語でフロントと繋げてブラウザからコントロールさせるとかもできるが
一つの記事でまとめると流石に書くのが大変すぎたのでやめた。一通りやるとこんな感じの動作ができる。
一旦まとめ
ひとまずだだーと勢いでここまで書いたけど
こういうハードウェアってココをオンにするオフにするといった感じのすごく単純なコードを書くだけで
制御自体は結構簡単にできてしまう。
それに付随するハードウェア自体のコントロール(今回使っているのだとPWM制御とか)がちょっと理解するのに時間かかったりするが
それも中高レベルの物理がなんとなく分かればそんなに難しくないのでハードウェアちょっとやってみたいんだけど
難しそうだよねってまだチャレンジしていない人がいたら是非やってみて欲しい。
そしてどんどんアウトプットして下さい!1人でも多くのハードもできるエンジニアができることを願って。。
以上!
- 投稿日:2019-12-18T00:44:47+09:00
.NET Core 3.1 & EFCore 3.0 で Azure PipelineでCIを構築する
この記事は
C# Advent Calendar 2019 の12/17
の記事です2019/12
.NET Core 3.1 LTSが公開になりました。
これにより、.NET Core 3.1は3年間のサポート対象になります今月自分の関わるプロジェクトを
.NET Core 2.2 ⇒3.1
EfCore 2.0 ⇒ 3.0 に変更を行いました。しかしCIで使っている Azure PipeLineが、.NET Core 3.1 規定値で対応していなかった為、
.NET Core 3.1で動くように変更しました。
そのメモを残します。(2019/12/9現在)Azure PipeLineでは .NET Core 3.0 のCLIが適用されています
.NET Core 3.1 アプリ デフォルト設定ではCIできません。
EFCore 3.0 から デフォルトでEFCore CLI がインストールされていないためMigration を実行するために CLIを別途インストールする手順を加える必要があります。
簡単に言えば 以下の作業を追加するだけで恐らくCIできるようになると思います。
Agent jobをプラスして
バージョンで 3.1.x を指定して(一応Include Preview Versionsをつけて)
これで、設定完了です。
EF Core を使って DB Migrationをするには Command Lineを使うのが簡単です。
Command LineをAdd して
EFCore 3.0 の場合は
dotnet tool install --global dotnet-ef
を使って ツールをインストールしたのち、Migration Scriptを実行します。
.NET Core 2.xの場合は、最初の.Use NET Core で
efCore CLIツールがもとから入ってるのでDB Migrationに
dotnet tool install --global dotnet-ef
コマンドは不要です。↓流れとしてはこんな感じになります