根據(jù)Unity官網(wǎng)的SpaceShooter教程視頻和參考資料的博客完成了這個小游戲溃蔫,下邊簡單的記錄一下健提。
參考資料:
http://www.reibang.com/p/8cc3a2109d3b
https://unity3d.com/cn/learn/tutorials/projects/space-shooter-tutorial
開發(fā)步驟
1.首先在Unity的 Asset Store下載游戲的資源包,包含了游戲的背景伟叛,模型矩桂,音頻等。
2.將飛機模型Player添加到新建的場景_Scenes中,調(diào)整相機位置侄榴,新建燈光雹锣,調(diào)整燈光位置。
3..為Player添加腳本PlayerController癞蚕,控制Player移動蕊爵。
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
//moveHorizontal和moveVertical記錄玩家輸入的方向數(shù)據(jù)
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
//剛體根據(jù)矢量方向movement移動,位移為speed
GetComponent<Rigidbody>().velocity = movement * speed;
//移動時傾斜桦山,以Z為中心軸旋轉(zhuǎn)
GetComponent<Rigidbody>().rotation = Quaternion.Euler(0.0f, 0.0f, GetComponent<Rigidbody>().velocity.x * - tilt);
//首先序列化Boundary類,保存飛船在X和Z軸上的范圍
//Mathf.Clamp將飛船的x和z值鎖定在屏幕范圍內(nèi)
GetComponent<Rigidbody>().position = new Vector3(
Mathf.Clamp(GetComponent<Rigidbody>().position.x, boundary.xMin, boundary.xMax),
0.0f,
Mathf.Clamp(GetComponent<Rigidbody>().position.z, boundary.zMin, boundary.zMax)
);
}
- 4.添加游戲背景攒射,制作子彈,為子彈添加碰撞:添加Rigidbody(diselect use gravity)恒水, 添加碰撞Mesh Collider(select is Trigger)会放,為子彈編寫腳本Mover,使子彈發(fā)射钉凌,將子彈設(shè)為Prefab
public float speed;
void Start () {
//transform.forward:The blue axis of the transform in world space.
GetComponent<Rigidbody>().velocity = transform.forward * speed;
}
- 5.設(shè)置火箭發(fā)射子彈咧最。設(shè)置子彈發(fā)射點,新建Shot Spawn御雕,把Shot Spawn拖入到Player下面矢沿,并調(diào)整Shot Spawn的位置到合適的點,在PlayerController編寫腳本
private float nextFire; //記錄下一發(fā)子彈的時間
public float fireRate; //控制子彈發(fā)射的間隔時間
public GameObject shot; //保存我們發(fā)射的子彈Prefab
public Transform shotSqawn; //保存子彈的發(fā)射點
void Update () {
//按下發(fā)射鍵(ctrl或鼠標左鍵)并且到達下一發(fā)子彈發(fā)射時間
if (Input.GetButton("Fire1") && Time.time > nextFire)
{
nextFire = Time.time + fireRate;//重置下一發(fā)子彈發(fā)射時間
//Instantiate方法生成一發(fā)子彈
//3個參數(shù)酸纲,一個是發(fā)射的對象(復(fù)制出一個新的)捣鲸,一個是發(fā)射對象的position,一個是發(fā)射對象的rotation
Instantiate(shot, shotSqawn.position, shotSqawn.rotation);
GetComponent<AudioSource>().Play();
}
}
- 6.回收子彈闽坡,飛船每發(fā)射一顆子彈栽惶,就會生成一個Bolt對象。創(chuàng)建一個盒子疾嗅,這個盒子大小和我們的屏幕差不多媒役,然后我們可以判斷如何子彈飛出了這個盒子,就意味著子彈飛出了屏幕宪迟,這個時候我們就可以通知程序來回收子彈酣衷。創(chuàng)建cube,命名Boundary次泽,將盒子調(diào)整到屏幕大小穿仪,刪除 mesh renderer,添加Box Colider意荤,is Trigger set on啊片,為盒子添加腳本DestroyByBoundary
//DestroyByBoundary中添加碰撞的觸發(fā)方法。即當子彈碰到時玖像,子彈被回收
void OnTriggerExit(Collider other)
{
//注意紫谷,避免將飛船回收
if(other.tag != "Player")
{
Destroy(other.gameObject);
}
}
- 7.創(chuàng)建障礙,創(chuàng)建GameObject,命名為Asteroid笤昨,為Asteroid添加美術(shù)效果祖驱,我們在Assets->Models文件夾中找到prop_asteroid_01文件,然后將其拖入Asteroid做其的子對象瞒窒,為Asteroid添加Rigidibody吹缔,diselect use gravity永脓,將阻力drag 和angular drag設(shè)為0,添加capsule collider茄猫,編寫腳本RandomRotator笛求,使其自動旋轉(zhuǎn)
public float tumble;
//tumble來設(shè)置障礙翻滾的幅度
void Start () {
GetComponent<Rigidbody>().angularVelocity = Random.insideUnitSphere * tumble;
}
- 8.首先我們需要檢測子彈和障礙是否碰撞度气,如果碰撞了校哎,我們需要創(chuàng)建一個爆炸效果來給予反饋薄辅,最后我們還需要將子彈和障礙刪除掉。再為子彈編寫一個腳本DestroyByContact
public GameObject explosion; //爆炸效果
public GameObject playerExplosion;
void OnTriggerEnter(Collider other)
{
//初始時障礙物會與盒子boundary發(fā)生碰撞觸發(fā)此函數(shù)巴比,要規(guī)避此種觸發(fā)
if (other.tag == "Boundary")
{
return;
}
//生成一個爆炸效果
Instantiate(explosion, transform.position, transform.rotation);
if(other.tag == "Player")
{
Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
gameController.GameOver();
}
gameController.AddScore(scoreValue);
//Debug.Log(other.name);
Destroy(other.gameObject);
Destroy(gameObject);
}
回到Unity編輯器术奖,接著在Assets->Prefabs->VFX->Explosions文件夾中找到explosion_asteroid文件,然后拖入到我們定義的explosion變量中匿辩。
- 8. 使障礙物向下移動,隨機生成障礙榛丢。mover是之前為子彈添加的腳本铲球,同樣可以用到障礙物上,只需將speed設(shè)置為負數(shù)晰赞,即可實現(xiàn)向下移動稼病。新建一個GameObject, 命名為GameController掖鱼,實現(xiàn)這些邏輯
public GameObject hazard; //設(shè)置生成的障礙對象
public Vector3 spawnValue; //設(shè)置障礙生成的位置
public int hazardCount; //每次生成的障礙的數(shù)量
public float spawnWait; //每個障礙生成的間隔時間
public float startWait; //游戲開始生成障礙的準備時間
public float waveWait; //設(shè)置每一波的間隔時間
IEnumerator SpawnWaves()
{
//讓代碼等待一定時間然走,這是個協(xié)同程序,可以讓游戲不暫停的同時戏挡,讓代碼暫停
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
//隨機生成障礙位置
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), spawnValue.y, spawnValue.z);
//Rotation屬性如何確定芍瑞,Rotation屬性是一個Quaternion類型的變量,
//所以我們定義了一個spawnRotation的變量褐墅,用來記錄障礙的Rotation屬性拆檬,在這里我們不需要讓障礙一開始帶著任何的旋轉(zhuǎn)
//所以我們直接賦值Quaternion.identity
Quaternion spawnRotation = Quaternion.identity;
Instantiate(hazard, spawnPosition, spawnRotation);
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
if(gameOver == true)
{
restartText.text = "Press R for Restart";
restart = true;
break;
}
}
}
void Start () {
//因為SpawnWaves中有協(xié)同程序,StartCoroutine為啟動一個協(xié)同程序
StartCoroutine(SpawnWaves());
}
- 9. 回收爆炸效果妥凳,新建腳本DestroyByTime竟贯,lifetime為存活時間,將此腳本添加到perfab下的爆炸效果中逝钥,并設(shè)置lifetime屑那。
public float lifeTime;
// Use this for initialization
void Start () {
Destroy(gameObject,lifeTime);
}
- 10.添加背景音樂,Assets->Audio文件夾中找到背景音樂music_background直接拖入GameController對象,同理持际,障礙爆炸和飛船爆炸音效沃琅,也是將其音頻文件直接拖到對應(yīng)的perfab文件中,這些音頻文件是默認play on awake的选酗,其中還要講背景音樂選中l(wèi)oop阵难,子彈音效不能設(shè)為play on wake,只需要在發(fā)射時有聲音芒填,需要在腳本中設(shè)置呜叫,只需在PlayController中添加
void Update () {
//按下發(fā)射鍵(ctrl或鼠標左鍵)并且到達下一發(fā)子彈發(fā)射時間
if (Input.GetButton("Fire1") && Time.time > nextFire)
{
nextFire = Time.time + fireRate;
//Instantiate方法生成一發(fā)子彈,他有3個參數(shù)
//一個是發(fā)射的對象(復(fù)制出一個新的)殿衰,一個是發(fā)射對象的position朱庆,一個是發(fā)射對象的rotation
Instantiate(shot, shotSqawn.position, shotSqawn.rotation);
//當子彈發(fā)射時開啟音效
GetComponent<AudioSource>().Play();
}
}
- 11.游戲計分按鈕等,新建 scoreText闷祥,restartText娱颊,gameOverText,并調(diào)整其在屏幕上的位置凯砍。在GameController腳本設(shè)置箱硕。
public Text scoreText;
public Text restartText;
public Text gameOverText;
private bool gameOver;
private bool restart;
private int score;
IEnumerator SpawnWaves()
{
//讓代碼等待一定時間,這是個協(xié)同程序悟衩,可以讓游戲不暫停的同時剧罩,讓代碼暫停
yield return new WaitForSeconds(startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
//隨機生成障礙位置
Vector3 spawnPosition = new Vector3(Random.Range(-spawnValue.x, spawnValue.x), spawnValue.y, spawnValue.z);
//Rotation屬性如何確定,Rotation屬性是一個Quaternion類型的變量座泳,
//所以我們定義了一個spawnRotation的變量惠昔,用來記錄障礙的Rotation屬性,在這里我們不需要讓障礙一開始帶著任何的旋轉(zhuǎn)
//所以我們直接賦值Quaternion.identity
Quaternion spawnRotation = Quaternion.identity;
Instantiate(hazard, spawnPosition, spawnRotation);
yield return new WaitForSeconds(spawnWait);
}
yield return new WaitForSeconds(waveWait);
if(gameOver == true)
{
restartText.text = "Press R for Restart";
restart = true;
break;
}
}
}
void UpdateScore()
{
scoreText.text = "Score :" + score;
}
public void AddScore(int newScoreValue)
{
score += newScoreValue;
UpdateScore();
}
public void GameOver ()
{
gameOverText.text = "Game Over !";
gameOver = true;
}
void Start () {
gameOver = false;
restart = false;
restartText.text = "";
gameOverText.text = "";
score = 0;
UpdateScore();
//因為SpawnWaves中有協(xié)同程序挑势,StartCoroutine為啟動一個協(xié)同程序
StartCoroutine(SpawnWaves());
}
void Update () {
if (restart)
{
if (Input.GetKeyDown(KeyCode.R))
{
//重新加載
Application.LoadLevel(Application.loadedLevel);
}
}
}
在GameController腳本中設(shè)置計分的相關(guān)方法镇防,在DestoryByContact的腳本中調(diào)用這些方法
using UnityEngine;
using System.Collections;
public class DestroyByContact : MonoBehaviour {
public GameObject explosion;
public GameObject playerExplosion;
public int scoreValue;//分值
private GameController gameController; //gameController腳本類
// Use this for initialization
void Start () {
//獲取GameController的實例
//首先找到GameController的GameObject,然后在通過GetComponent方法來獲取GameController腳本的實例
GameObject gameControllerObject = GameObject.FindWithTag("GameController");
if (gameControllerObject != null)
{
gameController = gameControllerObject.GetComponent<GameController>();
}
if (gameController == null)
{
Debug.Log("Cannot find 'GameController' script");
}
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider other)
{
//初始時障礙物會與盒子boundary發(fā)生碰撞觸發(fā)此函數(shù)潮饱,要規(guī)避此種觸發(fā)
if (other.tag == "Boundary")
{
return;
}
//生成一個爆炸效果
Instantiate(explosion, transform.position, transform.rotation);
//飛船碰到障礙物時爆炸效果
if(other.tag == "Player")
{
Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
//飛船爆炸時調(diào)用gameover方法
gameController.GameOver();
}
gameController.AddScore(scoreValue);
//Debug.Log(other.name);
Destroy(other.gameObject);
Destroy(gameObject);
}
}
總結(jié):在實際操作中来氧,對unity的一些構(gòu)建,基本的操作不熟悉香拉,還有場景的設(shè)置即位置等饲漾。了解到了腳本的功能,控制GameObject缕溉,觸發(fā)事件考传,不同的腳本之間相互調(diào)用。這個游戲证鸥,通過腳本新建的對象如子彈障礙物是不會自動回收的僚楞,需要在最外圍設(shè)置一個框來檢測物體碰撞觸發(fā)事件進行回收勤晚,同時爆炸時Instantiate方法產(chǎn)生的爆炸效果,也不會被自動回收泉褐,需要對這些效果寫腳本DestoryByTime定時進行Destory赐写。
畫一個非常丑的圖。膜赃。挺邀。
- PlayerController:飛船的移動,發(fā)射子彈跳座。
- Mover:設(shè)置速度端铛,自動控制移動。
- RandomRotator: 自動控制障礙物旋轉(zhuǎn)疲眷。
- DestoryByContact:碰撞時觸發(fā)的方法禾蚕,物體消失,爆炸特效狂丝,計分换淆,飛船爆炸時調(diào)用GameController的GameOver方法。
- DestoryByBoundary:子彈或障礙物出邊界几颜,即與Boundary相碰撞觸發(fā)方法Destory對象倍试。
- DestoryByTime:特效對象Destory。
- GameController: 產(chǎn)生障礙物蛋哭,計分县习,Restart等方法實現(xiàn)。