# 自主學習報告
## 1. 學習動機
現在大部分學生的生活中,遊戲已然是生活的一部分,既然我們那麼喜歡玩遊戲,**不如製作一個屬於自己的遊戲**,遊戲內容也可以依照自己的想法去修改。
## 2. 自主學習計畫
:::warning
| **週次** |**學習內容** |
| ----------------- |:----------------------- |
| 第一週第一節 | 繪製遊戲地圖(草稿) |
| 第一週第二節 | 在Unity上做出地圖模型 |
| 第二週第一節 | 繪製主角模型 |
| 第二週第二節 | 主角移動編寫 |
| 第三週第一節 | 設置鏡頭跟隨主角移動 |
| 第三週第二節 | 主角移動測試
| |1.左右移動是否有動畫|
| |2.移動速度快慢調整 |
| 第四週第一節 | 製作小火球、在地圖上幾處設置小火球、設定攻擊判斷範圍 |
| 第四週第二節 | 火球自動移動程式編寫 |
| 第五週第一節 | 火球碰到物件(主角除外)轉向程式編寫 |
| 第五週第二節 | 遊戲測試 |
||1.火球會在固定處出現|
||2.主角碰到火球會受到傷害|
| 第六週第一節 | 修改bug |
| 第六週第二節 | 製作發射器模型、子彈模型 |
| 第七週第一節 | 製作子彈射出時會造成的傷害判斷範圍、在地圖上幾處設置發射器 |
| 第七週第二節 | 發射器發射(子彈生成)程式編寫 |
| 第八週第一節 | 發射器等時間間隔發射子彈程式編寫 |
| 第八週第二節 | 子彈移動、碰撞到物體後消滅子彈物件程式編寫 |
| 第九週第一節 | 主角血量(回血、扣血)程式編寫 |
| 第九週第二節 | 設置物件當主角碰到時會扣血 |
| 第十週第一節 | 設置物件當主角碰到時會回血 |
| 第十週第二節 | 製作重新開始按鈕 |
| 第十一週第一節 | 製作重新開始按鈕(主角死亡、遊戲通關時跑出) |
| 第十一週第二節 | 製作背景音樂 |
| 第十二週第一節 | 製作背景音樂、套用於遊戲中 |
| 第十二週第二節 | 遊戲測試 |
| |1.各怪物移動、攻擊是否正常|
| |2.動畫是否會正常跑出|
| 第十三週第一節 | 修改bug |
| 第十三週第二節 | 給同學試玩並收取意見 |
| 第十四週第一節 | 統整意見 |
| 第十四週第二節 | 根據同學建議修改遊戲內容 |
| 第十五週第一節 | 根據同學建議修改遊戲內容 |
| 第十五週第二節 | 將這學期收穫內容整理成報告 |
| 第十六週第一節 | 將這學期收穫內容整理成報告 |
| 第十六週第二節 | 將這學期收穫內容整理成報告 |
:::
## 3. 認識Unity
我們在開始設計這些自主學習計畫之前,我們對於Unity可以說是完全陌生的,**<font color="#f00">儘管我們在高一有學習過關於C++的編寫但在Unity中卻是使用C#</font>**,雖然與C++有許多相似處,不過還是因為許久沒複習程式的關係,還有許多Unity實用的程式語法,也需要學習,因此我們在學習初期觀看了許多教學影片也自己體會過了多次的失敗和挫折。
## 4. 主角的繪製以及移動程編寫
### (1)主角繪製
我們運用小畫家進行描繪,再用填
顏色的方式讓角色有2D的感覺
(如圖)

### (2)主角移動程式的編寫
:::success
```
public float moveSpeed = 5f; //public可在Unity中自行調整速度
void Update()
{
if (Input.GetKey(KeyCode.D)) //按下鍵盤D時執行以下程式
{
transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
//朝著+X方向以movespeed速度移動
}
else if (Input.GetKey(KeyCode.A)) //按下鍵盤A時執行以下程式
{
transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
//朝著-X方向以movespeed速度移動
}
if (Input.GetKey(KeyCode.W)) //按下鍵盤W時執行以下程式
{
transform.Translate(0, 10*moveSpeed * Time.deltaTime/7, 0);
//朝著+Y方向以10/7倍的movespeed速度移動
}
if (Input.GetKey(KeyCode.S)) //按下鍵盤S時執行以下程式
{
transform.Translate(0, -moveSpeed * Time.deltaTime, 0);
//朝著-Y方向以movespeed速度移動
}
}
```
:::
### (3)主角鏡頭跟隨
此效果是運用Unity內建功能Cinemachine

添加此功能後將Follow目標設為player

就能實現鏡頭跟隨了
### (4)主角血量程式的編寫
#### (1)主角血條
首先將血量的圖示固定在鏡頭上,這樣就能血條跟隨人物移動,之後,為了讓主角在死亡時血條能夠消失,需要寫入以下程式
:::success
```
void UpdateHpBar()
//根據血量總數顯示血格
{
for (int i = 0; i < HpBar.transform.childCount; i++)
計算血格的數量
{
if (Hp > i)
{
HpBar.transform.GetChild(i).gameObject.SetActive(true);
}
else
{
HpBar.transform.GetChild(i).gameObject.SetActive(false);
}
}
}
```
:::
#### (2)主角回血
:::success
```
void OnCollisionEnter2D(Collision2D other)
//碰到其他2D物件
{
if (other.gameObject.tag == "recover block")
//碰到標籤為recover block的物件執行以下程式
{
if (other.contacts[0].normal == new Vector2(0f, 1f))
//踩到方塊執行以下程式
{
ModifyHp(1);
//回復一滴血
}
}
}
```
```
void ModifyHp(int num)
//用於更改血量
{
Hp += num;
//依照Modify後方括號內的數字更改血量總數
if (Hp > 10)
{
Hp = 10;//血量總數超過10時,設定為10
}
UpdateHpBar();
//將血量數值回傳到UpdateHpBar函數
}
```
:::
#### (3)主角扣血
:::success
```
void OnCollisionEnter2D(Collision2D other)
//碰到其他2D物件
{
if (other.gameObject.tag == "enemy")
//碰到標籤為enemy的物件執行以下程式
{
ModifyHp(-3);//扣除三滴血
}
if (other.gameObject.tag == "bullet")
//碰到標籤為bullet的物件執行以下程式
{
ModifyHp(-3);//扣除三滴血
}
}
```
```
void ModifyHp(int num)
//用於更改血量
{
Hp += num;
//依照Modify後方括號內的數字更改血量總數
if (Hp <= 0)
{
Hp = 0;//血量總數低於0時,設定為0
Die();//執行死亡函數
}
UpdateHpBar();
//將血量數值回傳到UpdateHpBar函數
}
```
:::
### (5)主角死亡程式的編寫
:::success
```
void Die()
{
Time.timeScale = 0f;//將遊戲暫停
replayButton.SetActive(true);//讓重新開始按鈕跳出
}
```
:::
### (6)遊戲重新開始
首先先創造按鈕並固定在鏡頭,不過因為要在玩家死亡或是通關時顯示,所以一開始要讓下圖該欄位顯示不打勾

之後,在執行死亡程式時會讓重新開始的按鈕顯示
只要在設定欄位,將點擊後要執行的程式寫入就可以了(下圖為示意圖)

:::success
```
public void Replay()
{
Time.timeScale = 1f;//將遊戲恢復為正常速度
SceneManager.LoadScene("SampleScene");
//重新載入遊戲,會將人物重製回起點
}
```
:::
## 5. 障礙物的繪製與程式編寫
我們一樣利用小畫家來繪製障礙物,但是為了讓遊戲有更多的變化性,我們製作了許多不同的障礙物模型,也設計了扣血的機制來增加遊戲的難度。
### (1)火球
#### (1)火球圖片
(如圖)

#### (2)火球程式碼
:::info
```
public float moveSpeed = 5f; //public可在Unity中自行調整速度
void Update()
{
transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
//物體持續往+X方向移動
}
void OnCollisionEnter2D(Collision2D coll)
{
if(coll.gameObject.tag == "block")
//當此物體碰觸到標籤為block的物件後執行以下指令
{
moveSpeed = -moveSpeed; //轉向
}
if (coll.gameObject.tag == "enemy")
//當此物體碰觸到標籤為enemy的物件後執行以下指令
{
moveSpeed = -moveSpeed; //轉向
}
}
```
:::
### (2)子彈
#### (2)子彈圖片
(如圖)

#### (2)子彈程式碼
:::info
```
public float moveSpeed = 5f;//public可在Unity中自行調整速度
void Update()
{
transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
//物體持續往+X方向移動
}
```
:::
#### (3)在想要消滅子彈的位置,設置一個透明區塊,子彈碰觸到區塊後會消滅子彈物體
:::info
```
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "bullet")
//當此物體碰觸到標籤為block的物件後執行以下指令
{
Destroy(col.gameObject);
//銷毀物件(子彈)
}
}
```
:::
### (3)尖刺
(如圖)

## 6.移動平台
### (1)移動平台圖片
(如圖)

### (2)移動平台程式碼
:::info
```
public float moveSpeed = 5f; //public可在Unity中自行調整速度
void Update()
{
transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
//物體持續往+X方向移動
}
void OnCollisionEnter2D(Collision2D coll)
{
if(coll.gameObject.tag == "block")
//當此物體碰觸到標籤為block的物件後執行以下指令
{
moveSpeed = -moveSpeed; //轉向
}
}
```
:::
## 7.各物件主角血量加減或不變的判別方法
此方法需要用到Unity內建功能:物件標籤

更改個物體的標籤,即可判定物體要對主角造成傷害、回血、或是不變動血量
**下表為我們製作遊戲時用到的標籤(Tag)**
:::danger
| 物體 | 標籤(Tag) | 作用 |
| --- | --- | ---- |
| 回血方塊 | recover block | 主角碰到時回血 |
| 火球 | enemy | 主角碰到時扣血 |
| 扣血方塊 | enemy | 主角碰到時扣血 |
| 子彈 | bullet | 主角碰到時扣血 |
| 尖刺 | enemy | 主角碰到時扣血 |
| 結束遊戲 | end game|主角碰到時結束遊戲 |
:::
## 8. 成果
下圖為整張地圖

下方連結為遊戲的壓縮檔,下載後解壓縮即可遊玩
https://drive.google.com/file/d/1aw0DRN42Mq03PENSHLcXvZLmBdOyjKCS/view?usp=sharing
遊玩方式:WASD操控玩家,只需要跑到終點即可