SlideShare a Scribd company logo
2D Platformer遊戲
Revised on January 1, 2020
 背景動畫與背景音樂
 玩家角色控制
 隨機生成敵人
 空投道具
 使用道具
 顯示分數
 暫停遊戲控制
 建立2D專案,my2DPlatformer
 選單命令File> Save As…,將預設場景另存新檔
 Exercise.unity
 調整Main Camera
 Position(X, Y, Z) = (-4.43, -0.15, -10)
 Background(R, G, B, A) = (163, 187, 196, 5)
 Size = 11
 Free Aspect
 滙入遊戲資源
 選單命令Assets> Import Package> Custom Package…,滙入
platformer2d.unitypackage
建立專案 1/3
2
 新增Tags
 ground、Crate、Enemy、Wall、Obstacle、
Bullet、BombPickup、ExplosionFX、
HealthBar
 新增Sorting Layers (遊戲圖層)
 Background、Character、Foreground、UI
 新增Layers (碰撞管理圖層)
 Bombs、Player、Enemies、Pickups、
Ground
建立專案 2/3
3
 選單命令Edit> Project Settings…
 Physics 2D Layer Collicion Matrix
建立專案 3/3
4
 將Assets/Prefebs/Environment/backgrounds.prefab加到場景
 Sorting Layer = Background
 設定所有子物件Sorting Layer = Background
 將Assets/Prefabs/Environment/Foregrounds.prefab加到場景
 設定所有子物件Sorting Layer = Foreground
建立場景
5
 選取Prefabs/Props/swan.prefab
 Sorting Layer = Background
 選取Prefabs/Environment/Cab.prefab
 Sorting Layer = Background
 Wheels子物件
 Sorting Layer = Background
 選取Prefabs/Environment/Bus.prefab
 Sorting Layer = Background
 Wheels子物件
 Sorting Layer = Background
設定背景動畫預製物件
6
 在_Scripts目錄下新增BackgroundPropSpawner.cs程式腳本
using UnityEngine;
using System.Collections;
public class BackgroundPropSpawner : MonoBehaviour {
public Rigidbody2D backgroundProp; //待生成的背景預製物件
public float leftSpawnPosX; //左側生成位置x座標
public float rightSpawnPosX; //右側生成位置x座標
public float minSpawnPosY; //生成位置y座標區間最小值
public float maxSpawnPosY; //生成位置y座標區間最大值
public float minTimeBetweenSpawns; //生成間隔時間最小值
public float maxTimeBetweenSpawns; //生成間隔時間最大值
public float minSpeed; //最小移動速度
public float maxSpeed; //移動速度最大值
void Start () {
Random.InitState(System.DateTime.Today.Millisecond);
StartCoroutine("Spawn"); //起始Spawn程序
}
隨機產生背景動畫物件 1/6
7
IEnumerator Spawn () {
float waitTime = Random.Range(minTimeBetweenSpawns, maxTimeBetweenSpawns);
yield return new WaitForSeconds(waitTime); //隨機等待一段時間
bool facingLeft = Random.Range(0,2) == 0; //隨機設定預製物件方向
float posX = facingLeft ? rightSpawnPosX : leftSpawnPosX;
float posY = Random.Range(minSpawnPosY, maxSpawnPosY);
Vector3 spawnPos = new Vector3(posX, posY, transform.position.z);
Rigidbody2D propInstance =
Instantiate(backgroundProp, spawnPos, Quaternion.identity) as Rigidbody2D;
if (!facingLeft) {
Vector3 scale = propInstance.transform.localScale;
scale.x *= -1;
propInstance.transform.localScale = scale;
}
float speed = Random.Range(minSpeed, maxSpeed); //隨機設定速度
speed *= facingLeft ? -1f : 1f;
propInstance.velocity = new Vector2(speed, 0);
StartCoroutine(Spawn());
隨機產生背景動畫物件 2/6
8
while (propInstance != null) {
if (facingLeft) {
if (propInstance.transform.position.x < leftSpawnPosX - 0.5f)
Destroy(propInstance.gameObject);
}
else {
if (propInstance.transform.position.x > rightSpawnPosX + 0.5f)
Destroy(propInstance.gameObject);
}
yield return null;
}
}
}
隨機產生背景動畫物件 3/6
9
 新增空物件,命名為swanCreator
 重置Transform
 加入BackgroundPropSpawner.cs程式腳本
 拖曳Prefabs/Props/swan.prefab到Prop欄
 Left Spawn Pos X = -24
 Right Spawn Pos X = 24
 Min Spawn Pos Y = 4
 Max Spawn Pos Y = 8
 Min Time Between Spawns = 2
 Max Time Between Spawns = 8
 Min Speed = 5
 Max Speed = 8
 測試遊戲,天空會隨機有天鵝飛過
隨機產生背景動畫物件 4/6
10
 新增空物件,命名為busCreator
 重置Transform
 加入BackgroundPropSpawner.cs程式腳本
 拖曳Prefabs/Environment/Bus.prefab到Prop欄
 Left Spawn Pos X = -24
 Right Spawn Pos X = 24
 Min Spawn Pos Y = -5.5
 Max Spawn Pos Y = -5.5
 Min Time Between Spawns = 8
 Max Time Between Spawns = 18
 Min Speed = 5
 Max Speed = 8
 測試遊戲,街道會隨機有巴士通過
隨機產生背景動畫物件 5/6
11
 新增空物件,命名為cabCreator
 重置Transform
 加入BackgroundPropSpawner.cs程式腳本
 拖曳Prefabs/Environment/Cab.prefab到Prop欄
 Left Spawn Pos X = -24
 Right Spawn Pos X = 24
 Min Spawn Pos Y = -6.4
 Max Spawn Pos Y = -6.4
 Min Time Between Spawns = 10
 Max Time Between Spawns = 15
 Min Speed = 5
 Max Speed = 8
 測試遊戲,街道會隨機有計程車通過
隨機產生背景動畫物件 6/6
12
 新增空物件,命名為music
 重置Transform
 加入AudioSource元件
 AudioClip = MainTheme
 勾選Play On Awake
 勾選Loop
 Volume = 0.1
 Reverb Zone Mix = 0
 測試專案,遊戲執行時會持續撥放背景音樂
加入背景音樂
13
 將Assets/Prefabs/Chractera/Mr.Bean.prefab加倒場景
 設定所有子物件Sorting Layer = Character
 Tag = Player
 Layer = Player
建立玩家角色
14
 Mr.Bean動作控制器
玩家角色控制 1/5
15
 在Mr.Bean物件加入PlayerControl.cs程式腳本
using UnityEngine;
using System.Collections;
public class PlayerControl : MonoBehaviour {
[HideInInspector]
public bool facingRight = true; //方向旗號,不顯示在屬性窗格,但可提其它程式腳本存取
[HideInInspector]
public bool jump = false; //跳躍旗號,不顯示在屬性窗格,但可提其它程式腳本存取
public float moveForce = 365f; //行進力道
public float maxSpeed = 5f; //移動速度上限
public AudioClip[] jumpClips; //跳躍動作音效庫
public float jumpForce = 1000f; //跳躍力道
private Transform groundCheck; //用來檢查玩家角色是否站在地面
private bool grounded = false; //落地旗號
private Animator anim; //玩家角色動作控制器參照
玩家角色控制 2/5
16
void Awake() {
groundCheck = transform.Find("groundCheck");
anim = GetComponent<Animator>();
}
void Update() {
grounded = Physics2D.Linecast(transform.position,
groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
//玩家角色位於在地面,並按下Jump鍵
if (Input.GetButtonDown("Jump") && grounded)
jump = true; //執行跳躍動作
}
玩家角色控制 3/5
17
void FixedUpdate () {
float h = Input.GetAxis("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(h));
if (h * GetComponent<Rigidbody2D>().velocity.x < maxSpeed)
GetComponent<Rigidbody2D>().AddForce(Vector2.right * h * moveForce); //加速
if (Mathf.Abs(GetComponent<Rigidbody2D>().velocity.x) > maxSpeed)
GetComponent<Rigidbody2D>().velocity =
new Vector2(Mathf.Sign(GetComponent<Rigidbody2D>().velocity.x) * maxSpeed,
GetComponent<Rigidbody2D>().velocity.y);
if (h > 0 && !facingRight) Flip(); //反轉方向
else if (h < 0 && facingRight) Flip(); //反轉方向
if (jump) {
anim.SetTrigger("Jump");
int i = Random.Range(0, jumpClips.Length); //隨機撥放跳躍音效
AudioSource.PlayClipAtPoint(jumpClips[i], transform.position);
GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, jumpForce)); //跳躍
jump = false; //防止重複跳躍
}
}
玩家角色控制 4/5
18
void Flip () {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
 拖曳Audio/Player/Jumps/Player-
jump[1-3].wav到Jump Clips欄
 測試遊戲,可操控主角移動
 適度調整移動速度及跳躍力道
玩家角色控制 5/5
19
 新增空物件,命名為healthUI
 Tag = HealthBar
 Position(X, Y, Z) = (0, 100, 0)
 在healthUI下新增Sprite物件,命名為HealthBar
 Position(X, Y, Z) = (-0.83, 0, 0)
 拖曳Assets/Sprites/_UI/Health.png到Sprite Renderer之Sprite欄
 拖曳Assets/Materials/Health.mat到Sprite Renderer之Material欄
 Sorting Layer = Foreground
建立玩家生命條 1/4
20
 在healthUI下新增Sprite物件,命名為HealthOutline
 Position(X, Y, Z) = (0, 0, 0)
 拖曳Assets/Sprites/_UI/Health-bg.png到Sprite Renderer之
Sprite欄
 拖曳Assets/Materials/DefaultPixelSnap.mat到Sprite Renderer
之Material欄
 Sorting Layer = Foreground
建立玩家生命條 2/4
21
 在healthUI加入FollowPlayer.cs程式腳本
using UnityEngine;
using System.Collections;
public class FollowPlayer : MonoBehaviour {
public Vector3 offset; //顯示位置偏移值
private Transform player;
void Awake () {
player = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update () {
transform.position = player.position + offset;
}
}
建立玩家生命條 3/4
22
 設定healthUI之FollowPlayer
 Offset(X, Y, Z) = (0, 1.2, 0)
 測試遊戲,玩家角色上方會顯示生命條並且跟隨移動
建立玩家生命條 4/4
23
 在Mr.Bean物件加入PlayerHealth.cs程式腳本
using UnityEngine;
using System.Collections;
public class PlayerHealth : MonoBehaviour {
public float health = 100f; //玩家生命值
public float repeatDamagePeriod = 1f; //玩家連續受傷之時時間隔
public AudioClip[] ouchClips; //玩家受傷音效
public float hurtForce = 100f; //玩家受傷時受到之推力
public float damageAmount = 10f; //每次受傷之損血值
private SpriteRenderer healthBar; //玩家生命條之sprite renderer參照
private float lastHitTime; //前次受傷時間戳記
private Vector3 healthScale; //生命條滿格時之大小
private PlayerControl playerControl; //玩家PlayerControl程式腳本參照
private Animator anim; //玩家動作控制器參照
玩家生命值管理 1/4
24
void Awake () {
playerControl = GetComponent<PlayerControl>(); //玩家PlayerControl參照
healthBar = GameObject.Find("HealthBar").GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>(); //玩家動作控制器參照
healthScale = healthBar.transform.localScale; //記錄生命條滿格之大小
}
void OnCollisionEnter2D (Collision2D col) {
if (col.gameObject.tag == "Enemy") { //敵人撞到玩家
if (Time.time > lastHitTime + repeatDamagePeriod) { //已達連續受傷之時間間隔
if (health > 0f) {
TakeDamage(col.transform); //使玩家損血
lastHitTime = Time.time; //記錄受傷時間
}
else { //玩家死亡,使玩家摔落河裡
Collider2D[] cols = GetComponents<Collider2D>();
foreach (Collider2D c in cols) { //將所有Collider2D調整為觸發器
c.isTrigger = true;
}
SpriteRenderer[] spr = GetComponentsInChildren<SpriteRenderer>();
玩家生命值管理 2/4
25
foreach(SpriteRenderer s in spr) { //將玩家角色移到UI圖層
s.sortingLayerName = "UI";
}
GetComponent<PlayerControl>().enabled = false; //停止PlayerControl程式腳本
GetComponentInChildren<Gun>().enabled = false; //停止Gun程式腳本
anim.SetTrigger("Die"); //觸發Die動畫
}
}
}
}
void TakeDamage (Transform enemy) {
playerControl.jump = false; //停止跳躍
Vector3 hurtVector = transform.position - enemy.position + Vector3.up * 5f;
GetComponent<Rigidbody2D>().AddForce(hurtVector * hurtForce);//向玩家施加衝撞力
health -= damageAmount; //扣減玩家血量
UpdateHealthBar(); //更新顯示生命條
int i = Random.Range (0, ouchClips.Length); //隨機撥放玩家受傷音效
AudioSource.PlayClipAtPoint(ouchClips[i], transform.position);
}
玩家生命值管理 3/4
26
public void UpdateHealthBar () {
//根據玩家的生命值,將生命條的顏色設置為綠色和紅色之間的比例
healthBar.material.color = Color.Lerp(Color.green, Color.red, 1 - health * 0.01f);
//根據玩家的生命值,調整生命條的寬度
healthBar.transform.localScale = new Vector3(healthScale.x * health * 0.01f, 1, 1);
}
}
 拖曳Assets/Audio/Player/Ouch/Player-ouch[1-4].wav到Ouch
Clips欄
玩家生命值管理 4/4
27
攝影機跟隨玩家移動 1/4
28
場景活動範圍
攝影機可視範圍
 在Main Camera加入CameraFollow.cs程式腳本
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour {
public float xMargin = 1f; //攝影機啟動跟隨前允許玩家移動的水平位移
public float yMargin = 1f; //攝影機啟動跟隨前允許玩家移動的垂直位移
public float xSmooth = 8f; //使相機平穩地捕捉目標運動之X軸修正值
public float ySmooth = 8f; //使相機平穩地捕捉目標運動之Y軸修正值
public Vector2 maxXAndY; //攝影機位置X與Y座標最大值
public Vector2 minXAndY; //攝影機位置X與Y座標最小值
private Transform player; //玩家角色transform屬性
void Awake () {
player = GameObject.FindGameObjectWithTag("Player").transform;
}
bool CheckXMargin() {
return Mathf.Abs(transform.position.x - player.position.x) > xMargin;
}
攝影機跟隨玩家移動 2/4
29
bool CheckYMargin() {
return Mathf.Abs(transform.position.y - player.position.y) > yMargin;
}
void FixedUpdate () {
TrackPlayer();
}
void TrackPlayer () {
float targetX = transform.position.x;
float targetY = transform.position.y;
if (CheckXMargin())
targetX = Mathf.Lerp(transform.position.x,
player.position.x, xSmooth * Time.deltaTime);
if (CheckYMargin())
targetY = Mathf.Lerp(transform.position.y,
player.position.y, ySmooth * Time.deltaTime);
targetX = Mathf.Clamp(targetX, minXAndY.x, maxXAndY.x);
targetY = Mathf.Clamp(targetY, minXAndY.y, maxXAndY.y);
transform.position = new Vector3(targetX, targetY, transform.position.z);
}
}
攝影機跟隨玩家移動 3/4
30
 設定Camera Follow參數
 X Margin = 2,Y Margin = 2
 X Smooth = 2,Y Smooth = 2
 Max(X, Y) = (5, 5)
 Min(X, Y) = (-5, -5)
 測試遊戲,玩家水平或垂直位移超過2時,攝影機就會自動跟隨
攝影機跟隨玩家移動 4/4
31
 選取Assets/Prefabs/Props/rocket.prefab
 子物件Sorting Layer = Character
 在Mr.Bean的Gun子物件加入Gun.cs腳本
using UnityEngine;
using System.Collections;
public class Gun : MonoBehaviour {
public Rigidbody2D rocket; //rocket預製物件
public float speed = 25f; //rocket速度
private PlayerControl playerCtrl; //PlayerControl程式腳本參照
private Animator anim; //角色動畫控制器參照
void Awake() {
anim = transform.root.gameObject.GetComponent<Animator>();
playerCtrl = transform.root.GetComponent<PlayerControl>();
}
Mr.Bean射擊控制 1/3
32
void Update () {
if (Input.GetButtonDown("Fire1")) { //按下發射鍵
anim.SetTrigger("Shoot");
GetComponent<AudioSource>().Play();
if (playerCtrl.facingRight) { //向右發射
Rigidbody2D bulletInstance = Instantiate(rocket, transform.position,
Quaternion.Euler(new Vector3(0, 0, 0))) as Rigidbody2D;
bulletInstance.velocity = new Vector2(speed, 0);
}
else { //向左發射
Rigidbody2D bulletInstance = Instantiate(rocket, transform.position,
Quaternion.Euler(new Vector3(0, 0, 180f))) as Rigidbody2D;
bulletInstance.velocity = new Vector2(-speed, 0);
}
}
}
}
Mr.Bean射擊控制 2/3
33
 拖曳Assets/Prefabs/Props/rocket.prefab到Gun腳本的Rocket
資料欄
 選取Assets/Prefabs/Props/rocketExplosion.prefab
 Sorting Layer = Foreground
 測試遊戲,點擊滑鼠左鍵可發射火箭彈
Mr.Bean射擊控制 3/3
34
 選單命令GameObject> UI> Text,命名為Score
 Anchor Presets = top, center
 Pos(X, Y, Z) = (0, -10, 0)
 Width = 500, Height = 100
 Pivot(X, Y) = (0.5, 1)
 Text = Score
 Font = BradBunR
 Font Size = 80
 Alignment = Center
 Color = white
加入計分板 1/5
35
 在Score加入Score.cs程式腳本
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Score : MonoBehaviour {
public int score = 0; //玩家分數值
void Update () {
GetComponent<Text>().text = "Score: " + score; //更新顯示分數
}
}
加入計分板 2/5
36
 選單命令GameObject> UI> Text,命名為Score-shadow
 Anchor Presets = top, center
 Pos(X, Y, Z) = (0, -14, 0)
 Width = 500, Height = 100
 Pivot(X, Y) = (0.5, 1)
 Text = Score
 Font = BradBunR
 Font Size = 80
 Alignment = Center
 Color = black
加入計分板 3/5
37
 在Score-shadow加入ScoreShadow.cs程式腳本
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ScoreShadow : MonoBehaviour {
public GameObject guiCopy; // Score物件參照
void Awake () {
Vector3 behindPos = transform.position;
behindPos = new Vector3(guiCopy.transform.position.x,
guiCopy.transform.position.y - 4f,
guiCopy.transform.position.z);
transform.position = behindPos;
}
void Update () {
GetComponent<Text>().text = guiCopy.GetComponent<Text>().text; //同步更新資料
}
}
加入計分板 4/5
38
 拖曳Score到Score-shadow之Gui Copy欄
 調整Hierarhy窗格中物件順序,使Score位於Score-shadow下方
 測試遊戲,場景中央上方會顯示陰影效果的得分值
加入計分板 5/5
39
 選取Assets/Prefabs/Characters/enemy1.prefab
 子物件Sorting Layer = Character
 選取Assets/Prefabs/Characters/enemy2.prefab
 子物件Sorting Layer = Character
隨機生成敵人 1/5
40
 選單命令GameObject> Create Empty建立空物件,命名為
EnemySpawners
 重置Transform
 Position(X,Y,Z) = (0, 15, 0)
 選單命令GameObject> Create Empty Child,在EnemySpawners
建立⼀個空子物件,命名為MidSpawner
 重置Transform
 Position(X,Y,Z) = (0.27, 0, 0)
 在MidSpawner加入EnemySpawner.cs腳本
隨機生成敵人 2/5
41
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawner : MonoBehaviour {
public float spawnTime = 5f; //敵人生成間隔時間
public float spawnDelay = 3f; //延遲時間後才開始生成
public GameObject[] enemies; //敵人預製物件庫
void Start () {
InvokeRepeating("Spawn", spawnDelay, spawnTime);
}
void Spawn () {
int enemyIndex = Random.Range(0, enemies.Length);
Instantiate(enemies[enemyIndex], transform.position, transform.rotation);
}
}
隨機生成敵人 3/5
42
 拖曳Assets/Prefabs/Characters/enemy[1~2].prefab到
EnemySpawner腳本的Enemies資料欄
隨機生成敵人 4/5
43
 複製2份MidSpawner,更名為LeftSpawner及RightSpawner
 重置Transform
 LeftSpawner Position(X,Y,Z) = (-13.8, 0, 0)
 RightSpawner Position(X,Y,Z) = (14.5, 0, 0)
 選取Assets/Prefabs/UI/ui_100points.prefab
 子物件Sorting Layer = UI
 測試遊戲
 碰到敵人會損血,擊斃敵人會得分
 玩家及敵人掉落河裡後,並不會被銷毀
隨機生成敵人 5/5
44
 選單命令GameObject> Create Empty建立空物件,命名Destroyer
 Position(X, Y, Z) = (0.54, -13.5, 0)
 Scale(X, Y, Z) = (1.9, 1, 1)
 加入Box Collider 2D
 勾選Is Trigger
 Size(X, Y) = (23, 1.9)
角色溺水作業 1/4
45
 在Destroyer物件加入Remover.cs程式腳本
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class Remover : MonoBehaviour {
public GameObject splash; //水花噴濺特效預製物件
void OnTriggerEnter2D(Collider2D col) {
if (col.gameObject.tag == "Player") { //玩家掉落河裡
GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraFollow>()
.enabled = false;
GameObject.FindGameObjectWithTag("HealthBar").GetComponent<FollowPlayer>().
enabled = false;
Instantiate(splash, col.transform.position, transform.rotation);
Destroy (col.gameObject);
StartCoroutine("ReloadGame"); //執行重新載入關卡程序
}
角色溺水作業 2/4
46
else { //其它角色掉落河裡
Instantiate(splash, col.transform.position, transform.rotation);
Destroy (col.gameObject);
}
}
IEnumerator ReloadGame() {
yield return new WaitForSeconds(2); //等待2秒
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex,
LoadSceneMode.Single);
}
}
角色溺水作業 3/4
47
 選取Assets/Prefabs/FX/splash.prefab
 Sorting Layer = Foreground
 拖曳Assets/Prefabs/FX/splash.prefab到Splash欄
 測試遊戲
 敵人掉落河裡會濺出水花並消毀
 玩家落河後會濺出水花並重新開始關卡
角色溺水作業 4/4
48
 選取Assets/Prefabs/Props/bombCrate.prefab
 子物件Sorting Layer = Foreground
 選取Assets/Prefabs/Props/healthCrate.prefab
 子物件Sorting Layer = Foreground
 在Assets/Prefabs/Props/healthCrate.prefab之health子物件加
入HealthPickup.cs程式腳本
製作空投道具箱 1/5
49
using UnityEngine;
using System.Collections;
public class HealthPickup : MonoBehaviour {
public float healthBonus; //補充生命值
public AudioClip collect; //收集道具時之音效
private PickupSpawner pickupSpawner; //PickupSpawner程式腳本參照
private Animator anim; //動畫控制器參照
private bool landed; //道具箱落地旗號
void Awake () {
pickupSpawner = GameObject.Find("pickupManager").GetComponent<PickupSpawner>();
anim = transform.root.GetComponent<Animator>();
}
void OnTriggerEnter2D (Collider2D other) {
if (other.tag == "Player") { //玩家碰到道具箱
PlayerHealth playerHealth = other.GetComponent<PlayerHealth>();
playerHealth.health += healthBonus; //補充玩家生命值
playerHealth.health = Mathf.Clamp(playerHealth.health, 0f, 100f); //上限值100
製作空投道具箱 2/5
50
playerHealth.UpdateHealthBar(); //更新顯示玩家生命條
pickupSpawner.StartCoroutine(pickupSpawner.DeliverPickup()); //啟動下一波空投程序
AudioSource.PlayClipAtPoint(collect,transform.position); //撥放音效
Destroy(transform.root.gameObject); //銷毀道具箱
}
else if(other.tag == "ground" && !landed) { //道具箱落地
anim.SetTrigger("Land"); //觸發道具箱落地動畫
transform.parent = null;
gameObject.AddComponent<Rigidbody2D>();
landed = true;
}
}
}
 拖曳Assets/Audio/FX/healthPickup.ogg到
Collect欄
製作空投道具箱 3/5
51
 在Assets/Prefabs/Props/bombCrate.prefab之crate子物件加入
BombPickup.cs程式腳本
using UnityEngine;
using System.Collections;
public class BombPickup : MonoBehaviour {
public AudioClip pickupClip; //收集道具時之音效
private Animator anim; //動畫控制器參照
private bool landed = false; //道具箱落地旗號
void Awake() {
anim = transform.root.GetComponent<Animator>();
}
void OnTriggerEnter2D (Collider2D other) {
if (other.tag == "Player") { //玩家碰到道具箱
AudioSource.PlayClipAtPoint(pickupClip, transform.position);
other.GetComponent<LayBombs>().bombCount++; //增加玩家炸彈數量
Destroy(transform.root.gameObject); //銷毀道具箱
}
製作空投道具箱 4/5
52
else if (other.tag == "ground" && !landed) { //道具箱落地
anim.SetTrigger("Land"); //觸發道具箱落地動畫
transform.parent = null;
gameObject.AddComponent<Rigidbody2D>();
landed = true;
}
}
}
 拖曳Assets/Audio/Player/Taunts/Player-IDefyYou.wav到
Pickup Clip欄
製作空投道具箱 5/5
53
 選單命令GameObject> Create Empty建立空物件,命名
pickupManager
 重置Transform
 加入PickupSpawner.cs程式腳本
空投道具 1/4
54
using UnityEngine;
using System.Collections;
public class PickupSpawner : MonoBehaviour {
public GameObject[] pickups; //道具預製物件陣列
public float pickupDeliveryTime = 5f; //傳送道具等待時間
public float dropRangeLeft; //道具空投範圍左邊界坐標
public float dropRangeRight; //道具空投範圍右邊界坐標
public float highHealthThreshold = 75f; //玩家生命值超過此設定值時,只空投炸彈道具
public float lowHealthThreshold = 25f; //玩家生命值低於此設定值時,只空投傷藥道具
private PlayerHealth playerHealth; //PlayerHealth程式腳本參照
void Awake () {
playerHealth = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHea
lth>();
}
空投道具 2/4
55
void Start () {
StartCoroutine(DeliverPickup()); //啟動DeliverPickup程序
}
public IEnumerator DeliverPickup() {
yield return new WaitForSeconds(pickupDeliveryTime); //等待一段指定時間
float dropPosX = Random.Range(dropRangeLeft, dropRangeRight);//隨機空投X坐標
Vector3 dropPos = new Vector3(dropPosX, 15f, 1f); //空投位置
if (playerHealth.health >= highHealthThreshold) //空投炸彈
Instantiate(pickups[0], dropPos, Quaternion.identity);
else if (playerHealth.health <= lowHealthThreshold) //空投傷藥
Instantiate(pickups[1], dropPos, Quaternion.identity);
else { //隨機空投道具
int pickupIndex = Random.Range(0, pickups.Length);
Instantiate(pickups[pickupIndex], dropPos, Quaternion.identity);
}
}
}
空投道具 3/4
56
 設定Pickups Spawner
 拖曳Assets/Prefabs/Props/bombCrate.prefab到Pickups欄
 拖曳Assets/Prefabs/Props/healthCrate.prefab到Pickups欄
 Pickup Delivery Time = 5
 Drop Range Left = -15
 Drop Range Right = 15
 High Health Threshold = 75
 Low Health Threshold = 25
 測試遊戲,會隨機飄下道具
 玩家Player Health之Health值小於25時,⼀定飄下急救箱
 玩家Player Health之Health值大於75時,⼀定飄下炸彈
空投道具 4/4
57
 選單命令GameObject> UI> Raw Image,命名為ui_bombHUD
 Anchor Presets = bottom, left
 Pos(X, Y, Z) = (10, 10, 0)
 Width = 84, Height = 70
 Pivot(X, Y) = (0, 0)
 拖曳Assets/Sprites/_Props/
prop_crate_ammo.png到Texture欄
製作炸彈圖示
58
 將Assets/Prefabs/Props/explosionParticle.prefab加到場景
 選取Assets/Prefabs/Props/bomb.prefab
 Sorting Layer = Character
 在Mr.Bean加入LayBombs.cs程式腳本
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class LayBombs : MonoBehaviour {
[HideInInspector]
public bool bombLaid = false; //玩家是否已放置炸彈
public int bombCount = 0; //玩家擁有的炸彈道具個數
public AudioClip bombsAway; //玩家放置炸彈時的語音
public GameObject bomb; //炸彈預製物件
private RawImage bombHUD; //炸彈道具圖示參照,當玩家擁有後就會開啟圖示
玩家放置炸彈 1/3
59
void Awake () {
bombHUD = GameObject.Find("ui_bombHUD").GetComponent<RawImage>();
}
void Update () {
if (Input.GetButtonDown("Fire2") && !bombLaid && bombCount > 0) { //放置炸彈
bombCount--;
bombLaid = true;
AudioSource.PlayClipAtPoint(bombsAway,transform.position);
Instantiate(bomb, transform.position, transform.rotation);
}
bombHUD.enabled = bombCount > 0; //更新炸彈圖示狀態
}
}
玩家放置炸彈 2/3
60
 拖曳Assets/Prefabs/Props/bomb.prefab到Bomb欄
 拖曳Assets/Audio/Player/Taunts/Player-BombsAway.ogg到
Bombs Away欄
 執行遊戲
 Bomb Count大於0時,會顯示炸彈道具圖示
 拿到炸彈道具後,使用滑鼠右鍵放置炸彈
玩家放置炸彈 3/3
61
 選單命令GameObject> Create Empty建立空物件,命名GameManager
 在GameManager加入Pauser.cs程式腳本
using UnityEngine;
using System.Collections;
public class Pauser : MonoBehaviour {
private bool paused = false;
void Update () {
if (Input.GetKeyUp(KeyCode.P)) { //按了P鍵
paused = !paused; //切換暫停狀態
}
if (paused)
Time.timeScale = 0;
else
Time.timeScale = 1;
}
}
 測試遊戲,按P鍵可暫停遊戲
遊戲控制 1/2
62
 編輯GameManager.cs程式腳本,按Esc鍵可結束遊戲
using UnityEngine;
using System.Collections;
public class Pauser : MonoBehaviour {
private bool paused = false;
void Update () {
if (Input.GetKey (KeyCode.Escape)) { //按了Esc鍵
Application.Quit (); //結束遊戲
}
if (Input.GetKeyUp(KeyCode.P)) { //按了P鍵
...
}
}
註:需建立執行檔測試
遊戲控制 2/2
63
 選單命令Files>Build Settings…
 設定遊戲場景、選擇遊戲平台
 點擊Build按鈕
建立執行檔
64

More Related Content

What's hot

There's Waldo by Patrick Wardle & Colby Moore
There's Waldo by Patrick Wardle & Colby MooreThere's Waldo by Patrick Wardle & Colby Moore
There's Waldo by Patrick Wardle & Colby Moore
Shakacon
 
Android Boot Time Optimization
Android Boot Time OptimizationAndroid Boot Time Optimization
Android Boot Time Optimization
Kan-Ru Chen
 

What's hot (6)

Study on Android Emulator
Study on Android EmulatorStudy on Android Emulator
Study on Android Emulator
 
There's Waldo by Patrick Wardle & Colby Moore
There's Waldo by Patrick Wardle & Colby MooreThere's Waldo by Patrick Wardle & Colby Moore
There's Waldo by Patrick Wardle & Colby Moore
 
The Anatomy of an Exploit (NDC TechTown 2019))
The Anatomy of an Exploit (NDC TechTown 2019))The Anatomy of an Exploit (NDC TechTown 2019))
The Anatomy of an Exploit (NDC TechTown 2019))
 
Android Boot Time Optimization
Android Boot Time OptimizationAndroid Boot Time Optimization
Android Boot Time Optimization
 
Run command
Run commandRun command
Run command
 
Building a turn-based game prototype using ECS - Unite Copenhagen 2019
Building a turn-based game prototype using ECS - Unite Copenhagen 2019Building a turn-based game prototype using ECS - Unite Copenhagen 2019
Building a turn-based game prototype using ECS - Unite Copenhagen 2019
 

Similar to Unity遊戲程式設計 - 2D platformer game

i need a taking turn method for a player vs computer battleship game.pdf
i need a taking turn method for a player vs computer battleship game.pdfi need a taking turn method for a player vs computer battleship game.pdf
i need a taking turn method for a player vs computer battleship game.pdf
petercoiffeur18
 
Fps tutorial 2
Fps tutorial 2Fps tutorial 2
Fps tutorial 2
unityshare
 
Gdc09 Minigames
Gdc09 MinigamesGdc09 Minigames
Gdc09 Minigames
Susan Gold
 
ARTDM 170, Week 11: User Interaction
ARTDM 170, Week 11: User InteractionARTDM 170, Week 11: User Interaction
ARTDM 170, Week 11: User Interaction
Gilbert Guerrero
 

Similar to Unity遊戲程式設計 - 2D platformer game (20)

Unity 13 space shooter game
Unity 13 space shooter gameUnity 13 space shooter game
Unity 13 space shooter game
 
Unity遊戲程式設計(15) 實作Space shooter遊戲
Unity遊戲程式設計(15) 實作Space shooter遊戲Unity遊戲程式設計(15) 實作Space shooter遊戲
Unity遊戲程式設計(15) 實作Space shooter遊戲
 
Tdd in unity
Tdd in unityTdd in unity
Tdd in unity
 
Flappy bird
Flappy birdFlappy bird
Flappy bird
 
Unity3 d devfest-2014
Unity3 d devfest-2014Unity3 d devfest-2014
Unity3 d devfest-2014
 
Monogame Introduction (ENG)
Monogame Introduction (ENG)Monogame Introduction (ENG)
Monogame Introduction (ENG)
 
i need a taking turn method for a player vs computer battleship game.pdf
i need a taking turn method for a player vs computer battleship game.pdfi need a taking turn method for a player vs computer battleship game.pdf
i need a taking turn method for a player vs computer battleship game.pdf
 
Rapid prototyping with ScriptableObjects
Rapid prototyping with ScriptableObjectsRapid prototyping with ScriptableObjects
Rapid prototyping with ScriptableObjects
 
Introduction to Game Programming: Using C# and Unity 3D - Chapter 7 (Preview)
Introduction to Game Programming: Using C# and Unity 3D - Chapter 7 (Preview)Introduction to Game Programming: Using C# and Unity 3D - Chapter 7 (Preview)
Introduction to Game Programming: Using C# and Unity 3D - Chapter 7 (Preview)
 
Silverlight as a Gaming Platform
Silverlight as a Gaming PlatformSilverlight as a Gaming Platform
Silverlight as a Gaming Platform
 
Fps tutorial 2
Fps tutorial 2Fps tutorial 2
Fps tutorial 2
 
Gdc09 Minigames
Gdc09 MinigamesGdc09 Minigames
Gdc09 Minigames
 
Unity 3D Runtime Animation Generation
Unity 3D Runtime Animation GenerationUnity 3D Runtime Animation Generation
Unity 3D Runtime Animation Generation
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
 
ARTDM 170, Week 11: User Interaction
ARTDM 170, Week 11: User InteractionARTDM 170, Week 11: User Interaction
ARTDM 170, Week 11: User Interaction
 
Lightweight Multiplayer HTML5 Games with PubNub
Lightweight Multiplayer HTML5 Games with PubNubLightweight Multiplayer HTML5 Games with PubNub
Lightweight Multiplayer HTML5 Games with PubNub
 
Report: Avalanche 'very likely' to host outdoor game at Coors Field
Report: Avalanche 'very likely' to host outdoor game at Coors FieldReport: Avalanche 'very likely' to host outdoor game at Coors Field
Report: Avalanche 'very likely' to host outdoor game at Coors Field
 
C++ game development with oxygine
C++ game development with oxygineC++ game development with oxygine
C++ game development with oxygine
 
Анатолій Ландишев - “Незв’язний код у Unity” GameCC 2017
Анатолій Ландишев - “Незв’язний код у Unity” GameCC 2017Анатолій Ландишев - “Незв’язний код у Unity” GameCC 2017
Анатолій Ландишев - “Незв’язний код у Unity” GameCC 2017
 

More from 吳錫修 (ShyiShiou Wu)

More from 吳錫修 (ShyiShiou Wu) (20)

mbot2.0教學-陀螺儀與三軸加速計應用.pdf
mbot2.0教學-陀螺儀與三軸加速計應用.pdfmbot2.0教學-陀螺儀與三軸加速計應用.pdf
mbot2.0教學-陀螺儀與三軸加速計應用.pdf
 
mbot2.0教學-使用makeblock雲服務.pdf
mbot2.0教學-使用makeblock雲服務.pdfmbot2.0教學-使用makeblock雲服務.pdf
mbot2.0教學-使用makeblock雲服務.pdf
 
mbot2.0教學-局域網路傳輸應用.pdf
mbot2.0教學-局域網路傳輸應用.pdfmbot2.0教學-局域網路傳輸應用.pdf
mbot2.0教學-局域網路傳輸應用.pdf
 
mbot2.0教學-四路顏色感測器應用.pdf
mbot2.0教學-四路顏色感測器應用.pdfmbot2.0教學-四路顏色感測器應用.pdf
mbot2.0教學-四路顏色感測器應用.pdf
 
mbot2.0教學-聲光控制應用.pdf
mbot2.0教學-聲光控制應用.pdfmbot2.0教學-聲光控制應用.pdf
mbot2.0教學-聲光控制應用.pdf
 
mbot2.0教學-光感測器與LED應用.pdf
mbot2.0教學-光感測器與LED應用.pdfmbot2.0教學-光感測器與LED應用.pdf
mbot2.0教學-光感測器與LED應用.pdf
 
mbot2.0教學-超音波感測應用.pdf
mbot2.0教學-超音波感測應用.pdfmbot2.0教學-超音波感測應用.pdf
mbot2.0教學-超音波感測應用.pdf
 
mbot2.0教學-移動控制.pdf
mbot2.0教學-移動控制.pdfmbot2.0教學-移動控制.pdf
mbot2.0教學-移動控制.pdf
 
mbot2.0教學-mblock5開發mBot 2.0應用程式.pdf
mbot2.0教學-mblock5開發mBot 2.0應用程式.pdfmbot2.0教學-mblock5開發mBot 2.0應用程式.pdf
mbot2.0教學-mblock5開發mBot 2.0應用程式.pdf
 
mbot2.0教學-組裝與測試.pdf
mbot2.0教學-組裝與測試.pdfmbot2.0教學-組裝與測試.pdf
mbot2.0教學-組裝與測試.pdf
 
Python元組,字典,集合
Python元組,字典,集合Python元組,字典,集合
Python元組,字典,集合
 
Python函式
Python函式Python函式
Python函式
 
Python串列資料應用
Python串列資料應用Python串列資料應用
Python串列資料應用
 
Python 迴圈作業
Python 迴圈作業Python 迴圈作業
Python 迴圈作業
 
Python分支作業
Python分支作業Python分支作業
Python分支作業
 
Python基本資料運算
Python基本資料運算Python基本資料運算
Python基本資料運算
 
建置Python開發環境
建置Python開發環境建置Python開發環境
建置Python開發環境
 
micro:bit加速度感測應用
micro:bit加速度感測應用micro:bit加速度感測應用
micro:bit加速度感測應用
 
C語言檔案處理
C語言檔案處理C語言檔案處理
C語言檔案處理
 
C語言列舉與聯合
C語言列舉與聯合C語言列舉與聯合
C語言列舉與聯合
 

Recently uploaded

AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 

Recently uploaded (20)

StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
 
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purityAPVP,apvp apvp High quality supplier safe spot transport, 98% purity
APVP,apvp apvp High quality supplier safe spot transport, 98% purity
 
A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data Migration
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
Advanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should KnowAdvanced Flow Concepts Every Developer Should Know
Advanced Flow Concepts Every Developer Should Know
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
how-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdfhow-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdf
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
Entropy, Software Quality, and Innovation (presented at Princeton Plasma Phys...
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
Abortion ^Clinic ^%[+971588192166''] Abortion Pill Al Ain (?@?) Abortion Pill...
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting software
 
5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by Skilrock
 

Unity遊戲程式設計 - 2D platformer game

  • 1. 2D Platformer遊戲 Revised on January 1, 2020  背景動畫與背景音樂  玩家角色控制  隨機生成敵人  空投道具  使用道具  顯示分數  暫停遊戲控制
  • 2.  建立2D專案,my2DPlatformer  選單命令File> Save As…,將預設場景另存新檔  Exercise.unity  調整Main Camera  Position(X, Y, Z) = (-4.43, -0.15, -10)  Background(R, G, B, A) = (163, 187, 196, 5)  Size = 11  Free Aspect  滙入遊戲資源  選單命令Assets> Import Package> Custom Package…,滙入 platformer2d.unitypackage 建立專案 1/3 2
  • 3.  新增Tags  ground、Crate、Enemy、Wall、Obstacle、 Bullet、BombPickup、ExplosionFX、 HealthBar  新增Sorting Layers (遊戲圖層)  Background、Character、Foreground、UI  新增Layers (碰撞管理圖層)  Bombs、Player、Enemies、Pickups、 Ground 建立專案 2/3 3
  • 4.  選單命令Edit> Project Settings…  Physics 2D Layer Collicion Matrix 建立專案 3/3 4
  • 5.  將Assets/Prefebs/Environment/backgrounds.prefab加到場景  Sorting Layer = Background  設定所有子物件Sorting Layer = Background  將Assets/Prefabs/Environment/Foregrounds.prefab加到場景  設定所有子物件Sorting Layer = Foreground 建立場景 5
  • 6.  選取Prefabs/Props/swan.prefab  Sorting Layer = Background  選取Prefabs/Environment/Cab.prefab  Sorting Layer = Background  Wheels子物件  Sorting Layer = Background  選取Prefabs/Environment/Bus.prefab  Sorting Layer = Background  Wheels子物件  Sorting Layer = Background 設定背景動畫預製物件 6
  • 7.  在_Scripts目錄下新增BackgroundPropSpawner.cs程式腳本 using UnityEngine; using System.Collections; public class BackgroundPropSpawner : MonoBehaviour { public Rigidbody2D backgroundProp; //待生成的背景預製物件 public float leftSpawnPosX; //左側生成位置x座標 public float rightSpawnPosX; //右側生成位置x座標 public float minSpawnPosY; //生成位置y座標區間最小值 public float maxSpawnPosY; //生成位置y座標區間最大值 public float minTimeBetweenSpawns; //生成間隔時間最小值 public float maxTimeBetweenSpawns; //生成間隔時間最大值 public float minSpeed; //最小移動速度 public float maxSpeed; //移動速度最大值 void Start () { Random.InitState(System.DateTime.Today.Millisecond); StartCoroutine("Spawn"); //起始Spawn程序 } 隨機產生背景動畫物件 1/6 7
  • 8. IEnumerator Spawn () { float waitTime = Random.Range(minTimeBetweenSpawns, maxTimeBetweenSpawns); yield return new WaitForSeconds(waitTime); //隨機等待一段時間 bool facingLeft = Random.Range(0,2) == 0; //隨機設定預製物件方向 float posX = facingLeft ? rightSpawnPosX : leftSpawnPosX; float posY = Random.Range(minSpawnPosY, maxSpawnPosY); Vector3 spawnPos = new Vector3(posX, posY, transform.position.z); Rigidbody2D propInstance = Instantiate(backgroundProp, spawnPos, Quaternion.identity) as Rigidbody2D; if (!facingLeft) { Vector3 scale = propInstance.transform.localScale; scale.x *= -1; propInstance.transform.localScale = scale; } float speed = Random.Range(minSpeed, maxSpeed); //隨機設定速度 speed *= facingLeft ? -1f : 1f; propInstance.velocity = new Vector2(speed, 0); StartCoroutine(Spawn()); 隨機產生背景動畫物件 2/6 8
  • 9. while (propInstance != null) { if (facingLeft) { if (propInstance.transform.position.x < leftSpawnPosX - 0.5f) Destroy(propInstance.gameObject); } else { if (propInstance.transform.position.x > rightSpawnPosX + 0.5f) Destroy(propInstance.gameObject); } yield return null; } } } 隨機產生背景動畫物件 3/6 9
  • 10.  新增空物件,命名為swanCreator  重置Transform  加入BackgroundPropSpawner.cs程式腳本  拖曳Prefabs/Props/swan.prefab到Prop欄  Left Spawn Pos X = -24  Right Spawn Pos X = 24  Min Spawn Pos Y = 4  Max Spawn Pos Y = 8  Min Time Between Spawns = 2  Max Time Between Spawns = 8  Min Speed = 5  Max Speed = 8  測試遊戲,天空會隨機有天鵝飛過 隨機產生背景動畫物件 4/6 10
  • 11.  新增空物件,命名為busCreator  重置Transform  加入BackgroundPropSpawner.cs程式腳本  拖曳Prefabs/Environment/Bus.prefab到Prop欄  Left Spawn Pos X = -24  Right Spawn Pos X = 24  Min Spawn Pos Y = -5.5  Max Spawn Pos Y = -5.5  Min Time Between Spawns = 8  Max Time Between Spawns = 18  Min Speed = 5  Max Speed = 8  測試遊戲,街道會隨機有巴士通過 隨機產生背景動畫物件 5/6 11
  • 12.  新增空物件,命名為cabCreator  重置Transform  加入BackgroundPropSpawner.cs程式腳本  拖曳Prefabs/Environment/Cab.prefab到Prop欄  Left Spawn Pos X = -24  Right Spawn Pos X = 24  Min Spawn Pos Y = -6.4  Max Spawn Pos Y = -6.4  Min Time Between Spawns = 10  Max Time Between Spawns = 15  Min Speed = 5  Max Speed = 8  測試遊戲,街道會隨機有計程車通過 隨機產生背景動畫物件 6/6 12
  • 13.  新增空物件,命名為music  重置Transform  加入AudioSource元件  AudioClip = MainTheme  勾選Play On Awake  勾選Loop  Volume = 0.1  Reverb Zone Mix = 0  測試專案,遊戲執行時會持續撥放背景音樂 加入背景音樂 13
  • 14.  將Assets/Prefabs/Chractera/Mr.Bean.prefab加倒場景  設定所有子物件Sorting Layer = Character  Tag = Player  Layer = Player 建立玩家角色 14
  • 16.  在Mr.Bean物件加入PlayerControl.cs程式腳本 using UnityEngine; using System.Collections; public class PlayerControl : MonoBehaviour { [HideInInspector] public bool facingRight = true; //方向旗號,不顯示在屬性窗格,但可提其它程式腳本存取 [HideInInspector] public bool jump = false; //跳躍旗號,不顯示在屬性窗格,但可提其它程式腳本存取 public float moveForce = 365f; //行進力道 public float maxSpeed = 5f; //移動速度上限 public AudioClip[] jumpClips; //跳躍動作音效庫 public float jumpForce = 1000f; //跳躍力道 private Transform groundCheck; //用來檢查玩家角色是否站在地面 private bool grounded = false; //落地旗號 private Animator anim; //玩家角色動作控制器參照 玩家角色控制 2/5 16
  • 17. void Awake() { groundCheck = transform.Find("groundCheck"); anim = GetComponent<Animator>(); } void Update() { grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground")); //玩家角色位於在地面,並按下Jump鍵 if (Input.GetButtonDown("Jump") && grounded) jump = true; //執行跳躍動作 } 玩家角色控制 3/5 17
  • 18. void FixedUpdate () { float h = Input.GetAxis("Horizontal"); anim.SetFloat("Speed", Mathf.Abs(h)); if (h * GetComponent<Rigidbody2D>().velocity.x < maxSpeed) GetComponent<Rigidbody2D>().AddForce(Vector2.right * h * moveForce); //加速 if (Mathf.Abs(GetComponent<Rigidbody2D>().velocity.x) > maxSpeed) GetComponent<Rigidbody2D>().velocity = new Vector2(Mathf.Sign(GetComponent<Rigidbody2D>().velocity.x) * maxSpeed, GetComponent<Rigidbody2D>().velocity.y); if (h > 0 && !facingRight) Flip(); //反轉方向 else if (h < 0 && facingRight) Flip(); //反轉方向 if (jump) { anim.SetTrigger("Jump"); int i = Random.Range(0, jumpClips.Length); //隨機撥放跳躍音效 AudioSource.PlayClipAtPoint(jumpClips[i], transform.position); GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, jumpForce)); //跳躍 jump = false; //防止重複跳躍 } } 玩家角色控制 4/5 18
  • 19. void Flip () { facingRight = !facingRight; Vector3 theScale = transform.localScale; theScale.x *= -1; transform.localScale = theScale; } }  拖曳Audio/Player/Jumps/Player- jump[1-3].wav到Jump Clips欄  測試遊戲,可操控主角移動  適度調整移動速度及跳躍力道 玩家角色控制 5/5 19
  • 20.  新增空物件,命名為healthUI  Tag = HealthBar  Position(X, Y, Z) = (0, 100, 0)  在healthUI下新增Sprite物件,命名為HealthBar  Position(X, Y, Z) = (-0.83, 0, 0)  拖曳Assets/Sprites/_UI/Health.png到Sprite Renderer之Sprite欄  拖曳Assets/Materials/Health.mat到Sprite Renderer之Material欄  Sorting Layer = Foreground 建立玩家生命條 1/4 20
  • 21.  在healthUI下新增Sprite物件,命名為HealthOutline  Position(X, Y, Z) = (0, 0, 0)  拖曳Assets/Sprites/_UI/Health-bg.png到Sprite Renderer之 Sprite欄  拖曳Assets/Materials/DefaultPixelSnap.mat到Sprite Renderer 之Material欄  Sorting Layer = Foreground 建立玩家生命條 2/4 21
  • 22.  在healthUI加入FollowPlayer.cs程式腳本 using UnityEngine; using System.Collections; public class FollowPlayer : MonoBehaviour { public Vector3 offset; //顯示位置偏移值 private Transform player; void Awake () { player = GameObject.FindGameObjectWithTag("Player").transform; } void Update () { transform.position = player.position + offset; } } 建立玩家生命條 3/4 22
  • 23.  設定healthUI之FollowPlayer  Offset(X, Y, Z) = (0, 1.2, 0)  測試遊戲,玩家角色上方會顯示生命條並且跟隨移動 建立玩家生命條 4/4 23
  • 24.  在Mr.Bean物件加入PlayerHealth.cs程式腳本 using UnityEngine; using System.Collections; public class PlayerHealth : MonoBehaviour { public float health = 100f; //玩家生命值 public float repeatDamagePeriod = 1f; //玩家連續受傷之時時間隔 public AudioClip[] ouchClips; //玩家受傷音效 public float hurtForce = 100f; //玩家受傷時受到之推力 public float damageAmount = 10f; //每次受傷之損血值 private SpriteRenderer healthBar; //玩家生命條之sprite renderer參照 private float lastHitTime; //前次受傷時間戳記 private Vector3 healthScale; //生命條滿格時之大小 private PlayerControl playerControl; //玩家PlayerControl程式腳本參照 private Animator anim; //玩家動作控制器參照 玩家生命值管理 1/4 24
  • 25. void Awake () { playerControl = GetComponent<PlayerControl>(); //玩家PlayerControl參照 healthBar = GameObject.Find("HealthBar").GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); //玩家動作控制器參照 healthScale = healthBar.transform.localScale; //記錄生命條滿格之大小 } void OnCollisionEnter2D (Collision2D col) { if (col.gameObject.tag == "Enemy") { //敵人撞到玩家 if (Time.time > lastHitTime + repeatDamagePeriod) { //已達連續受傷之時間間隔 if (health > 0f) { TakeDamage(col.transform); //使玩家損血 lastHitTime = Time.time; //記錄受傷時間 } else { //玩家死亡,使玩家摔落河裡 Collider2D[] cols = GetComponents<Collider2D>(); foreach (Collider2D c in cols) { //將所有Collider2D調整為觸發器 c.isTrigger = true; } SpriteRenderer[] spr = GetComponentsInChildren<SpriteRenderer>(); 玩家生命值管理 2/4 25
  • 26. foreach(SpriteRenderer s in spr) { //將玩家角色移到UI圖層 s.sortingLayerName = "UI"; } GetComponent<PlayerControl>().enabled = false; //停止PlayerControl程式腳本 GetComponentInChildren<Gun>().enabled = false; //停止Gun程式腳本 anim.SetTrigger("Die"); //觸發Die動畫 } } } } void TakeDamage (Transform enemy) { playerControl.jump = false; //停止跳躍 Vector3 hurtVector = transform.position - enemy.position + Vector3.up * 5f; GetComponent<Rigidbody2D>().AddForce(hurtVector * hurtForce);//向玩家施加衝撞力 health -= damageAmount; //扣減玩家血量 UpdateHealthBar(); //更新顯示生命條 int i = Random.Range (0, ouchClips.Length); //隨機撥放玩家受傷音效 AudioSource.PlayClipAtPoint(ouchClips[i], transform.position); } 玩家生命值管理 3/4 26
  • 27. public void UpdateHealthBar () { //根據玩家的生命值,將生命條的顏色設置為綠色和紅色之間的比例 healthBar.material.color = Color.Lerp(Color.green, Color.red, 1 - health * 0.01f); //根據玩家的生命值,調整生命條的寬度 healthBar.transform.localScale = new Vector3(healthScale.x * health * 0.01f, 1, 1); } }  拖曳Assets/Audio/Player/Ouch/Player-ouch[1-4].wav到Ouch Clips欄 玩家生命值管理 4/4 27
  • 29.  在Main Camera加入CameraFollow.cs程式腳本 using UnityEngine; using System.Collections; public class CameraFollow : MonoBehaviour { public float xMargin = 1f; //攝影機啟動跟隨前允許玩家移動的水平位移 public float yMargin = 1f; //攝影機啟動跟隨前允許玩家移動的垂直位移 public float xSmooth = 8f; //使相機平穩地捕捉目標運動之X軸修正值 public float ySmooth = 8f; //使相機平穩地捕捉目標運動之Y軸修正值 public Vector2 maxXAndY; //攝影機位置X與Y座標最大值 public Vector2 minXAndY; //攝影機位置X與Y座標最小值 private Transform player; //玩家角色transform屬性 void Awake () { player = GameObject.FindGameObjectWithTag("Player").transform; } bool CheckXMargin() { return Mathf.Abs(transform.position.x - player.position.x) > xMargin; } 攝影機跟隨玩家移動 2/4 29
  • 30. bool CheckYMargin() { return Mathf.Abs(transform.position.y - player.position.y) > yMargin; } void FixedUpdate () { TrackPlayer(); } void TrackPlayer () { float targetX = transform.position.x; float targetY = transform.position.y; if (CheckXMargin()) targetX = Mathf.Lerp(transform.position.x, player.position.x, xSmooth * Time.deltaTime); if (CheckYMargin()) targetY = Mathf.Lerp(transform.position.y, player.position.y, ySmooth * Time.deltaTime); targetX = Mathf.Clamp(targetX, minXAndY.x, maxXAndY.x); targetY = Mathf.Clamp(targetY, minXAndY.y, maxXAndY.y); transform.position = new Vector3(targetX, targetY, transform.position.z); } } 攝影機跟隨玩家移動 3/4 30
  • 31.  設定Camera Follow參數  X Margin = 2,Y Margin = 2  X Smooth = 2,Y Smooth = 2  Max(X, Y) = (5, 5)  Min(X, Y) = (-5, -5)  測試遊戲,玩家水平或垂直位移超過2時,攝影機就會自動跟隨 攝影機跟隨玩家移動 4/4 31
  • 32.  選取Assets/Prefabs/Props/rocket.prefab  子物件Sorting Layer = Character  在Mr.Bean的Gun子物件加入Gun.cs腳本 using UnityEngine; using System.Collections; public class Gun : MonoBehaviour { public Rigidbody2D rocket; //rocket預製物件 public float speed = 25f; //rocket速度 private PlayerControl playerCtrl; //PlayerControl程式腳本參照 private Animator anim; //角色動畫控制器參照 void Awake() { anim = transform.root.gameObject.GetComponent<Animator>(); playerCtrl = transform.root.GetComponent<PlayerControl>(); } Mr.Bean射擊控制 1/3 32
  • 33. void Update () { if (Input.GetButtonDown("Fire1")) { //按下發射鍵 anim.SetTrigger("Shoot"); GetComponent<AudioSource>().Play(); if (playerCtrl.facingRight) { //向右發射 Rigidbody2D bulletInstance = Instantiate(rocket, transform.position, Quaternion.Euler(new Vector3(0, 0, 0))) as Rigidbody2D; bulletInstance.velocity = new Vector2(speed, 0); } else { //向左發射 Rigidbody2D bulletInstance = Instantiate(rocket, transform.position, Quaternion.Euler(new Vector3(0, 0, 180f))) as Rigidbody2D; bulletInstance.velocity = new Vector2(-speed, 0); } } } } Mr.Bean射擊控制 2/3 33
  • 34.  拖曳Assets/Prefabs/Props/rocket.prefab到Gun腳本的Rocket 資料欄  選取Assets/Prefabs/Props/rocketExplosion.prefab  Sorting Layer = Foreground  測試遊戲,點擊滑鼠左鍵可發射火箭彈 Mr.Bean射擊控制 3/3 34
  • 35.  選單命令GameObject> UI> Text,命名為Score  Anchor Presets = top, center  Pos(X, Y, Z) = (0, -10, 0)  Width = 500, Height = 100  Pivot(X, Y) = (0.5, 1)  Text = Score  Font = BradBunR  Font Size = 80  Alignment = Center  Color = white 加入計分板 1/5 35
  • 36.  在Score加入Score.cs程式腳本 using UnityEngine; using System.Collections; using UnityEngine.UI; public class Score : MonoBehaviour { public int score = 0; //玩家分數值 void Update () { GetComponent<Text>().text = "Score: " + score; //更新顯示分數 } } 加入計分板 2/5 36
  • 37.  選單命令GameObject> UI> Text,命名為Score-shadow  Anchor Presets = top, center  Pos(X, Y, Z) = (0, -14, 0)  Width = 500, Height = 100  Pivot(X, Y) = (0.5, 1)  Text = Score  Font = BradBunR  Font Size = 80  Alignment = Center  Color = black 加入計分板 3/5 37
  • 38.  在Score-shadow加入ScoreShadow.cs程式腳本 using UnityEngine; using System.Collections; using UnityEngine.UI; public class ScoreShadow : MonoBehaviour { public GameObject guiCopy; // Score物件參照 void Awake () { Vector3 behindPos = transform.position; behindPos = new Vector3(guiCopy.transform.position.x, guiCopy.transform.position.y - 4f, guiCopy.transform.position.z); transform.position = behindPos; } void Update () { GetComponent<Text>().text = guiCopy.GetComponent<Text>().text; //同步更新資料 } } 加入計分板 4/5 38
  • 39.  拖曳Score到Score-shadow之Gui Copy欄  調整Hierarhy窗格中物件順序,使Score位於Score-shadow下方  測試遊戲,場景中央上方會顯示陰影效果的得分值 加入計分板 5/5 39
  • 40.  選取Assets/Prefabs/Characters/enemy1.prefab  子物件Sorting Layer = Character  選取Assets/Prefabs/Characters/enemy2.prefab  子物件Sorting Layer = Character 隨機生成敵人 1/5 40
  • 41.  選單命令GameObject> Create Empty建立空物件,命名為 EnemySpawners  重置Transform  Position(X,Y,Z) = (0, 15, 0)  選單命令GameObject> Create Empty Child,在EnemySpawners 建立⼀個空子物件,命名為MidSpawner  重置Transform  Position(X,Y,Z) = (0.27, 0, 0)  在MidSpawner加入EnemySpawner.cs腳本 隨機生成敵人 2/5 41
  • 42. using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemySpawner : MonoBehaviour { public float spawnTime = 5f; //敵人生成間隔時間 public float spawnDelay = 3f; //延遲時間後才開始生成 public GameObject[] enemies; //敵人預製物件庫 void Start () { InvokeRepeating("Spawn", spawnDelay, spawnTime); } void Spawn () { int enemyIndex = Random.Range(0, enemies.Length); Instantiate(enemies[enemyIndex], transform.position, transform.rotation); } } 隨機生成敵人 3/5 42
  • 44.  複製2份MidSpawner,更名為LeftSpawner及RightSpawner  重置Transform  LeftSpawner Position(X,Y,Z) = (-13.8, 0, 0)  RightSpawner Position(X,Y,Z) = (14.5, 0, 0)  選取Assets/Prefabs/UI/ui_100points.prefab  子物件Sorting Layer = UI  測試遊戲  碰到敵人會損血,擊斃敵人會得分  玩家及敵人掉落河裡後,並不會被銷毀 隨機生成敵人 5/5 44
  • 45.  選單命令GameObject> Create Empty建立空物件,命名Destroyer  Position(X, Y, Z) = (0.54, -13.5, 0)  Scale(X, Y, Z) = (1.9, 1, 1)  加入Box Collider 2D  勾選Is Trigger  Size(X, Y) = (23, 1.9) 角色溺水作業 1/4 45
  • 46.  在Destroyer物件加入Remover.cs程式腳本 using UnityEngine; using UnityEngine.SceneManagement; using System.Collections; public class Remover : MonoBehaviour { public GameObject splash; //水花噴濺特效預製物件 void OnTriggerEnter2D(Collider2D col) { if (col.gameObject.tag == "Player") { //玩家掉落河裡 GameObject.FindGameObjectWithTag("MainCamera").GetComponent<CameraFollow>() .enabled = false; GameObject.FindGameObjectWithTag("HealthBar").GetComponent<FollowPlayer>(). enabled = false; Instantiate(splash, col.transform.position, transform.rotation); Destroy (col.gameObject); StartCoroutine("ReloadGame"); //執行重新載入關卡程序 } 角色溺水作業 2/4 46
  • 47. else { //其它角色掉落河裡 Instantiate(splash, col.transform.position, transform.rotation); Destroy (col.gameObject); } } IEnumerator ReloadGame() { yield return new WaitForSeconds(2); //等待2秒 SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex, LoadSceneMode.Single); } } 角色溺水作業 3/4 47
  • 48.  選取Assets/Prefabs/FX/splash.prefab  Sorting Layer = Foreground  拖曳Assets/Prefabs/FX/splash.prefab到Splash欄  測試遊戲  敵人掉落河裡會濺出水花並消毀  玩家落河後會濺出水花並重新開始關卡 角色溺水作業 4/4 48
  • 49.  選取Assets/Prefabs/Props/bombCrate.prefab  子物件Sorting Layer = Foreground  選取Assets/Prefabs/Props/healthCrate.prefab  子物件Sorting Layer = Foreground  在Assets/Prefabs/Props/healthCrate.prefab之health子物件加 入HealthPickup.cs程式腳本 製作空投道具箱 1/5 49
  • 50. using UnityEngine; using System.Collections; public class HealthPickup : MonoBehaviour { public float healthBonus; //補充生命值 public AudioClip collect; //收集道具時之音效 private PickupSpawner pickupSpawner; //PickupSpawner程式腳本參照 private Animator anim; //動畫控制器參照 private bool landed; //道具箱落地旗號 void Awake () { pickupSpawner = GameObject.Find("pickupManager").GetComponent<PickupSpawner>(); anim = transform.root.GetComponent<Animator>(); } void OnTriggerEnter2D (Collider2D other) { if (other.tag == "Player") { //玩家碰到道具箱 PlayerHealth playerHealth = other.GetComponent<PlayerHealth>(); playerHealth.health += healthBonus; //補充玩家生命值 playerHealth.health = Mathf.Clamp(playerHealth.health, 0f, 100f); //上限值100 製作空投道具箱 2/5 50
  • 51. playerHealth.UpdateHealthBar(); //更新顯示玩家生命條 pickupSpawner.StartCoroutine(pickupSpawner.DeliverPickup()); //啟動下一波空投程序 AudioSource.PlayClipAtPoint(collect,transform.position); //撥放音效 Destroy(transform.root.gameObject); //銷毀道具箱 } else if(other.tag == "ground" && !landed) { //道具箱落地 anim.SetTrigger("Land"); //觸發道具箱落地動畫 transform.parent = null; gameObject.AddComponent<Rigidbody2D>(); landed = true; } } }  拖曳Assets/Audio/FX/healthPickup.ogg到 Collect欄 製作空投道具箱 3/5 51
  • 52.  在Assets/Prefabs/Props/bombCrate.prefab之crate子物件加入 BombPickup.cs程式腳本 using UnityEngine; using System.Collections; public class BombPickup : MonoBehaviour { public AudioClip pickupClip; //收集道具時之音效 private Animator anim; //動畫控制器參照 private bool landed = false; //道具箱落地旗號 void Awake() { anim = transform.root.GetComponent<Animator>(); } void OnTriggerEnter2D (Collider2D other) { if (other.tag == "Player") { //玩家碰到道具箱 AudioSource.PlayClipAtPoint(pickupClip, transform.position); other.GetComponent<LayBombs>().bombCount++; //增加玩家炸彈數量 Destroy(transform.root.gameObject); //銷毀道具箱 } 製作空投道具箱 4/5 52
  • 53. else if (other.tag == "ground" && !landed) { //道具箱落地 anim.SetTrigger("Land"); //觸發道具箱落地動畫 transform.parent = null; gameObject.AddComponent<Rigidbody2D>(); landed = true; } } }  拖曳Assets/Audio/Player/Taunts/Player-IDefyYou.wav到 Pickup Clip欄 製作空投道具箱 5/5 53
  • 54.  選單命令GameObject> Create Empty建立空物件,命名 pickupManager  重置Transform  加入PickupSpawner.cs程式腳本 空投道具 1/4 54
  • 55. using UnityEngine; using System.Collections; public class PickupSpawner : MonoBehaviour { public GameObject[] pickups; //道具預製物件陣列 public float pickupDeliveryTime = 5f; //傳送道具等待時間 public float dropRangeLeft; //道具空投範圍左邊界坐標 public float dropRangeRight; //道具空投範圍右邊界坐標 public float highHealthThreshold = 75f; //玩家生命值超過此設定值時,只空投炸彈道具 public float lowHealthThreshold = 25f; //玩家生命值低於此設定值時,只空投傷藥道具 private PlayerHealth playerHealth; //PlayerHealth程式腳本參照 void Awake () { playerHealth = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHea lth>(); } 空投道具 2/4 55
  • 56. void Start () { StartCoroutine(DeliverPickup()); //啟動DeliverPickup程序 } public IEnumerator DeliverPickup() { yield return new WaitForSeconds(pickupDeliveryTime); //等待一段指定時間 float dropPosX = Random.Range(dropRangeLeft, dropRangeRight);//隨機空投X坐標 Vector3 dropPos = new Vector3(dropPosX, 15f, 1f); //空投位置 if (playerHealth.health >= highHealthThreshold) //空投炸彈 Instantiate(pickups[0], dropPos, Quaternion.identity); else if (playerHealth.health <= lowHealthThreshold) //空投傷藥 Instantiate(pickups[1], dropPos, Quaternion.identity); else { //隨機空投道具 int pickupIndex = Random.Range(0, pickups.Length); Instantiate(pickups[pickupIndex], dropPos, Quaternion.identity); } } } 空投道具 3/4 56
  • 57.  設定Pickups Spawner  拖曳Assets/Prefabs/Props/bombCrate.prefab到Pickups欄  拖曳Assets/Prefabs/Props/healthCrate.prefab到Pickups欄  Pickup Delivery Time = 5  Drop Range Left = -15  Drop Range Right = 15  High Health Threshold = 75  Low Health Threshold = 25  測試遊戲,會隨機飄下道具  玩家Player Health之Health值小於25時,⼀定飄下急救箱  玩家Player Health之Health值大於75時,⼀定飄下炸彈 空投道具 4/4 57
  • 58.  選單命令GameObject> UI> Raw Image,命名為ui_bombHUD  Anchor Presets = bottom, left  Pos(X, Y, Z) = (10, 10, 0)  Width = 84, Height = 70  Pivot(X, Y) = (0, 0)  拖曳Assets/Sprites/_Props/ prop_crate_ammo.png到Texture欄 製作炸彈圖示 58
  • 59.  將Assets/Prefabs/Props/explosionParticle.prefab加到場景  選取Assets/Prefabs/Props/bomb.prefab  Sorting Layer = Character  在Mr.Bean加入LayBombs.cs程式腳本 using UnityEngine; using System.Collections; using UnityEngine.UI; public class LayBombs : MonoBehaviour { [HideInInspector] public bool bombLaid = false; //玩家是否已放置炸彈 public int bombCount = 0; //玩家擁有的炸彈道具個數 public AudioClip bombsAway; //玩家放置炸彈時的語音 public GameObject bomb; //炸彈預製物件 private RawImage bombHUD; //炸彈道具圖示參照,當玩家擁有後就會開啟圖示 玩家放置炸彈 1/3 59
  • 60. void Awake () { bombHUD = GameObject.Find("ui_bombHUD").GetComponent<RawImage>(); } void Update () { if (Input.GetButtonDown("Fire2") && !bombLaid && bombCount > 0) { //放置炸彈 bombCount--; bombLaid = true; AudioSource.PlayClipAtPoint(bombsAway,transform.position); Instantiate(bomb, transform.position, transform.rotation); } bombHUD.enabled = bombCount > 0; //更新炸彈圖示狀態 } } 玩家放置炸彈 2/3 60
  • 61.  拖曳Assets/Prefabs/Props/bomb.prefab到Bomb欄  拖曳Assets/Audio/Player/Taunts/Player-BombsAway.ogg到 Bombs Away欄  執行遊戲  Bomb Count大於0時,會顯示炸彈道具圖示  拿到炸彈道具後,使用滑鼠右鍵放置炸彈 玩家放置炸彈 3/3 61
  • 62.  選單命令GameObject> Create Empty建立空物件,命名GameManager  在GameManager加入Pauser.cs程式腳本 using UnityEngine; using System.Collections; public class Pauser : MonoBehaviour { private bool paused = false; void Update () { if (Input.GetKeyUp(KeyCode.P)) { //按了P鍵 paused = !paused; //切換暫停狀態 } if (paused) Time.timeScale = 0; else Time.timeScale = 1; } }  測試遊戲,按P鍵可暫停遊戲 遊戲控制 1/2 62
  • 63.  編輯GameManager.cs程式腳本,按Esc鍵可結束遊戲 using UnityEngine; using System.Collections; public class Pauser : MonoBehaviour { private bool paused = false; void Update () { if (Input.GetKey (KeyCode.Escape)) { //按了Esc鍵 Application.Quit (); //結束遊戲 } if (Input.GetKeyUp(KeyCode.P)) { //按了P鍵 ... } } 註:需建立執行檔測試 遊戲控制 2/2 63
  • 64.  選單命令Files>Build Settings…  設定遊戲場景、選擇遊戲平台  點擊Build按鈕 建立執行檔 64