20210620のC#に関する記事は11件です。

プレイ中にFreezePositionとFreezeRotationのオンオフ

空中アスレチックを作成していて、回る物体を作りました。 回してPlayerを移動させるとPlayerが物体に当たったら下の床に食い込むんですね。 この原因はPlayerのRigidbodyのFreezePositionのY軸のチェックが入ってなかったからです。 では、チェックを入れるとどうなるのか? 現象は解消されました! が。。。床から落とすと当然下に落ちません。。。 こういう場合、床に乗っている時はFreezePositionのY軸をオン、 床に乗っていない時はオフにしたい訳です。 まず、RigidbodyのFreezePositionとFreezeRotationを全てチェックを外します。 それからコードを書いていきます。 rb.constraints = RigidbodyConstraints.FreezeRotation; //Rotationを全てオン rb.constraints = RigidbodyConstraints.FreezePosition; //Positionを全てオン rb.constraints = RigidbodyConstraints.FreezeRotationX; //RotationのXのみオン rb.constraints = RigidbodyConstraints.FreezeRotationY; //RotationのYのみオン rb.constraints = RigidbodyConstraints.FreezeRotationZ; //RotationのZのみオン rb.constraints = RigidbodyConstraints.FreezePositionX; //PositionのXのみオン rb.constraints = RigidbodyConstraints.FreezePositionY; //PositionのYのみオン rb.constraints = RigidbodyConstraints.FreezePositionZ; //PositionのZのみオン ※rbはRigidbodyの変数です。 では、私の場合はRotationは全てオン、PositionはY軸のみオンにしたいので。。。 rb.constraints = RigidbodyConstraints.FreezeRotation; //Rotationを全てオン rb.constraints = RigidbodyConstraints.FreezePositionY; //PositionのYのみオン こう書けば大丈夫!! だと思いましたが、残念ながら違います… rb.constraints = RigidbodyConstraints.FreezeRotation //Rotationを全てオン | RigidbodyConstraints.FreezePositionY; //PositionのYのみオン こう書かないといけないのです。 1行目はセミコロンのみ書かなくて良くてrb.constraintsは書かなくていいです!! これで大丈夫!! ちなみに 私の場合は常にRotationは全てオン、 床に乗っている時だけPositionはY軸のみオンにしたいので。。。 //collisionに接触している場合はRotationは全てオン、PositionはY軸のみオンにする。 void OnCollisionStay(Collision collision) { rb.constraints = RigidbodyConstraints.FreezeRotation | RigidbodyConstraints.FreezePositionY; } //collisionに接触してない時はPositionのY軸はオフ。Rotationは全てオンのまま。 void OnCollisionExit() { rb.constraints = RigidbodyConstraints.FreezeRotation; } これで最初は全てチェック外している状態ですが、Rotationは全て、PositionのY軸のみプレイしたらチェックが入り、床から落ちるとPositionのY軸のみチェックが外れます!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】市松模様を描画する(Bitmap)

はじめに これまでにC#を使った画像処理の記事を2つほど書きましたが、画像の描画について「1ピクセルずつ描画する」というレベルに留まっていました。 そこで今回は「複数の図形を一定のパターンで描画する」という、これまでとは違ったアプローチで画像を生成してみました。 【C#】画像生成の基本(Bitmap) 【C#】2つの画像の差分を得る(Bitmap) コード 処理は大まかに以下のようになっています。 キャンバスとなるBitmapオブジェクトを作り、背景色を設定する。 前景色で塗りつぶされた正方形を一定間隔で描画する。 キャンバスサイズ÷格子サイズ が割り切れない場合には、キャンバス外にはみ出る部分は描画されないので「キャンバスサイズを超えて格子柄が描画される」ということはありません。 IchimatsuDrawer.cs using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CSharpStudy.Image { /// <summary> /// 市松模様を描くクラス。 /// </summary> public class IchimatsuDrawer { /// <summary> /// 市松模様を描画するキャンバス。 /// </summary> private Bitmap bmp; /// <summary> /// 市松模様の「格子」のサイズ。 /// </summary> private int gridSize; /// <summary> /// 背景色。 /// デフォルトは白。 /// </summary> private Brush backgroundColor; /// <summary> /// 前景色。 /// デフォルトは黒。 /// </summary> private Brush foregroundColor; /// <summary> /// コンストラクタ。 /// グリッド(格子)のサイズがキャンバスサイズを超える時は例外を発生させる。 /// </summary> /// <param name="canvasWidth">キャンバスの幅(px)</param> /// <param name="canvasHeight">キャンバスの高さ(px)</param> /// <param name="gridSize">グリッド(格子)のサイズ(px)</param> public IchimatsuDrawer(int canvasWidth = 300, int canvasHeight = 200, int gridSize = 20) { if (canvasHeight < gridSize || canvasWidth < gridSize) { new Exception("グリッド(格子)のサイズが大きすぎます。"); } this.bmp = new Bitmap(canvasWidth, canvasHeight); this.gridSize = gridSize; this.backgroundColor = Brushes.White; this.foregroundColor = Brushes.Black; } /// <summary> /// 背景色をセットする。 /// </summary> /// <param name="backgroundColor">背景色。Brushesクラスのプロパティを指定する。</param> public void SetBackgroundColor(Brush backgroundColor) { this.backgroundColor = backgroundColor; } /// <summary> /// 前景色をセットする。 /// </summary> /// <param name="foregroundColor">前景色。Brushesクラスのプロパティを指定する。</param> public void SetForegroundColor(Brush foregroundColor) { this.foregroundColor = foregroundColor; } /// <summary> /// 市松模様を描画する。 /// </summary> /// <param name="filePath">出力先のファイルパス。</param> public void Draw(string filePath) { using (Graphics g = Graphics.FromImage(bmp)) { // キャンバスの背景色を設定する。 g.FillRectangle(this.backgroundColor, g.VisibleClipBounds); // 縦・横の格子柄の繰り返し回数を求める。 // 繰り返し回数に余りが出る場合は格子柄がはみ出る形になるが、キャンバス外は描画されないので仕上がりの画像には影響しない。 double widthCount = (double)this.bmp.Width / (double)this.gridSize; double heightCount = (double)this.bmp.Height / (double)this.gridSize; for (int i=0; i< widthCount; i++) { for (int j=0; j< heightCount; j++) { if ((i + j) % 2 == 0) { // 前景色で塗りつぶされた長方形(格子柄)を描画する。 g.FillRectangle(this.foregroundColor, (i * this.gridSize), (j * this.gridSize), this.gridSize, this.gridSize); } } } } // 画像をPNG形式で保存する。 bmp.Save(filePath, System.Drawing.Imaging.ImageFormat.Png); } } } 実行用のコード IchimatsuDrawerTest.cs using System; using CSharpStudy.Image; using System.Drawing; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CSharpStudyTest.Image { [TestClass] public class IchimatsuDrawerTest { private const string BITMAP_PATH1 = @"C:\Users\NKOJIMA\source\repos\CSharpStudy\CSharpStudy\Image\ichimatsu1.png"; private const string BITMAP_PATH2 = @"C:\Users\NKOJIMA\source\repos\CSharpStudy\CSharpStudy\Image\ichimatsu2.png"; /// <summary> /// キャンバスサイズが格子サイズで割り切れるパターン。 /// </summary> [TestMethod] public void TestMethod1() { IchimatsuDrawer drawer = new IchimatsuDrawer(); drawer.SetBackgroundColor(Brushes.DarkGreen); drawer.SetForegroundColor(Brushes.Black); drawer.Draw(BITMAP_PATH1); } /// <summary> /// キャンバスサイズが格子サイズで割り切れないパターン。 /// </summary> [TestMethod] public void TestMethod2() { IchimatsuDrawer drawer = new IchimatsuDrawer(310, 210); drawer.SetBackgroundColor(Brushes.DarkGreen); drawer.SetForegroundColor(Brushes.Black); drawer.Draw(BITMAP_PATH2); } } } 生成された画像 最近一大ブームを起こしたキャラクターの着物の柄を真似してみました。 キャンバスサイズが格子サイズで割り切れるパターン キャンバスサイズが格子サイズで割り切れないパターン まとめ 長方形(正方形)、円などは、.NETで用意されているメソッドを使えば簡単に描画できます。 多角形については、全ての頂点の座標を指定しないといけないので少々勝手が違いますが...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# ブラックジャックの実装

はじめに Qiitaで色々検索をしていると プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし という記事を見つけたので、学習中のC#で作成してみました。 ソースコードを載せています。 実装方法など改善すべき箇所や、もっと一般的な方法などがありましたらコメントを頂けると助かります。 ブラックジャックのルール 基本ルールはそのままです。 ・カードの点数 Aは、1または11として計算する 2 ~ 10 は数値がそのまま計算する J, Q, K は 10点として計算する ・参加人数 1~4人 コマンドライン上の入力で参加人数を決定します。 勝敗は各プレイヤーとディーラーの点数で判定します。 ・実装していないルール サレンダー、ダブルダウン、スプリットは実装していません。 完成品 コマンドライン上で実行できるものを作成しました。 ソースコード 山札クラス Deck.cs public class Deck { /// <summary> /// 山札用管理リスト /// </summary> private List<Card> cards = new List<Card>(); /// <summary> /// 引いたカードの枚数を管理 /// 山札からカードを引くときに使用 /// </summary> private int _drawn_Card_Number = 0; public Deck() { // 山札の生成 Deck_Prepare(); } public Card this[int i] { get => this.cards[i]; } /// <summary> /// カードを52枚分生成して山札とする /// </summary> private void Deck_Prepare() { if (cards == null) { return; } // Cardを生成して山札とする(52枚) for (int mark_i = 1; mark_i <= 4; mark_i++) { for (int no_i = 1; no_i <= 13; no_i++) { cards.Add(new Card(mark_i, no_i)); } } // 山札をシャッフルする Shuffle(); } /// <summary> /// 山札をシャッフルする /// ゲームの途中でシャッフルする可能性があるためメソッド化 /// </summary> public void Shuffle() { if (cards != null) { // 山札をシャッフルする cards = cards.OrderBy(a => Guid.NewGuid()).ToList(); } } /// <summary> /// 山札からカードを一枚引く /// </summary> /// <returns></returns> public Card Hit_Card() { try { return cards[_drawn_Card_Number]; } finally { // 必ず+1させる。(次のカードを引くため) _drawn_Card_Number++; } } } カードクラス Card.cs /// <summary> /// トランプのカードを表すクラス /// </summary> public class Card { // マーク private int _mark; /// <summary> /// トランプのマーク(1~4) /// 1:ハート 2:スペード 3:クローバー 4:ダイヤ /// </summary> public int Mark { get => _mark; private set { if (value > 0 && value < 5) { _mark = value; } } } private int _no; /// <summary> /// トランプの数字(1~13) /// </summary> public int No { get => _no; private set { if (value > 0 && value < 14) { _no = value; } } } /// <summary> /// コンストラクタ /// カード情報を設定 /// </summary> /// <param name="mark">マーク</param> /// <param name="no">数字</param> public Card(int mark, int no) { _mark = mark; _no = no; } /// <summary> /// ToStringメソッドのオーバーライド /// カードのマークと数字を返す /// </summary> /// <returns></returns> public override string ToString() { return string.Format("{0}の{1}", MarkToString(), NoToString()); } /// <summary> /// マークを文字列に変換 /// </summary> public string MarkToString() { switch (Mark) { case 1: return "ハート"; case 2: return "スペード"; case 3: return "クローバー"; case 4: return "ダイヤ"; default: throw new ArgumentOutOfRangeException(nameof(Mark)); } } /// <summary> /// カードの数字を文字列に変換 /// </summary> public string NoToString() { switch (No) { case 1: return "A"; case 11: return "J"; case 12: return "Q"; case 13: return "K"; default: return No.ToString(); } } } プレイヤー(ディーラー)インターフェース Iplayer.cs public interface IPlayer { /// <summary> /// 合計値が21を超えたか判定 /// </summary> bool isBusted { get; } /// <summary> /// 手札の合計値 /// </summary> int TotalValue { get; } /// <summary> /// 自分の手札 /// </summary> List<Card> MyHand { get; } /// <summary> /// /// </summary> /// <param name="card"></param> void Add_MyHand(Card card); /// <summary> /// 自分のターンで実行する処理 /// </summary> void MyTurn(Deck cards); /// <summary> /// A(エース)を考慮して最善手の合計値を求める /// </summary> void BestSelect_TotalValue(); } プレイヤー(ディーラー)抽象クラス BasePlayer.cs abstract class BasePlayer : IPlayer { /// <summary> /// 21を超えたか判定 /// true:21以上 /// </summary> public bool isBusted { get { return TotalValue > 21; } } /// <summary> /// 手札の合計値 /// </summary> public int TotalValue { get; private set; } = 0; /// <summary> /// 自分の手札 /// </summary> public List<Card> MyHand { get; set; } = new List<Card>(); /// <summary> /// 自分のターンの処理 /// </summary> /// <param name="cards">山札</param> public void MyTurn(Deck cards) { Exe_MyTurn(cards); } /// <summary> /// 手札にカードを加える /// <param name="card">手札に追加するカード</param> /// </summary> public virtual void Add_MyHand(Card card) { MyHand.Add(card); Calculate_TotalValue(card.No); Console.WriteLine(Properties.Resource.Msg_DrawCard, this.ToString(), card.ToString()); Disp_MyHand(); } /// <summary> /// 手札の合計値の計算 /// バーストしているかの判定するための計算処理なので、A(エース)は1として計算する /// </summary> /// <param name="no">手札に追加されたカードの数値</param> protected void Calculate_TotalValue(int no) { // J(11)、Q(12)、K(13)は10として計算する if (no > 10) { no = 10; } TotalValue += no; } /// <summary> /// 自分の手札を画面表示する /// </summary> protected void Disp_MyHand() { var dispStr = ""; foreach (var card in MyHand) { dispStr += "[" + card.ToString() + "]"; } Console.WriteLine(Properties.Resource.Msg_MyHand, this.ToString(), dispStr); } /// <summary> /// A(エース)を考慮した最善手の合計値に更新する /// </summary> public void BestSelect_TotalValue() { Disp_MyHand(); // 手札にA(エース)が含まれない場合は終了 if (MyHand.Count(x => x.No == 1) == 0) { return; } // 合計値計算用 var tmpVal = 0; // A(エース)以外を抽出して計算 var otherA_MyHand = MyHand.Where(x => x.No != 1); foreach (var card in otherA_MyHand) { if (card.No > 10) { tmpVal += 10; } else { tmpVal += card.No; } } // A(エース)を抽出して計算 var onlyA_MyHand = MyHand.Where(x => x.No ==1); foreach (var card in onlyA_MyHand) { // バーストしなければA(エース)を[11」として計算 // バーストする場合は、A(エース)を「1」として計算 if ((tmpVal + 11) > 21 ) { tmpVal += 1; } else { tmpVal += 11; } } // 合計値を更新する TotalValue = tmpVal; } /// <summary> /// 抽象メソッド /// 継承先で自分のターンの処理を実行する /// </summary> /// <param name="cards"></param> protected abstract void Exe_MyTurn(Deck cards); } バーストしているかの判定時は、Aは1として計算します。 勝敗を決定する際はBestSelect_TotalValueメソッドにて、Aを1とするか、11とするか決定して計算します。 プレイヤークラス Player.cs class Player : BasePlayer { /// <summary> /// プレイヤー番号 /// </summary> private int playerNumber; /// <summary> /// ToStringのオーバーライド /// プレイヤー+番号を返す /// </summary> /// <returns></returns> public override string ToString() { return string.Format("プレイヤー{0}", playerNumber); } /// <summary> /// コンストラクタ /// </summary> /// <param name="num">プレイヤー番号</param> public Player(int num) { // プレイヤー番号の割り当て playerNumber = num; } /// <summary> /// プレイヤーのターンに実行する処理 /// </summary> /// <param name="cards">山札</param> protected override void Exe_MyTurn(Deck cards) { Console.WriteLine(); Console.WriteLine(Properties.Resource.Msg_Turn, this.ToString()); Disp_MyHand(); // バーストしていない間(isBusted=false)はループを続行 // プレイヤーがスタンドした場合はループを終了 while (! isBusted) { // カードを引くか尋ねる(false:カードを引くのをやめる) if (! Game_Controller.Confilm(Properties.Resource.Msg_Stand)) { return; } // カードを手札に加える var card = cards.Hit_Card(); Add_MyHand(card); } if (isBusted) { // バーストした Console.WriteLine(Properties.Resource.MSg_Busted); } } } ディーラークラス Dealer.cs class Dealer : BasePlayer { /// <summary> /// ディーラーがカードを引き続ける合計値のボーダーライン /// </summary> private readonly int Boarder_TotalValue = 17; /// <summary> /// ToStringのオーバーライド /// ディーラーを返す /// </summary> /// <returns></returns> public override string ToString() { return "ディーラー"; } /// <summary> /// 手札にカードを加える /// </summary> /// <param name="card">手札に追加するカード</param> public override void Add_MyHand(Card card) { MyHand.Add(card); Calculate_TotalValue(card.No); // ディーラーの初期手札の2枚目を表示しない考慮 if (MyHand.Count != 2) { Console.WriteLine(Properties.Resource.Msg_DrawCard, this.ToString(), card.ToString()); Disp_MyHand(); } } /// <summary> /// ディーラーのターンの処理 /// 自分の手札の合計値が17以上になるまでカードを引く /// </summary> /// <param name="cards">山札</param> protected override void Exe_MyTurn(Deck cards) { Console.WriteLine(); Console.WriteLine(Properties.Resource.Msg_Turn, this.ToString()); // ディーラーの2枚目のカードをオープンする Console.WriteLine(Properties.Resource.Msg_Dealers_2nd, MyHand[1].ToString()); Disp_MyHand(); // 自分の手札の合計値が17以上になるまでカードを引く while (TotalValue < Boarder_TotalValue) { // カードを手札に加える var card = cards.Hit_Card(); Add_MyHand(card); } if (isBusted) { // バーストした Console.WriteLine(Properties.Resource.MSg_Busted); } } } ゲーム管理クラス Game_Controller.cs public class Game_Controller { // ディーラー private IPlayer _dealer; // 山札 private Deck cards; // 参加プレイヤー管理リスト private List<IPlayer> players; // ゲームを開始するメソッド public void Start_Game() { // 参加プレイヤー数を取得する var entryNum = Get_EntryNumber_Player(); // 初期化 Initialize(entryNum); // 最初の手札を配る Deal_Card(); // プレイヤーのターン foreach (var player in players) { player.MyTurn(cards); } // ディーラーのターン _dealer.MyTurn(cards); // 判定 Judge(); } /// <summary> /// 初期化処理 /// </summary> private void Initialize(int entryNum) { _dealer = new Dealer(); cards = new Deck(); players = new List<IPlayer>(); // プレイヤーをリストで管理 for (int i = 1; i <= entryNum; i++) { players.Add(new Player(i)); } } /// <summary> /// 参加プレイヤー数を取得する(1~4人) /// </summary> /// <returns>参加プレイヤー数</returns> private int Get_EntryNumber_Player() { while (true) { Console.WriteLine(Properties.Resource.Msg_EntryNum); var input = Console.ReadLine(); if (int.TryParse(input, out int num)) { if (num > 0 && num < 5) { return num; } } } } /// <summary> /// プレイヤーとディーラーに最初の手札を2枚ずつ配る /// </summary> private void Deal_Card() { // カードを引いて画面に表示する for (int i = 1; i <= 2; i++) { foreach (var player in players) { player.Add_MyHand(cards.Hit_Card()); } _dealer.Add_MyHand(cards.Hit_Card()); } } /// <summary> /// 勝敗の判定 /// </summary> private void Judge() { /* ブラックジャックの勝敗 */ // プレイヤーとディーラーの両方がバースト => 引き分け // プレイヤーがバースト => ディーラーの勝ち // ディーラーがバースト => プレイヤーの勝ち // プレイヤーの得点がディーラーより大きい => プレイヤーの勝ち // ディーラーの得点がプレイヤーより大きい => ディーラーの勝ち // 得点が同じ => 引き分け // プレイヤーごとにディーラーとの勝敗を表示 foreach (var player in players) { Console.WriteLine(); Console.WriteLine(Properties.Resource.Msg_Judge, player.ToString()); // A(エース)を考慮して再計算 player.BestSelect_TotalValue(); _dealer.BestSelect_TotalValue(); if (player.isBusted && _dealer.isBusted) { Console.WriteLine(Properties.Resource.Msg_Draw); } else if (player.isBusted) { Console.WriteLine(Properties.Resource.Msg_Win, _dealer.ToString()); } else if (_dealer.isBusted) { Console.WriteLine(Properties.Resource.Msg_Win, player.ToString()); } else if (player.TotalValue > _dealer.TotalValue) { Console.WriteLine(Properties.Resource.Msg_Win, player.ToString()); if (player.TotalValue == 21) { Console.WriteLine(Properties.Resource.Msg_BlackJack); } } else if (player.TotalValue < _dealer.TotalValue) { Console.WriteLine(Properties.Resource.Msg_Win, _dealer.ToString()); } else { // ここまで来たら同点 Console.WriteLine(Properties.Resource.Msg_Draw); } Console.WriteLine(Properties.Resource.Msg_JudgeValue, player.ToString(), player.TotalValue, _dealer.TotalValue); } } /// <summary> /// プレイヤーにYes/Noの確認をする /// </summary> /// <param name="msg">表示メッセージ</param> /// <returns>true:Yes false:No</returns> public static bool Confilm(string msg) { while (true) { try { Console.WriteLine(msg + " y/n"); ConsoleKeyInfo key = Console.ReadKey(); switch (key.KeyChar) { case 'y': return true; case 'n': return false; default: break; } } finally { Console.WriteLine(); } } } } メッセージリソース リソースファイルの載せ方が分からなかったので画像です。 メイン Program.cs class Program { static void Main(string[] args) { Game_Controller game_Controller = new Game_Controller(); while (true) { game_Controller.Start_Game(); Console.WriteLine(); Console.WriteLine(); if (! Game_Controller.Confilm("もう一度遊びますか?")) { break; } Console.Clear(); } } } 最後に もっとソースに触れて、数をこなしていく必要があるなと感じました。 冒頭に記載しましたが、今後の学習のためにも改善点などありましたらコメントをよろしくお願いします。 また、こういう課題に取り組んだ方が良いなどのアドバイスもあればよろしくお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

床が自動で動くと同時にPlayerも一緒に動く方法

前回の続きです。 void OnCollisionStay(Collision collision) { if (collision.gameObject.tag == "Stage") { transform.parent = collision.gameObject.transform; } } void OnCollisionExit() { transform.parent = null; } 動く床にCollisionをつけてタグも設定し、Playerスクリプトに書きます。 OnCollisionStayはブジェクト同士が接触している間なので、 Stageタグの上に乗っている間Playerが移動する。 OnCollisionExitはオブジェクト同士が接触から離れた瞬間なので 離れた瞬間はnullします。 こうすることで床が動くと同時にPlayerも動くようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

床が自動で動く方法

空中アスレチックを作成していて、床が自動で動く方法をご紹介します。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class UpDown : MonoBehaviour { private Vector3 StagePos; //StagePos変数を設定 void Start() { StagePos = transform.position; } void Update() { transform.position = new Vector3(StagePos.x, Mathf.Sin(Time.time) * 5.0f + StagePos.y, StagePos.z); } } Mathf.Sin関数を使います。 5.0fは動く範囲を判断。 今回は上下に動く床を作りたかったのでyに足しています。 左右に動いてほしかったらxに足せば良いです。 床が動くとPlayerも動かさないと落ちてしまいます。 つまり、床の上に乗るとPlayerは一緒に動いてくれません。 次回はPlayerも一緒に動かす方法をご紹介します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Playerが動く方向にカメラも動く方法

CameraManager public class CameraManager : MonoBehaviour { //格納する変数 public GameObject Player; public float CameraX; public float CameraY; public float CameraZ; void Start() { } void Update() { Vector3 Pos = Player.transform.position; //カメラとプレイヤーの位置を同じにする transform.position = new Vector3(Pos.x + CameraX, Pos.y + CameraY, Pos.z + CameraZ); } } ここまで書けたらカメラにスクリプトをアタッチ。 インスペクターにある CameraX CameraY CameraZ にはPlayerとカメラの位置を設定する。 PlayerにはヒエラルキーのPlayerを入れる。 (私の場合はPlayerですが、カメラで追いかけたいものを紐づける) これでPlayerが動くと同時にカメラが追いかけるようになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

全部消えたらクリア画面を表示

ブロック崩しゲームを作成していて、全部消えたらクリア画面に遷移したいと思います。 全部消えたらの表現はタグを作って実装しています。 GameClear using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; //シーン遷移に必要な記述 public class GameClear : MonoBehaviour { private GameObject[] BlockObjects; //GameObjectにBlockObjectsを格納します void Update() { //消えるオブジェクトにBlockタグをつけます。 BlockObjects = GameObject.FindGameObjectsWithTag("Block"); if (BlockObjects.Length == 0) //Blockタグがついてる残りが0になれば { SceneManager.LoadScene("GameClear"); //クリアシーンを表示 } } } シーン遷移する時はBuildsettingもしないといけません。 ※以前にBuildsettingのやり方を書いた記事があります。 これで無事、ブロックが全て消えた時にシーンが切り替わるようになりました。 ここで注意点なのですが、このスクリプトは消えるブロックにアタッチすると 全部消えた際にシーンは切り替えされません。 理由はブロックが消えてしまったらゲーム中にヒエラルキー上のブロックオブジェクト自体も消えてしまい、アタッチしたスクリプトも消えてしまいます。 なので、他のオブジェクトにアタッチするか、新たにオブジェクトを作成する必要があります。 こういうミスする人はいないかもしれませんが、私はこれで悩まされました(笑)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#/単体テスト】Moqの参考サイトのまとめ(随時更新)

概要 Moqは単体テスト用の代替オブジェクトを簡単に作るライブラリ。 オープンソースで商用利用可能。 C#の単体テストのフレームワークとして、一般的なもの。 Moqの使用上の制約 インターフェースあるいは抽象クラスを継承すること。  → 設計段階でDependency Injection(DI)の考え方を取り入れる必要がある。 参考:DIって何のためにするのかわからない人向けに頑張って説明してみる 静的なメソッドは代替できない  →これはの有償ツールが必要。Microsoft Fakesなど。 Moqの説明に関して参考になったもの No リンク 説明 1 Moq : Mocking Framework for .NET Moqの説明と実装例がセットで載っている。Quiitaの記事の中だと、個人的に最も分かりやすかった。 2 Moq & Fakes Framework を使った実践的ユニットテスト - BuildInsider SlideShareにMoqの概要・メリットが纏められている。資料が2013年のため、少し古い。 Moqを用いた単体テストコードの作成に役立つもの No リンク 説明 1 Git:Moq Quickstart Git上にあるMoqのクイックスタートページ。実装例とMoqの使い方が記載されている。 2 Moq d0cument Moqのドキュメント。公式のものか判断できないが、クラスやメソッドの解説がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#/単体テスト】Moqの参考サイトのまとめと実装例

概要 Moqの参考サイトのまとめ + 簡単な実装例の紹介 Moqとは Moqは単体テスト用の代替オブジェクトを簡単に作るライブラリ。 オープンソースで商用利用可能。 C#の単体テストで使用するライブラリとして、一般的なもの。 Moqの使用上の制約 インターフェースあるいは抽象クラスを継承すること。  → 設計段階でDependency Injection(DI)の考え方を取り入れる必要がある。 参考:DIって何のためにするのかわからない人向けに頑張って説明してみる 静的なメソッドは代替できない  →これはの有償ツールが必要。Microsoft Fakesなど。 Moqの説明に関して参考になったもの No リンク 説明 1 Moq : Mocking Framework for .NET Moqの説明と実装例がセットで載っている。Quiitaの記事の中だと、個人的に最も分かりやすかった。 2 Moq & Fakes Framework を使った実践的ユニットテスト - BuildInsider SlideShareにMoqの概要・メリットが纏められている。資料が2013年のため、少し古い。 Moqを用いた単体テストコードの作成に役立つもの No リンク 説明 1 Git:Moq Quickstart Git上にあるMoqのクイックスタートページ。実装例とMoqの使い方が記載されている。 2 Moq d0cument Moqのドキュメント。公式のものか判断できないが、クラスやメソッドの解説がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#/単体テスト】Moqの簡単な実装例と参考サイトのまとめ

概要 Moqの簡単な実装例の紹介 + 参考サイトのまとめ Moqとは Moqは単体テスト用の代替オブジェクトを簡単に作るライブラリ。 オープンソースで商用利用可能。 C#の単体テストで使用するライブラリとして、一般的なもの。 Moqの使用上の制約 インターフェースあるいは抽象クラスを継承すること。  → 設計段階でDependency Injection(DI)の考え方を取り入れる必要がある。 参考:DIって何のためにするのかわからない人向けに頑張って説明してみる 静的なメソッドは代替できない  →これはの有償ツールが必要。Microsoft Fakesなど。 Moqの簡単な実装例 テスト対象クラスの作成 テスト対象クラス using System; namespace MoqTutorial { /// <summary> /// 評価対象クラスのインターフェース /// 参考 /// https://github.com/Moq/moq4/wiki/Quickstart /// </summary> public interface IFooSample { bool DoSomething(string value); } /// <summary> /// 評価対象クラス /// </summary> public class FooSample : IFooSample { private readonly IFooSample _ifooSampleObject; /// <summary> /// コンストラクタ・インジェクション /// </summary> /// <param name="iObject"></param> public FooSample(IFooSample iObject) { _ifooSampleObject = iObject; } public bool DoSomething(string value) { var result = _ifooSampleObject.DoSomething(value); //結果に応じた処理を記載する if (result) { Console.WriteLine("成功時の処理を書く"); } else { Console.WriteLine("成功時の処理を書く"); } return result; } } } テストコードの作成 テストコード using Moq; using MoqTutorial; using NUnit.Framework; namespace TestProject1 { [TestFixture] public class IFooSampleTest { [Test] public void TestSample_Methoods() { //Mockを作成(インターフェースを指定) var mock = new Mock<IFooSample>(); //関数名と戻り値を指定 mock.Setup(foo => foo.DoSomething("ping")).Returns(true); //評価対象クラスにMockを渡す var fooObject = new FooSample(mock.Object); //評価対象クラスで関数コール Assert.IsTrue(fooObject.DoSomething("ping")); } } } 単体テストコード作成に関しては、Git:Moq Quickstartを参考に実装すると良いと思います。 参考サイトのまとめ Moqの解説が参考になったもの No リンク 説明 1 Moq : Mocking Framework for .NET Moqの説明と実装例がセットで載っている。Quiitaの記事の中だと、個人的に最も分かりやすかった。 2 Moq & Fakes Framework を使った実践的ユニットテスト - BuildInsider SlideShareにMoqの概要・メリットが纏められている。資料が2013年のため、少し古い。 Moqを用いた単体テストコードの作成に役立つもの No リンク 説明 1 Git:Moq Quickstart Git上にあるMoqのクイックスタートページ。実装例とMoqの使い方が記載されている。 2 Moq d0cument Moqのドキュメント。公式のものか判断できないが、クラスやメソッドの解説がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xamarin.Forms+AppSync+Lambdaでデータを取得してみる

背景 昨年、Xamarin.FormsでKudanARアプリをリリースしました。 Xamarin.FormsでKudan ARを試してみる それから約1年が経過し、そろそろKudanARライブラリのAPIキーの使用期限が近づいています。 APIキーを切り替えるためだけに、都度アプリのリリースが必要になるのは面倒… そこで、KudanARライブラリのAPIキーのデータだけLambdaで管理するよう切り離し、AppSyncを経由して取得することにしました。 環境 主に使用したライブラリ バージョン Xamarin.Forms v5.0.0.2012 GraphQL.Client v3.2.4 GraphQL.Client.Serializer.Newtonsoft v3.2.4 主に使用したAWS機能 AppSync Lambda 作成したもの Lambdaからデータを返却し、AppSyncを経由してレスポンスを取得する点に焦点を当てたサンプルリポジトリを作成しました。 AWS側(AppSync、Lambda)に関してはREADME.mdに記載しています。 当記事では省略している箇所が多々あるので、詳細はこちらをご覧ください。 AppSyncDemo ソースコード このサンプルでは、AppSyncに定義されている変数のデータをすべて取得するGraphQLにて実行すると、以下のようなレスポンスが返却されます。 Lambda自体は、以下のGetSampleキーの値にあたる箇所を返却しています。 name="Xamarin"で実行した場合のAppSyncレスポンス { "data": { "GetSample": { "result": { "status_code": 200 }, "data": { "message": "Hello Xamarin!!!", "hoge": "abc", "fuga": "あいう", "piyo": "xyz", "foo": 123, "bar": 456 } } } } Lambdaの返却値の中で、必要なデータがmessageのみだった場合、以下のようにGraphQLを定義します。 GraphQLでは、行の先頭に#をつけると以降がコメント行になるので、コメント行を使用して不要な変数を除いても構いません。 フロントエンドで必要なデータを定義 query MyQuery($name: String) { GetSample(name: $name) { data { message } } } Modelクラスは、GraphQLに合わせて実装します。 今回はGraphQLのdataの内側部分のみを対象としたいと思います。 SampleModel.cs using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; namespace AppSyncDemo.Models { [DataContract] public class SampleModel { [DataMember(Name = "message")] public string Message { get; set; } } } 以下のソースは、GraphQLを読み込んで、Queryとして実行する処理を行います。 GraphQLは、AppSyncDemoプロジェクトのGraphQLsフォルダに、埋め込みリソースとして格納しています。 AppSyncService.cs using AppSyncDemo.Models; using GraphQL; using GraphQL.Client.Http; using GraphQL.Client.Serializer.Newtonsoft; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace AppSyncDemo.Services { public class AppSyncService { public static AppSyncService Instance { get; private set; } = new AppSyncService(); private GraphQLHttpClient GraphQLHttpClient { get; set; } public AppSyncService() { var options = new GraphQLHttpClientOptions { EndPoint = new Uri(ApiKey.AppSyncApiUrl), }; this.GraphQLHttpClient = new GraphQLHttpClient(options, new NewtonsoftJsonSerializer()); this.GraphQLHttpClient.HttpClient.DefaultRequestHeaders.Add("x-api-key", ApiKey.AppSyncApiKey); } public async Task<SampleModel> GetSampleAsync(string name) { var apiName = "GetSample"; var variables = new { name = name, }; var response = await ExecQueryAsync<SampleModel>(apiName, variables); return response; } /// <summary> /// Queryを実行する /// </summary> /// <typeparam name="T"></typeparam> /// <param name="apiName"></param> /// <param name="variables"></param> /// <returns></returns> public async Task<T> ExecQueryAsync<T>(string apiName, object variables) { try { // GraphQL取得 var resourceId = $"AppSyncDemo.GraphQLs.{apiName}.gql"; var query = await GetQueryAsync(resourceId); // リクエスト作成 var request = new GraphQLRequest { Query = query, OperationName = "MyQuery", Variables = variables, }; // Query実行 // Mutation実行の場合はSendMutationAsync()を使用 var response = await this.GraphQLHttpClient.SendQueryAsync<JObject>(request); // [apiName]から先が必要かどうかはレスポンスの構造に応じて変更する必要あり var json = response.Data[apiName]["data"].ToString(); var ret = JsonConvert.DeserializeObject<T>(json); return ret; } catch (Exception) { return default(T); } } /// <summary> /// GraphQLを取得 /// </summary> /// <param name="resourceId"></param> /// <returns></returns> private async Task<string> GetQueryAsync(string resourceId) { var assembly = Assembly.GetExecutingAssembly(); using (var stream = assembly.GetManifestResourceStream(resourceId)) using (var reader = new StreamReader(stream)) { return await reader.ReadToEndAsync(); } } } } 一通りの流れは以上です。 元々の目的は、APIキーのデータのみ取得だったので、AppSync経由ではなくAPI Gateway経由でもよかったのですが、あまり記事を見かけなかったので、AppSyncの記事を作成しました。 AppSyncの記事をあまり見かけなかった、というよりも、C#とAppSyncを使用した記事をあまり見かけなかった、と言った方が正確かもしれません。 そもそも、「APIキーを取得するAPI」というのがあまりよろしくなさそうな気はしていますが、その点についてはあまり考えないことにしました。 参考リンク sagulati/dotnet-lambda-refarch-imagerecognition C#で不定形JSONを自在に扱いたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む