Siki_Unity_2-1_API常用方法和类详细讲解(上)
Unity 2-1 API常用方法和类详细讲解(上)
任务1&2:课程前言、学习方法 && 开发环境、查API文档
API: Application Programming Interface 应用程序编程接口
查看Unity文档和API手册:
help->Unity Manual/ Scripting Reference
任务3&4:事件函数 && 事件函数的执行时机
每一个脚本默认都是继承MonoBehaviour的
  MonoBehaviour是继承Behaviour的
  Behaviour是继承Component的
  Component是继承Object的
  因此Script脚本是一个Object
Manual->Scripting->Scripting Overview->Execution Order of Event Functions

Reset() 只会在Editor模式下触发,运行时或build完以后就不会触发了
  当在Editor模式下(运行的时候不触发),当script第一次attached到GameObject时/
  当GameObject的Reset按钮被按下时会触发。
  用于initialise the script's properties
Awake() 当场景开始运行时\ 当一个Prefab被实例化出来的时候会触发
   Awake总是在任何Start()之前被触发
   If a GameObject is inactive during start up, Awake is not called until it's made active
OnEnable() 当场景开始运行时\ 当一个Object被enabled时会触发
  前提是这个Object为active时
OnDisable() 当一个Object becomes disabled\inactive时
OnLevelWasLoaded() is to inform the game that a new level has been loaded
Start() is called before the first frame update (only if the script instance is enabled)
FixedUpdate(), Update(), LateUpdate()
FixedUpdate() 会先调用,之后是Update(),最后是LateUpdate()
  FixedUpdate是每秒固定调用N次
  Update和LateUpdate是每帧调用一次 -- 与运行环境有关
FixedUpdate() all physics calculations and updates occur immediately after FixedUpdate. When applying 
  movement calculations inside FixedUpdate, you don't need to multiply your values by Time.deltaTime 
  since FixedUpdate is called on a reliable timer, which is independent of the frame rate.
  一般把与物理有关的更新写在FixedUpdate里 -- 可以保证物体的运动是平滑的,
Update() is called once per frame.
LateUpdate() is called once per frame, after Update has finished. A common use for LateUpdate would 
  be a following third-person camera: make the character move and turn inside Update, you can perform 
  all camera movement and rotation calculations in LateUpdate -- to ensure that the character has moved 
  completely before the camera tracks its position.
OnTrigger...()
OnCollision...()
yield WaitForFixedUpdate() 可以在FixedUpdate()中调用yield WaitForFixedUpdate()
OnMouse...() 是Input events
Scene Rendering之后详叙
OnApplicationPause() is called after the frame where the pause occurs but issues another frame before 
  actually pausing. One extra frame will be issued after OnApplicationPause is called to allow the game to 
  show graphics that indicate the paused state. 点击Unity中间暂停按钮的时候会调用。
OnApplicationQuit() 退出游戏的时候触发。
  It is called before the application is quit; In the editor, it is called when the user stops playmode.
OnDisable() is called when the behaviour becomes disabled or inactive.
OnDestroy() is called after all frame updates for the last frame of the object's existence

Update和LateUpdate的数量是一样的,而与FixedUpdate的数量不同
Start在第一个xxxUpdate之前
OnApplicationPause在这里出现了,点击Unity中的暂停键也没反应,不知为何
uncheck gameoject的勾选框,执行了OnDisable(),重新勾选,执行了OnEnable()
禁用的时候所有的xxxUpdate也不会进行更新了

运行结束后:OnApplicationQuit-->OnDisable->OnDestroy
任务5:Time类中的静态变量
Time类
Time.captureFramerate -- 设置帧的速率,进而进行屏幕截图
Time.deltaTime -- 当前帧所用时间(单位秒)
  经常在Update中使用:* Time.deltaTime 表示一秒
Time.fixedDeltaTime -- 和FixedUpdate()与Update()的区别相似
Time.smoothDeltaTime -- 平滑,不会变化过大的deltaTime
Time.time -- 从游戏开始(start of the game)到当前帧开始(the time at the beginning of this frame)所用的时间
Time.fixedTime -- 从游戏开始到最新一个FixedUpdate()开始(the latest FixedUpdate has started)所用的时间
Time.unscaledTime
Time.realtimeSinceStartup -- the real time in seconds since the game started, not affected by Time.timeScale.
  It keeps increasing while the player is paused. Using realtimeSinceStartup is useful when you wanna pause the 
  game by setting Time.timeScale=0, but still want to be able to measure time somehow.
Time.timeSinceLevelLoad -- 从the last level has been loaded到this frame has started所用的时间
Time.frameCount -- the total number of frames that have passed
Time.timeScale -- the scale at which the time is passing, 可以用于调整游戏的播放速率,默认为1
注:
Time.time≈Time.fixedTime 游戏运行时间
Time.unscaledTime, Time.realtimeSinceStartup 不受timeScale影响 (不受游戏暂停影响), 但是time会被影响
Time.time, Time.unscaledTime 在FixedUpdate()中调用时,会返回对应的fixed的值fixedTime\fixedUnscaledTime



time = timeSinceLevelLoad : start~~the beginning of this frame
fixedTime略大于time : FixedUpdate在Update之后
fixedDeltaTime = 0.02 -- 每秒50帧,Edit->Project Setting->Time->Time Manager中可以设置
由于fixedDeltaTime设置为0.02,则deltaTime会在0.02上下波动
smoothDeltaTime的波动比deltaTime的平滑很多
timeScale = 1(默认值)
任务6:Time.deltaTime和Time.realtimeSinceStartup的使用
Time.deltaTime的使用 -- 常用于控制物体的移动
控制一个物体的移动
public Transform cube;
cube.Translate(Vector3.forward);
-- cube物体会移动很快:调用一次移动一米,每秒调用很多次
-> cube.Translate(Vector3.forward * 3 * Time.deltaTime); // 速度*当前帧所用时间=当前帧运行距离:3m/s
Time.timeScale = 1f;  // 设置timeScale
--> Time.deltaTime会乘以timeScale
timeScale=0.5f; 进行慢动作回放/ timeScale=2f; 两倍播放速度等等
若timeScale=0; cube就不会移动了,常用timeScale=0来暂停游戏,前提是所有物体的控制都用了deltaTime
Time.realtimeSinceStartup的使用 -- 常用于性能测试
比如要测试某个方法、某种处理方式是否耗费性能
例:测试Method1()和Method2()哪一个更耗费性能
Method1() {
    int i =  + ;
}
Methods2() {
    int i =  * ;
}
    float time1 = Time.realtimeSinceStartup;
    int runCount = ;
    for(int i=; i<runCount; i++) {
        Method1();
    }
    float times2 = Time.realtimeSinceStartup;
    Debug.Log(time2 - time1);
    // 把Method2换成Method1再执行一次,比较两次的输出即可
一般还有一个对照组:Method0() {} 方法体为空
任务7&8:创建游戏物体的三种方法 && 并添加组件
GameObject类
与实例化有关的方法:
Instantiate()
CreatePrimitive()
1. 第一种创建物体的方法 new GameObject();
// 创建游戏物体
// 创建一个名为New Game Object的Empty Object, with Component Transform
new GameObject();
// 名为Cube
GameObject cube = new GameObject("Cube");
cube.AddComponent<Rigidbody>();

这个方法一般用来创建空物体,用于放置其他物体
2. 第二种创建物体的方法 GameObject.Instantiate();
通常为初始化一个Prefab或复制另外一个游戏物体
使用Instantiate()创建的物体的名字默认跟上(Clone)
// 创建游戏物体 GameObject.Instantiate()
// 根据Prefab
// 根据另外一个游戏物体
// prefab在Unity中可以被prefab或gameObject赋值
GameObject.Instantiate(prefab);
3. 第三种创建物体的方法 GameObject.CreatePrimitive();
通常用于创建一些原始的形状,如Create->3D Object->Cube/Sphere/Capsule/Cylinder等等
GameObject.CreatePrimitive(PrimitiveType.Plane);
GameObject sphere =  GameObject.CreatePrimitive(PrimitiveType.Sphere);
任务9:启用和禁用游戏物体
tag -- 属性,多用于判断游戏物体的分类
transform -- 所有GameObject都有一个transform组件,且不能被删除
activeSelf -- 是否locally处于激活状态
activeInHierarchy -- Is the GameObejct active in the scene? -- 多用于判断该物体是否激活
  二者区别:B是A的子物体,禁用A,则A.activeInHierarchy=false; B.activeInHierarchy=false
  但是A.activeSelf=false; B.activeSelf=true; (在Inspector中B是checked状态)
gameObject.SetActive(true/false); -- 设置gameObject的activeInHierarchy状态
禁用了游戏物体后,它还是在内存中存储的,因此可以获取对应的属性
只不过物体不会在Scene中显示,Update()也不再执行,不需要渲染,不耗费性能了
任务10&11&12&13&14:GameObject、Component、和Object的关系
&& UnityEngine下Object && GameObject拥有的静态方法
&& 游戏物体间消息的发送和接收
&& 得到组件的方法
一个游戏(Game)由多个场景(Scene)组成
一个场景(Scene)由多个游戏物体(GameObject)组成
一个游戏物体(GameObject)由多个组件(Component)组成
C#的共同基类:System.Object
Unity中类的共同基类:UnityEngine.Object
Component和GameObject都是继承自UnityEngine.Object的,他们有许多相同的属性
Object中的属性:
name -- 通过GameObject或通过它的Component获取到的name都是GameObject的name
Object中的方法:
Destroy() -- Removes a gameobject/ component/ asset.
  调用后,游戏物体在场景中会立刻被删除,但是还没被回收;在Unity中会把将要销毁的游戏物体放入垃圾池统一管理
  Destroy(gameObject);
  Destroy(this); // script
  Destroy(rigidbody); // remove component
  Destroy(gameObject, 5);  // removes gameObject in 5 seconds
DestroyImmediate() -- Destroy the object immediately, strongly recommended to use Destroy instead.
  该方法会立刻删除游戏物体,有可能导致空指针
DontDestroyOnLoad(gameObject) -- A场景跳转到B场景时,可以调用DontDestroyOnLoad()
  一般而言跳转后,A场景中的所有游戏物体都会被销毁,调用后gameObject不会被销毁
  一般用于设置一个两个场景共享的游戏物体
FindObject(s)OfType -- 根据组件类型进行全局查找,找到符合type类型的组件(返回找到的第一个)
  注意,只会查找激活了的游戏物体
  Light light = FindObjectOfType<Light>();
  light.enabled = false;
    -- 对于一个组件,使用.enabled来控制激活,对于一个对象,使用SetActive()来控制。
  Transform[] transforms = FindObjectsOfType<Transform>();
  foreach(Transform fransform in transforms) { ... }
Instantiate()
  Instantiate(Object original);
  Instantiate(Object original, Transform parent);  // 设置为parent的子对象
  Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);  // 是否用worldSpace -- 位置为局部或是世界
  Instantiate(Object original, Vector3 position, Quaternion rotation);
  Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);
GameObject的变量:
activeInHierarchy; activeSelf; isStatic; layer; scene; tag; transform
GameObject的静态方法:
CreatePrimitive
Find -- 对全局进行遍历查找;耗费性能,尽量不要在 Update中调用,少在Start中调用
  GameObject gameObject = GameObject.Find("Main Camera");
  gameObject.SetActive(false);
FindGameObjectWithTag -- 返回游戏物体的数组;在标签范围内进行查找 -- 较快
  GameObject[] gameObjects = GameObject.FindGameObjectsWithTag("Main Camera");
FindWithTag -- 返回查找到的第一个符合条件的游戏物体
注意:一般而言,需要进行null的判断,因为有可能出现tag错误或无激活物体等情况
GameObject的方法:通过对象进行调用
AddComponent
GetComponent -- 如果游戏物体上有多个相关组件,则会返回得到的第一个 
GetComponents
GetComponentInChildren -- 该游戏物体以及所有子物体
GetComponentsInChildren
GetComponentInParent -- 该游戏物体以及所有直系祖先
GetComponentsInParent
BroadcastMessage(string methodName, object paramter=null, SendMessageOptions opts) -- 广播消息
  calls the method named methodName on every script in this game object or any of its children
  搜索自身和子物体,若有methodName()则调用
  优点:减少游戏物体之间的耦合性,因为若是使用SendMsg则需要先得到游戏物体的指向
  SendMessageOptions.DontRequireReceiver:即使没有Receiver也不会报错
  SendMessageOptions.RequireReceiver: 若是没有Receiver,会报错
target.BroadcastMessage("Attack", null, SendMessageOptions.DontRequireReceiver);
target.BroadcastMessage("Attack", null, SendMessageOptions.RequireReceiver);
SendMessage(string methodName, object value=null, SendMessageOptions opts)
  -- 针对某个对象发送消息,但不会对其子对象发送;对该对象中所有methodName()进行调用
  public GameObject target;
  target.SendMessage("Attack");
SendMessageUpwards(...) -- 针对该对象以及其所有ancestors(父亲&爷爷等),与BroadcastMessage相反
  注意:父亲只会有一个,其他的是叔叔
  target.SendMessageUpwards("Attack", null);
CompareTag -- 判断两个物体的标签是否相同
SetActive -- 激活/禁用游戏物体
注意:
  对于BroadcastMessage, SendMessage, SendMessageUpwards, CompareTag,
  GetComponent(s), GetComponent(s)InChildren, GetComponent(s)InParent,
  在对组件Component进行这些操作时,就是在对组件所在物体进行相应操作
Component组件:
Transform
Rigidbody
Mesh Renderer
MeshFilter
Collider
NavemeshAgent
Animation
Animator
自定义脚本 .cs
任务15&16:MonoBehaviour简介
&& 17:Invoke的使用
&& 18&19&20:协程的执行
&& 21:鼠标相关事件函数
MonoBehaviour中大多数都是Message事件,由Unity自动调用
变量:
runInEditMode:By default, script components are only executed in play mode. By setting this property, the MonoBehaviour will have   its callback functions executed while the Editor is not in playmode.
  控制脚本在EditMode运行
对应:[ExecuteInEditMode]
useGUILayout:略
继承的变量:
enabled:禁用/ 激活该脚本
  注:禁用脚本是指禁用了脚本的Update方法,因此如果某脚本没有写Update(),则也不会出现勾选的框
isActiveAndEnabled:该脚本是否被激活(activeInHierarchy用来判断物体时候被激活)
gameObject:该脚本所在的游戏物体
tag, transform, name -- 脚本所在的游戏物体的对应属性
hideFlags:
静态方法:
print() -- 是MonoBehaviour的静态方法,因此只能在MonoBehaviour中调用
而Debug.Log()在任何位置都能调用
方法:
任务17:Invoke的使用
Invoke:
CancelInvoke() -- 取消当前MonoBehaviour中(不是当前对象中)所有的Invoke calls
Invoke("methodName", time) -- Invokes the mothod methodName in time seconds
InvokeRepeating("name", time, rate) -- invoke methodName(), then repeatedly every repeatRate seconds
  多用于与CancelInvoke()配合使用
IsInvoking -- 判断是否有Invoke方法pending(正在被调用) -- Invoke后到执行前均返回true
  相当于,Invoke会将某方法加入一个队列,IsInvoking会判断该方法是否在该队列中
  i.e. 在2.0f秒之内再次按下Space时,不会执行Invoke("LaunchProjectile")
void Update() {
    if(Input.GetKeyDown(KeyCode.Space) && !IsInvoking("LaunchProjectile")) {
        Invoke("LaunchProjectile", 2.0f);
    }
}
void LaunchProjectile() {
    Rigidbody instance = Instantiate(projectile);
    instance.velocity = Random.insideUnitSphere * ;
}
任务18&19&20:协程的执行
协程:Coroutine
程序的正常执行顺序:顺序执行,遇到方法则进入方法后顺序执行方法
协程:如果方法是一个协程方法,则可看作是一个新的Thread,可能同步执行,可能有先后,具体看CPU如何调度
协程方法好处:不会阻塞当前方法的运行;可以进行协程方法自身的暂停
StartCoroutine(); StopAllCoroutines(); StopCoroutine()
StartCoroutine(IEnumerator routine)/ StartCoroutine(string methodName, object value=null)
  -- Starts a coroutine
  注:两种写法与StopCoroutine()的两种写法分别对应,不能混淆
  注:通过methodName启动协程时,最多只能传递一个参数
void Start () {
    print("before changing");
    // 开始Coroutine
    StartCoroutine(ChangeColor());
    print("after changing");
}
// Coroutine
// 1. 返回值为IEnumerator
// 2. 返回参数的时候使用yield return
// 3. 使用StartCoroutine(method())进行调用
IEnumerator ChangeColor () {
    // 等待三秒
    yield return new WaitForSeconds(3f);
    print("Color: before changing");
    cube.GetComponent<MeshRenderer>().material.color = Color.blue;
    print("Color: after changing");
    // 返回null
    yield return null;
}
例子:通过协程实现颜色渐变
public class API8CoroutineFadeColor : MonoBehaviour {
    public GameObject cube;
    // private bool started = false;
    // Update is called once per frame
    void Update () {
        // if (Input.GetKeyDown(KeyCode.Space) && !started) {
        if(Input.GetKeyDown(KeyCode.Space)) {
            StartCoroutine(Fade());
        }
    }
    IEnumerator Fade() {
        // started = true;
        for(float i = ; i>=; i -= 0.1f) {
            cube.GetComponent<MeshRenderer>().material.color=new Color(i,i,i);
            yield return new WaitForSeconds(0.1f);
        }
        yield return null;
    }
}
方法2:利用差值 Lerp()
IEnumerator Fade() {
    while(true) {
        Color startColor = cube.GetComponent<MeshRenderer>().material.color;
        Color newColor = Color.Lerp(startColor, Color.red, 0.02f);
        cube.GetComponent<MeshRenderer>().material.color = newColor;
        yield return new WaitForSeconds(0.02f);
        Debug.Log(Mathf.Abs(newColor.g - Color.red.g));
        if(Mathf.Abs(newColor.g - Color.red.g) <= 0.1f) {
            break;
        }
        yield return null;
    }
}
StopAllCoroutines() -- Stops all coroutines running on this behaviour
StopCoroutine(string methodName)/ StopCoroutine(IEnumerator routine)
   -- Stops the first coroutine named methodName, or the coroutine stored in routine running on this behaviour
  使用方法与StartCoroutine对应
例1:通过IEnumerator实现
private IEnumerator coroutine;
void Update () {
    // if (Input.GetKeyDown(KeyCode.Space) && !started) {
    if(Input.GetKeyDown(KeyCode.Space)) {
        coroutine = Fade();
        StartCoroutine(coroutine);
    }
    if (Input.GetKeyDown(KeyCode.S)) {
        StopCoroutine(coroutine);
    }
}
例2:通过直接填写方法名methodName实现 (最多只能传递一个参数)
void Update () {
    if(Input.GetKeyDown(KeyCode.Space)) {
        StartCoroutine("Fade");
    }
    if (Input.GetKeyDown(KeyCode.S)) {
        StopCoroutine("Fade");
    }
}
任务21:鼠标相关事件函数
OnMouseXXX():对象为GUIElement or Collider
  注:一般物体需要添加Collider后才有效
OnMouseDown() -- 鼠标按下的瞬间调用
OnMouseDrag() -- 鼠标按下后进行拖拽的时候
OnMouseEnter() -- 鼠标移入的时候
OnMouseExit() -- 鼠标移出的时候
OnMouseOver() -- 鼠标停留在物体上方的时候
OnMouseUp() -- 鼠标松开的时候(与OnMouseDown相对)
OnMouseUpAsButton() -- 鼠标在之前按下的物体上方松开时 (与OnMouseDown相对)
任务22&23&24:Mathf详解
&25:Lerp 差值运算
&26:MoveTowards 匀速运动
&27:PingPong 往复运动
静态变量:Read-Only
Deg2Rad/ Rad2Deg -- Degree <-> Radian: Deg2Rad=2*PI/360; Rad2Deg=360/(2*PI)
Epsilon -- A tiny positive floating point value: 
  anyValue +/- Epsilon = anyValue; 
  0 +/- Epsilon = Epsilon
Infinity/ NegativeInfinity
PI
静态方法:
Abs() -- absolute value
Approximately() -- Compares two floats and returns true if they're similar (within Eposilon)
  Floating point imprecision makes comparing floats using "=" inaccurate.
Cell() -- 向上取整(进一法),注:负数取大的(Mathf.Cell(-10.7f)=-10)
CeilToInt() -- 返回值为Int类型
Clamp(value, min, max) -- 夹紧
  return min if value<min; return max if value>max; else return value
  常用于TakeDamage()或GainHealth() -- 受伤或补血不用判断hp超出
    hp = Mathf.Clamp(hp, 0, 100);
Clamp01(value) -- 夹紧在0,1之间,等同于Clamp(value, 0, 1);
ClosestPowerOfTwo(int value) -- returns the nearest power of two value
  ClosestPowerOfTwo(30)=32 -- 2^5=32
Exp(float power) -- e to the power of "power"
Pow(float f, float p) -- f to the power of p
Sqrt(f) -- square root of f
DeltaAngle(float current, float target) -- the shortest difference btw two given angles given in degrees 最小夹角
Floor() -- 向下取整(退一法),注:负数取小的(Mathf.Cell(-10.7f)=-11)
FloorToInt()
Max(a,b,c...)/ Max(float[] values)/ Min(...)
MoveTowards() -- Moves a value current towards target
Lerp(float a, float b, float t) --  插值运算
  t为0~1的一个比例,t<=0时返回a,t>=1时返回b
Start() {
    cube.position = new Vector3(, , );
}
Update() {
    float x = cube.position.x;
    float newX = Mathf.Lerp(x, , 0.1f); 
   // 从x~10 -- 从当前位置向目标位置移动10% 
   // 不是匀速运动,移动越来越慢,而且只会无限接近目标位置
    cube.position = new Vector3(newX, , );
}
一般情况会使用Mathf.Lerp(x, 10, Time.deltaTime * speed); -- 若干秒后到达目标位置
LerpAngle() -- 360度内的角度插值
MoveTowards(float current, float target, float maxDelta) -- 匀速运动
  常用newX = MoveTowards(x, target, Time.deltaTime * speed);  
  // 一秒移动speed米,加上负号即为反向移动
PingPong(float t, float b) -- 往复运动
  返回值基于t的值的变化而变化,在0~b之间
  一般会使用PingPong(Time.time * speed, b); 匀速往返运动
  // 如果想要返回a~b怎么办? a + PingPong(speed, b-a); 即可
任务28:Input输入类
&29:(键盘按键 Getkey、
&30:鼠标按键 GetMouseButton、
&31&32:虚拟按键 GetButton/ GetAxis、触摸 GetTouch)
&33:屏幕坐标系 mousePosition
Input -- 键盘事件/鼠标事件/触摸屏事件
静态变量:
acceleration -- 重力感应
gyro -- gyroscope 陀螺仪
ime..... -- input method 输入法相关
anyKey -- 当任意键key or mouse button按下时,即为true
anyKeyDown -- is true when the first frame the user hits any key or mouse button
mousePosition -- 鼠标在屏幕上的像素位置
  左下方为(0, 0, 0),可以为负值,表示在窗口外
  常用于Camera.main.screenPointToRay将位置转化为射线判断鼠标时候点到游戏物体
  详见Camera的方法
静态方法:
键盘按键:GetKey...(KeyCode key)/ GetKey...(string keyName)
GetKey -- 键盘按键,被按下时会一直return true
GetKeyDown/ GetKeyUp -- 按下/ 抬起瞬间
void Update () {
    // GetKey
    if (Input.GetKeyDown(KeyCode.Space)) {
        // 只会在按下瞬间被触发
        print("Get Space Down");
    }
    if (Input.GetKeyUp(KeyCode.Space)) {
        // 只会在抬起瞬间被触发
        print("Get Space Up");
    }
    if (Input.GetKey(KeyCode.Space)) {
        // 按下时一直触发
        print("Get Space");
    }
    if (Input.GetKeyDown("left shift")) {
        print("shift");
    }
鼠标按键 -- GetMouseButton...(int button)
int button: 0 for left button, 1 for right button, 2 for middle button
GetMouseButton()
GetMouseButtonDown()/ GetMouseButtonUp()
虚拟按键 -- GetButton...(String buttonName)
在InputManager中定义的即为虚拟按键
Input Manager设置:Edit -> Project Settings -> Input
  alt button: alternative button, 备用物理按键
好处:1. 一个虚拟按键可以对应多个物理按键
      2. 名字很形象
GetButton()/ GetButtonDown()/ GetButtonUp()
GetAxis(string axisName) -- 返回该轴上的对应值(-1~1)
常用cube.Translate(Vector3.forward * Time.deltaTime * Input.GetAxis("Horizontal"));
返回值是渐变效果的,所以是非匀速运动,更圆滑
GetAxisRaw(...) -- 返回的是-1/0/1 -- 返回值确定,是匀速运动,更灵敏
触摸操作 -- GetTouch
多指触摸 -- 好用的插件 easytouch
任务34&35&36&37:Vector2
Vector2:二维向量,或二维点坐标 -- 上下左右,少了z轴
  Vector2和Vector3是struct,而不是继承MonoBehaviour的类
  就像transform.position.x不能被直接赋值修改一样,是值类型,要整体赋值
  修改的时候需要Vector3 pos = transform.position; pos.x = ...; transform.position = pos;
静态变量:
down/ left/ right/ up/ zero (0,0)/ one (1,1)
变量:
magnitude -- 向量的长度
sqrMagnitude -- 向量长度的平方,即x^2+y^2
  用处:比如在比较两个向量的长度时,使用sqr会减少性能的消耗
normalized -- 该向量的单位化(长度变为1)
x/y/ this[int] -- 向量的各值 this[0] == x; this[1] == y
Constructors:
new Vector2(x,y);
方法:
Equals() -- 两个Vector2值相同的时候,返回true
也可以直接使用 == 来进行比较
Normalize() -- 单位化该向量
Set(newX, newY) -- 或者直接进行赋值也可以
静态方法:
Angle(Vector2 from, Vector2 to) -- 取得两个向量之间的夹角
ClampMagnitude(Vector2 vector, float maxLength) -- 将向量的长度限定在maxLength之内
Distance(Vector2 a, Vector2 b) -- 即(a-b).magnitude
Dot() -- dot product
Lerp(Vector2 a, Vector2 b, float t) -- 差值
LerpUnclamped(Vector2 a, Vector2 b, float t) -- 差值,差别在于t<0或t>1时不会返回a或b,而是继续缩小/ 扩大
Vector2 v1 = new Vector2(, );
Vector2 v2 = new Vector2(, );
print(Vector2.Lerp(v1, v2, 0.5f)); // ((2+3)/2, (2+4)/2)
print(Vector2.LerpUnclamped(v1, v2, 0.5f)); // ((2+3)/2, (2+4)/2) print(Vector2.Lerp(v1, v2, 2f)); // v2
print(Vector2.LerpUnclamped(v1, v2, 2f)); // t大于1时便继续按比例扩大 (4,6)
Max/ Min(Vector2 a, Vector2 b)
MoveTowards(Vector2 a, Vector2 b, float maxDistanceDelta) -- 对x, y分别进行运算(匀速)
任务38:Vector3
静态变量:
相比Vector2而言,Vector3多了z轴,所以多了两个方向 forward和 back
方法:
Cross(Vector3 lhs, Vector3 rhs) -- cross product 叉乘
  叉乘的结果方向由左手法则确定,大拇指为a,食指为b,中指即为结果方向
Distance()
Dot()
Lerp()
LerpUnclamped()
Max/ Min()
MoveTowards()
Normalize()
OrthoNormalize(Vector3 normal, Vector3 tangent) -- Normalize normal, Normalize tangent, makes sure tangent is orthogonal (90 degrees) to normal.
Project(Vector3 v, Vector3 onNormal) -- v对onNormal做投影后的结果向量
ProjectOnPlane(Vector3 v, Vector3 planeNormal) -- v对planeNormal所表示的平面做投影
Reflect(Vector3 inDirection, Vector3 inNormal) -- 返回入射光inDirection对于由inNormal所确定 的镜子平面的反射光
Slerp(Vector3 a, Vector3 b, float t) -- spherically interpolates btw a and b (a and b are treated   as directions rather than points); the direction of the returned vector is interpolated by the   angle and its magnitude is interpolated btw the magnitudes of a and b
  常用于角色的转向
SlerpUnclamped()
任务39:对向量的加减乘除操作
+ :(a+b)即向量相接(可以用来做方向的叠加)
- :(a-b)即b指向a的向量(可以用来指向)
  比如敌人指向Player的向量,就用Player的坐标所在向量-敌人的坐标所在向量)
* :*数字 即magnitude变大,方向不变
/ :/数字 即magnitude变小,方向不变
== :三个值相对相同
任务40&41:使用Random生成随机数
暴击率或是爆率等
静态变量:
value -- 0.0~1.0之间的随机小数 == Range(0, 1f); (0.0 and 1.0 are inclusive)
state -- 当前seed的状态。可以被使用来保存当前随机数生成期的状态。比如使用seed1生成了三个随机数后,使用
  Random.State oldState = Random.state; 保存当前状态,之后若是想要继续在该seed1下生成随机数,
  只需使用Random.state = oldState; 即可继续生成seed1下的第四个随机数了。
rotation -- 随机得到一个朝向
insideUnitCircle -- 按照半径为1,圆心为(0,0)的一个圆,随机生成一个在圆内的二维坐标
  可以赋值给Vector3的变量,只会改变x和y的值
insideUnitSphere -- 按照半径为1,圆心为(0,0)的一个球,随机生成一个在球内的三维坐标
InitState(int seed) -- 初始化状态
  计算机产生的是伪随机数,是通过一系列计算生成的随机数,不是真正的随机数。seed就是生成随机数的算法需要的一个参数。
  该方法目的是给生成随机数的算法(如Range)提供seed。一些情况下,如果要求生成的随机数有一定规律(比如Debug的时候),
  就提供确定的seed;如果要求生成不确定的随机数,可以选择时间作为seed:
    Random.InitState((int)System.DateTime.Now.Ticks);
  注意,不设置InitState()的时候,Unity也会自动设置,保证 每次游戏运行时Range生成的随机数有规律可循
Range(int min, int max) -- 返回min~max之间的整数(不包含max)
Range(float min, float max) -- 返回min~max之间的小数(包含max)
ColorHSV(hueMin, hueMax, saturationMin, saturationMax, valueMin, valueMax, alphaMin, alphaMax) -- Generates a random color from HSV and alpha ranges
任务42&43&44:Quaternion四元数
四元数:w, x, y, z 四个值组成一个四元数,表示一个物体的旋转
欧拉角 Rotation: 三维向量Vector3,通过x, y, z三个值确定一个物体的旋转
  注意,围绕y轴旋转时,是按照世界坐标中的y轴而不是局部坐标系的y轴旋转
  而围绕x或z轴旋转是围绕自身的x或z轴进行旋转
优劣:四元数在进行旋转的计算时更方便,而欧拉角在进行肉眼观察时更直观
没有旋转时,默认的EulerAngles=(0, 0, 0); 默认的Rotation=(0, 0, 0, 1);
围绕x轴旋转90°时,默认的EulerAngles=(90, 0, 0); 默认的Rotation=(0.7, 0, 0, 0.7);
Quaternion中的方法:
Euler() -- 把一个EulerAngle变换成一个四元数
  cube.rotation = Quaternion.Euler(new Vector3(45, 45, 45));
变量.eulerAngles -- 把一个四元数转换成一个欧拉角:
  cube.rotation.eulerAngles;
LookRotation(Vector3 forward, Vector3 upwards=Vector3.up) -- 返回一个Quaternion,表示主
  角的朝向;用来使主角面向敌人,使主角的z轴正方向与传递进来的forward方向保持一致
案例:
  创建Plane作为Floor;
  创建Capsule作为Player,Capsule的z轴方向即Player朝向
  创建Cube作为Player的子物体,放在z轴正方向突出作为眼睛
  创建Cylinder作为Enemy
  Vector3 dir = enemy.position - player.position;  // player指向enemy的向量
  player.rotation = Quaternion.LookRotation(dir);// 将Vector3转换成四元数,并赋值
问题:若Enemy与Player不在同一高度上,则Player会弯腰或抬头,事实上不需要
解决:
  dir.y = 0; 即可
问题:需要让Player慢慢转向而不是一下子就转向
解决:
  使用Quaternion.Slerp()
    (如果是使用Lerp(),则是分别对w,x,y,z四个值分别做差值运算;
    Slerp()是适合做旋转的;
    但是Lerp()旋转较快,looks worse if the rotations are far apart
    可以这么理解,Lerp()是按直线变化过去的,而Slerp()是按圆弧变化过去的)
  Quaternion target = Quaternion.LookRotation(dir);
  player.rotation = Quaternion.Slerp(player.rotation, target, Time.deltaTime*speed);
private bool isRotating = false;
private Vector3 dir;
private float speed = 2f;
private void Update() {
if (Input.GetKeyDown(KeyCode.Space)) {
isRotating = true;
dir = enemy.position - player.position;// player指向enemy的向量
dir.y = ;
}
if (isRotating) {
player.rotation = Quaternion.Slerp(player.rotation,
Quaternion.LookRotation(dir), Time.deltaTime * speed);
// 将Vector3转换成一个四元数,并赋值)
}
}
任务45:Rigidbody刚体中的position和MovePosition控制位置
46:rotation和MoveRotation控制旋转
47:通过AddForce控制运动
控制位置:
position -- If you change the position of a Rigidbody using Rigidbody.position, the transform   will be updated after the next physics simulation step. This is faster than updating the   position using Transform.position as the latter will cause all attached Colliders to   
  recalculate their positions relative to the Rigidbody.
playerRgd.position = playerRgd.position + Vector3.forward * Time.deltaTime;
  If you want to continuously move a rigidbody, use MovePosition() instead, which 
  takes interpolation into account. If you want to teleport a rigidbody from one position to   another, with no intermediate positions being rendered.
MovePosition(Vector3 position) -- Moves the rigidbody to position.
playerRgd.MovePosition(playerRgd.position + Vector3.forward * Time.deltaTime);
控制旋转
rotation -- 和position相似,faster than Transform.rotation, 否则Collider范围发生了改变,需要
  重新进行计算,所以Transform的改变会更耗费性能
MoveRotation() -- 持续转向操作就使用MoveRotation(),一次性转向操作就直接修改rotation
public Rigidbody playerRgd;
public Transform enemy; private bool isRotating = false;
private Vector3 dir;
private float speed = 2f; void Update () {
// playerRgd.position = playerRgd.position+Vector3.forward*Time.deltaTime;
playerRgd.MovePosition(playerRgd.position+Vector3.forward*Time.deltaTime);
if (Input.GetKeyDown(KeyCode.Space)) {
isRotating = true;
dir = enemy.position - playerRgd.position;// player指向enemy的向量
dir.y = ;
}
if (isRotating) {
// 将Vector3转换成一个四元数,并赋值)
playerRgd.rotation = Quaternion.Slerp(playerRgd.rotation,
Quaternion.LookRotation(dir), Time.deltaTime * speed); if (playerRgd.rotation.Equals(dir)) {
isRotating = false;
}
}
}
施加外力:
AddForce() -- 运动为加速/减速过程
注:力太小的时候也可能不运动
任务48:Camera类
默认的Camera的tag="MainCamera"
实例:点击鼠标,用射线的方法判断是否点击了某个GameObject
1. 得到Camera组件
  a. 查找游戏物体:GameObject.Find("MainCamera").GetComponent<Camera>();
  b. 通过静态变量main获取到标签为"MainCamera"的游戏物体的Camera组件:
    Camera.main;
2. 将鼠标点击转化为射线
void Update () {
    // 将屏幕上的点Input.mousePosition转换为一个射线
    Ray ray = camera.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    // 检测是否碰撞到,将结果储存在hit中
    bool isCollidered = Physics.Raycast(ray,out hit);
    if(isCollidered) {
        Debug.Log(hit.collider);
        Debug.DrawRay(ray.origin, ray.direction * , Color.red);
    }
}
任务49:通过Application获取datapath
Application类:
datapath -- 数据路径:在不同平台下返回值不同
  Unity Editor: <path to project folder>/Assets
  Mac player: <path to player app bundle>/Contents
  iOS player: <path to player app bundle>/<AppName.app>/Data
  Win/Linux player: <path to exe_Data folder>/
  ...
persistentDataPath -- A directory path where data expected to be kept between runs can be 
  stored. When publishing on iOS and Android, persistentDataPath will point to a public 
  directory on the device. Files in this location won't be erased with each update of the 
  app. When you build the app, a GUID will be generated based on the Bundle Identifier, 
  and this GUID will be part of persistentDataPath. If you keep the same Bundle Identifier 
  in future versions then the app will continue accessing the same location on every 
  update.
streamingAssetsPath -- 流资源文件:在Project目录下新建一个StreamingAssets文件夹,该文
  件夹在游戏安装好后是单独存在的,而其他文件夹下的资源会统一被打包成资源包(Most 
  assets in Unity are combined into the project when it is built. However, it is sometimes 
  useful to place files into the normal filesystem on the target machine to make them 
  accessible via a pathname. Any files placed in a folder called StreamingAssets in a Unity 
  project will be copied verbatim to a particular folder on the target machine. You can 
  retrieve the folder using the Application.streamingAssetsPath property.)
  On MacOS or Windows: path = Application.dataPath + "/StreamingAssets"
  On iOS: path = Application.dataPath + "/Raw"
  On Android: path = "jar:file://" + Application.dataPath + "!/assets/"
temporaryCachePath -- 临时文件目录
print(Application.dataPath); // 工程路径
print(Application.streamingAssetsPath); // 流文件路径
print(Application.persistentDataPath); // 存储文件路径(持久化)
print(Application.temporaryCachePath); // 临时文件路径

任务50:Application中的常用变量和方法
companyName:Build Settings->Player Settings中的Company Name
identifier:Android上的'package' 包名;Build Settings->Player Settings中的Bundle Identifier
installerName:安装包名
productName:游戏名Build Settings->Player Settings中的Product Name
isEditor:判断是否是在Unity Editor模式下运行
isFocused:判断用户当前是否将焦点处于该游戏
isMobilePlatform:判断当前是否在已知的移动设备上运行
isPlaying:判断是否in any kind of player or in Unity editor's playmode
platform: 返回一个RuntimePlatform类型的值,如RuntimePlatform.WindowsPlayer
  某段代码只在某些特定平台下运行时可使用
runInBackground:default is false;是否允许游戏在后台运行(无法在Android/ iOS上起作用)
version:Build Settings->Player Settings中的Version
OpenURL(string url) -- 在浏览器中打开url
Quit() -- 退出程序,注:在Editor模式下无效,需要设UnityEditor.EditorApplication.isPlayer = false;
if (Input.GetKeyDown(KeyCode.Escape)) {
    if (Application.isEditor) {
        UnityEditor.EditorApplication.isPlaying = false;
    } else {
        Application.Quit(); // 编辑器模式下无效
    }
}
CaptureScreenshot(string screenshotName) -- 游戏截图
  已弃用,现在使用ScreenCapture.CaptureScreenshot(string)
任务51&52:SceneManager
之前加载场景的方法被放在Application中,如Application.LoadLevel(index) -- 否决的
新的类:UnityEngine.SceneManagerment.SceneManager
变量:
sceneCount -- 当前loaded的场景的总数
方法:
LoadScene() -- 加载场景
  public static void LoadScene(int sceneBuildIndex, SceneManagement.LoadSceneMode mode);
  public static void LoadScene(string sceneName, SceneManagement.LoadSceneMode mode);
  LoadSceneMode默认值为LoadSceneMode.Single,表示销毁当前场景的游戏物体,载入新场景|
  LoadSceneMode.Additive表示 Additive loads a Scene which appears in the Hierarchy window while another is active.
LoadSceneAsync() -- 异步加载场景
  LoadScene()在加载场景时如果加载场景时间需要很久,则选择LoadSceneAsync()
  LoadSceneAsync()会返回一个AsyncOperation类的值,存储progress、isDone等信息
  可以在加载页面显示进度条、提示信息等等
CreateScene() -- Create an empty new Scene at runtime with the given name. The new Scene will be opened additively into the hierarchy alongside any existing Scenes that are currently open. For Edit-time creation, use EditorSceneManager.NewScene()
GetActiveScene() -- 返回当前场景的Scene对象
GetSceneAt(index) -- 返回该index(0~sceneCount已加载场景) 的Scene对象(可以获取该Scene的某些属性)
GetSceneByBuildIndex(index) -- 与GetSceneAt的区别在于这里的index是Build Setting中的index,并且该Scene是loaded的状态
GetSceneByName(string name)/ GetSceneByPath(string scenePath) -- 前提也是该Scene是loaded的状态
SetActiveScene(Scene scene) -- Set the scene to be active. Returns false if the Scene is not loaded yet.
  The active Scene is the Scene which will be used as the target for new GameObjects instantiated by scripts.
Event:
activeSceneChanged -- 当activeScene改变时
sceneLoaded -- 当有新scene被载入时
sceneUnloaded -- 当有scene被卸载时
void Start () {
    SceneManager.activeSceneChanged += OnActiveSceneChanged;
    SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnActiveSceneChanged(Scene a, Scene b) {
    print("OnActiveSceneChanged:" + a.name + " to " + b.name);
    // 这里a scene的name无法获取到,因为此时a不是loaded状态
}
private void OnSceneLoaded(Scene a, LoadSceneMode mode) {
    print("OnSceneLoaded:" + a.name + " on " + mode);
    // OnSceneLoaded的执行会在OnActiveSceneChanged之后
}
任务53:课程结束语
其他API的讲解会在Unity API常用方法和类详细讲解(下)中继续讲解。
Siki_Unity_2-1_API常用方法和类详细讲解(上)的更多相关文章
- Siki_Unity_2-1_API常用方法和类详细讲解(下)
		
Unity 2-1 API常用方法和类详细讲解(下) 任务101&102:射线检测 射线origin + direction:射线检测:射线是否碰撞到物体 (物体需要有碰撞器),碰撞物体的信息 ...
 - dart类详细讲解
		
dart 是一个面向对象的语言;面向对象有 (1)继承 (2)封装 (3)多态 dart的所有东西都是对象,所有的对象都是继承与object类 一个类通常是由属性和方法组成的哈: 在dart中如果你要 ...
 - 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
		
前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...
 - sencha touch 入门系列 (七)sencha touch 类系统讲解(上)
		
在mvc结构的基础上,sencha touch又使用了sencha公司为extjs4开发出来的类系统,在面向对象的编程语言中,类是对对象的定义,它描述了对象所包含的大量属性和方法. 跟面向对象语言类似 ...
 - 05-- C++ 类的静态成员详细讲解
		
C++ 类的静态成员详细讲解 在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用.所以在所有对象中都可以共享它.使用静态成员变量实现多个对象之间的数据共享不 ...
 - [VC++]用CTime类得到当前日期、时间、星期,格式化(详细讲解)
		
用CTime类得到当前日期.时间.星期,格式化(详细讲解)2009/05/12 09:48 A.M.① 定义一个CTime类对象 CTime time; ② 得到当前时间 time = CTime:: ...
 - 第四节:详细讲解Java中的类和面向对象思想
		
前言 大家好,给大家带来详细讲解Java中的类和面向对象思想的概述,希望你们喜欢 类和面向对象 在Java中怎样理解对象,创建对象和引用:什么是引用,对于基础学习的同学,要深入了解引用.示例:Stri ...
 - 详细讲解nodejs中使用socket的私聊的方式
		
详细讲解nodejs中使用socket的私聊的方式 在上一次我使用nodejs+express+socketio+mysql搭建聊天室,这基本上就是从socket.io的官网上的一份教程式复制学习,然 ...
 - Android webservice的用法详细讲解
		
Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...
 
随机推荐
- mybatis关联集合List&分布查询传递多列值
			
场景:查询部门的同时,要求查询此部门下的所有用户. 部门(Department) private Integer id; private String departmentName; private ...
 - sqoop 1.99.7 安装及配置
			
一 下载sqoop 1.99.7 http://mirror.bit.edu.cn/apache/sqoop/1.99.7/ 二 解压安装文件 三 配置Sqoop 环境变量 最后把mysql的驱动j ...
 - VS2008 工具栏CMFCToolBar的使用总结(转)
			
(一)自定义工具栏 自定义工具栏,分两种情况:一是直接添加工具栏,并自己绘制图标:二是,添加工具栏,然后与BMP关联,与VC6.0中的自定义彩色工具栏类似. 1. 自绘工具栏 1)添加Toolbar ...
 - 数据库——MySQL——事务
			
数据的事务是指作为单个逻辑工作单元执行的一系列操作,要么完全执行,要么完全不执行. 事务必须具备四个特性: 原子性 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚 一致性 在事务T开始时, ...
 - LightOJ 1203--Guarding Bananas(二维凸包+内角计算)
			
1203 - Guarding Bananas PDF (English) Statistics Forum Time Limit: 3 second(s) Memory Limit: 32 M ...
 - (Oracle)自定义调用AWR&ADDM
			
Oracle->自定义调用AWR&ADDM 需求描述: 前面设定每天自动生成AWR用于提供前一天的数据库状态信息,但因数据库和信息过多不利于直观检查.此次新增ADDM诊断. ADDM诊断 ...
 - IDEA一直提示 错误: 找不到或无法加载主类
			
1.把http://repo1.maven.org/maven2...下载下来2.放到本地Manen仓库archetype 文件夹下3.设置IDEA Maven->Runner 界面的VM Op ...
 - linux下的学习之路下的小困难
			
centos下源码安装python3wget --no-check-certificate https://www.python.org/ftp/python/3.6.2/Python-3.6.2.t ...
 - 4 二维数组中的查找 JavaScript
			
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
 - java 用接口实现加减乘除计算器
			
class Test{ public static void main(String[] args) { fun i=new fun(); jiafa s1=new jiafa(); jianfa s ...