狠狠撸

狠狠撸Share a Scribd company logo
Unityでゲームを作るスライド
for Public
C#編
狠狠撸ShareID : Chy72
KMCID : murata
twitterID : paradigm_9
今日やること
?①基礎の基礎からの拡張
②クラス
という順で駆け巡ろうと思います。
時間を見て練習問題を設けようかな?
?ちょっと速く進みますが、後で「こんなのあったな」と思った時に、
学ぶきっかけになれば、と思っております。
そもそもC#とは?
?文法的にはC系統。関連のある言語を軽く紹介する。
?C++ -処理が速い。競技プログラミングの相棒。
?Java –C#と一番似てるけど、時々C#と比べて辛いことがある。
?個人的に一番辛いのが関数オプショナル引数がないこと
?VB -出来ることは颁#と同じです。贰虫肠别濒が动かせます。
①基礎の基礎
?そもそもプログラミングするのに最低限必要な機能とは?
①条件分岐したい →if文 if(){}else if(){}else{}
②処理を保存したい →関数 int Cube(int n){return n * n * n;}
③値を保存したい →変数?配列 int a; int[] A = new int[3];A[0] =7;
④同じ処理したい →繰り返し処理 for(int i = 0; i< n ;i++){}
?最低限これだけあればプログラミングは出来る!
?しかし、それだけでは絶対物足りない…
?これらの基礎の基礎の機能にもっと利便性をつけたのだ!
考え方の原則
?二回以上同じコードを書きたくない!
?二か所以上に同じコードがあるとバグの元
?基準となるべきコードがどちらか分からなくなる。
?人間がコピペすると間違いうる。
?仕様変更時に、その二か所を変える必要がある。
?単純に、二回以上書かないので楽。
考え方の原則2
?なるべく安全に書きたい!
?変更する場所が分かっていた方が後で楽。
?どこででもその値を変えられるとしたら、そこが原因っぽい
バグの時、どこが怪しいか分かったもんじゃない。
?そこでしか使わないものはそこでしか使えないようにしたい。
→機能を分化して考えることが出来て、頭がすっきりする。
①基礎の基礎
?そもそもプログラミングするのに最低限必要な機能とは?
①条件分岐したい →if文
②処理を保存したい →関数
③値を保存したい →変数?配列
④同じ処理したい →繰り返し処理
①-①条件分岐したい -Switch-
?ぷよぷよの連鎖ボイスを伝える関数の例。
?Chainしか使っていないのに、
毎回Chain == と書いている!
?Chain という名前を変えにくい。
?こういう場面は実際多い。
?== を = としたりするかもしれない
?同じChain数を書いてしまうかも。
string ChainVoice(int Chain) {
string Voice = "アルル「";
if (Chain == 0) {
Voice += "やぁ!";
} else if (Chain == 1 || Chain == 2) {
Voice += "とお!";
} else if (Chain == 3 || Chain == 4) {
Voice += "ファイアー!";
} else if (Chain == 5) {
Voice += "アイスストーム!";
} else if (Chain == 6) {
Voice += "ブレインダムド";
} else {
Voice += "ばよえーん";
}
Voice += "」" + Chain + "連鎖!";
return Voice;
}
①-①条件分岐したい -Switch-
こういう時はSwitchを使いましょう!
string ChainVoice(int Chain) {
string Voice = "アルル「";
if (Chain == 0) {
Voice += "やぁ!";
} else if (Chain == 1 || Chain == 2) {
Voice += "とお!";
} else if (Chain == 3 || Chain == 4) {
Voice += "ファイアー!";
} else if (Chain == 5) {
Voice += "アイスストーム!";
} else if (Chain == 6) {
Voice += "ブレインダムド";
} else {
Voice += "ばよえーん";
}
Voice += "」" + Chain + "連鎖!";
return Voice;
}
string ChainVoice(int Chain) {
string Voice = "アルル「";
switch (Chain) {
case 0: Voice += "やぁ!"; break;
case 1:case 2:
Voice += "とお!";break;
case 3:case 4:
Voice += "ファイアー!";break;
case 5:
Voice += "アイスストーム!";break;
case 6:
Voice += "ブレインダムド";break;
default:
Voice += "ばよえーん";
}
Voice += "」" + Chain + "連鎖!";
return Voice;
}
①-①条件分岐したい -Enum-
void EnemyAI(int EnemyNumber) {
switch (EnemyNumber) {
case 0: KaeruAI(); break;
case 1: ArleAI();break;
case 2: SkeletonAI();break;
case 3: TaraAI();break;
case 4: HarpyAI();break;
default: NormalAI(); break;
}
}
?敵を数字で管理している。
?いちいち自分でこの数字は誰かを
覚えておく必要があって死ぬ。
?数字自体には意味がない。
①-①条件分岐したい -Enum-
void EnemyAI(int EnemyNumber) {
switch (EnemyNumber) {
case 0: KaeruAI(); break;
case 1: ArleAI();break;
case 2: SkeletonAI();break;
case 3: TaraAI();break;
case 4: HarpyAI();break;
default: NormalAI(); break;
}
}
enum Enemy {
Kaeru,Arle,Skeleton,Tara,Harpy,Normal
}
void EnemyAI(Enemy enemy) {
switch (enemy) {
case Enemy.Kaeru: KaeruAI(); break;
case Enemy.Arle: ArleAI(); break;
case Enemy.Skeleton: SkeletonAI(); break;
case Enemy.Tara: TaraAI(); break;
case Enemy.Harpy: HarpyAI(); break;
case Enemy.Normal: NormalAI(); break;
}
}
①基礎の基礎
?そもそもプログラミングするのに最低限必要な機能とは?
①条件分岐したい →if文
②処理を保存したい →関数
③値を保存したい →変数?配列
④同じ処理したい →繰り返し処理
①-②関数の拡張 -OverLoad-
?関数は同じ名前で使ってもOK!
?引数や返り値で区別が出来る
ようになっていたら大丈夫です。
?オーバーロードといいます。
?追記:返り値だけで区別する
のは出来ません。
//三乗する関数
int Cube(int x) { return x * x * x; }
float Cube(float x) { return x * x * x; }
double Cube(double x) { return Cube((float)x); }
string Cube(string x) { return "三乗した" + x ; }
Vector3 Cube(Vector3 x) {//ベクトル三乗積
return Vector3.Cross (Vector3.Cross(x, x),x);
}
//正を返すかをオプションに取れるバージョン
int Cube(int x,bool returnPositiveNumber) {
if (returnPositiveNumber && x*x*x < 0) {
return (-1) * x * x * x;
} else return x * x * x;
}
①-②関数の拡張 –省略可能引数-
?関数の引数には省略可能に
することも可能です。
?何も指定しなかった場合は
そのデフォルトの値のまま
実行することが出来ます。
//正を返すかをオプションに取れるバージョン
int Cube(int x,bool returnPositiveNumber = true) {
if (returnPositiveNumber && x*x*x < 0) {
return (-1) * x * x * x;
} else return x * x * x;
}
int UseCubeExample() {
return Cube(-72);
}
①-②関数の拡張 –可変長引数-
?関数の引数の長さを可変にする
ことも出来ます。
?params キーワードを使います。
配列として受け取れるように
なります。
?引数が0個にもなりうるので、
気を付けましょう
//全部掛けた結果をデバッグログに残す関数
static int Mul(params int[] A) {
int res = 1;
for (int i = 0 ;i < A.Length; i++) {
res *= A[i];
}
return res;
}
void UseMulExample() {
Debug.Log(Mul());
Debug.Log(Mul(72));
Debug.Log(Mul(7, 2, 3));
}
①-②関数の拡張 –ジェネリック-
?最大値を返す関数Maxがあった
として、それを型毎に全部
かくのは面倒です。
?そこで、ジェネリックという
機能を使うことが出来ます。
?Icomparable(比較可能)のように、とれる型に制限を付けることも出来ます。
public int Max(int a, int b) {
return a > b ? a : b ;
}
public double Max(double a, double b) {
return a > b ? a : b ;
}
//using System;が必要
public T Max<T>(T a, T b) where T : IComparable{
return a.CompareTo (b) > 0 ? a : b;
}
①-②関数の拡張 –ラムダ式-
?関数を一時的に作成?格納
することも可能です。
?ラムダ式(匿名関数)です!
?Func<引数,引数,…,返値>
で定義出来ます。
?返値がなければActionです。
?昔は delegate使っていた
らしい。
//using System ; を忘れないようにしましょう
int Factorial(int n) {return n > 0 ? n * Factorial(n - 1) : 1;}
void Example() {
Func<int, int> Square = x => x * x;
Func<int, int> factorial = Factorial;
Func<int, int> Cube = x => x * x * x;
Func<int, int>[] functions = new Func<int, int>[] {
Square,factorial,Cube
};
Action<int> debug = x => Debug.Log(x);
for (int i = 0; i < functions.Length;i++) {
debug(functions[i](7));
}
}
①基礎の基礎
?そもそもプログラミングするのに最低限必要な機能とは?
①条件分岐したい →if文
②処理を保存したい →関数
③値を保存したい →変数?配列
④同じ処理したい →繰り返し処理
①-③配列の拡張 -List-
?普通の配列では、最初に宣言したものまでしか保持出来ない…。
?追加?削除とかが出来ない…。
?Listを使いましょう!(using System.Collections.Genericをしてから)
int ArrayExample() {
int[] A ;
A = new int[5];//new して初めて実体を作成
for (int i = 0; i < A.Length; i++) {
A[i] = i * i;//要素に代入
}
return A.Length;//要素数を返却
}
int ListExample() {
List<int> A;
A = new List<int>();//new して初めて実体が
for (int i = 0; i < 5; i++) {
A.Add(i * i);//末尾に要素を追加
}
return A.Count;//要素数を返却
}
int ListExample2() {
List<int> A = new List<int>();
A.Add(72);//末尾に要素を追加
A.Add(765); A.Add(-902);
A.Insert(2,34);//途中に挿入も出来る
A.Remove(-902);//探して最初の要素を削除も出来る
int[] B = A.ToArray();//配列に変換も出来る
A[0] = 10;//既に作成したものまでは添え字アクセスもできる。
int index = A.BinarySearch(765);//ソートした後二分探索して検索も出来る
return A.Count;//要素数を取得出来る
}
①-③配列の拡張 -List-
?Listを使いましょう!
(using System.Collections.Generic
をしてから)
①-③配列の拡張 -Dictionary-
?数字でなくて文字とかでアクセスしたい→Dictionaryを使いましょう!
int DictionaryTest() {
Dictionary<string, int> Dic = new Dictionary<string, int>();
Dic.Add("高槻やよい",841);//新しく追加出来る!
Dic.Add("如月千早",72); Dic.Add("城ケ崎",72);//確保する値は当然被りうる。
int Takatsuki = Dic["高槻やよい"];
Dic.Remove("城ケ崎");//削除も出来る
bool A = Dic.ContainsValue(72);//その値を持つものがあるかも探せる
Dictionary<string, Vector3> DicVec = new Dictionary<string, Vector3>();
DicVec.Add("Zero",new Vector3(0,0,0));//別にintじゃなくてなんでも大丈夫
DicVec.Add("Gravity",new Vector3(0,-9.8f,0));
return DicVec.Count;//要素数も取得できる。
}
①-③配列の拡張 -LINQ-
?配列に対して色々操作したい。→using System.Linq;してLINQ使おう!
int LinqTest() {
int[] A = new int[] {31,41,59,26,53,58,97,93,23,84,62 };
int sum = A.Sum();//総和を返却したり
int[] B = A.OrderBy(s => s).ToArray();//昇順ソートしたり
int[] C = A.OrderByDescending(s => s).ToArray();//降順ソートも
int[] D = A.Where(s => s % 2 == 0).ToArray();//フィルタかけたり
int[] E = A.Reverse().ToArray();//反転したり
List<int> F = A.ToList();//using Linqしてるので、Listに変換したり
int [] G = A.Select(s => s* 2).ToArray();//マッピングしたり
//本家風にこういう書き方もできる
var H = from p in A where p > 10
orderby p select p * 2;
return A.Length;
}
①基礎の基礎
?そもそもプログラミングするのに最低限必要な機能とは?
①条件分岐したい →if文
②処理を保存したい →関数
③値を保存したい →変数?配列
④同じ処理したい →繰り返し処理
①-④ 繰返し処理 -foreach-
?普通の配列や List や Dictionary とか、そういうものに対して、 for文[for(int i = 0;i < n;i++){}
そのiを使うのならいいが、その中身だけに用があってiは関係ない時は
アクセスし間違えたりする可能性があるし、そもそもDictionaryに順番とかは無いからアクセス出来ない
→foreachを使いましょう。今日紹介した例のでも、こっち使う方がよさそうなのも多いです。
void ForeachTest(){
int [] A = new int[]{72,765,225,100};
List<int> L = A.ToList<int>();
//宣言で面倒な時はvarが使える。(型が確定出来る時)
var D = new Dictionary<string,int>();
foreach(int a in A){print(a);}//配列にも
foreach(int l in L){print(l);}//リストにも
foreach(var d in D){//Dictionaryにも
print(d.Key );
}
}
②クラス
?ここまで座学でしたが、ここからは手を動かしてもらいます!
?さっきまでの項目も、便利なので必要に応じて調べて使ってくださいね。
?さあ今からクラスやります。
?今までUnityでクラスの使い方の面(オブジェクト指向)は
分かってもらえたと思うので、実際に一から作ってみる(MonoBehaviorを
継承しない)ことで、より理解していきましょう。
②クラス
?去年のクラスの説明
spi8823「クラスとは、女子高生である!」 「???」
?使い方の説明にはとてもいいのだけれども
(去年のUnityのページにあるので、ぜひ見て下さい)
練習問題としては少し地味ですが今回は複素数クラスを作成してみる例から、
もう一度クラスを学んでいきましょう。
そして女子高生の例でも学んでいきましょう。
複素数からオブジェクト指向に入るのはSICPの受け売りです。
?UnityでもMonoBehaviorを継承しないクラスは当然作成可能ですよ。
②クラス
?クラスはいうなれば設計図です。
設計していって、それを実体化して使用すると。うーむ説明はむずいな…
?複素数クラスに必要な機能とは?
1 実部と虚部を取り出す機能
2 絶対値を取ったり、加減乗除などの演算が出来る機能
3 复素数を角度と长さで表す机能
②-1 実部と虚部を取り出す機能
?まずは最低限の機能ですね。
実部と虚部を保存してます。
?public が付いていると、
外からでもアクセス出来ます
?クラス名と同じ名前のものは
コンストラクタといって、
new で 実体を作る時に初期処理を
行えます。
class Complex {
public float Im;
public float Re;
public Complex(float _Re,float _Im){
Re = _Re;
Im = _Im;
}
}
void UseComplex() {
Complex C = new Complex( 1,1);
Debug.Log("Im =" + C.Im + ": Re = " C.Re);
}
②-2 演算機能
?だいぶ複素数らしくなりました。
?Addで加算、Lengthで絶対値、Argumentで
角度を取得できるようにしてみました。
?Addはオーバーロードもしてありますね!
?実際に加算も出来て、だいぶ複素数クラス
らしくなってきました!
class Complex {
public float Im, Re;
public Complex(float _Re,float _Im){
Re = _Re; Im = _Im;
}
public float Length() {
return Mathf.Sqrt( Im * Im + Re * Re);
}
public float Argument() {
return Mathf.Atan2(Im ,Re);
}
public Complex Add(Complex C) {
return new Complex(Re + C.Re, Im + C.Im);
}
public Complex Add(float _Re,float _Im) {
return Add(new Complex(_Re,_Im));
}
}
void UseComplex() {
Complex C1 = new Complex( 1,1);
Debug.Log("Length = " + C1.Length());
Debug.Log("Argment = " + C1.Argument());
Complex C2 = new Complex(-2, 3);
Complex C3 = C1.Add(C2);
}
②-2 演算子のオーバーライド
?Complexの足し算で
わざわざAddと書くのは
気が引けます。
?多少の制約はありますが
演算子をオーバーロード
(上書き)する
ということも出来ます。
?すっきりしてわかりやすいけど
乱用は禁物です。
class Complex {
public float Im, Re;
public Complex(float _Re,float _Im){
Re = _Re; Im = _Im;
}
public static Complex operator+ (Complex C1, Complex C2)
{
return new Complex(C1.Re + C2.Re, C1.Im + C2.Im);
}
}
void UseComplex() {
Complex C1 = new Complex( 1,1);
Complex C2 = new Complex(-2, 3);
Complex C3 = C1 + C2;
}
②-3 角度と長さ
?複素数は、現在、虚部と実部を
参照して実装していますが、
角度と長さで指定することも
もちろん可能なはずですよね?
?get set を書いてみてみました。
これでその角度のまま長さ変更や
その長さのままの角度変更も出来る
?なんか冗長。
class Complex {
public float Im, Re;
public Complex(float _Re,float _Im){
Re = _Re; Im = _Im;
}
public float getLength() {
return Mathf.Sqrt(Im * Im + Re * Re);
}
public void setLength(float _length) {
float arg = getArgument();
Re = _length * Mathf.Cos(arg);
Im = _length * Mathf.Sin(arg);
}
public float getArgument() {
return Mathf.Atan2(Im, Re);
}
public void setArgument(float _argument) {
float length = getLength();
Re = length * Mathf.Cos(_argument);
Im = length * Mathf.Sin(_argument);
}
}
void UseComplex() {
Complex C1 = new Complex( 1,1);
C1.setArgument(3.14f/2f);
C1.setLength(10f);
}
②-3 角度と長さ
?複素数使う側からしてみれば、
中でRe Im で管理してようが
Length Argument で管理してようが
関係ない。
?プロパティというものを使えば
このように書くことができます。
?より自由になってきた。
class Complex {
public float Im, Re;
public Complex(float _Re,float _Im){
Re = _Re; Im = _Im;
}
public float Length {
get { return Mathf.Sqrt(Im * Im + Re * Re); }
set {
float arg = Argument;
Re = value * Mathf.Cos(arg);
Im = value * Mathf.Sin(arg);
}
}
public float Argument {
get { return Mathf.Atan2(Im, Re); }
set {
float length = Length;
Re = length * Mathf.Cos(value);
Im = length * Mathf.Sin(value);
}
}
}
void UseComplex() {
Complex C1 = new Complex( 1,1);
C1.Argument = 3.14f/2f;
C1.Length = 10f;
}
②クラス
?ここまで作成したクラスは、じつは参照型です。
?どういうことかというと、
右みたいにすると、C2を変更したら
C2とC1と同じところを指していて
変更されるので、C1も変わってしまう。
?それが嫌なら、値型(データを値として保存する)のstructを利用して下さい
?些細な違いはありますが、殆どclassと同じなので、使い分けて下さい。
?class を struct ってすればOKです。
void UseComplex() {
Complex C1 = new Complex(1, 1);
Complex C2 = C1;
C2.Re = -1;
Debug.Log(C1.Re);
}
②クラス
?ここまでの機能を使って、分数クラス(Fraction)を作ってみよう。
?引数は整数二つを取って分数を作ります。
?加減乗除も実装してください。
?既約分数になっているべきです。
?しかし、それは変更があるたびに既約にするのか、
値を取得する時に既約にして返すのかは自由です。
?使う側からすれば、動けば問題ないので。
?自信がある人は、分数クラスではなくて、多倍長整数クラスを作って下さい
ついでに Const Readonly
?値を変更したくない時には
const や readonlyを使います
?const はつまり定数として絶対不変の
値を宣言と同時に代入して、
書き込み出来ないようにします。
?readonlyも書き込み出来ないのは
同じですが、コンストラクタでのみ
変更できるので、インスタンスごとに
異なる値を持てます。
まさに読み込み専用ですね。
class Complex {
public const string ClassName = "Complex";
public readonly float Im, Re;
public Complex(float _Re,float _Im){
Re = _Re; Im = _Im;
}
}
void ComplexUse() {
Complex C = new Complex(3,2);
Debug.Log(C.Re);
C.Re = 0;//エラー
}
Debug.Log(Complex.ClassName);
ついでに
?ただ単に、値を保存しておきたいだけなら、Tupleというものを使うという
選択肢もあります。
?Tuple<int ,int ,string> T みたいに組で管理できます。
②クラスの継承
?さて、クラスの醍醐味は継承なわけで、同時に継承でつまずいてしまう人も
多いわけですが、やはり最後としてやるしかないですね。やりましょう。
?昨年のUnity担当者(spi8823)も女子高生を例にして説明してましたし、
僕も女子高生を例にして説明することにします。
?この女子高生編が今年のUnity講座の最後の項目です。
②クラスの継承
?女子高生クラスをつくるといっても目的がはっきりしていないので
はっきりさせましょう。
?RPGを作ることにしたとして、普通の人間は普通の力しかないが、
女子高生は特に凄い能力をもつRPGを作るとしよう。
?このとき、それぞれを別にするのではなく、女子高生クラスは、
基底クラスである人間クラスを継承した派生クラスとすると
クラスの継承の概念を説明しやすいかなあ。
②クラスの継承
?人間クラスは、攻撃力、防御力、体力、名前、性別を持っています。
性別と名前は流石に不変ですのでreadonlyにしました。
class Human{
public readonly bool isGirl;
public int Attack, Defence, HP;
public readonly string Name ;
public Human(bool _isGirl,int _Attack,int _Defence,int _HP,string _Name) {
isGirl = _isGirl;
Attack = _Attack; Defence = _Defence;
HP = _HP ; Name = _Name;
}
}
void Example() {
Human Kyon = new Human(false,100,200,150,"キョン");
Human Chihaya = new Human(true,72,72,72,"如月千早");
Human Freeza = new Human(false,530000,530000,530000,"フリーザ");
Human Goku = new Human(false,10000000,10000000,1000000,"孫悟空");
}
②クラスの継承
?さて、このままでは女子高生である千早はフリーザはおろかキョンにも
勝てないですよね。ということで女子高生はMP,Avoid(回避率),
MagicAttack(魔法攻撃力)も持っているという設定にしましょう。
?女子高生は人間クラスを継承した派生クラスです。
?base() は基底クラスのコンストラクタを呼んでいます。
class JoshiKosei : Human{
public int MP ,Avoid, MagicAttack ;
public JoshiKosei(int _MP ,int _Avoid ,int _MagicAttack ,
int _Attack,int _Defence,int _HP,string _Name)
:base(true, _Attack, _Defence, _HP, _Name){
MP = _MP;Avoid = _Avoid; MagicAttack = _MagicAttack;
}
}
②クラスの継承
?派生したクラスは、基底クラスのpublicの項目をそのまま使用できます。
?下の例みたいに。
?ちなみに、基底クラスでprivateにしている項目は、外からはもちろん
派生クラスでも見ることが出来ません。
?もしNameとかはprivateでいいやってしたら派生クラスでもいじれない!
void KyonVSChihaya() {
Human Kyon = new Human(false, 100, 200, 150, "キョン");
JoshiKosei Chihaya = new JoshiKosei(720,72,720, 72, 72, 72, "如月千早");
Kyon.HP -= Chihaya.MagicAttack;
int AttackSuccess = UnityEngine.Random.Range(0, 100) < Chihaya.Avoid ? 0:1;
Chihaya.HP -= AttackSuccess * Kyon.Attack;
}
②クラスの継承
?派生したクラスは、基底クラスのpublicの項目をそのまま使用できます。
?基底クラスでprivateにしている項目は、外からはもちろん
派生クラス内でも見ることが出来ません。
?派生クラスでも見たい場合には、privateとpublicの中間のprotect を
使用します。
?こうすると、外からは見えないけれども派生クラスでは見れます。
②クラスの継承
?攻撃と防御の関数を追加してみましょう。
?基底クラスにあって
派生クラスに無いものは
無いので、派生クラスは
基底クラスとして振る舞う
ことが出来ます。
class Human{
public int Assault(){return Attack;}
public void Damaged(Human Enemy){
int damage = -Defence + Enemy.Assault();
if (damage > 0) HP -= damage;
}
//…以降定義は続く…
}
void Ex3() {
Human Kyon = new Human(false, 100, 200, 150, "キョン");
JoshiKosei JChihaya = new JoshiKosei(720, 72, 720, 72,
72, 72, "如月千早");
Human HChihaya = JChihaya;
Kyon.Damaged(JChihaya);
Kyon.Damaged(HChihaya);
}
②普通に書き換えると
?普通に書き換えれば、それでいけます。
?意味的には新しく作るので、混合を避けるために、new をつけることが推奨されてます。
?しかし、ダウンキャスト
するときに、元の情報が
失われてしまいます。
?クラスは参照なので
元の情報を上手く使える
はずです。
class JoshiKosei : Human{
public new int Assault() {
return base.Assault() + MagicAttack;
}
public new void Damaged(Human Enemy) {
int AttackSuccess = UnityEngine.Random.Range(0, 100) < Avoid ? 0 : 1;
int damage = - Defence + AttackSuccess * Enemy.Assault();
if (damage > 0) HP -= damage;
}
}
void Ex3() {
Human Kyon = new Human(false, 100, 200, 150, "キョン");
JoshiKosei JChihaya = new JoshiKosei(720, 72, 720, 72, 72, 72, "如月千早");
Human HChihaya = JChihaya;
Kyon.Damaged(JChihaya);//AssaultはJoshiKoseiの方のが実行される
Kyon.Damaged(HChihaya);//Assaultは元のHumanのが実行される
Jchihaya.Damaged(Kyon);//DamagedeはJoshiKoseiの方が実行される。
}
②virtual ->override
?基底クラスにキャストしたものでも使いたい場合は、virtualを元の関数に付けます、
派生クラスの方で、override というものを付けて書けばOKです。派生元の関数を呼びたい
ときは、base.~と書きます。
?HumanのKyonは
JoshiKoseiでAssaultが
上書きされているのを
知る由もないが…
class JoshiKosei : Human{
public override int Assault() {
return base.Assault() + MagicAttack;
}
public override void Damaged(Human Enemy) {
int AttackSuccess = UnityEngine.Random.Range(0, 100) < Avoid ? 0 : 1;
int damage = - Defence + AttackSuccess * Enemy.Assault();
if (damage > 0) HP -= damage;
}
}
void Ex3() {
Human Kyon = new Human(false, 100, 200, 150, "キョン");
JoshiKosei JChihaya = new JoshiKosei(720, 72, 720, 72, 72, 72, "如月千早");
Human HChihaya = JChihaya;
Kyon.Damaged(JChihaya);//AssaultはJoshiKoseiの方のが実行される
Kyon.Damaged(HChihaya);//AssaultはJoshiKoseiのが実行される
Jchihaya.Damaged(Kyon);//DamagedeはJoshiKoseiの方が実行される。
}
②abstract
?ちなみに、基底クラスでは実装をせずに、
派生クラスで実装をする、という選択肢も
あります。
?その時には、基底クラスにvirtualではなく
abstract(抽象化)というものをつければいいです。
?もっと進んでinterfaceというのもあります。
interfaceなら複数継承出来ます。
②クラスの継承
?最終形態です。
このスライド作るために使ったideone はコチラ https://ideone.com/Jbwybt
void Ex3() {
Human Kyon = new Human(false, 100, 200, 150, "キョン");
JoshiKosei Chihaya = new JoshiKosei(720, 72, 720, 72, 72, 72, "如月千早");
Human Freeza = new Human(false, 530000, 530000, 530000, "フリーザ");
JoshiKosei Nagato = new JoshiKosei(10000, 50, 500000, 60, 60, 60, "長門有希");
Human[] HumanArray = new Human[] {Kyon,Chihaya,Freeza,Nagato };
foreach(Human Offence in HumanArray){
foreach(Human Defence in HumanArray){
if (!Offence.Equals(Defence)) {
Defence.Damaged(Offence);
}
}
}
}
② static
?ところで、Unityでシーンをまたいで値を保存したい
ときにどうすればいいか聞かれたことがあります。
?シーンをまたぐと、通常はシーンのオブジェクトは全部
破棄されるので、データを持っておくことが出来ない
わけです。
?クラス固有の変数や関数を static というワードを
使って所持することが出来ます。
?これで値を保存できます。
class Hoge {
public static int n;
}
void Ex(){
Hoge hoge1 = new Hoge();
Hoge.n = 72;
hoge1.n = 10;//出来ない
Debug.Log(Hoge.n);
}
② singleton
?インスタンスが一個だけなだけで
staticとはしたくない場合がある
?static function や
本当に staticなものと区別したい
?シングルトンというのを
使う方法もあります。
?どこからでも変えられるかも
しれないstaticより安心。
class Hoge {
//外からコンストラクタを実行できないように
private Hoge() { }
private static Hoge mInstance;//唯一のインスタンス
public static Hoge Instance {
get {
if (mInstance == null) mInstance = new Hoge();
return mInstance;
}
}
public int n;
}
void Ex(){
Hoge hoge1 = Hoge.Instance;
hoge1.n = 72;
Debug.Log(hoge1.n);
}

More Related Content

Unity2015_No10_~UGUI&Audio~