Try   HackMD

Unity課程 Lesson2

課程前言

哈喽~大家好我是Ray~也是本次課程的講師
這是Unity課程的第二堂課
這次課程會教導有關物件導向基礎概念與Unity基礎語法
並且在本次課程中會帶領大家開發出一個可以被鍵盤控制移動
移動時會有移動動畫的人物
並進一步講述偵測系統的製作

本網站是根據學習進度進行編寫
你可以根據個人的能力跳過不必要的章節

C#語法

本課程是授課給有程式基礎的人
如果未曾學過程式可以參考下方網站來學習
https://www.w3schools.com/cs/index.php

新建腳本

在上一堂課當中,我們了解如何進行Unity的基本操作並且成功新增了角色
在遊戲中
這堂課我將教導如何撰寫C#腳本來控制角色
新增腳本方式
1.點選欲新增腳色


2.點擊角色右邊的Inspector下方的Add Component

3.找到New script 並輸入腳本名稱

4.點擊Create and Add
便可以看到我們新建的腳本了喔
這個腳本會被放置在專案的Assets資料夾中
從Unity連點兩下可以透過Visual Studio開啟

基礎框架

打開我們剛剛新建的腳本,裏頭Unity會幫我們先生成好基礎框架
範例程式說明
程式會逐漸拆解

using System.Collections; using System.Collections.Generic; using UnityEngine; public class test : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } }



Using語法

由於Unity包含了非常多的功能,不過並不是任何功能我們功能都會用到
第一行到第三行的using便是在這種環境下問世而生的
我們可以告訴系統要使用什麼樣的內建程式功能

各語言相似功能語法
C++ or C

#include <iostream> using namespace std;

Java

import java.util.Scanner;

python

from abc import cde

物件導向

public class test : MonoBehaviour

她的功能是宣告一個 名稱為 test 的公開類別
而且該類別繼承了MonoBehaviour 的功能

類別?繼承?功能? WHAT IS THIS?!!!
向下是物件導向的相關介紹(這個介紹是給新手快速入門用 用詞較通俗且可能偏離原義)

物件導向是程式設計的方式
有別於初學者常使用的指令導向
物件導向將程式包裝成了如同人與人的交流一般
大家都是人 但是人與人之間都會有相異之處 或類似之處
像是我跟妳都有存款 但是我的存款可能只剩100 妳還有5000
妳跟我可能都會騎車
我會寫程式 但妳可能不會
聽不懂?上實際例子

假設我要開發一個系統
可以記錄100人的存款
然後用兩個function實踐 存款跟提款 某個人的指定金額

常規的指令導向

int individualMoney[100]; //以陣列宣告100個的存款數目 void Withdrawal(int who, int howMuchMoney){ //存款 individualMoney[who] += howMuchMoney; //將金額直接加入該存款裡 } void Deposit(int who, int howMuchMoney){ //提款 if(individualMoney[who] >= howMuchMoney){ //如果她的存款大於提款金額 individualMoney[who] -= howMuchMoney; //進行提款 } }

物件導向(詳細說明請向下看)

class People{ private int Money; public People(int InitMoney){ this.Money = InitMoney; } public void Withdrawal(int howMuchMoney){ //存款 this.Money += howMuchMoney; //將金額直接加入該存款裡 } public void Deposit(int howMuchMoney){ //提款 if(this.Money >= howMuchMoney){ //如果她的存款大於提款金額 this.Money -= howMuchMoney; //進行提款 } } } People[] people = new People[100]; //宣告100個使用People模板的變數名叫people //People[] 表示接下來宣告的東西是個陣列 //People[100] 表示宣告陣列為長度100 值得一提的是這裡只是告知長度 還沒使用建構函數 完全建立 for(int i = 0;i < people.Lengh; i ++){ people[i] = new People(0); } //利用for迴圈 讓每一個people都啟動其建構函數

第一行的class People是什麼呢?
剛剛提到大家都是
class存在的意義就如同模板
模板會將所有有關人的參數定下來

而第二行的int Money便是說明每個人都會有各自的存款

第八行與第十二行便是人都共同會做的事情(function)

第四行看起來非常特別,看起來很function卻少了回傳型態的宣告(像是void表示不回傳 int表示回傳int)
這是建構function 故名思義,這個function僅會在模板把人給打模出來的瞬間被使用
並且不會回傳數值(建構function默認就是不會回傳數值的)

第十九行的

People people[100] = new People(0); for(int i = 0;i < people.Lengh; i ++){ people[i] = new People(0); }

可以理解成這樣

使用People模板而且是建立陣列 該變數名稱為people = new 宣告陣列長度為100 利用for迴圈 讓每一個people都啟動其建構函數

this.Money 中的this.又表示什麼呢?
因為單純寫Money有個問題就是你可能會在其他地方也宣告同名為Money的變數
this.Money 代表的意思是我自己的Money變數

很好?那為何每一個function最前面都有一個public呢? 甚至連變數Money前面也有private
public代表任何人都可以使用我的function 或者讀取該數值
與之相對的是private
private代表只有我自己能使用該function 或者讀取該數值

還是看不懂嗎?我現在加上完整註解

class People{ //宣告一個名為People的模板 private int Money; //這個模板中定義一個數值叫做people 只有我自己的function能讀取或修改這個數值 public People(int InitMoney){ //建構函數 當有個變數被這個模板打印時啟動該function this.Money = InitMoney; //將"我自己"的Money變數變成傳入數值 } public void Withdrawal(int howMuchMoney){ //這是一個任何人都能使用的function 目的是存款 this.Money += howMuchMoney; //將金額直接加入該存款裡 } public void Deposit(int howMuchMoney){ //這是一個任何人都能使用的function 目的是提款 if(this.Money >= howMuchMoney){ //如果她的存款大於提款金額 this.Money -= howMuchMoney; //進行提款 } } } People people[100] = new People(0); 使用People模板 打印100個變數名稱為people的變數 = new 打印時同時激活People模板裏頭的建構函數並且傳數值0進入

說完最基礎的物件導向與類別概念
該是來提到繼承的概念ㄌ

假設Ray跟Shane都是人
但是我會寫程式她不會
她會彈琴我不會
但是我們都會走路
我便會使用繼承概念來開發
繼承便是有兩個模板 一個父模板跟子模板
繼承的話子模板會完全獲得父模板的能力 子模板也可以根據自己的需求再做宣告
like this

class People{//宣告一個名為People的模板 void wake(){ //走路 } } class Ray : People{//宣告一個名為Ray的模板 並且繼承People模板的一切能力 void code(){ //寫程式 } } class Shane : People{//宣告一個名為Shane的模板 並且繼承People模板的一切能力 void piane(){ //彈琴 } }

上面的寫法功能會等於下面的寫法

class Ray{//宣告一個名為Ray的模板 void code(){ //寫程式 } void wake(){ //走路 } } class Shane{//宣告一個名為Shane的模板 void piane(){ //彈琴 } void wake(){ //走路 } }

這便是繼承 使其他物件擁有我的一切屬性

我們回到

public class test : MonoBehaviour

便是宣告一個公開可被其他人讀取得class 名稱為test
並且繼承了MonoBehaviour的功能
值得一提的是MonoBehaviour是Unity幫妳寫好的程式庫
使其能使用許多Unity的程式功能

Start() 和 Update()

void Start() { } void Update() { }

Start()是Unity的默認function 當遊戲一開始的時執行裡頭程式

Update()是每一fps執行一次 (不穩定 畢竟妳的fps不會永遠保持在60 她會浮動)




按鍵偵測與人物移動

using System.Collections; using System.Collections.Generic; using UnityEngine; public class test : MonoBehaviour{ Transform m_transform; //Transform是Unity內建的class 她記錄著玩家的座標 可以透過更改Transform.position更改我的位置 Rigidbody2D m_rigidbody; //Rigidbody是Unity內建的物理系統 , Rigidbody2D.velocity 能更改玩家移動速度 float velocity_x, velocity_y; //宣告變數來告訴系統 我的移動速度應該是多少((見Update() void Start() { m_transform = gameObject.GetComponent<Transform>(); //雖然第七行把m_transform宣告出來 但是系統並不會知道這個Transform是屬於誰的座標 //gameObject默認代表我自己 .GetComponent表示取得我自己的<Transform>物件 這樣便能告訴系統m_transform是我自己的Transform物件 m_rigidbody = gameObject.GetComponent<Rigidbody2D>(); //同上理 告訴系統這是我的Rigidbody2D } void Update() { if(Input.GetKey (KeyCode.W)){ //Unity專有語法 如果滑鼠W按著時為True //Input.GetKey 表示持續按著 //Input.GetKeyDown 表示按鍵按下瞬間 //Input.GetKeyUp 表示按鍵放開瞬間 velocity_y = 1f; //將 velocity_y 變數 轉為 1 }else if(Input.GetKey (KeyCode.S)){ //如果滑鼠S按著時為True velocity_y = -1f; //將 velocity_y 變數 轉為 -1 }else{ velocity_y = 0f; } if(Input.GetKey (KeyCode.D)){ velocity_x = 1f; }else if(Input.GetKey (KeyCode.A)){ velocity_x = -1f; }else{ velocity_x = 0f; } m_rigidbody.velocity = new Vector3(velocity_x, velocity_y, 0f); //將數值轉為Vector3形式並存入m_rigidbody.velocity } }



Unity動畫教學與語法

private Animator m_animator; m_animator = gameObject.GetComponent<Animator>(); m_animator.SetBool("running", false);



Unity雷射偵測

RaycastHit2D hit = Physics2D.Raycast(Vector3 OriginPosition, Vector3 MoveDirection, float lenght, 1 << LayerMask.NameToLayer("可被雷射偵測的層數")); if(hit.collider != null){ //如果雷射有撞到東西 if (hit.collider.gameObject.tag = "xxx"){ //而且那個東西的tag是"xxx" } }



Unity碰撞箱偵測

void OnTriggerEnter2D(Collider2D collision){ //如果有東西進入我的偵測箱裏頭觸發該function if(collision.gameObject.tag == "xxxx"){ 而且該物件的tag為xxx則... // } }



Unity生成物件

public GameObject generatedObject; //宣告被生成物體 Instantiate(generatedObject,Vector3 generatedPosition, Vector3 Rotation, parent.Transform);