1 of 93

それとなく学ぶ�プログラミング

もっと詳しく上級生のために

2 of 93

このスライドには以下の要素が含まれます

  • プログラミングⅡが前提
  • 2~3年生向け(いつこのスライド使うかは発表者の判断で)
  • Unityに触れたことがある
  • 発表者はこのスライドを把握している

  (連続で教えた方がいい内容がある)

  • 必要とあらば改良なり追加なりして、後輩に継いでください

3 of 93

JAVAとC#の�間違い探し

今更ですが

4 of 93

微妙な違いメモ

  • Stringはstring
  • メソッドじゃなくて関数
  • 関数の名前は大文字で始まるのが好ましい( public void Method(){} )
  • Getter関数とSetter関数を書く必要がない(後述)

5 of 93

多重配列(長方形)

Java

int [][] a = new int[5][3];

a.length;

for(int i=0;i<a.length;i++){

for(int j=0;j<a[i].length;j++);

}

C#

int [,] a = new int[5,3];

a.Length;

for(int i=0;i<a.GetLength(0); i++){

for(int j=0;j<a.GetLength(1); j++);

}

6 of 93

多重配列(ガタガタの長さ)

C# 宣言の書き方はJavaと同じ

int[][] jaggedArray = {

new int[] { 1, 3, 5, 7, 9 },

new int[] { 0, 2, 4, 6 },

new int[] { 11, 22 }

};

いわゆるジャグ配列は初期化すれば使える

7 of 93

拡張for文

Java

int[] a = new int[5];

for(int b : a){

}

C#

int[] a = new int[5];

foreach(int b in a){

}

8 of 93

継承とコンストラクタ

Java

public class A extend B{

int a;

public A (int i) {

a = i;

}

public A (){

this(0);

}

}

C#

public class A : B{

int a;

public A (int i) {

a = i;

}

public A () : this(0) {

}

}

9 of 93

getterとsetter (C#ではプロパティ)

Java

public class {

private int life = 100;

public void SetLife(int life){

this.life = life;

}

public int GetLife(){

return life;

}

}

C#

public class {

private int life = 100;

public int Life {

set { life = value; }

get { return life; }

}

}

10 of 93

public class {

private int life = 100;

public int Pow {

get { return life * life ; }

}

}

public class {

private int life = 100;

public int Life {

protected set { life = value ; }

get { return life ; }

} }

public class {

public int AP {

private set ;

get ;

}

}

get だけ外部

から使える

Setのカプセル化をprotected

変数を利用しない

//使用時

int powLife= Pow ;

Life = 100 ;

AP = powLife ;

変数のように

使用できる

※詳しくは後述

11 of 93

構造体

クラスみたいな構造しただけの構造体

12 of 93

定義例

public struct SampleStruct{

public int value;

public string str;

public SampleStruct(int v = 0,string s = “”){ //コンストラクタ

value = v;

str = s;

}

public override string ToString(){ //ToString関数

return value + “ : ” + str;

}

}

引数なしのコンストラクタは不可

13 of 93

クラスと違って値型

SampleStruct a = new SampleStruct(0,”A”);

SampleStruct b = a;

b.value = 10;

b.str = “B”;

print(“a→” + a);

print(“b→”+ b);

 実行結果(Bでの変更はAに反映されない)

a→0 : A

b→10 : B

(UnityではVector3とかが構造体)

14 of 93

オプション引数

知識のオプション

15 of 93

引数の数が増減できる

struct Structer {

float time;

int mode;

Structer(float t = 0, int m) { time = t; mode = m; }

void Method(int a = 0, int b=10) { }

}

Structer str;

str = new Structer(1.5f, 0);

str.Method(10, 2);

str = new Structer();

str.Method();

値を代入しない場合のデフォルト値を設定できる

→new Vector3();で引数なしが使える

16 of 93

3項演算子

参考までに

17 of 93

簡易的なif文

if(a > 0){

print(“P”);

}

else{

print(“M”);

}

( a > 0 ? print(“P”) : print(“M”) ) ;

print( ( a > 0 ? ”P” : ”M” ) ) ;

( 条件 ? 真の時の処理 : 偽の時の処理 )

は処理の優先順位が低いため

()で囲って使うのが基本

18 of 93

条件次第で代入とか

//X座標次第で0か1を代入

float alpha = (transform.position.x >= 0 ? 1 : 0);

//三項演算子in三項演算子

float sign = (transform.position.x > 0 ? 1 :

(transform.position.x == 0 ? 0 : -1));

19 of 93

代入のすゝめ

もしtrueならtrueって、それもうtrue

20 of 93

Q. Debug.log(10 >= 0); どう表示される

A . true

つまり 条件 if()の中は true や false になる

ということを踏まえて↓

21 of 93

void DrawPics(){

if(a >= 0){

pic1.enabled = true;

pic2.enabled = true;

}

else{

pic1.enabled = false;

pic2.enabled = false;

}

}

あるところにこんな関数がありました

もし0以上ならば

画像の表示、非表示を

true,falseにする関数

22 of 93

void DrawPics(){

pic1.enabled = a >= 0;

pic2.enabled = a >= 0;

}

もし0以上ならば

trueを代入

void DrawPics(){

pic1.enabled = pic2.enabled = a >= 0;

}

さらに省略

23 of 93

void DrawPics(){

if(a >= 0){

pic1.enabled = true;     Instantiate(pic1.gameObject);

}

else{

pic1.enabled = false; Destroy(pic1.gameObject);

}

}

では、条件ごとに処理が違ったら?

もし0以上ならば

画像の表示、非表示を

true,falseにして

それぞれ別の処理を

する関数

24 of 93

void DrawPics(){

if(pic1.enabled = a >= 0)

Instantiate(pic1.gameObject);

else Destroy(pic1.gameObject);

}

代入してif文の条件に使うとかもできる

同じ処理でも

かなりコンパクトに

なにを処理しているかを把握することが大事

※見やすさを失いすぎないように

25 of 93

SWITCH文

Switch文に見た目をスイッチ

26 of 93

Switch文・・・見た目がスッキリする以外特に…

if(a == 0){

} else if(a == 1){

} else if(a == 2){

} else {

}

switch ( a ){

case 0:

break;

case 1:

break;

case 2;

break;

default:

break;

}

if( a > 10)

比較のifは

不可

27 of 93

細かな場合分けに向いてます

インデントが かさばらない

28 of 93

列挙型 ENUM

別の記述方法を列挙します

29 of 93

(public) enum Animation{� Stop , Walk , Run , Jump

}

int a = 0;

Animation anim = Animation.Stop;

[Serialized]Animation = anim2;

列挙型・・・値が限定された型を作れる

Animationという型を作り

その中のどれかしか入らない

SelializeField外部入力も可能

Unityは日本語にも対応

30 of 93

Switch文との相性がいい

Switch(anim){

case Animation.Stop:

break;

case Animation.Walk:

break;

case Animation.Run:

break;

case Animation.Jump:

break;

}

Defaultがいらない

→想定外の処理はありえない

処理が見やすい

見返しや修正が楽

31 of 93

オブジェクト指向�継承

継承について継承します

32 of 93

継承

いいこと

    • 汎用性が高い
    • 多態性が便利
    • 同じ記述を減らせる

33 of 93

class Enemy : Character

{ //コロン ↑ で継承

[Serialized] protected int HP;

protected int AP;

public int Attack{

get { return AP; }

}

  

  protected virtual void Move() { }

}

[Serialized]も継承

継承先でoverrideさせたい場合は「virtual」で宣言だけしておく

34 of 93

ぽりもーふぃずむ�多態性

多態性をたたえよ

35 of 93

class Zombie : Enemy

クラス Player

OnCollisionEnter(Collision coll){

if(coll.gameObjct.tag == “Enemy”){

coll.GetComponent<?クラス名?>();

Damage( ??? );

}

}

//オーバーロードを利用した場合

void Damage(Zombie zom){}

void Damage(Pumpkin pum){}

void Damage(Boss bos){}

class Pumpkin : Enemy

class Boss : Enemy

クラス名の判定ができない

36 of 93

クラス Player

OnCollisionEnter(Collision coll){

if(coll.gameObjct.tag == “Enemy”){

Damage(coll.GetComponent<Enemy>());

}

}

//多態性を利用

void Damage(Enemy enemy){

HP -= enemy.Attack;

}

class Zombie : Enemy

class Pumpkin : Enemy

class Boss : Enemy

多態性で複数のクラスに対応

37 of 93

動的配列�LIST

あなたの知識にリストをadd

38 of 93

突然始まる

こともある

アルゴリズムを

考えてみよう

public class Node{

 public Node Next;

 //あとなんかint とかデータ

}

public class Node{

 public Node Next;

 //あとなんかint とかデータ

}

public class Node{

 public Node Next;

 //あとなんかint とかデータ

}

39 of 93

リスト・・・長さが可変長な配列

→配列のように長さが決まってない

List<クラス型> a = new List<クラス型>();

例)

List<int> data = new List<int>();

data.add(5); //一番後ろに追加

for(int i=0; I < data.Count() ;i++){

print( data[i] ); //配列と同じ使い方

}

data.insert(3,25); //3番目の後ろに追加

Vector2型だとこんな

data.add(new Vector2(2,3));

途中にデータを追加できる

1つ1つの要素の型が違ってもいいリストも作れる

GetComponent<○○>();

40 of 93

プロパティ

Getter Setterはもういらない

41 of 93

C#にGetterSetter関数はいらない

int a = 0;

public int GetA(){ return a; }

public void SetA(int A){ a = A; }

void Start(){

int b = GetA();

SetA(b);

}

int a = 0;

public int A{

get { return a; }

set { a = value; }

}

void Start(){

int b = A;

A = b;

}

42 of 93

片方だけprivateっていうのにも対応可

float a = 0;

public int GetA(){ return a; }

private void SetA(float A){ a = A; }

~~他クラス~~

void Start(){

float b = GetComponet<A>().GetA();

}

public float A{

get { return a; }

private set { a = value; }

}

~~他クラス~~

void Start(){

float b = GetComponet<A>(). A;

}

43 of 93

小さい関数だと思っていただいて

int a = 0;

public int GetAA(){

return a * a;

}

int a = 0;

public int AA{

get { return a * a; }

}

int a = 0;

private void SetA(int A){

a = A * A;

}

private int A{

set { a = value * value; }

}

44 of 93

そのまま変数みたいに使えたりも

public float Ab{ get; set; }

public int Bc{ get; private set; }

void Start(){

Ab = 1.5f;

Bc = Ab;

int bc = Bc;

}

ifの中を省略するためになど、割と便利

bool CanJamp{

get {

return isGround && jampTime < 0.5f

}

}

45 of 93

インデクサー

インデクサーってなんですかー

46 of 93

見た目は()が[ ]になった関数で、中身はプロパティみたいな

class Indexer {

public this[引数,…] {

get;

set;

}

public this[引数] {

get;

set;

}

}

Indexer index = new Indexer();

value = new ();

index[引数] = value;

value2 = index[引数];

名前はthis

引数は型も数も任意

オーバーロードも可

Indexerを実装したクラスの変数index

変数名[引数]で使う

代入と利用はプロパティと同じ

47 of 93

プロパティと同様の機能

class Official { string pos; string name;}

public class OfficerList {

private List< Official > data = new List< Official >();

public string this[string key] {

set {

int i=0; for(;i < data.Count;i++)if(data[i].pos==key) data[i].name = value;

if(i==data.Count)data.Add(new Official(key,value));� }

get {

for(int i=0; i < data.Count;i++)

if(data[i].pos == key) return data[i].name;

return null;

}

} }

OfficerList dic = new OfficerList();

dic[“Leader”] = Nekocan;

dic[“SubLeader”] = Wingman;

dic[“Leader”] = Hilljin;

print(dic[“Leader”] dic[“SubLeader”]);

実行結果

HilljinWingman

探索に使う引数代入に使う値で2つ1組

役職名名前を管理する例

リスト内の役職を探索して名前を上書き

未登録の役職ならリストにデータ追加

48 of 93

JSON

金曜日の文字列

49 of 93

文字列に変換する機能

変数の名前とその中身を文字列にする

string name = “Kusa”;

int HP = 100;

float Speed = 7.5f;

一つの文字列に変換

{ name : Kusa , HP : 100 , Speed : 7.5f }

Json

50 of 93

クラスを文字列に変換する機能

class Character {

string name = “Kusa”;

int HP = 100;

float Speed 7.5f;

}

Character chara = new Character();

string s = JsonUtility.ToJson(chara);

Debug.log(“s = ” + s); //右上に続く

実行結果 Json形式

s = {“name”:”Kusa”,”HP”:100,”Speed”:7.5}

//変数と中身を文字列に出来る

また、文字列からクラスに戻すことも可能

Character c = JsonUtility.FromJson<Characer>(chara);

51 of 93

ぷれいやーぷれふす�PLAYERPREFS

保存します。データと知識。

52 of 93

ゲームに保存機能をつけられます

保存できるのは int型 float型 string型 の3種類

PlayerPrefs.Set〇〇(string key , 〇〇型); //keyには保存データ名を入れる

例)

PlayerPrefs.SetString(“name”, name);

PlayerPrefs.SetInt(“HP”, HP);

PlayerPrefs.SetFloat(“speed”, Speed);

PlayerPrefs.Save(); //ディスクに書き込む処理

53 of 93

ゲームに保存機能をつけられます

取得できるのも int型 float型 string型 の3種類

PlayerPrefs.Get〇〇(string key , default);

//keyには保存データ名を入れる

//defaultにはデータが保存されてない場合に代入する値

例)

PlayerPrefs.GetString(“name”, ”default name”);

PlayerPrefs.GetInt(“HP”, 100);

PlayerPrefs.GetFloat(“speed”, 10);

54 of 93

じぇいそん せぇ~ぶ�JSON SAVE

複合錬金術

55 of 93

Json と PlayerPrefs でクラスを保存

保存したいデータをJsonで文字列に変換

Character player = new Character();

string player_Data_Str = JsonUtility.ToJson(player);

PlayerPrefs で保存

PlayerPrefs.SetString(“Data_Of_Player”,player_Data_Str);

PlayerPrefs.Save();

56 of 93

保存したデータを取得する

予め保存したデータを文字列に入れる

string player_Data = PlayerPrefs.GetString(“Data_Of_Player”);

player_Dataには、Json形式の文字列が入る

{“name”:”Kusa”,”HP”:100,”Speed”:7.5}

Json形式の文字列をクラスに代入

player = JsonUtility.FromJson<Character>(player_Data);

以前セーブしたデータが player に復元される

57 of 93

←インデクサを利用したセーブ用クラスの例

public class SaveData {

public int num; public SaveData(int n = 0){ num = n; }

}

public class SaveManager : MonoBeaviour {

public SaveData data;

public SaveData this[string key] {

set {

PlayerPrefs.SetString(key, JsonUtility.ToJson(value));

}

get {

string json = PlayerPrefs.GetString(key, ””);

if(json.Length == 0) return new SaveData();

return JsonUtility.FromJson<SaveData>(json);

}

}

public void Save() { PlayerPrefs.Save(); } }

SaveManager saveManager ;

string saveKey = “player1” ;

SaveData saveData ;

void Start() {

saveManager = new SaveManager();

saveData = saveManager[saveKey];

}

public void Save() {

saveManager[saveKey] = saveData;

}

public void CloseMenuWindow() {

saveManager.Save();

}

「key」の保存データがない場合「””」が代入される

58 of 93

コルーチン�IENUMERATOR

中断します。関数の処理と ついでに思考。

59 of 93

コルーチン・・・処理を中断→再開

IEnumerator Timer(float t){

print(“0”);

yield return WaitForSeconds (t);

print(“Waited : ” + t);

}

引数に5を入れた場合の実行結果

0

Waited : 5

5秒後

表示される

引数の秒数だけ待機する

関数

(ちなみに)次のフレームで再開

yield return null;

・クセが強い

・返り値が利用出来なくなる

・使いこなせば便利

・C#独自の処理

・関数ごとにタイマーを作れる

→イベントに対するタイマーと 

 して重宝される

60 of 93

ラムダ式を用いた複数OR演算を可能にする関数

何個でもイコール判定先輩

61 of 93

何個でもOR判定できる

事例) 複数のタグに対して処理したい場合など

void OnTriggerEnter(Collider other){

if(other.tag == “A” || other.tag == “B” || other.tag == “C”)

if(other.tag.AnyEquale(“A”,”B”,”C”)) //カンマ区切りでOR演算が出来る

}

参考文献

【C#】1つのオブジェクトが複数のオブジェクトのいずれかと等しいか判定する拡張メソッド - コガネブログ

http://baba-s.hatenablog.com/entry/2014/07/23/120735

62 of 93

かきかた(要コピペ)

どこでもいいので以下のクラスを記述する

using System.Linq; //必須

//非ジェネリックで静的なクラス(関数名は自由でいい)

public static class GenericExtensions

{

public static bool AnyEquale<E>(this E self, params E[ ] data)

{

return data.Any( c => c.Equals(self) );

}

}

63 of 93

間違ったFOR文の�正しい使い方

プレゼン for For

64 of 93

√(ルート)を1行で書ける

int r = Random.Range(1,101);

int i ;

for (i = 1 ; i * i < r ; i++); //※正確にはこの後 i--;しないといけない

正確な値を求めるなら

float I = 0;

float prevI = ram = Random.Range(1,101);

for( I = 1;( I = ( I + ram / I ) / 2) != prevI ; prevI = I );

65 of 93

乱数に制限を

-2~2以外の乱数が必要なとき

int ram;

for (ram = 0; Abs(ram) < 2 ; ram = Random.Range(-10,10) );

使うタイミングはそうそうない

66 of 93

乱数調整

ランダムじゃない乱数

67 of 93

ランダムに網羅してほしい時って�あるじゃん?

  • ミュージックリストから、ランダムで曲を流す

  • テト〇スとか、一通り選択してほしい(ずっと選ばれない物があるのは困る)

→袋の中からボールを取り出していくのと同じ概念

68 of 93

2

3

2

3

3

網羅した乱数

69 of 93

70 of 93

かきかたの例(一番シンプルな例)

public GameObject[ ] Blocks = null; //インスペクタでプレハブを代入

List<GameObject> list = new List<GameObject>(); //袋の役目

void InstRandom(){ //リストが空なら全部追加↓

if(list.Count == 0) foreach(GameObject obj in Blocks) list.Add(obj);

int ram = Random.Range(0 , list.Count);

Instantiate(list[ram]); // list の ram 番目を生成

list.RemoveAt(ram); // list から ram 番目の要素を削除

}

71 of 93

エディタ拡張

見やすさの可能性を拡張します

72 of 93

エディタ拡張

→なんかちょっと見やすくするやつ(ゲームに影響なし)

73 of 93

[SerializeField] private int HP; //privateでも外部入力を可能に

[SerializeField,Tooltip(“説明”)] int AP; //簡易的な説明文の追加

[SerializeField ,Range(-10,10)] //スライダーを表示

float Speed 0;

[SerializeField ,Tooltip(“A”),Range(0,5)] int a; //カンマ区切りで複数も可能

PublicならSerializeField

なしでも可

74 of 93

より高度なエディタ拡張

  • どこでもいいので「Editor」というフォルダを作り、その中にエディタ拡張専用のスクリプトを用意 ○○Editor みたいな
  • 「unity エディタ拡張」で検索する
  • 頑張る
  •   以下、ブロック崩し体験を作る時に参考にした資料
  • 【エディタ拡張徹底解説】初級編①:ウィンドウを自作してみよう【Unity】
  • 【Unity:エディタ拡張】Unityでエディタ拡張を始めよう
  • インスペクタで設定できる値を動的に変更する【Unity】【エディタ拡張】
  • 【Unity】インスペクタの表示を動的に変更する

75 of 93

パラメーター修飾子

あたいも値を参照したい

76 of 93

パラメーター修飾子

  • パラメーター修飾子

関数の引数「intやfloatや構造体」などの値型を参照渡しができる

(返り値を使わずに結果を出力する)(関数内で値を入力する)

  • out

初期値なし

  • ref

初期値あり

  • in

初期値あり、値の変更不可(参照渡しにすることでメモリ削減で高速化)

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/out-parameter-modifier

https://www.atmarkit.co.jp/ait/articles/1804/25/news021.html

77 of 93

struct Str {

float s,a,b,c,f;

//コンストラクタ 略

}

void ParamMethod(out int age,

ref float height, in Str str) {

age = 10;

height += 1.5f;

print(“str.a:”+str.a);

}

Str str = new Str(2,3,4,1,0);

int Age;

float Height = 160f;

ParamMethod(out Age, ref Height, in str);

print(“Age:”+Age);

print(“Height:”+Height);

実行結果

str.a:2

Age:10

Height:161.5

78 of 93

命名規則

君の名は。

79 of 93

名前は名詞、処理は動詞

  • クラス名、変数名 …名詞
    • 何を表す、何のためのクラスor変数なのかを表せるような名詞で命名

  • 関数名 …動詞
    • 何を処理するのかを簡潔表すような動詞で命名

80 of 93

効率化の話

効率と意識の向上を

81 of 93

疑問に思いませんか?transform

Q.

Rigidbody などのコンポーネントは

GetConponent<>() で取得するのに

なぜ transform は使わなくていい?

82 of 93

A.実は毎回やっているから

Unity側で毎回GetComponentレベルの処理をしていた

→頻繁に使うなら保持して負荷軽減するのもアリ

 (今更気にするほど負荷が高いわけでもない)

いつも通りtransformを使う例

new Transform transform;

void Start() {

transform = GetComponent<Transform>();

}

83 of 93

オブジェクトプール

  • 大量生産、大量消費のこの時代に
  • 再度使用する予定があるオブジェクトを 

  Destroyせずにとっておく手法

Destroy()Instantiate()の回数が減り、負荷が減る

次ページ 記述例

[Unity]簡単にオブジェクトプールを作る

https://qiita.com/NekoCan/items/e3908b8e4e91b95d726a

84 of 93

Transform pool; //オブジェクトを保存する空オブジェクトのtransform

void Start(){

pool = new GameObject(“名前”).transform;

}

void GetObject(GameObject obj , Vector3 pos , Quaternion qua){

foreach(Transform t in pool) {

if( ! t.gameObject.activeSelf){ //オブジェが非アクティブなら使い回し

t.SetPositionAndRotation(pos,qua); t.gameObject.SetActive(true);

} //位置と回転を設定後、アクティブにする

} //非アクティブなオブジェクトがないなら生成

Instantiate(obj , pos , qua , pool);//生成と同時にpoolを親に設定

} //ギリギリ入りきってよかった

85 of 93

※オブジェクトプールはデータを保存するため

メモリを圧迫します

大量にプールしすぎても重くなるので注意

transformが実は重いって告白

86 of 93

計算量を減らす

ベクトルの長さを得る「magnitude」→ √(ルート)を使うため重い

sqrMagnitudeを使う

→ルートする前の値なので、比較対象を2乗して同等の比較が行える

例)

if( (target.position – transform.position).magnitude < 50)

if( (target.position – transform.position).sqrMagnitude < 50 * 50)

同等の比較になる

87 of 93

FixedUpdate()の利用

FixedUpdate ・・・ 定期的に呼ばれる関数 

物理演算するならこの関数内に記述するのが良い

呼ばれる頻度は 0.2f秒 で、Updateの時と同じ記述で問題ないが

時間計測の変数が違う

Time.FixedDeltaTime

88 of 93

関数を使わないという提案

void Method(int a) ←引数にメモリを使用する

同時に大量にメソッドを使用するとメモリの圧迫になり重くなる

再帰なんて使わない → for文の方が軽い

Mathf.Abs(X) → (X>0 ? X : -X)

Mathf.Sign(X) → (X>0 ? 1 : -1)

Mathf.Pow(X,2) → (X * X)

89 of 93

プログラマの話

最優秀不健康賞

90 of 93

寝ないでください

明日が納期ですって!?

→寝ないでください

 →とりあえず、システムの完成だけを考えてください

明日までに機能の追加をしたい!?

→寝ないでください

 →不眠による思考力の低下を加味した上で、それは可能か適切   であるか、判断してください

91 of 93

寝てください

眠い状態で考えても無駄な時間を過ごします

→下手な考え眠るに同じ ということわざもあるし

→早く寝て、朝一番でプログラムの見直しをした方が効率的です

(経験談)

どうせ考え事してる時に力尽きるだけです

92 of 93

93 of 93

寝かしましょう

作ったゲームやプログラミング、レポート等を一旦忘れて

翌日見直してみましょう

明らかに修正すべき点が見つかりやすいです

あと、製作者も寝かしましょう