Unity開啟篇(十二) —— Unity腳本簡介(一)

版本記錄

版本號 時間
V1.0 2019.01.26 星期六

前言

Unity是由Unity Technologies開發(fā)的一個讓玩家輕松創(chuàng)建諸如三維視頻游戲、建筑可視化腻惠、實時三維動畫等類型互動內(nèi)容的多平臺的綜合型游戲開發(fā)工具,是一個全面整合的專業(yè)游戲引擎构回。Unity類似于Director,Blender game engine, Virtools 或 Torque Game Builder等利用交互的圖型化開發(fā)環(huán)境為首要方式的軟件。其編輯器運行在Windows 和Mac OS X下瓶埋,可發(fā)布游戲至Windows兼蕊、Mac果正、WiiiPhone忆谓、WebGL(需要HTML5)裆装、Windows phone 8和Android平臺。也可以利用Unity web player插件發(fā)布網(wǎng)頁游戲倡缠,支持Mac和Windows的網(wǎng)頁瀏覽米母。它的網(wǎng)頁播放器也被Mac 所支持。網(wǎng)頁游戲 坦克英雄和手機游戲王者榮耀都是基于它的開發(fā)毡琉。
下面我們就一起開啟Unity之旅铁瞒。感興趣的看下面幾篇文章。
1. Unity開啟篇(一) —— Unity界面及創(chuàng)建第一個簡單的游戲 (一)
2. Unity開啟篇(二) —— Unity界面及創(chuàng)建第一個簡單的游戲 (二)
3. Unity開啟篇(三) —— 一款簡單射擊游戲示例 (一)
4. Unity開啟篇(四) —— 一款簡單射擊游戲示例 (二)
5. Unity開啟篇(五) —— 一款簡單射擊游戲示例 (三)
6. Unity開啟篇(六) —— Unity動畫簡介 (一)
7. Unity開啟篇(七) —— Unity動畫簡介 (二)
8. Unity開啟篇(八) —— Unity聲音簡介(一)
9. Unity開啟篇(九) —— Unity聲音簡介(二)
10. Unity開啟篇(十) —— Unity粒子系統(tǒng)簡介(一)
11. Unity開啟篇(十一) —— Unity粒子系統(tǒng)簡介(二)

開始

在本教程中桅滋,通過創(chuàng)建一個經(jīng)典的競技場射擊游戲來學(xué)習(xí)Unity中腳本的基礎(chǔ)知識慧耍。

Unity的大部分功能都在于其豐富的腳本語言C#身辨。 您可以使用它來處理用戶輸入,操縱場景中的對象芍碧,檢測碰撞煌珊,生成新的游戲?qū)ο笠约霸趫鼍爸車渡涠ㄏ蚬饩€以幫助您的游戲邏輯。 這可能聽起來令人生畏泌豆,但Unity公開了具有完備文檔的API定庵,使這些任務(wù)變得輕而易舉 - 即使對于新手開發(fā)人員也是如此!

在本教程中踪危,您將創(chuàng)建一個自上而下的射擊游戲蔬浙,它使用Unity腳本來處理敵人的產(chǎn)生,玩家控制贞远,射擊射彈以及游戲玩法的其他重要方面畴博。

注意:本教程假設(shè)您已具備使用C#或類似編程語言的經(jīng)驗蓝仲,并了解Unity的界面和工作流程俱病。

本教程是為Unity 5.3或更高版本編寫的。 您可以在此處here下載最新版本的Unity袱结。

雖然Unity也支持UnityScriptBoo亮隙,但C#是大多數(shù)開發(fā)人員傾向于使用的編程語言,并且有充分的理由垢夹。 C#被全球數(shù)百萬開發(fā)人員用于應(yīng)用程序溢吻,Web和游戲開發(fā),并且有大量的信息和教程可以幫助您棚饵。

打開已有的工程煤裙,具體頁面如下所示:

Scene view視圖中查看。 這是一個小型區(qū)域噪漾,將成為游戲的戰(zhàn)場硼砰,相機和燈光。 如果您的布局與屏幕截圖中的布局不同欣硼,請選擇右上角的下拉菜單并將其更改為2 by 3题翰。

沒有英雄角色算什么游戲? 您的第一個任務(wù)是創(chuàng)建一個GameObject來表示場景中的玩家诈胜。


Creating the Player

Hierarchy中豹障,單擊Create按鈕,然后從“3D”部分中選擇Sphere焦匈。 將球體定位在(X:0血公,Y:0.5,Z:0)并將其命名為Player

Unity使用entity-component系統(tǒng)來構(gòu)建其GameObjects缓熟。 這意味著所有GameObject都是組件的容器累魔,可以附加這些組件以賦予其行為和屬性摔笤。 以下是Unity內(nèi)置組件的幾個示例:

  • Tranform:每個GameObject都附帶此組件。 它保存GameObject的位置垦写,旋轉(zhuǎn)和縮放吕世。
  • Box Collider:立方體形狀的對撞機,可用于探測碰撞梯投。
  • Mesh Filter:用于顯示3D模型的網(wǎng)格數(shù)據(jù)命辖。

Player GameObject需要響應(yīng)與場景中其他對象的碰撞。

要實現(xiàn)此目的分蓖,請在Hierarchy window窗口中選擇Player尔艇,然后單擊“檢查器”窗口中的Add `Component按鈕。 在彈出的菜單中選擇Physics> Rigidbody咆疗,這將為播放器添加一個Rigidbody組件漓帚,以便它可以使用Unity的物理引擎母债。

像這樣調(diào)整Rigidody的值:設(shè)置Drag到1午磁,Angular Drag到0并選中Freeze Position旁邊的Y復(fù)選框。

這將確保Player無法上下移動毡们,并且在旋轉(zhuǎn)時不會增加阻尼迅皇。


Creating the Player Movement Script

現(xiàn)在Player已準備就緒,是時候創(chuàng)建將從鍵盤輸入并移動Player的腳本衙熔。

Project window中登颓,單擊Create按鈕并選擇Folder。 將新文件夾命名為Scripts红氯,并在其中創(chuàng)建一個名為Player的子文件夾框咙。
Player文件夾中,單擊Create按鈕并選擇C#Script痢甘。 將新腳本命名為PlayerMovement喇嘱。 序列如下所示:

注意:使用這樣的文件夾可以輕松地按角色組織所有內(nèi)容并減少混亂。 你將為Player制作幾個腳本塞栅,所以給它自己的文件夾是有意義的者铜。

雙擊PlayerMovement.cs腳本。 這將打開您首選的代碼編輯器并加載腳本放椰。 Unity附帶預(yù)裝在所有平臺上的MonoDevelop作烟,Windows用戶可以選擇安裝Visual Studio,并在運行安裝程序時使用它砾医。

本教程假設(shè)您使用的是MonoDevelop拿撩,但Visual Studio用戶應(yīng)該能夠順利進行操作而不會出現(xiàn)任何問題。

一旦您選擇的編輯器打開如蚜,您將看到以下界面:

這是Unity為新腳本生成的默認類压恒。它源自基類MonoBehaviour头滔,它確保此腳本將在游戲循環(huán)中運行,并具有對某些事件作出反應(yīng)的附加功能涎显。如果你來自iOS世界坤检,這個對象相當于UIViewController。當腳本運行時期吓,Unity會按預(yù)定順序調(diào)用多個方法早歇。以下是一些最常見的:

  • Start():在腳本第一次更新之前,將調(diào)用此方法一次讨勤。
  • Update():當游戲運行且腳本啟用時箭跳,此方法將在每一幀被觸發(fā)。
  • OnDestroy():此方法在附加到此腳本的GameObject之前被調(diào)用潭千。
  • OnCollisionEnter():當連接此腳本的碰撞器或剛體觸及另一個碰撞器或剛體時谱姓,此方法被調(diào)用。

有關(guān)事件的完整列表刨晴,請查看Unity’s documentation on MonoBehaviours的文檔屉来。

Start()方法上方添加以下兩行:

public float acceleration;
public float maxSpeed;

它應(yīng)該是這樣的:

這些是公共變量聲明(variable declarations),這意味著這些變量將在Inspector中可見狈癞,并且可以調(diào)整而無需在腳本和編輯器之間來回切換茄靠。

acceleration描述了Player's速度隨著時間的推移而增加的程度。 maxSpeed是“速度限制”蝶桶。

在其下方慨绳,聲明以下變量:

private Rigidbody rigidBody;
private KeyCode[] inputKeys;
private Vector3[] directionsForKeys;

私有變量無法通過Inspector設(shè)置,開發(fā)人員有責(zé)任在適當?shù)臅r候初始化它們真竖。
rigidBody將保存對附加到Player GameObjectRigidbody組件的引用脐雪。

inputKeys是一個用于檢測輸入的密鑰代碼數(shù)組。
directionsForKeys包含一個Vector3變量數(shù)組恢共,它將保存方向數(shù)據(jù)战秋。

用以下內(nèi)容替換Start()方法:

void Start () {
  inputKeys = new KeyCode[] { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };
  directionsForKeys = new Vector3[] { Vector3.forward, Vector3.left, Vector3.back, Vector3.right };
  rigidBody = GetComponent<Rigidbody>();
}

這段代碼鏈接每個密鑰的相應(yīng)方向,例如 按W可將物體向前移動旁振。 最后一行獲取對atttached Rigidbody組件的引用获询,并將其保存在rigidBody變量中供以后使用。

要實際移動Player拐袜,您必須處理鍵盤輸入吉嚣。

Update()重命名為FixedUpdate()并添加以下代碼:

// 1
void FixedUpdate () {
  for (int i = 0; i < inputKeys.Length; i++){
    var key = inputKeys[i];

    // 2
    if(Input.GetKey(key)) {
      // 3
      Vector3 movement = directionsForKeys[i] * acceleration * Time.deltaTime;
    }
  }
}

這里有幾件重要的事情:

  • 1) FixedUpdate()與幀速率無關(guān),在使用Rigidbodies時應(yīng)該使用蹬铺。此方法將以恒定間隔觸發(fā)尝哆,而不是盡可能快地運行。
  • 2) 此循環(huán)檢查是否按下了任何輸入鍵甜攀。
  • 3) 獲取按下的鍵的方向秋泄,將其乘以加速度和完成最后一幀所花費的秒數(shù)琐馆。這將生成一個方向向量(X,Y和Z軸上的速度)恒序,用于移動Player對象瘦麸。

如果您不熟悉游戲編程,您可能會問自己為什么必須乘以Time.deltaTime歧胁。當游戲運行時滋饲,幀速率(或每秒幀數(shù))將根據(jù)硬件和它所承受的壓力而變化,這可能導(dǎo)致在強大的機器上發(fā)生很快喊巍,而在較弱的機器上發(fā)生太慢可能導(dǎo)致不期望的行為屠缭。一般規(guī)則是當你每個(固定)幀執(zhí)行一個動作時,你需要乘以Time.deltaTime崭参。

FixedUpdate()下面添加以下方法:

void movePlayer(Vector3 movement) {
  if(rigidBody.velocity.magnitude * acceleration > maxSpeed) {
    rigidBody.AddForce(movement * -1);
  } else {
    rigidBody.AddForce(movement);
  }
}

上述方法對ridigbody施加力呵曹,使其移動。 如果當前速度超過maxSpeed何暮,則力量向相反方向移動以減慢玩家的速度奄喂,并有效地限制最大速度。

FixedUpdate()中郭卫,在if語句的右括號之前砍聊,添加以下行:

movePlayer(movement);

很好背稼! 保存此腳本并返回Unity編輯器贰军。 在Project window中,將PlayerMovement腳本拖動到“層次結(jié)構(gòu)”內(nèi)的Player上蟹肘。

GameObject添加腳本會創(chuàng)建組件的實例词疼,這意味著將為您附加的GameObject執(zhí)行所有代碼。

使用InspectorAcceleration設(shè)置為625帘腹,將Max Speed設(shè)置為4375

運行場景并使用WASD鍵移動Player

只有幾行代碼贰盗,這是一個非常好的結(jié)果!

然而阳欲,有一個明顯的問題 - 玩家可以快速移出視線舵盈。


Creating the Camera Script

Scripts文件夾中,創(chuàng)建一個名為CameraRig的新腳本球化,并將其附加到Main Camera秽晚。 需要一些幫助來弄清楚步驟? 您可以查看下面的提示筒愚。

選擇Scripts文件夾后赴蝇,單擊項目瀏覽器(Project Browser)中的Create按鈕,然后選擇C#Script巢掺。 將新腳本命名為CameraRig句伶。 最后劲蜻,將其拖放到Main Camera對象上,如下所示:

現(xiàn)在考余,在新創(chuàng)建的CameraRig類中先嬉,在Start()方法的正上方創(chuàng)建以下變量:

public float moveSpeed;
public GameObject target;

private Transform rigTransform;

您可能已經(jīng)猜到了,moveSpeed是攝像機跟隨目標的速度 - 可以是場景中的任何游戲?qū)ο蟆?/p>

Start()內(nèi)楚堤,添加以下行:

rigTransform = this.transform.parent;

此代碼獲取對場景層次結(jié)構(gòu)中父Camera對象的變換的引用坝初。 場景中的每個對象都有一個變換Transform,它描述了對象的位置钾军,旋轉(zhuǎn)和縮放鳄袍。

在同一個腳本中,添加以下方法:

void FixedUpdate () {
  if(target == null){
    return;
  }

  rigTransform.position = Vector3.Lerp(rigTransform.position, target.transform.position, 
    Time.deltaTime * moveSpeed);
}

CameraRig移動代碼比PlayerMovement中的移動代碼稍微簡單一些吏恭。 這是因為你不需要Rigidbody拗小;只需在rigTransform和目標的位置之間進行插值即可。

Vector3.Lerp()在空間中取兩個點樱哼,在[0,1]范圍內(nèi)取浮點哀九,它描述了沿兩個端點的點。 左端點為0搅幅,右端點為1阅束。將0.5傳遞給Lerp()將在兩個端點之間準確返回一個點。

這會使rigTransform更接近目標位置并稍微緩和一下茄唐。 簡而言之 - 相機跟隨player息裸。

回到Unity。 確保在層次結(jié)構(gòu)中仍然選擇了Main Camera沪编。 在Inspector中呼盆,將Move Speed設(shè)置為8,將Target設(shè)置為Player

運行游戲并在場景中移動蚁廓;無論走到哪里访圃,攝像機都應(yīng)該順利地跟蹤目標變換。


Creating an Enemy

沒有敵人的射擊游戲很容易被擊敗相嵌,但有點無聊腿时。 通過單擊頂部菜單中的GameObject \ 3D Object \ Cube創(chuàng)建敵人立方體。 將您的Cube重命名為Enemy并添加Rigidbody組件饭宾。

Inspector中批糟,首先將CubeTransform設(shè)置為(0,0.5,4)。 在Rigidbody組件的Constraints部分中捏雌,選中Freeze Position類別中的Y復(fù)選框跃赚。

優(yōu)秀 - 現(xiàn)在讓你的敵人以威脅的方式四處走動。 在Scripts文件夾中創(chuàng)建一個名為Enemy的腳本。 您現(xiàn)在應(yīng)該成為專家满败,但如果沒有净嘀,請查看教程前面的說明以供參考厢漩。

接下來,在類中添加以下公共變量:

public float moveSpeed;
public int health;
public int damage;
public Transform targetTransform;

您可能無需太多困難就能弄清楚這些變量代表什么。 您之前使用moveSpeed創(chuàng)建了攝像機裝備涯曲,它在此處具有相同的效果绰沥。 healthdamage有助于確定敵人何時死亡以及他們的死亡會對Player造成多大傷害。 最后弧哎,targetTransform引用了Player的變換寻拂。

說到玩家梦染,你需要創(chuàng)建一個類來表示敵人想要摧毀的所有玩家。

在項目瀏覽器中朴皆,選擇Player文件夾并創(chuàng)建一個名為Player的新腳本帕识;此腳本將對碰撞做出反應(yīng)并跟蹤玩家的健康狀況。 雙擊腳本進行編輯遂铡。

添加以下公共變量以存儲Player's的生命值:

public int health = 3;

這為健康狀況提供了默認值肮疗,但也可以在檢查器中進行修改。

要處理碰撞扒接,請?zhí)砑右韵路椒ǎ?/p>

void collidedWithEnemy(Enemy enemy) {
  // Enemy attack code
  if(health <= 0) {
    // Todo 
  }
}

void OnCollisionEnter (Collision col) {
    Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();
    collidedWithEnemy(enemy);
}

當兩個帶有碰撞器的剛體接觸時伪货,OnCollisionEnter()會觸發(fā)。 Collision參數(shù)包含有關(guān)接觸點和碰撞速度等信息钾怔。 在這種情況下碱呼,您只對碰撞對象的Enemy組件感興趣,因此您可以調(diào)用collidedWithEnemy()并執(zhí)行攻擊邏輯 - 您接下來將添加該邏輯宗侦。

切換回Enemy.cs并添加以下方法:

void FixedUpdate () {
  if(targetTransform != null) {
    this.transform.position = Vector3.MoveTowards(this.transform.position, targetTransform.transform.position, Time.deltaTime * moveSpeed);
  }
}

public void TakeDamage(int damage) {
  health -= damage;
  if(health <= 0) {
    Destroy(this.gameObject);
  }
}

public void Attack(Player player) {
  player.health -= this.damage;
  Destroy(this.gameObject);
}

你已經(jīng)熟悉FixedUpdate()了愚臀,不同的是你使用的是MoveTowards()而不是Lerp()。 這是因為敵人應(yīng)該始終以相同的速度移動矾利,而不是在接近目標時輕松進入姑裂。 當敵人被射彈擊中時,會調(diào)用TakeDamage()男旗;當敵人達到0健康時舶斧,它會自我毀滅。Attack()類似 - 它對玩家施加傷害然后敵人摧毀自己察皇。

切換回Player.cs并在collidedWithEnemy()中茴厉,用以下內(nèi)容替換Enemy attack code

enemy.Attack(this);

玩家將受到傷害,敵人將在此過程中自我毀滅。

切換回Unity矾缓。 將Enemy腳本附加到Enemy對象并在Inspector中怀酷,在Enemy上設(shè)置以下值:

  • 1) Move Speed: 5
  • 2) Health: 2
  • 3) Damage: 1
  • 4) Target Transform: Player

到現(xiàn)在為止,您應(yīng)該能夠自己完成所有這些工作而账。 自己嘗試一下胰坟,然后將結(jié)果與下面的GIF進行比較:

在游戲中,與玩家對抗的敵人構(gòu)成有效的敵人攻擊泞辐。 使用Unity的物理檢測碰撞幾乎是一項微不足道的任務(wù)笔横。

最后,將Player腳本附加到層次結(jié)構(gòu)中的Player咐吼。

運行游戲吹缔,并密切關(guān)注控制臺:

當敵人到達玩家時,它會成功執(zhí)行攻擊并將玩家的健康變量減少到2锯茄。但是在控制臺中拋出NullReferenceException厢塘,指向Player腳本:

啊哈 - 玩家不僅可以與敵人發(fā)生碰撞,還可以與游戲世界的其他部分發(fā)生碰撞肌幽,例如競技場晚碾。 這些游戲?qū)ο鬀]有Enemy腳本,因此GetComponent()返回null喂急。

打開Player.cs格嘁。 在OnCollisionEnter()中,在if語句中包裝collidedWithEnemy()

if(enemy) {
  collidedWithEnemy(enemy);
}

這樣就沒有null了廊移。

后記

本篇主要講述了Unity腳本簡介糕簿,感興趣的給個贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市狡孔,隨后出現(xiàn)的幾起案子懂诗,更是在濱河造成了極大的恐慌,老刑警劉巖苗膝,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殃恒,死亡現(xiàn)場離奇詭異,居然都是意外死亡荚醒,警方通過查閱死者的電腦和手機芋类,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來界阁,“玉大人,你說我怎么就攤上這事胖喳∨萸” “怎么了援制?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啊奄。 經(jīng)常有香客問我个绍,道長,這世上最難降的妖魔是什么写穴? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任惰拱,我火速辦了婚禮,結(jié)果婚禮上啊送,老公的妹妹穿的比我還像新娘偿短。我一直安慰自己,他們只是感情好馋没,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布昔逗。 她就那樣靜靜地躺著,像睡著了一般篷朵。 火紅的嫁衣襯著肌膚如雪勾怒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天声旺,我揣著相機與錄音笔链,去河邊找鬼。 笑死腮猖,一個胖子當著我的面吹牛鉴扫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缚够,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼幔妨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谍椅?” 一聲冷哼從身側(cè)響起误堡,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雏吭,沒想到半個月后锁施,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡杖们,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年悉抵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摘完。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡姥饰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出孝治,到底是詐尸還是另有隱情列粪,我是刑警寧澤审磁,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站岂座,受9級特大地震影響态蒂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜费什,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一钾恢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸳址,春花似錦瘩蚪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闻察,卻和暖如春拱礁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辕漂。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工呢灶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钉嘹。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓鸯乃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跋涣。 傳聞我的和親對象是個殘疾皇子缨睡,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容