投稿記事

2021年 11月の記事 (4)

A-Nest 2021/11/24 23:01

進捗など

どうも、グラフィック担当のガフです。
最近すっかり寒くなってきましたね。
あと数日もすればすっかり冬になりそうな季節
体調には気を付けていきたい今日このごろ

まだまだ作らないといけない素材がまだまだたくさんあり、天手古舞になりながら作業をしております。

ゆっくりみかんさんが進めているおさわりパートのUIがある程度完成し
それ合わせに他のUIもちょこちょこと出来上がってきました。

こまごまとした、おさわりパーツのアニメーションは終わったのですが
UI素材用であったりアニメーション用だったりの素材を作るべく
メインのキャラクターのポーズを制作しようとした所で問題が起こりました。

ボーンデータが壊れてる!

これに関しては、auto rig proを使っていて基本自動で作ったリグなのですが
恐らく長い期間の製作と、度重なるツールの更新 blenderデータの更新のせいで
不整合が発生してしまったものと思われます。
これに関しては、モデルを長期に扱うなかでほいほいバージョンを変えていってしまった自分のミスですね・・・・非常に反省。

お詫びのオッパイ貼っておきますね(・ω・;)

ルックをどう落とし込むか、実はまだ悩み中でして
少しリアルよりにするか、少しトゥーンよりにするか・・・

そしてちょっと前作のキャラも登場したり?

UIに関しては、前作非常に不満があったので
今回はしっかりとデザインしてます!

だいぶ見やすくまとまったかな
そんなこんなで形はできています!

さて、今年もあとわずか・・・
残り作業も気合入れていきましょう!

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

A-Nest 2021/11/16 22:17

開発者向け - インターフェイスの使い方2

どうも、プログラマのゆっくりみかんです。
さて、前回の話の続きをしていこうかと思います。
今回の内容は、Unityの2019.3以降である事が前提となっています。

インターフェイスの使い方

実際にインターフェイスを自分で使って行こうとすると、どういう感じで使う事になるでしょうか。
前回も書いた通り、中々イメージがし辛い機能なわけですが、簡単な例を出してみましょう。

まず、uGUIのボタンがあったとします。

こんな具合ですね。
こういった感じでボタンやらスライダーやらを色々と配置してGUIをUnityでは作っていくわけですが、押しても反応して欲しく無い場合があったとします。
例えば、

//ゲームモードの定義
public enum GameMode {
	InGame,		//ゲーム中
	Talking,	//会話シーン
    Menu,		//メニュー
    Pause,		//ポーズ
}

こんな風にゲームモードの定義があって、

//ゲーム全体に係る管理マネージャ、シングルトンクラスとする
public class GameManager : SingletonObject {
	[SerializeField]
    private GameMode _currentGameMode;	//現在のゲームモード
    public GameMode CurrentGameMode => _currentGameMode;
}

こんなゲームのマネージャで現在のゲームモードを管理していたとします。
そして、ゲーム中のボタン・・・ 例えば、魔法を使うボタンとか。
そういうものがあったとして、それは会話シーンとかメニューとかポーズ画面で使えちゃ困るわけです。
そういった時、どうするか?
そのまま、普通の実装だとこうなると思います。

//魔法使用ボタン
public class SpellButton : MonoBehaviour {
	[SerializeField]
    private Button _spellButton;	//uGUIのボタンの参照

	[SerializeField]
	private SpellData _spellData;	//魔法データを持ったスクリプタブルオブジェクト

	private Enemy _targetEnemy;		//ターゲットとなる敵
	public Enemy TargetEnemy {		//公開用のターゲットとなる敵プロパティ
		get => _targetEnemy;
		set => _targetEnemy = value;
     }
    
    void Start() {
    	//onClickイベントへの登録
    	_spellButton.onClick.AddListener(() => {
        	//ゲームモードがInGameの時だけ実行
        	if(GameManager.Instance.CurrentGameMode == GameMode.InGame) {
				_spellData.CastSpell();	//呪文を唱える
            }
		});
    }
}

こんな感じですね。
さて、この実装に問題はあるでしょうか?
まあ別にこれでもいいのですが、問題は
「じゃあ他の似たようなボタンはどうするの?」
という事です。
攻撃ボタンがあったとしましょう。その時も

	//ゲームモードがInGameの時だけ実行
	if(GameManager.Instance.CurrentMode == GameMode.InGame) {
		//~何らかの攻撃処理~
	}

このコードをわざわざ挿入するのでしょうか?
じゃあ、もしゲームモードに変更が入って
「Bossモードを追加して、そっちでも使える様にしよう」
とかなった時、全てのコードを書き換えるのでしょうか?
めんどくさいし、管理が大変ですよね。
さて、考えてみましょう。
攻撃ボタンと魔法ボタンで共通した「機能」は何でしょうか?
それは
「特定の状況でのみクリック可能である事」
です。
さて、インターフェイスとはどういう機能だったでしょうか?
「クラスの機能を明示的に宣言して、共通の形で扱える様にするもの」
です。
先ほどの
「特定の状況でのみクリック可能である事」
というのは、じゃあどの様にインターフェイスとして実装すれば良いのでしょうか?

実装例

まず、
「特定の状況でのみクリック可能である事」
を示すインターフェイスを実装してみましょう。

//クリック可能かどうかを示すインターフェイス
public interface IClickable {
	public bool NowClickable { get; }	//クリック可能かどうかを示すプロパティ
}

とりあえずはこれだけでも十分です。
さて、このインターフェイスをどう実装するかというと、こうなります。

//戦闘中だけクリック可能なIClickable
public class BattleClickable : IClickable {
	public bool NowClickable => GameManager.Instance.CurrentGameMode == GameMode.InGame;
}

こんな感じですね。
では、これをどの様に実際のボタンに実装すれば良いでしょうか?

//魔法使用ボタン
public class SpellButton : MonoBehaviour {
	[SerializeField]
    private Button _spellButton;	//uGUIのボタンの参照

	[SerializeField]
	private SpellData _spellData;	//魔法データを持ったスクリプタブルオブジェクト
    
    [SerializeReference]
    private IClickable _clickable;	//クリック可能かどうか

	private Enemy _targetEnemy;		//ターゲットとなる敵
    
    void Start() {
    	//onClickイベントへの登録
    	_spellButton.onClick.AddListener(() => {
        	//クリック可能な状態か判定
        	if(_clickable.NowClickable) {
				_spellData.CastSpell();	//呪文を唱える
            }
		});
    }
}

こういう感じになります。
SerializeReferenceというのは、インターフェイスや抽象クラスだけで使える特別なシリアライズ属性です。
インターフェイスや抽象クラスではないメンバ変数にこれを付けても、特に意味はありません。
また逆もしかりで、インターフェイスにSerializeFieldを付けても無意味です。
抽象クラスは、唯一このどちらも受け付けますが、それによる使い分けに関してはまた追々。
さて、このボタンスクリプトを実装してコンポーネントとしてボタンに登録すると、どの様になるでしょうか?

こんな感じになります(SpellDataは省略しているのでインスペクタにはありません)。
さて、このインスペクタ画像の

この赤線に注目してみましょう。Nullになっています。
これは、定義が

    private IClickable _clickable;

こうなっており、ただのインターフェイスの入れ物でしかないからです。中身がからっぽなわけですね。
では、ここをクリックしてみましょう。すると、こういったメニューが出ます。

リストに、先ほどサンプルとして挙げたBattleClickableが入っていますね。
これを選ぶと

この様に、インターフェイスの中身としてBattleClickableが入ります。
すると、先ほどの様に

        	if(_clickable.NowClickable) {
				_spellData.CastSpell();	//呪文を唱える
            }

と書いた箇所のNowClickableを呼び出したら、BattleClickableの

	public bool NowClickable => GameManager.Instance.CurrentGameMode == GameMode.InGame;

このコードが呼び出され、bool値として値を返すわけです。
さて、先ほど例として挙げた
「Bossというモードを追加し、そこでも使える様にしたい」
場合はどうすれば良いでしょうか?

	public bool NowClickable =>
    	GameManager.Instance.CurrentGameMode == GameMode.InGame || 
        GameManager.Instance.CurrentGameMode == GameMode.Boss;

この様に、IClickableを継承したBattleClickableを書き換えるだけで良いのです。
さて、こういった事に対して
「クラス継承するだけでも似たような事は出来るジャン? ジャンジャン?」
と思う方もいるかも知れません。
しかし、こちらの方には

  • 一部のロジックを外に出せる
  • 簡単にバリエーションを追加できる

というメリットがありますし、無理に継承で実現しようとすると難しいケースも発生してきます。

インターフェイスのメリット

さて、これらのメリットはどういう事でしょうか?
まず、ロジックを外に出せる、というのは一言で言うと
「使い回しがしやすくなる」
という事です。
「簡単にバリエーションを追加できる」
という例も含めて、実際にコードを書いていきます。

先ほどの例のSpellButtonを
「場合によってクリック出来たり出来なかったりするボタン」
という汎用的なクラスで作りたいとしましょう。
インターフェイスを使うとこんな風に汎用化出来ます。
命名があんまり適切じゃない感じがしますが、ご容赦を。

//クリック出来たり出来なかったりするボタン
public class ClickableButton : MonoBehaviour {
	[SerializeField]
    private Button _button;	//uGUIのボタンの参照

    [SerializeReference]
    private IClickable _clickable;	//クリック可能かどうか
    
    [SerializeReference]
    private IOnClickProcess _onClick;	//OnClickの処理

    void Start() {
    	//onClickイベントへの登録
    	_button.onClick.AddListener(() => {
        	//クリック可能かどうかで分岐
        	if(_clickable.NowClickable) {
				IOnClickProcess.Process();	//何らかの処理
            }
		});
    }
}

さて、Clickableは先ほどの説明通りですね。
そこに加えて、今回はIOnClickProcessというメンバ変数が追加されています。
IOnClickProcessというインターフェイスは、中身はこんな感じです。

//OnClickの処理インターフェイス
public interface IOnClickProcess {
	public void Process();
}

この実装として

//呪文処理
public class SpellProcess : IOnClickProcess {
	[SerializeField]
    private SpellData _spellData;	//呪文データのスクリプタブルオブジェクト
    
    [SerializeField]
    private Enemy _targetEnemy;	//ターゲットの敵

	//クリック時の処理
    public void Process() {
    	_spellData.CastSpell(_targetEnemy);	//呪文を唱える
    }
}

こんな呪文のクラスや

//攻撃処理
public class AttackProcess : IOnClickProcess {
	[SerializeField]
    private Weapon _weapon;	//装備している武器
    
    [SerializeField]
    private Enemy _targetEnemy;	//ターゲットの敵

    public void Process() {
    	_weapon.Attack(_targetEnemy);	//攻撃!
    }
}

こんな実装があるとします。
するとどうでしょう、先ほどの汎用クラスのインスペクタでonClickを選択し、SpellProcessやAttackProcessを選ぶ事によって、どちらのボタンにも早変わりも出来るわけです。
しかも、無効化するタイミングも自由自在です。
IClickableインターフェイスを継承したクラスを書いて、それをインスペクタから選ぶだけです。
ボタンクリック時の処理の場合なら、攻撃、魔法以外にも特技というボタンを作りたければ、IOnClickProcessを実装したSkillProcessというクラスを実装してやれば、後はクリックして設定するだけです。
これが
「簡単にバリエーションを追加できる」
という事です。

そして、ClickableButtonというクラスは、特定のゲームでしか使えない独自の何かを含んでいるでしょうか?
最初の例だとGameModeという列挙型を実行の可否の条件にしていますが、GameModeなどは、今回作るゲームと次回作るゲームで内容が違ってきて当然です。
しかし、ClickableButtonには、そういった
「ゲームによって違う」
コードが極力含まれていません。
これはつまり、次回の作品でもそのまま使い回せる可能性が高い事を意味します。

これを通常の継承で実現出来るでしょうか?
中々難しいと思います。
なぜなら、C#においては
「継承は一つしか出来ない」
という、多重継承を許さない制約があるからです。
上手くやらないと、どこかで破綻が起こります。
それと比べ、インターフェイスはこの例外で、いくつでも継承可能なのです。

ちなみに、こういったインターフェイスを使って独自処理を追い出すやり方を
「依存性注入(Dependency Injection)」
と呼びます。このワードで検索をすると、色々ヒットするので調べてみるのも良いでしょう。

使い回ししやすいコード資産を作る為に

使い回ししやすいコードである、という事は非常に重要な事です。
毎回毎回、ゲーム独自のコードを含んでいると、そのコードを次回利用したい場合に
「これは今回は使わないから消して・・・ 今回はあれがあるから追加して・・・ 今回のこの要素は前回とは別のロジックで組んでるから、ここも書き直して・・・」
みたいな作業が発生して、結局一から書くのと大差ない、みたいになりがちです。
また、そういった作業は新しいバグを仕込みがちです。
インターフェイスを上手く使ってクラスを汎用化しておくと、ゲームに依存する部分だけをインターフェイスの実装として書けば、それで事足ります。
便利!

めんどくさい

インターフェイスの唯一の難点は、めんどくさい感じがする、という事です。
何というか、そのまま素直に書くのと違って遠回りをしている様な気分にさせられる感じがします。
しかし、こういった事はまあ慣れですし、インターフェイスを活用すればするほど、めんどくささも消えて自然にインターフェイスを活用出来る様になっていく事でしょう。

というかまあ、正直僕も
「めんどくせー」
ってなってインターフェイス化せずに書いてしまう事が多々あったりします。
実際問題、わざわざインターフェイス化する程の事でもないものをそうしたって意味がないので、使いどころ、という要素はあります。
その辺りの見極めは僕もまだまだ甘いですが、経験を積めば積むほど
「ここはインターフェイス化しておいた方が後々楽かなー」
みたいな見極めが出来る様になっていくんだと思います。

終わり

というわけで、インターフェイスを実際に使った例でした。
他にも色々プログラムのノウハウを書くかどうかは未定ですが、気が向いたらまた色々書くかも知れません。

開発する皆様、お互い頑張りましょう!
僕も頑張ってプログラミングします!

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

A-Nest 2021/11/10 22:40

タイトルロゴが出来ましたぁ

どうもグラフィック担当ガフです。

キャラのモデルに関しては、一式揃い…後はディテールを詰めるだけ。
そして今は、モーション作業を進めています。

ただ、blenderでのモーションが不慣れなので少し滞り気味・・・。
皆さんにお見せできる段階になったらまた改めて報告させていだきます。


そして、すでに告知したか分からないのですが。
新作のタイトルも決まりロゴを作成しました。

まぁ悪くないのではないでしょうか?

前作は3dで動かせるけどあくまで動画作品だったので
今回はゲーム性に加え
おさわりパートの追加やシチュエーションも
脅迫してレ〇プ、や屈服セックス、そして睡姦と
いろいろと楽しめるように奮闘中です。

個人的には衣装を強化したいと考えており
着衣ックス好きとしては外せない要素。
「こんな衣装があったらいな」みたいなのをいただければ
製作の参考にさせていたこうと思っています!

それでは、予定しているリリース日も迫ってきているので
気合入れて作業を進めていきます!

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

A-Nest 2021/11/03 12:41

開発者向け - インターフェイスの使い方1

どうも、最近あまり一般受けしない感じの記事ばっかり書いてるプログラマのゆっくりみかんです。
僕の趣味もあってどうもこういう話ばかりになっちゃっていますが、いずれは作品の進捗も載せられると思うので、しばしお待ちを。

さて、今回はC#におけるインターフェイスや抽象クラス等々の使い方についてです。
何というか、理解出来ると素晴らしく便利で綺麗にコードが書ける様になる概念なのですが、どうも理解が難しい。
かく言う僕も、最初文章を読んだ時点では、理解は出来るもののどこで使うのがいいのやら? という感じでした。

しかし、こういうものは斜め読みでも参考書や仕様書を何度も何度も読んでいれば、ある日突然
「あれ、こう使えばいいんじゃね?」
と天啓の元になるものです。諦めずに頑張ってみましょう。
というか、インターフェイスとか抽象クラスの解説って、大体抽象的な物言いになってて実際の利用シーンをイメージしたサンプルになってない事が多いのも原因な気がします。
そこら辺を分かりやすく書けたらなぁ、と。

ちなみに、僕の解説は完全に正しいかというとそうでもない気がします。
おかしいと思った所があれば、コメントなりでツッコミを頂ければ幸いです。
ただ、オブジェクト指向の説明で自動販売機や動物などを使う時、ああいう例えに
「正確ではない」
と言う方がいたりしますが、そういうタイプのツッコミは不要です。
正しさよりも、まず使える様にならないと話にならないと思っている人間なので・・・
あと、記事に書くコードは実行を確認しておりません。
説明としては正しい物を書く様気を付けてはいるので、ご容赦を。

本解説は、既にC#をある程度扱えている人向けです。

インターフェイス

インターフェイスとは、一言で言うと「クラスの機能を明示的に宣言して、共通の形で扱える様にするもの」です。
インターフェイスそれ自体に実装はなく、単に
「こういうメソッドあるよ」
「こういうプロパティあるよ」
と宣言するのみで、実装は継承した先のクラスで行います。
(最新のC#だとインターフェイスのデフォルト実装という概念がありますが、それについては割愛します)

C#では基本的に最初に大文字の「I」を付けるルールになっており、Visual Studio等々の入力候補で「I」を打ってみると標準のインターフェイスが色々ぞろぞろ出てくる事でしょう。
標準的な物としては、

  • IDisposable(破棄可能である事を示すインターフェイス)
  • IEnumerable(列挙可能である事を示すインターフェイス)

等がありますね。
特にこの二つを挙げたのは、C#の言語仕様に食い込んでいるインターフェイスであり、分かりやすいからです。

IDisposable

例えば、IDisposableを例に挙げてみましょう。

//一時的なインスタンスマテリアルを保持するテストクラス
public class DisposableMaterialTest : IDisposable {
    private Material _material;	//保持するインスタンスマテリアル
    public Material Material => _material;	//保持するインスタンスマテリアルを公開するプロパティ

	//レンダラを引数に取るコンストラクタ
	void DisposableTest(Renderer re){
    	_material = re.material;	//レンダラに割り当てられたマテリアルを一時的なインスタンスマテリアルとして取得する
    }
    
    //IDisposableインターフェイスを継承した場合、このメソッドを実装しなければならない。
    //実装していないと、エラーになり、コンパイル出来なくなる。
    //また、Disposeは何度呼んでもエラーにならない様に実装しなければならない。
    //つまり、一度解放が済んだら次に呼び出した時は何もしない様にする。
    public void Dispose(){
    	if(_material != null) {
    		UnityEngine.Object.Destroy(_material);
        }
    }
}

実装はこんな感じですね。概ね、コメントに内容は記述しています。
さて、これをどう使えば良いのかと言うと・・・

//説明用の適当クラス
public class Hoge : MonoBehaviour {
	[SerializeField]
    private Renderer _renderer;	//何らかのレンダラが割り当てられているものとする

	public void Fuga(){
    	using(var testMaterial = new DisposableMaterialTest(_renderer)){
        	//マテリアルを使う何らかの処理
        }
    }
}

こんな風に使います。
using構文は、括弧内で変数の宣言を行い、そのブロック内から外に出た時に宣言した変数のDisposeメソッドを勝手に呼び出してくれる構文です。
つまり、先ほどのusingは次の例と等価です。

public void Fuga(){
	var testMaterial = new DisposableMaterialTest(_renderer);

	//マテリアルを使う何らかの処理
    
	testMaterial.Dispose();
}

Unityではあまり見かけないですが、使いこなすと色々便利な局面があるかも知れません。
Unity以外では、アプリ開発とかでファイル等のStream関連を扱う時に特によく使う構文ですね。
Disposeの呼び忘れを確実に防いでくれる、素晴らしい構文です。

さて、この様にIDisposableを実装するとusing構文で自作のクラスを活用出来る様になります。
IDisposableを実装していないと、当然using構文で使う事は出来ません。
つまり、IDisposableは「破棄可能である」という機能を示すインターフェイスで、そのインターフェイスを実装する事によって自分のクラスが「破棄可能である」事をC#のコンパイラに対して明示的に示しているわけです。
その様に示した事によって、using構文を使う事が出来るわけです。

IEnumerable

さて、お次はIEnumerableについてです。
このクラスもIDisposableの様に実装するとC#の特定の構文で使える様になります。
その構文は超便利な「foreach」です。
IEnumerable<T>というジェネリック版のインターフェイスもありますが、通常版の使い方がわかったら少し追加で調べるだけでジェネリック版の使い方もわかると思います。

public class TestEnumerator : IEnumerable {
	//IEnumerableインターフェイスを継承する場合、これを実装しなければならない。
	//このクラスがforeachされた時に、どういうデータを返すかを実装する。
	IEnumerator GetEnumerator() {
		//yield returnはイテレータと呼ばれる特別な構文。
		//通常のreturnはその時点で実行を終了するのに対し、yield returnは値を返した後も処理をそのまま続行する様なイメージ。
		yield return "Ringo";
		yield return "Gorira";
		yield return "Rappa";
	}
}

はい、これで実装は終わりです。
これをforeachするとどうなるでしょうか?

public class Hoge() {
	public void Fuga() {
    	var test = new TestEnumerator();
    
    	foreach(var str in test) {
        	Debug.Log(str);
        }
    }
}

こんな感じで使う事になりますが出力としては

Ringo
Gorira
Rappa

になります。自作メソッドが見事にforeachに対応しました。
説明の為に簡単な例にしましたが、実際に使う際はこの様に使う事が多いと思われます。

public class TestEnumerator : IEnumerable {
	private List<string> _testList;	//テスト用リスト
    public List<string> TestList => _testList;	//テスト用リストを公開するプロパティ
    
    //コンストラクタでインスタンス化しつつ文字列追加
    public TestEnumerator() {
    	_testList = new List<string>() {"Ringo", "Gorira", "Rappa"};
    }

	//IEnumerableインターフェイスを継承する場合、これを実装しなければならない。
	//このクラスがforeachされた時に、どういうデータを返すかを実装する。
	IEnumerator GetEnumerator() {
    	//面倒な場合はforeachで済ませるが、頻繁に呼び出す場合は
        //for(int i = 0; i < _testList.Count; i++){}
        //の形にした方が、わずかに軽くなる。
		foreach(var str in _testList) {
			yield return str;
        }
	}
}

こちらでも先ほどのクラスと同じような結果が得られます。
何が違うかというと、文字列をリストで管理している事ですね。
ListはIEnumerableを実装しているので当然foreachが使えます。
なので、このクラスの場合はこういう形でforeachする事も可能です。

public class Hoge() {
	public void Fuga() {
    	var test = new TestEnumerator();
    
    	foreach(var str in test.TestList) {
        	Debug.Log(str);
        }
    }
}

ただ、一々公開用のプロパティを作らないとこういう形には出来ませんし(変数をpublicにすればって? HAHA! ナイスジョーク!)、見た目にもきちんとIEnumerableを実装してそれ自体をforeachで回す方がスマートな見た目になりますよね。

IEnumerableおまけ

余談ですが、今回解説したIEnumerableの実装は簡易化されたもので、ちょっと書くのが面倒な実装のやり方が他にもあります。
ただ、これは余程の拘りが無い限り使う事はないと思われるので、割愛します。
逆に、クラス内のリスト等を単純に出したいだけであれば、もっと簡単な書き方もあったりします。

//テスト用クラス
public class Hoge : IEnumerable {
	//テスト用のリスト
	private List<string> _testList = new ();
    
    //インターフェイスの実装
	public IEnumerator GetEnumerator() => _testList.GetEnumerator();
}

これだけで終わりです。簡単!
これは何をしているかというと、ListクラスはIEnumerableを実装している為、そのListクラスの実装をそのまま使っているだけです。
これで事足りる場合も多いでしょう。

一段落

さて、こんな感じでインターフェイスのサンプルを色々書いてみました。
ただ、これは参考書にもよくある程度のもので、理屈はわかったけどゲームでどうやって使うんだってばよ!? って感じになる方も多いでしょう。
思ったより分量が多くなってしまったので、続きはまた次回とさせて頂きます。

それでは、引き続き作品作り頑張ります!

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

月別アーカイブ

記事を検索