Unity學習筆記(03):Unity C#模塊化開發、基本數據類型、權限修飾符、const & readonly

五、Unity C#編程

遊戲運行模式

  • 程序首先初始化
  • 然後進入一個while(true)循環 檢查是否有消息(包括鼠標事件等)
    若有消息 則處理後 然後計算 繪製場景
    程序處在這麼一個大循環中 不斷檢查是否有事件 若有則處理

幀頻

在while循環中 遊戲會有一秒循環的次數 比如CPU可以一秒繪製80次畫面
人對於畫面的流暢感若到了60 其實已經非常流暢了
幀頻若達到60 則可以不用繼續提升了 若繼續提升 其實也感覺不出來 而且會更加消耗CPU
因此 在繪製的時候可以看時間是否到達 若還沒到 則sleep
1/60=0.0166秒 但比如只有0.01秒就全部處理完了 那麼可以休眠0.0066秒 休眠是爲了節約CPU

因此 在while中 有:

  • 事件處理(包括各種事件)
  • 繪製場景
  • 檢測是否需要休眠(維持幀頻在60左右)

若CPU比較低端 那麼繪製速度會變慢 此時while會不斷地繪製 就不會循環了

FPS

FPS有兩個概念

  • 1、幀頻 (Frames Per Second)
  • 2、第一人稱射擊 (First Person Shoot)

🚩組件的代碼入口

每個節點都有多個組件
因此 組件是經常面對的開發模式

  • 當組件被掛載到節點的時候 會調用組件的一個函數:Awake
  • 當節點在while循環裏 刷新前 會調用Start
  • 當節點在while循環裏 要處理的時候 會調用Update
    每個while循環要處理的時候都會調用每個組件的Update

模塊化開發 & 代碼模塊

實際上 組件成了很多入口的模塊
因此 其實是根據Unity邏輯來開發模塊

給Unity寫代碼 實際上是給Unity寫代碼模塊

開發

先在Project的scripts裏右鍵 -> Create -> C# Script 以創建一個C#代碼塊
在這裏插入圖片描述
此時的代碼是一個組件 組件只有掛載到節點上纔會在Unity的while裏循環被調用
在Hierarchy右鍵 -> Create Empty 創建一個根節點
點擊Add Component添加組件
在這裏插入圖片描述
點擊Scripts 然後選擇腳本即可:
在這裏插入圖片描述
雙擊Project裏的腳本圖標 即可打開Visual Studio編輯器
在這裏插入圖片描述
自動生成的代碼:

using UnityEngine;
using System.Collections;

public class game_scene : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

改一下:

using UnityEngine;
using System.Collections;

// game_scene組件類繼承於Unity提供的基類MonoBehaviour
public class game_scene : MonoBehaviour {

    // 組件實例加載的時候調用
    void Awake()
    {

    }

	// Use this for initialization
    // 組件實例在第一次Update之前調用
	void Start () {
	
	}
	
	// Update is called once per frame
    // 遊戲每次刷新的時候調用
	void Update () {
	
	}

    // 物理引擎每次固定刷新的時候調用(與幀頻無關)
    // 主要用於物理計算
    void FixedUpdate()
    {

    }
}

其中 MonoBehaviour就是Unity的代碼模塊/組件 是組件的基本規則

public class game_scene : MonoBehaviour

game_scene : MonoBehaviour代表game_scene繼承/擴展自MonoBehaviour 所有組件都必須基層與MonoBehavior 必須遵守這個規則
在一個腳本里有且只能有一個類繼承自MonoBehavior 且該類名必須與腳本文件名保持一致

Awake Start Update 和 FixedUpdate都是很重要的接口 他們是在Unity上開發組件代碼的入口
(也類似於其它編程語言或框架的生命週期)

FixedUpdate是Unity提供的固定的機制
Update幀頻是隨時在變的 是浮動的 是實時的 只是維持在60上下
而FixedUpdate是以固定的頻率來調用的 根據當前CPU的幀頻給出一個固定的頻率

在繼承了MonoBehaviour之後 就具備了MonoBehaviour的所有特性
正因如此 當game_scene組件實例化之後加到節點上 Unity的while循環才能調用到諸如Awake Start Update 和 FixedUpdate的基本函數

還有個OnGUI接口
Unity提供了一種GUI(Graphic User Interface 界面)元素的繪製機制 這就是OnGUI
比如在遊戲裏要顯示暱稱等2D文字 Unity提供OnGUI 當繪製3D物體了 要將其變爲2D成像 然後會調用OnGUI接口 此時 即可繪製GUI元素
並不是生成一個GUI節點 而是繪製(draw)出GUI元素
OnGUI在每次的刷新(Update)的時候都會被調用

// 繪製2D元素的入口的時候調用 例如玩家的暱稱和血量條
voidOnGUI()
{

}

一個組件可以掛載多個腳本

使用Debug.Log()打印Debug日誌輸出語句

組件實例化

定義了類只是一個描述 而並不是一個實例 class只是組件的類型
要將類創建爲實例纔行

掛載的並不是類的本身 而是該類的類型的實例 因此 掛載多個並不會衝突

在添加組件的時候 創建了該組件類的對象實例
然後在gameobject對象中保存了該組件的實例

🚩Unity C#基本數據類型

由於是Unity C# 所以和C#其實還是有一些細微差別的

程序包含數據代碼 數據是在運行過程中產生的
這些都是存放在內存中的
內存存儲的最小單位是字節
1字節=8比特(bit)

  • 整數 / 1字節
    • sbyte 帶符號的整數 / 1字節 (需要多拿出1bit來表示符號位)
    • byte 不帶符號的整數 / 1字節
    • short 帶符號的整數 / 2字節
    • ushort 不帶符號的整數 / 2字節
    • int 帶符號的整數 / 4字節
    • uint 不帶符號的整數 / 4字節
    • long 帶符號的整數 / 8字節
    • ulong 不帶符號的整數 / 8字節
  • 浮點數
    • float / 4字節
    • double / 8字節
  • 邏輯
    • true
    • false
  • 字符 / 2字節(16位Unicode字符)
  • 複雜類型的引用變量(用於表示一個變量 指向另一個複雜的對象 保存着對該變量的引用)
    • 若在64位的.net那就是64bit/8字節
    • 若在32位的.net那就是32bit/4字節
    • String字符串也是一個複雜類型
    • 類可能有多個成員 因此叫做複雜對象

Unity C#權限修飾符

  • public 類型成員的修飾符
  • private 類型成員的修飾符(默認)
  • protected 類型成員的修飾符
  • internal 類型成員的修飾符(默認)

用public修飾的類可以在外部使用 用internal修飾的類只能在類的內部使用
類型成員分爲兩個:一個是數據成員(不屬於類 只屬於類的實例) 另一個是類的方法成員
類的實例會擁有數據成員 而每個實例的數據成員不一定相同 但是方法成員必定都是相同的 因爲都是由這個類所產生的
比如 都是人 都會走路跑跳 但是每個人的頭髮或者身體都是其自己的

// 定義一個類
publicclass Person
{
    // 數據成員的定義
    private string name;
    private int age;
    private int gender;

    // 成員函數的定義
    public string eat(int age)
    {
        return "asd";
    }
}

類的實例化

類也需要實例化
類的實例就是類的數據成員所需要的內存體

C#內存模型

C#的內存模型裏面共有四塊區域
分別是:

  • 代碼段 用於存放函數指令常量字符串 所有實例共用
  • 還有個數據段 用於存放全局變量(static靜態變量)
  • 還有個 用於存放new出來的複雜對象 當沒有任何一個引用變量指向該new出的內存時即被回收
  • 還有個 用於存放局部變量 函數返回變量即被回收

堆和棧會在一定情況下被回收 代碼段和數據段是常駐內存不回收的
堆和棧中的是程序運行纔會產生的 代碼段和數據段是程序一加載首先加載的

out關鍵字

out修飾的參數 在函數內可直接修改該變量的值
out可以理解爲將修改後的值帶出來 有些類似於引用和指針的概念

由於是在函數裏另外new了一個對象 因此地址是不同的 帶出來的是在函數內new出來的對象
但若不加out的話 即使在函數內將傳入的對象改了 那麼在外面的也不會受影響
這就是加不加out的區別

Person p=new Person();
p.age=10;
create_person(out p);
Debug.Log(p.age); // 12

void create_person(out Person p)
{
	p=new Person();
	p.age=12;
}

繼承

在C#中 使用:來繼承
比如 Son繼承Parent

public class Son : Parent
{

}

在繼承的時候 若基類/父類爲internal 那麼子類也必須爲internal
若基類/父類爲public 那麼子類可以爲public 也可以爲internal

調用順序

this.xxx()調用的是自己的函數
繼承後 子類成員函數調用時的查找方式是先從當前類中找
若找到則調用自己的 若未找到 則往基類找 直到找到爲止

base關鍵字

若當前類和基類有同名函數 那麼base.xxx()調用的是基類的函數
base只能在類的內部使用

虛函數

爲同時管理多個成員 提出了虛函數的概念

  • 基類的引用變量保存子類的實例
  • 爲方便管理 需在基類定義幾個接口以統一管理
    基類定義幾個函數接口 子類自己重載 然後有不同的實現

子類繼承了基類 若調用方法 那麼會調用子類的該方法
此時 若想要調用父類的該同名方法 那麼可以用虛函數(virtual關鍵字)

public virtual void sayHello()
{
	Debug.Log("Hello");
}

virtual表示該函數爲一個虛函數 若遇到該函數爲虛函數 那麼會去基於該實例查找基類是否爲virtual
若爲virtual 則會查找子類是否重載該虛函數
此時 還需要在子類的函數上用override關鍵字顯式地定義重寫 代表重寫了基類的同名虛函數

public override void sayHello()
{
	Debug.Log("Hello World");
}

若有 則調用子類的函數
——“我不知道外界會傳入什麼樣的實例 但我依然能夠分情況調用不同的函數 執行各自的邏輯處理”

🚩const & readonly

const常量

const常量全局唯一 只有一個
const修飾的是類的成員變量
它是在編譯的時候就確定的常量

readonly只讀

每個實例都會有一個readonly只讀變量
它是在實例化的時候確定的常量
readonly的變量只有一次修改的機會 在對象構造的時候 在構造函數裏修改

🚩名稱空間

在代碼中有可能會使用同樣的名字
若名字出現重複 則會產生衝突

因此 要使用名稱空間 名稱空間帶有自己的烙印
代碼全都寫到該名稱空間中 這樣 及時代碼中出現了同樣的名稱 但名稱空間不同 因此不會出現衝突
namespace關鍵字來定義名稱空間

namespace my_namespace
{
    class Person
    {
        int gender;
    }
}

my_namespace.Person p = new my_namespace.Person();

簡寫 / 省略名稱空間

每次前面都要加上名稱空間 過於麻煩
此時 可以using 名稱空間 往搜索範圍中添加該名稱空間 然後使用時即可省略名稱空間了

流程:首先去當前名稱空間查找 若找不到 去using的名稱空間裏查找 直至找着爲止
在using的全部名稱空間裏都找不到 則會報錯

// 往搜索範圍中添加該名稱空間
using my_namespace;

namespace my_namespace
{
    class Person
    {
        int gender;
    }
}

Person p = new Person();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章