Unity中级编程
创建属性
从类的外部去访问这个类的成员变量,有两种方法:第一种是通过定义公共变量,第二种是通过使用属性的办法(更佳)
使用属性的办法可以完成公共变量无法实现的操作:
- 通过省略get或set将字段变为只写或只读
- 可以将访问器视为函数,这表示你可以在访问器内部执行其他操作
using UnityEngine;
using System.Collections;
public class Player
{
//成员变量可以称为字段。
private int experience;
//Experience 是一个基本属性
public int Experience
{
get
{
//其他一些代码
return experience;
}
set
{
//其他一些代码
experience = value;
}
}
//Level 是一个将经验值自动转换为玩家等级的属性
public int Level
{
get
{
return experience / 1000;
}
set
{
experience = value * 1000;
}
}
//这是一个自动实现的属性的
//示例
public int Health{ get; set;}
}
using UnityEngine;
using System.Collections;
public class Game : MonoBehaviour
{
void Start ()
{
Player myPlayer = new Player();
//属性可以像变量一样使用
myPlayer.Experience = 5;
int x = myPlayer.Experience;
}
}
三元运算符
using UnityEngine;
using System.Collections;
public class TernaryOperator : MonoBehaviour
{
void Start ()
{
int health = 10;
string message;
//这是一个三元运算的示例,其中根据
//变量“health”选择一条消息。
message = health > 0 ? "Player is Alive" : "Player is Dead";
}
}
静态
包括静态变量、静态方法、静态类。
using UnityEngine;
using System.Collections;
public class Enemy
{
//静态变量是在类的所有实例之间共享的变量。
public static int enemyCount = 0;
public Enemy()
{
//通过递增静态变量了解已创建此类的多少个对象。
enemyCount++;
}
}
using UnityEngine;
using System.Collections;
public class Game
{
void Start ()
{
Enemy enemy1 = new Enemy();
Enemy enemy2 = new Enemy();
Enemy enemy3 = new Enemy();
//可以使用类名和点运算符来访问静态变量。
int x = Enemy.enemyCount;
}
}
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour
{
//静态变量是在类的所有实例之间共享的变量。
public static int playerCount = 0;
void Start()
{
//通过递增静态变量了解已创建此类的多少个对象。
playerCount++;
}
}
using UnityEngine;
using System.Collections;
public class PlayerManager : MonoBehaviour
{
void Start()
{
//可以使用类名和点运算符来访问静态变量。
int x = Player.playerCount;
}
}
using UnityEngine;
using System.Collections;
public static class Utilities
{
//可以在没有类对象的情况下调用静态方法。
//请注意,静态方法无法访问非静态成员变量。
public static int Add(int num1, int num2)
{
return num1 + num2;
}
}
using UnityEngine;
using System.Collections;
public class UtilitiesExample : MonoBehaviour
{
void Start()
{
//可以使用类名和点运算符来访问静态方法。
int x = Utilities.Add (5, 6);
}
}
方法重载
方法重载的匹配方式:
- 首先查找是否有完全匹配的版本;
- 其次查找是否有唯一一个需要最少参数转换量的版本;
- 最后如果没有可能匹配的版本或多个版本所需的转换量相同则报错。
using UnityEngine;
using System.Collections;
public class SomeClass
{
//第一个 Add 方法的签名为“Add(int, int)”。该签名必须具有唯一性。
public int Add(int num1, int num2)
{
return num1 + num2;
}
//第二个 Add 方法的签名为“Add(string, string)”。同样,该签名必须具有唯一性。
public string Add(string str1, string str2)
{
return str1 + str2;
}
}
using UnityEngine;
using System.Collections;
public class SomeOtherClass : MonoBehaviour
{
void Start ()
{
SomeClass myClass = new SomeClass();
//具体调用的 Add 方法将取决于传入的参数。
myClass.Add (1, 2);
myClass.Add ("Hello ", "World");
}
}
通用(泛型)
包括泛型方法和泛型类。
注:可以通过关键词
where
来限制泛型T的类型
using UnityEngine;
using System.Collections;
public class SomeClass
{
//这是一个通用方法。
//注意通用类型“T”。该“T”将在运行时替换为实际类型。
public T GenericMethod<T>(T param)
{
return param;
}
}
using UnityEngine;
using System.Collections;
public class SomeOtherClass : MonoBehaviour
{
void Start ()
{
SomeClass myClass = new SomeClass();
//为了使用此方法,必须告诉此方法用什么类型替换“T”。
myClass.GenericMethod<int>(5);
}
}
using UnityEngine;
using System.Collections;
//这是一个通用类。注意通用类型“T”。
//“T”将被替换为实际类型,同样,
//该类中使用的“T”类型实例也将被替换。
public class GenericClass <T>
{
T item;
public void UpdateItem(T newItem)
{
item = newItem;
}
}
using UnityEngine;
using System.Collections;
public class GenericClassExample : MonoBehaviour
{
void Start ()
{
//为了创建通用类的对象,必须
//指定希望该类具有的类型。
GenericClass<int> myClass = new GenericClass<int>();
myClass.UpdateItem(5);
}
}
继承
注:创建子类实例时自动调用父类的构造函数。可以用base的方法显式地指定调用父类的哪个构造函数。
using UnityEngine;
using System.Collections;
//这是基类,
//也称为父类。
public class Fruit
{
public string color;
//这是 Fruit 类的第一个构造函数,不会被任何派生类继承。
public Fruit()
{
color = "orange";
Debug.Log("1st Fruit Constructor Called");
}
//这是 Fruit 类的第二个构造函数,不会被任何派生类继承。
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;
//这是派生类,也称为子类。
public class Apple : Fruit
{
//这是 Apple 类的第一个构造函数。
//它立即调用父构造函数,甚至在它运行之前调用。
public Apple()
{
//注意 Apple 如何访问公共变量 color,
//该变量是父 Fruit 类的一部分。
color = "red";
Debug.Log("1st Apple Constructor Called");
}
//这是 Apple 类的第二个构造函数。
//它使用“base”关键字指定要调用哪个父构造函数。
public Apple(string newColor) : base(newColor)
{
//请注意,该构造函数不会设置 color,
//因为基构造函数会设置作为参数
//传递的 color。
Debug.Log("2nd Apple Constructor Called");
}
}
using UnityEngine;
using System.Collections;
public class FruitSalad : MonoBehaviour
{
void Start ()
{
//让我们用默认构造函数来说明继承。
Debug.Log("Creating the fruit");
Fruit myFruit = new Fruit();
Debug.Log("Creating the apple");
Apple myApple = new Apple();
//调用 Fruit 类的方法。
myFruit.SayHello();
myFruit.Chop();
//调用 Apple 类的方法。
//注意 Apple 类如何访问Fruit 类的所有公共方法。
myApple.SayHello();
myApple.Chop();
//现在,让我们用读取字符串的构造函数来说明继承。
Debug.Log("Creating the fruit");
myFruit = new Fruit("yellow");
Debug.Log("Creating the apple");
myApple = new Apple("green");
//调用 Fruit 类的方法。
myFruit.SayHello();
myFruit.Chop();
//调用 Apple 类的方法。
//注意 Apple 类如何访问Fruit 类的所有公共方法。
myApple.SayHello();
myApple.Chop();
}
}
多态
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 有自己的 Chop() 和 SayHello() 版本。
//运行脚本时,请注意何时调用Fruit 版本的这些方法以及何时调用Apple 版本的这些方法。
//此示例使用“new”关键字禁止来自 Unity 的警告,同时不覆盖Apple 类中的方法。
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 ()
{
//请注意,这里的变量“myFruit”的类型是
//Fruit,但是被分配了对 Apple 的引用。这是
//由于多态而起作用的。由于 Apple 是 Fruit,
//因此这样是可行的。虽然 Apple 引用存储
//在 Fruit 变量中,但只能像 Fruit 一样使用
Fruit myFruit = new Apple();
myFruit.SayHello();
myFruit.Chop();
//这称为向下转换。Fruit 类型的变量“myFruit”
//实际上包含对 Apple 的引用。因此,
//可以安全地将它转换回 Apple 变量。这使得
//它可以像 Apple 一样使用,而在以前只能像 Fruit
//一样使用。
Apple myApple = (Apple)myFruit;
myApple.SayHello();
myApple.Chop();
}
}
成员隐藏
注:使用
new
关键字进行成员隐藏,这与重载(覆盖)不同。
using UnityEngine;
using System.Collections;
public class Humanoid
{
//Yell 方法的基版本
public void Yell()
{
Debug.Log ("Humanoid version of the Yell() method");
}
}
using UnityEngine;
using System.Collections;
public class Enemy : Humanoid
{
//这会隐藏 Humanoid 版本。
new public void Yell()
{
Debug.Log ("Enemy version of the Yell() method");
}
}
using UnityEngine;
using System.Collections;
public class Orc : Enemy
{
//这会隐藏 Enemy 版本。
new public void Yell()
{
Debug.Log ("Orc version of the Yell() method");
}
}
using UnityEngine;
using System.Collections;
public class WarBand : MonoBehaviour
{
void Start ()
{
Humanoid human = new Humanoid();
Humanoid enemy = new Enemy();
Humanoid orc = new Orc();
//注意每个 Humanoid 变量如何包含对继承层级视图中
//不同类的引用,但每个变量都调用 Humanoid Yell() 方法。
human.Yell();
enemy.Yell();
orc.Yell();
}
}
覆盖
using UnityEngine;
using System.Collections;
public class Fruit
{
public Fruit ()
{
Debug.Log("1st Fruit Constructor Called");
}
//这些方法是虚方法,因此可以在子类中将它们覆盖
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");
}
//这些方法是覆盖方法,因此可以覆盖父类中的任何虚方法。
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();
//请注意,Apple 版本的方法将覆盖 Fruit 版本。
//另外请注意,由于 Apple 版本使用“base”关键字来调用 Fruit 版本,因此两者都被调用。
myApple.SayHello();
myApple.Chop();
//“覆盖”在多态情况下也很有用。
//由于 Fruit 类的方法是“虚”的,而 Apple 类的方法是“覆盖”的,
//因此当我们将 Apple 向上转换为 Fruit 时,
//将使用 Apple 版本的方法。
Fruit myFruit = new Apple();
myFruit.SayHello();
myFruit.Chop();
}
}
接口
定义接口的好处:
- 一个类可以继承多个接口;
- 接口用于跨多个互不相关的类定义通用功能
using UnityEngine;
using System.Collections;
//这是只有一个必需方法的基本接口。
public interface IKillable
{
void Kill();
}
//这是一个通用接口,其中 T 是将由实现类提供的数据类型的占位符。
public interface IDamageable<T>
{
void Damage(T damageTaken);
}
using UnityEngine;
using System.Collections;
public class Avatar : MonoBehaviour, IKillable, IDamageable<float>
{
//IKillable 接口的必需方法
public void Kill()
{
//执行一些有趣操作
}
//IDamageable 接口的必需方法
public void Damage(float damageTaken)
{
//执行一些有趣操作
}
}
列表和字典
- List是一个动态改变大小的数组
- Dictionary是通过一个或多个key访问的值的集合
using UnityEngine;
using System.Collections;
using System; //这允许 IComparable 接口
//这是您将存储在不同集合中的类。
//为了使用集合的 Sort() 方法,此类需要
//实现 IComparable 接口。
public class BadGuy : IComparable<BadGuy>
{
public string name;
public int power;
public BadGuy(string newName, int newPower)
{
name = newName;
power = newPower;
}
//IComparable 接口需要此方法。
public int CompareTo(BadGuy other)
{
if(other == null)
{
return 1;
}
//返回力量差异。
return power - other.power;
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SomeClass : MonoBehaviour
{
void Start ()
{
//这是创建列表的方式。注意如何在
//尖括号 (< >) 中指定类型。
List<BadGuy> badguys = new List<BadGuy>();
//这里将 3 个 BadGuy 添加到列表
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);
}
//这会清除列表,使其为空。
badguys.Clear();
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SomeOtherClass : MonoBehaviour
{
void Start ()
{
//这是创建字典的方式。注意这是如何采用
//两个通用术语的。在此情况中,您将使用字符串和
//BadGuy 作为两个值。
Dictionary<string, BadGuy> badguys = new Dictionary<string, BadGuy>();
BadGuy bg1 = new BadGuy("Harvey", 50);
BadGuy bg2 = new BadGuy("Magneto", 100);
//可以使用 Add() 方法将变量放入字典中。
badguys.Add("gangster", bg1);
badguys.Add("mutant", bg2);
BadGuy magneto = badguys["mutant"];
BadGuy temp = null;
//这是一种访问字典中值的更安全
//但缓慢的方法。
if(badguys.TryGetValue("birds", out temp))
{
//成功!
}
else
{
//失败!
}
}
}
协程
注:与属性结合使用时更具优势
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.");
}
}
点击移动功能
using UnityEngine;
using System.Collections;
public class PropertiesAndCoroutines : MonoBehaviour
{
public float smoothing = 7f;
public Vector3 Target
{
get { return target; }
set
{
target = value;
StopCoroutine("Movement");
StartCoroutine("Movement", target);
}
}
private Vector3 target;
IEnumerator Movement (Vector3 target)
{
while(Vector3.Distance(transform.position, target) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target, smoothing * Time.deltaTime);
yield return null;
}
}
}
using UnityEngine;
using System.Collections;
public class ClickSetPosition : MonoBehaviour
{
public PropertiesAndCoroutines coroutineScript;
void OnMouseDown ()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Physics.Raycast(ray, out hit);
if(hit.collider.gameObject == gameObject)
{
Vector3 newTarget = hit.point + new Vector3(0, 0.5f, 0);
coroutineScript.Target = newTarget;
}
}
}
四元数
四元数是处理旋转的最佳方式,可以避免万向节锁问题。
using UnityEngine;
using System.Collections;
public class MotionScript : MonoBehaviour
{
public float speed = 3f;
void Update ()
{
transform.Translate(-Input.GetAxis("Horizontal") * speed * Time.deltaTime, 0, 0);
}
}
using UnityEngine;
using System.Collections;
public class LookAtScript : MonoBehaviour
{
public Transform target;
void Update ()
{
Vector3 relativePos = target.position - transform.position;
transform.rotation = Quaternion.LookRotation(relativePos);
}
}
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);
}
}
委托
委托有利用动态控制调用哪个函数。
委托还支持多播,即允许单个委托变量同时代表多个方法。
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;
}
}
属性
Range属性
在编辑器中,加上Range
属性的变量将多一个滑动条可更改其值。
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));
}
}
ExecuteInEditMode属性
ExecuteInEditMode
属性使得脚本在非运行模式下即可做出改变(注意:该改变不可逆,是永久性的)
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class ColorScript : MonoBehaviour
{
void Start()
{
renderer.sharedMaterial.color = Color.red;
}
}
事件
使用事件构造广播系统。
如果想创建一个包含多个类的动态方法系统,使用事件变量,而非委托变量。
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;
}
}
using UnityEngine;
using System.Collections;
public class TurnColorScript : MonoBehaviour
{
void OnEnable()
{
EventManager.OnClicked += TurnColor;
}
void OnDisable()
{
EventManager.OnClicked -= TurnColor;
}
void TurnColor()
{
Color col = new Color(Random.value, Random.value, Random.value);
renderer.material.color = col;
}
}