Properties
如何創建Properties去訪問類中的私有成員變量
這個方法一般用於隱藏類中的變量而又開放出讀取或設置的權限,見示例:
using UnityEngine;
using System.Collections;
public class Player
{
//Member variables can be referred to as fields.
private int experience;
//Experience is a basic property
public int Experience
{
get
{
//Some other code
return experience;
}
set
{
//Some other code
experience = value;
}
}
//Level is a property that converts experience
//points into the leve of a player automatically
public int Level
{
get
{
return experience / 1000;
}
set
{
experience = value * 1000;
}
}
//This is an example of an auto-implemented
//property
public int Health{ get; set;}}
using UnityEngine;
using System.Collections;
public class Game : MonoBehaviour
{
void Start ()
{
Player myPlayer = new Player();
//Properties can be used just like variables
myPlayer.Experience = 5;
int x = myPlayer.Experience;
}
}
三元運算符(*?:)
如何用三元運算符快速建立一個if-else語句
例:
message = health > 0 ? "Player is Alive" : "Player is Dead";
?前面是判定條件,?和:中間是條件爲true的結果,:後面是條件爲false的結果
靜態(Static)
可以設置類,函數,屬性爲靜態,靜態即不能被實例化,唯一
重載方法(Method Overloading)
using UnityEngine;
using System.Collections;
public class SomeClass
{
//The first Add method has a signature of
//"Add(int, int)". This signature must be unique.
public int Add(int num1, int num2)
{
return num1 + num2;
}
//The second Add method has a sugnature of
//"Add(string, string)". Again, this must be unique.
public string Add(string str1, string str2)
{
return str1 + str2;
}
}
重寫方法,函數名相同而傳入參數不同,方法會尋找最匹配類型的方法執行,若找不到,則返回錯誤
泛型(Generics)
泛型方法
當然可以不止是T,也可以是U,V等多個範類型,若需要規定類型的範圍,可以如下一樣添加where語句來規定
using UnityEngine;
using System.Collections;
public class SomeClass
{
//Here is a generic method. Notice the generic
//type 'T'. This 'T' will be replaced at runtime
//with an actual type.
public T GenericMethod<T>(T param) where T : int
{
return param;
}
}
泛型類
using UnityEngine;
using System.Collections;
//Here is a generic class. Notice the generic type 'T'.
//'T' will be replaced with an actual type, as will also
//instances of the type 'T' used in the class.
public class GenericClass <T>
{
T item;
public void UpdateItem(T newItem)
{
item = newItem;
}
}
繼承(Inheritance)
面向對象思想編程
父類
using UnityEngine;
using System.Collections;
//This is the base class which is
//also known as the Parent class.
public class Fruit
{
public string color;
//This is the first constructor for the Fruit class
//and is not inherited by any derived classes.
public Fruit()
{
color = "orange";
Debug.Log("1st Fruit Constructor Called");
}
//This is the second constructor for the Fruit class
//and is not inherited by any derived classes.
public Fruit(string newColor)
{
color = newColor;
Debug.Log("2nd Fruit Constructor Called");
}
public void Chop()
{
Debug.Log("The " + color + " fruit has been chopped.");
}
public void SayHello()
{
Debug.Log("Hello, I am a fruit.");
}
}
子類
using UnityEngine;
using System.Collections;
//This is the derived class whis is
//also know as the Child class.
public class Apple : Fruit
{
//This is the first constructor for the Apple class.
//It calls the parent constructor immediately, even
//before it runs.
public Apple()
{
//Notice how Apple has access to the public variable
//color, which is a part of the parent Fruit class.
color = "red";
Debug.Log("1st Apple Constructor Called");
}
//This is the second constructor for the Apple class.
//It specifies which parent constructor will be called
//using the "base" keyword.
public Apple(string newColor) : base(newColor)
{
//Notice how this constructor doesn't set the color
//since the base constructor sets the color that
//is passed as an argument.
Debug.Log("2nd Apple Constructor Called");
}
}
- 子類會繼承父類public和protect的屬性和方法,而對於private則不行
- 子類構造函數使用base關鍵字來說明使用了父類哪個構造函數,使用用類似於java的super,因爲C#不支持多繼承,父類用base即可,C++支持多繼承,所以要寫父類名字說明,也是兩者區別之一。
多態(Polymorphism)
using UnityEngine;
using System.Collections;
public class Fruit
{
public Fruit()
{
Debug.Log("1st Fruit Constructor Called");
}
public void Chop()
{
Debug.Log("The fruit has been chopped.");
}
public void SayHello()
{
Debug.Log("Hello, I am a fruit.");
}
}
using UnityEngine;
using System.Collections;
public class Apple : Fruit
{
public Apple()
{
Debug.Log("1st Apple Constructor Called");
}
//Apple has its own version of Chop() and SayHello().
//When running the scripts, notice when Fruit's version
//of these methods are called and when Apple's version
//of these methods are called.
//In this example, the "new" keyword is used to supress
//warnings from Unity while not overriding the methods
//in the Apple class.
public new void Chop()
{
Debug.Log("The apple has been chopped.");
}
public new void SayHello()
{
Debug.Log("Hello, I am an apple.");
}
}
using UnityEngine;
using System.Collections;
public class FruitSalad : MonoBehaviour
{
void Start ()
{
//Notice here how the variable "myFruit" is of type
//Fruit but is being assigned a reference to an Apple. This
//works because of Polymorphism. Since an Apple is a Fruit,
//this works just fine. While the Apple reference is stored
//in a Fruit variable, it can only be used like a Fruit
Fruit myFruit = new Apple();
myFruit.SayHello();
myFruit.Chop();
//This is called downcasting. The variable "myFruit" which is
//of type Fruit, actually contains a reference to an Apple. Therefore,
//it can safely be turned back into an Apple variable. This allows
//it to be used like an Apple, where before it could only be used
//like a Fruit.
Apple myApple = (Apple)myFruit;
myApple.SayHello();
myApple.Chop();
}
}
- new關鍵字是當子類沒有重寫父類方法的時候提供來自unity的警告
- 當子類引用儲存在父類變量中,只能像父類一樣使用,當強制安全降級到子類變量中,就可以像子類一樣使用
成員隱藏(Member Hiding)
using UnityEngine;
using System.Collections;
public class Humanoid
{
//Base version of the Yell method
public void Yell()
{
Debug.Log ("Humanoid version of the Yell() method");
}
}
using UnityEngine;
using System.Collections;
public class Enemy : Humanoid
{
//This hides the Humanoid version.
new public void Yell()
{
Debug.Log ("Enemy version of the Yell() method");
}
}
using UnityEngine;
using System.Collections;
public class WarBand : MonoBehaviour
{
void Start ()
{
Humanoid human = new Humanoid();
Humanoid enemy = new Enemy();
//Notice how each Humanoid variable contains
//a reference to a different class in the
//inheritance hierarchy, yet each of them
//calls the Humanoid Yell() method.
human.Yell();
enemy.Yell();
}
}
- new關鍵字顯式隱藏從父類繼承來的成員,不然unity會有警告
重寫/覆蓋(Overriding)
using UnityEngine;
using System.Collections;
public class Fruit
{
public Fruit ()
{
Debug.Log("1st Fruit Constructor Called");
}
//These methods are virtual and thus can be overriden
//in child classes
public virtual void Chop ()
{
Debug.Log("The fruit has been chopped.");
}
public virtual void SayHello ()
{
Debug.Log("Hello, I am a fruit.");
}
}
using UnityEngine;
using System.Collections;
public class Apple : Fruit
{
public Apple ()
{
Debug.Log("1st Apple Constructor Called");
}
//These methods are overrides and therefore
//can override any virtual methods in the parent
//class.
public override void Chop ()
{
base.Chop();
Debug.Log("The apple has been chopped.");
}
public override void SayHello ()
{
base.SayHello();
Debug.Log("Hello, I am an apple.");
}
}
using UnityEngine;
using System.Collections;
public class FruitSalad : MonoBehaviour
{
void Start ()
{
Apple myApple = new Apple();
//Notice that the Apple version of the methods
//override the fruit versions. Also notice that
//since the Apple versions call the Fruit version with
//the "base" keyword, both are called.
myApple.SayHello();
myApple.Chop();
//Overriding is also useful in a polymorphic situation.
//Since the methods of the Fruit class are "virtual" and
//the methods of the Apple class are "override", when we
//upcast an Apple into a Fruit, the Apple version of the
//Methods are used.
Fruit myFruit = new Apple();
myFruit.SayHello();
myFruit.Chop();
}
}
- 當子類重寫了父類後,子類需要通過base關鍵字來調用父類
- 重寫也用於多態,父類方法有virtual關鍵字時,子類相同方法添加override關鍵字,這樣,即使子類引用上升了父類變量,依舊會使用子類的方法
接口(Interfaces)
using UnityEngine;
using System.Collections;
//This is a basic interface with a single required
//method.
public interface IKillable
{
void Kill();
}
//This is a generic interface where T is a placeholder
//for a data type that will be provided by the
//implementing class.
public interface IDamageable<T>
{
void Damage(T damageTaken);
}
using UnityEngine;
using System.Collections;
public class Avatar : MonoBehaviour, IKillable, IDamageable<float>
{
//The required method of the IKillable interface
public void Kill()
{
//Do something fun
}
//The required method of the IDamageable interface
public void Damage(float damageTaken)
{
//Do something fun
}
}
- 接口不能實例化,只能進行簡單的定義
- 繼承與接口類似,至於爲什麼不使用繼承而使用接口,區別就是C#支持實現多個接口而不支持繼承多個父類,而繼承與接口使用的方向也不同,繼承的父類大多指的是物體,而接口大多指的是狀態
擴展方法(extension methods)
using UnityEngine;
using System.Collections;
//It is common to create a class to contain all of your
//extension methods. This class must be static.
public static class ExtensionMethods
{
//Even though they are used like normal methods, extension
//methods must be declared static. Notice that the first
//parameter has the 'this' keyword followed by a Transform
//variable. This variable denotes which class the extension
//method becomes a part of.
public static void ResetTransformation(this Transform trans)
{
trans.position = Vector3.zero;
trans.localRotation = Quaternion.identity;
trans.localScale = new Vector3(1, 1, 1);
}
}
using UnityEngine;
using System.Collections;
//It is common to create a class to contain all of your
//extension methods. This class must be static.
public static class ExtensionMethods
{
//Even though they are used like normal methods, extension
//methods must be declared static. Notice that the first
//parameter has the 'this' keyword followed by a Transform
//variable. This variable denotes which class the extension
//method becomes a part of.
public static void ResetTransformation(this Transform trans)
{
trans.position = Vector3.zero;
trans.localRotation = Quaternion.identity;
trans.localScale = new Vector3(1, 1, 1);
}
}
- 擴展方法方便爲已有的類中添加方法,其中有三個條件:
1.靜態類
2.靜態方法
3.靜態方法的第一個參數帶有this關鍵字
命名空間(Namespaces)
using UnityEngine;
using System.Collections;
namespace SampleNamespace
{
public class SomeClass : MonoBehaviour
{
void Start ()
{
}
}
}
- using也就是使用哪些命名空間裏,方便使用裏面類或方法,設置命名空間爲了避免同名方法的混淆
列表和字典(Lists and Dictionaries)
using UnityEngine;
using System.Collections;
using System; //This allows the IComparable Interface
//This is the class you will be storing
//in the different collections. In order to use
//a collection's Sort() method, this class needs to
//implement the IComparable interface.
public class BadGuy : IComparable<BadGuy>
{
public string name;
public int power;
public BadGuy(string newName, int newPower)
{
name = newName;
power = newPower;
}
//This method is required by the IComparable
//interface.
public int CompareTo(BadGuy other)
{
if(other == null)
{
return 1;
}
//Return the difference in power.
return power - other.power;
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SomeClass : MonoBehaviour
{
void Start ()
{
//This is how you create a list. Notice how the type
//is specified in the angle brackets (< >).
List<BadGuy> badguys = new List<BadGuy>();
//Here you add 3 BadGuys to the List
badguys.Add( new BadGuy("Harvey", 50));
badguys.Add( new BadGuy("Magneto", 100));
badguys.Add( new BadGuy("Pip", 5));
badguys.Sort();
foreach(BadGuy guy in badguys)
{
print (guy.name + " " + guy.power);
}
//This clears out the list so that it is
//empty.
badguys.Clear();
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SomeOtherClass : MonoBehaviour
{
void Start ()
{
//This is how you create a Dictionary. Notice how this takes
//two generic terms. In this case you are using a string and a
//BadGuy as your two values.
Dictionary<string, BadGuy> badguys = new Dictionary<string, BadGuy>();
BadGuy bg1 = new BadGuy("Harvey", 50);
BadGuy bg2 = new BadGuy("Magneto", 100);
//You can place variables into the Dictionary with the
//Add() method.
badguys.Add("gangster", bg1);
badguys.Add("mutant", bg2);
BadGuy magneto = badguys["mutant"];
BadGuy temp = null;
//This is a safer, but slow, method of accessing
//values in a dictionary.
if(badguys.TryGetValue("birds", out temp))
{
//success!
}
else
{
//failure!
}
}
}
- List跟C++中List相似,而Dictionary是鍵值對的形式,跟map相似,以上是相應的使用範例
協程(Coroutines)
using UnityEngine;
using System.Collections;
public class CoroutinesExample : MonoBehaviour
{
public float smoothing = 1f;
public Transform target;
void Start ()
{
StartCoroutine(MyCoroutine(target));
}
IEnumerator MyCoroutine (Transform target)
{
while(Vector3.Distance(transform.position, target.position) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);
yield return null;
}
print("Reached the target.");
yield return new WaitForSeconds(3f);
print("MyCoroutine is now finished.");
}
}
- StartCoroutine開啓協程
- StopCoroutine停止協程
- 協程方法要返回IEnumerator接口類型
- yield跟return類似,不過是等待一定延遲後繼續執行
四元數(quaternion)
Quaternion是Unity中處理旋轉的常用類,下面是一些代碼例子
using UnityEngine;
using System.Collections;
public class GravityScript : MonoBehaviour
{
public Transform target;
void Update ()
{
Vector3 relativePos = (target.position + new Vector3(0, 1.5f, 0)) - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePos);
Quaternion current = transform.localRotation;
transform.localRotation = Quaternion.Slerp(current, rotation, Time.deltaTime);
transform.Translate(0, 0, 3 * Time.deltaTime);
}
}
- Quaternion.LookRotation得出朝向目標的旋轉
- Quaternion.Slerp現有旋轉角度轉到目標角度取插值,使平滑
Delegate
delegate就是方法的容器
using UnityEngine;
using System.Collections;
public class DelegateScript : MonoBehaviour
{
delegate void MyDelegate(int num);
MyDelegate myDelegate;
void Start ()
{
myDelegate = PrintNum;
myDelegate(50);
myDelegate = DoubleNum;
myDelegate(50);
}
void PrintNum(int num)
{
print ("Print Num: " + num);
}
void DoubleNum(int num)
{
print ("Double Num: " + num * 2);
}
}
using UnityEngine;
using System.Collections;
public class MulticastScript : MonoBehaviour
{
delegate void MultiDelegate();
MultiDelegate myMultiDelegate;
void Start ()
{
myMultiDelegate += PowerUp;
myMultiDelegate += TurnRed;
if(myMultiDelegate != null)
{
myMultiDelegate();
}
}
void PowerUp()
{
print ("Orb is powering up!");
}
void TurnRed()
{
renderer.material.color = Color.red;
}
}
- delegate容器可以存一個或多個同一類型的方法,方便同時調用不同的方法,使用之前最好判斷下是否爲null
Attributes
using UnityEngine;
using System.Collections;
public class SpinScript : MonoBehaviour
{
[Range(-100, 100)] public int speed = 0;
void Update ()
{
transform.Rotate(new Vector3(0, speed * Time.deltaTime, 0));
}
}
- 此處的attribute就是限定了speed的範圍,在inspector界面也只能在此範圍內調節,更多attribute請參考官方文檔
事件(Events)
events也是主要靠delegate實現委託的,不過添加event關鍵字增強了代碼的安全性,使其他類只能訂閱或者取消訂閱事件,而不能執行或者複寫事件
using UnityEngine;
using System.Collections;
public class EventManager : MonoBehaviour
{
public delegate void ClickAction();
public static event ClickAction OnClicked;
void OnGUI()
{
if(GUI.Button(new Rect(Screen.width / 2 - 50, 5, 100, 30), "Click"))
{
if(OnClicked != null)
OnClicked();
}
}
}
using UnityEngine;
using System.Collections;
public class TeleportScript : MonoBehaviour
{
void OnEnable()
{
EventManager.OnClicked += Teleport;
}
void OnDisable()
{
EventManager.OnClicked -= Teleport;
}
void Teleport()
{
Vector3 pos = transform.position;
pos.y = Random.Range(1.0f, 3.0f);
transform.position = pos;
}
}
- 事件推薦設置爲靜態,這樣在其他類中訂閱事件就不必實例化