このブログで使用しているUnityのVer
・Unity 2022.3.19f1
はじめに
こんちゃっす!
ゲーム制作で、戦闘システムを作っていて、いい感じになってきたと思ってたのに気づいたらプレイヤーのHPがマイナスになっている、でも毎回条件を書くのはクソ面倒。
そんなこと、あるよね?ね?
そこで今回は、そんな悩みを解決する、プロパティについて。特にSet/Getキーワードに関して掘り下げていきたいと思います。
それでは元気にやってこー
プロパティとは?
最初に、そもそもプロパティとは何か?を説明できればと思います。
プロパティとはUnityC#でクラスに記述したデータ(フィールドと言います)がprivate等の本来触れない修飾子がついたフィールドでもアクセスが出来るようにする仕組みのことです。
プロパティはメソッドに似た構造で、フィールドの値を取得したり、再設定をして返すような仕組みで、外部からフィールドを読み書きするための受付窓口になります。
メソッド・修飾子に関しては下記の記事で詳しく解説してあります。
アクセサー
プロパティ内でデータ(フィールド)の取得や設定をそれぞれ行う処理構造を「アクセサー」と言います。
これはプロパティの更に内部にある「小さなメソッド」のようなイメージです。
アクセサーはプロパティ内部でフィールドの読み書きを司る部分になります。
TIPS:カプセル化という概念
ここで少し、カプセル化という概念に関してお話します。
カプセル化とはC#などの「オブジェクト指向」プログラミングの基本的な概念です。
簡単に例えると、ゲームのコントローラーみたいなイメージです。
ゲームコントローラーは内部の無線の発射や、動作の処理は私達からは見ることができません。
しかし、ボタンを押すだけで設定された通りの内部処理が行われます。
これはコントローラーの外観をカバーとしてボタンのみをユーザーから触れるようにしている=カプセルのような構造にしている、と言う状態です。
これがカプセル化です。
ゲームのオプションでボタン設定が変更できるタイプも、そこでだけ設定を変えられるようにしているので、カプセル化と言えますね。
このようなカプセル化はC#ではアクセス修飾子による制限で行われています。「private」とかは内部からのみでアクセスできるので、まさにそうですね。
つまり、このカプセル化されたデータにアクセスするための機能、コントローラーのボタンが、プロパティという事ですね。
プロパティの使い方
それでは、プロパティの概念部分が分かったところで実際にプロパティを使用してみましょう。
プロパティの基本構造〜get/setキーワード〜
プロパティの基本構造は下記のようになります。
アクセサーとしてgetキーワード(取得)/setキーワード(設定)というものを使用して記述していきます。
private データ型 フィールド名; //非公開で触りたいフィールド
アクセス修飾子 データ型 プロパティ名 //プロパティ
{
get // 値を取得するアクセサー
{
//値を取得した際に行う処理を記述
return フィールド名; // フィールドの値を返す
}
set // 値を設定するアクセサー
{
//値を設定する際に行う処理を記述
フィールド名 = value; // フィールドに値を設定する(valueは設定したい値)
}
}
それではこのコードを簡単に説明していきます。
private データ型 フィールド名; //非公開で触りたいデータ
ここはプロパティでは無いのですが、プロパティで触りたいフィールドの記述例になります。
修飾子はprivateじゃなくても可能です。
アクセス修飾子 データ型 プロパティ名
3行目のここがプロパティの設定部分になります。メソッドと書き方はほぼ一緒ですね。
get // 値を取得するアクセサー
{
//値を取得した際に行う処理を記述
return フィールド名; // フィールドの値を返す
}
ここがgetアクセサー部分になります。
getアクセサーはプロパティを呼び出した時に実行されます。
その際に現在のフィールドの値を取得してreturnで返します。
returnで返すまでに処理をしたい場合は間に記述しておく事もできます。
set // 値を設定するアクセサー
{
//値を設定する際に行う処理を記述
フィールド名 = value; // フィールドに値を設定する(valueは設定したい値)
}
ここがsetアクセサー部分となります。
valueはこのプロパティを呼び出して値を設定する時に自動で格納されるパラメーターで宣言せずに使用する事ができます。
valueを使わず値を直接set内に具体的な数字を設定する事もできますが、外部から設定できる方が柔軟性があります。
valueはわかりにくいけど、次の具体例でしっかり見ていこう!
プロパティの具体的な使い方
ここではプロパティを使ってゲームのHPを外部から増減させるような仕組みを作ってみました。
プレイヤーのHPに対して
Checkボタンを押すと現在のHPがConsoleに表示され、
Healボタンを押すと回復(HP増加、※max100)、
Damageボタンを押すとダメージ(HP減少、※min0)
というよく使われそうな場面をシンプルなものにしています。
コードは下記となります。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HP : MonoBehaviour
{
private int hp; // 非公開のフィールド
public int HPpro // HPプロパティ
{
get
{
return hp; // 現在のhpを取得
}
set
{
// 値を設定する際の条件をチェックし、フィールドに代入
if (value <= 0)
{
hp = 0; // HPが0未満なら0に設定
Debug.Log("戦闘不能");
}
else if (value >= 100)
{
hp = 100; // HPが100を超えた場合、100に設定
Debug.Log("全快です");
}
else
{
hp = value; // 正常な値ならそのまま設定
Debug.Log("現在のHP: " + hp);
}
}
}
void Start()
{
HPpro = 100;
}
public void damage()
{
HPpro -= 50;
}
public void heal()
{
HPpro += 50;
}
public void nowhp()//getが呼び出される
{
Debug.Log("現在のHPは"+HPpro);
}
}
Unity上でのC#ファイルの設定方法などは本筋では無いので詳しい説明は省略しますが、さっと説明すると
1.UIオブジェクトのcanvasにこのスクリプトを挿入。
2.各ボタンのbuttonコンポーネントのOnClickイベントに呼び出したいメソッドを登録。
→例えばHealボタンだったらheal()メソッド
という風にしています。
※コードの細かい説明は後ほど記述します。
このように各ボタンから呼び出されるメソッドからプロパティを呼び出して、HPを外部から自由に設定しています。
それではスクリプトの記述を細かくみていきましょう。
※不明なところだけ読めばOKだと思います。
private int hp; // 非公開のフィールド
ここが元となる非公開にしているフィールドになります。プロパティを介してのみ調整したい値になります。
public int HPpro // HPプロパティ
プロパティの作成。publicにして外部メソッドから呼び出せるようにしつつ、hpと同じint型で作成します。
get
{
return hp; // 現在のhpを取得
}
プロパティ内でgetアクセサーを設定します。
ここではreturnのみでプロパティを呼び出した際に現在のhpの値を返します。
set
{
// 値を設定する際の条件をチェックし、フィールドに代入
if (value <= 0)
{
hp = 0; // HPが0未満なら0に設定
Debug.Log("戦闘不能");
}
else if (value >= 100)
{
hp = 100; // HPが100を超えた場合、100に設定
Debug.Log("全快です");
}
else
{
hp = value; // 正常な値ならそのまま設定
Debug.Log("現在のHP: " + hp);
}
}
setアクセサーを設定します。
ここで、valueは外部から渡された値となります。(メソッドの引数に似ていますね。)
それぞれvalueの値によってhpを設定する値を変えています。
valueが0以下の時はhp=0(最小値)
value100以上の時はhp=100(最大値)
valueが上記以外のときはvalue=hp
という設定にしています。
valueは次の項目でどのように渡されるか説明します。
void Start()
{
HPpro = 100;
}
ゲームスタート時に呼び出されるメソッドStart()内でHPproプロパティを呼び出し、valueに100の値を入れて渡しています。
この処理の内部的な流れは
HPproプロパティを呼び出す
→getアクセサーでhpの初期値を取得
→setアクセサーの処理をvalue=100として処理
→100以上なので hp = 100に設定して戻す。※Debug.Log(“全快です”)の表示。
という風になります。
public void damage()
{
HPpro -= 50;
}
public void heal()
{
HPpro += 50;
}
この2つのメソッドはやってることは同じなので同時に説明します。
damageメソッドの方のHPpro -= 50は意味としては
HPpro = hp-50;
を示しています。
この処理の内部的な流れは
HPproプロパティをが呼び出される
→getアクセサーでhpの現在値を取得
→valueに(取得したhp)-50が設定される
→valueの値を元にsetアクセサーの条件分岐が処理される
という風になります。
同様にhealメソッドの方は+になっているだけなので説明は省略します。
public void nowhp()//getが呼び出される
{
Debug.Log("現在のHPは"+HPpro);
}
このメソッドではDebug.Log(“現在のHPは”+HPpro);のHPproの部分でプロパティを呼び出しています。
しかし、valueとなる値を渡していませんのでgetアクセサーのみ処理され、現在のhpを返します。
以上でスクリプトの説明を終わりにします。
プロパティは一度書いてしまえば、呼び出して使う時は非常にシンプルに使えるふるね!
TIPS:get/setの短縮記述
set/getアクセサー内部で処理の記述が必要なく、それぞれただ取得と設定のみを行う場合は次のように省略した記述ができます。
修飾子 データ型 プロパティ名 {get; set;}
更にプロパティの初期値の設定も下記のようにそのまま記述できます。(初期化子と言います)
public int HP{ get; set; } = 100;
終わりに
今回はプロパティを見ていきました。
プロパティはイマイチメリットが分かりにくいですが、使えるようになるとカプセル化によるデータの保護はもちろんですが、プロパティ内で処理を介して返す事で柔軟なステータス管理ができたり、例のようにHPがマイナスにならないように値の幅を設定して制御をしたりすることができます。
また、可読性も上がるのでステータスやゲームの設定などよく触る部分に関しての恩恵は大きいのではないかと思います。
コメント貰えると元気も出ますので、感想やご意見どうぞお気軽にお願いしますふる!
※このブログは、UnityTechnologiesまたはその関連会社が後援または提携しているものではありません。「Unity」は、UnityTechnologiesまたはその関連会社の米国およびその他の国における商標または登録商標です。
コメント