転職を繰り返したサラリーマンの多趣味ブログ

30才未経験でSEに転職した人の多趣味ブログ

【Unity】ダメージをくらったら、Spriteを点滅させて無敵時間を作る方法

横スクロールアクションや、シューティングゲームでよくあるあれです。
まずSpriteを点滅させる方法は、SpriteRendererコンポーネントのアルファ値を変化させてやれば、実装できる。そのときのソースコードがこれ。

public SpriteRenderer sp;

// ダメージ判定フラグ
private bool isDamage{get; set;}

void Update() {

    // ダメージを受けている場合、点滅させる
    if(isDamage) {

        float level = Mathf.Abs(Mathf.Sin(Time.time * 10));
        sp.color = new Color(1f, 1f, 1f, level);

    }

}

// トリガー発生時
private void OnTriggerEnter2D(Collider2D collider) {

    StartCoroutine(OnDamage());

}

public IEnumerator OnDamage() {

    yield return new WaitForSeconds(3.0f);
        
    // 通常状態に戻す
    isDamage = false;
    sp.color = new Color(1f, 1f, 1f, 1f);

}

このソースであれば、敵からダメージを受けたら3秒間は点滅する。

あと無敵時間を作るのは簡単で、このダメージ判定フラグを利用する。
OnTriggerEnter2Dメソッド内に、ダメージを受けた際の挙動を実装する場合、ダメージ判定フラグがTrueならば処理をスキップさせてやればいい。

// トリガー発生時
private void OnTriggerEnter2D(Collider2D collider) {

    // ダメージ中は処理スキップ
    if(isDamage) {
        return;
    }

    StartCoroutine(OnDamage());

}

【Unity】TextがSpriteの後ろの隠れてしまう問題

タイトルそのまんまです。ゲーム作ってたら、こんな感じでTextオブジェクトがSpriteの後ろになってしまう問題が発生。
f:id:uuc1h:20200221200003p:plain
Sprite同士なら簡単にいくのだが、TextとSpriteだとどうしていいのかわからず、結構悩んでいた。
で、解決した方法がこちら。

  • Override Sortingにチェックをつける。
  • Order in Layerを1以上にする。

f:id:uuc1h:20200221200309p:plain
これで、さきほどSpriteに隠れていた「READY」というTextオブジェクトが、Spriteより上にくるようになった。
f:id:uuc1h:20200221200427p:plain

C#を勉強する⑩_初期化に関して

基本的には、変数の宣言と値の設定はセットで行うことがイディオム(慣用的なコード)とされている。そうしないと、コードの可読性が落ちるから。例としては、以下のような感じ。

int age;
Console.WriteLine("hoge");
// 宣言と値の設定の間に、別なコードが記載されているため可読性が落ちている
age = 25;

C#では、配列、Dictionary、オブジェクトでも初期化構文が用意されている。これを順に書いていく。

配列とリストの初期化

var langs = new string[] { "C#", "VB", "C++", };
var nums = new List<int> { 10, 20, 30, 40, };

Dictionaryの初期化

var dict = new Dictionary<string, string>() {
    {"ja", "日本語" },
    {"en", "英語" },
    {"es", "スペイン語" },
    {"de", "ドイツ語" },
};

オブジェクトの初期化

var person = new Person {
    Name = "山田太郎",
    Birthday = new DateTime(1995, 11, 22),
    PhoneNumber = "012-345-678",
};

ここらへんは便利機能というよりかは、あくまでコードの可読性をあげるためのお作法な気がする。

C#を勉強する⑨_可変長引数、オプション引数、コンストラクタ定義について

可変長引数

メソッド定義で、引数の数を限定しない書き方。引数は、配列として扱う。

public static void Main(string[] args) {

    // 3が出力される
    Console.WriteLine(LastNum(1, 2, 3));

    // 5が出力される
    Console.WriteLine(LastNum(1, 2, 3, 4, 5));

}

public static int LastNum(params int[] args) {

    return args[args.Length - 1];

}

オーバーロードのオプション引数

オーバーロードのオプション引数を使えば、メソッドの呼び出し方でデフォルト値を渡すことができる。

public static void Main(string[] args) {

    // 5fault3 と出力
    DoSomething(5);

    // 5success3と出力
    DoSomething(5, "success");

    // 5success15と出力
    DoSomething(5, "success", 15);

}

public static void DoSomething(int num, string message = "fault", int retryCount = 3) {

    Console.WriteLine(num + message + retryCount);

}

複数のコンストラクタ定義

コンストラクタ内で、引数の違うコンストラクタを呼ぶ場合の書き方。書く行は少なくて、引数の指定が色々できるので便利。

public Sale(int m, int n)
    : this(m, n, 0, 0) { // 引数4つのコンストラクタを呼び出し

}

public Sale(int m, int n, int b)
    : this(m, n, 0, b) { // 引数4つのコンストラクタを呼び出し

}

public Sale(int m, int n, int a, int b) {

}

C#を勉強する⑧_プロパティの初期化

これまでプロパティの初期化といったら、コンストラクタでやるものだと思ってた。

class Sale {
    public int number { get; set; }
 
    // コンストラクタ
    public Sale() {
        number = 10;
    }
}

けど、C#ではコンストラクタを使わずにプロパティの初期化ができる。これは便利。

public int number { get; set; } = 6;

上の書き方だと外部のクラスからプロパティが変更されてしまうので、読み取り専用プロパティの書き方も別途ある。

public int number { get; private set; } = 6;

読み取り専用は、こんな書き方もある。

public string Name => "山田太郎";

だが、参照型のプロパティを読み取り専用にするには、特別な書き方を必要とする。

// これで要素の変更などはできない
public IReadOnlyList<int> MyList { get; private set; }

C#を勉強する⑦_条件演算子、null合体演算子、null条件演算子

条件演算子

if文で書くと4行になるところが、条件演算子を使うと1行で書ける。たしかに便利だが、慣れないと普通にif文使ってしまいそう。

// ?より前が条件式
// trueなら1、falseなら0を返却
var num = list.Contains(key) ? 1 : 0;

null合体演算子

// GetMessageの戻り値がnullの場合、message変数にはDefaultMessageの戻り値をセットする
// GetMessageがnull以外を返したら、message変数にそのままセットする
var message = GetMessage(code) ?? DefaultMessage();

こちらもif文で書くと複数行になるところが、null合体演算子を使えば1行でまとめられる。

null条件演算子

// sale変数がnull以外なら、Productプロパティを返す
// sale変数がnullなら、そのままnullを返す
return sale?.Product;

これは条件演算子を使っても書ける。

return sale == null ? null : sale.Product;

null条件演算子は配列に対しても書けるが、その場合はドットが不要。

// customersがnull以外なら、配列の0番目の要素を返す
return customers?[0];

これらを使えこなせば、if文に頼らず簡潔なコードが書けそう!けど、常に意識しないと絶対if文を使っちゃいそうではある。。

C#を勉強する⑥_LINQ to Objects

LINQは「Language Integrated Query」の略。自分が勉強したイメージだと、SQLの構文をC#で使えるイメージ。LINQを使うには、usingディレクティブを使って名前空間System.Linqを指定する。
イメージでSQLと言ったのは、LINQにはクエリ演算子なるものが用意されているから。以下に例を示す。

var names = new List<string> {
                "Tokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Hong Kong",
            };

// listより、文字列が5以下の要素を抽出
IEnumerable<string> query = names.Where(s => s.Length <= 5);

なお、クエリ演算子は数が多いため、必要に応じて都度調べることにする。

遅延実行

LINQの特徴に、遅延実行がある。これは、本当にデータが必要になったときにクエリが実行されることをさす。

var names = new List<string> {
                "Tokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Hong Kong",
            };

// listより、文字列が5以下の要素を抽出
IEnumerable<string> query = names.Where(s => s.Length <= 5);

foreach(var item in query) {

    // TokyoとParisが出力
    Console.WriteLine(item);

}

// リストを更新
names[0] = "Osaka";

foreach(var item in query) {

    // OsakaとParisが出力される
    // Tokyoが出力されないのは、このタイミングでWhereメソッドが実行されているため
    Console.WriteLine(item);

}

逆に即時実行をしたい場合は、WhereメソッドとToArrayメソッドをメソッドチェーンでつなぎ、配列に変換してしまえばいい。

var names = new List<string> {
                "Tokyo", "New Delhi", "Bangkok", "London", "Paris", "Berlin", "Canberra", "Hong Kong",
            };

// listより、文字列が5以下の要素を抽出し、配列に変換
IEnumerable<string> query = names.Where(s => s.Length <= 5).ToArray();

foreach(var item in query) {

    // TokyoとParisが出力
    Console.WriteLine(item);

}

// リストを更新
names[0] = "Osaka";

foreach(var item in query) {

    // TokyoとParisが出力される
    Console.WriteLine(item);

}