1. 程式人生 > >[Unity3D]Unity3D官方案例SpaceShooter開發教程

[Unity3D]Unity3D官方案例SpaceShooter開發教程

1、先匯入SpaceShooter的環境資源,將Models下的vehicle_playerShip放到Hierachy視窗,改名為Player.Rotation x改為270,為Player新增mesh collider元件,將vehicle_playerShip_collider放到Mesh屬性裡面,為Player新增剛體,去掉重力的勾選,將Prefebs中的engines_player放到Hierarchy,並調好位置。
2、進入3D檢視,將MainCamera中的Projection改為Orthographic,攝像機檢視變為矩形檢視。將Camered的背景改為黑色。將Player的Rotation x 改為零。Camera的Rotation改為90,移動攝像機,調節位置在Game檢視中檢視。通過調節Camera的Size來調節Player的大小,通過調節Camera的Position來調節Player的位置,適當即可。
3、新增一個Directional Light,改名為Main Light,然後reset,改Rotation x為20,y為245.Intensity改為0.75,同理繼續新增一個,改名為Fill Light,將Rotation y改為125,點開color將RGB分別改為128,192,192.在新增一個Rim Light,將Rotation x,y分別改為345,65,Indensity改為0.25,Ctrl+Shift+N建立一個GameObject,改名為Light,將三個Light放進去。
4、新增一個Quad元件,改名為Background,rotation x 90,新增一個紋理tile_nebula_green_dff,將tile_nebula_green_dff中的shader改為Unlity/Texture,將Position y 改為-10,將Scale x, y改為15和30,這是紋理圖片的大小比例決定的,1:2。
5、建立GameObject,改名為GameCotroller,新增指令碼GameCotroller.cs,為Player新增一個新的指令碼,命名為PlayerController,指令碼為:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
   
    public float zMin = -3.3f;
    public float zMax = 14f;
    public float xMin = -6.5f;
    public float xMax = 6.5f;
}
public class PlayerController : MonoBehaviour {
    public float speed = 10f;
    public Boundary bound;
    void FixedUpdate()
    {
       
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);
        rigidbody.velocity = speed * move;
        rigidbody.position = new Vector3(Mathf.Clamp(rigidbody.position.x, bound.xMin, bound.xMax), 0, Mathf.Clamp(rigidbody.position.z, bound.zMin, bound.zMax));
    }
} 程式碼心得:限制需要在Game檢視移動來調節。記住了rigidbody的velocity方法,限制Player的飛行範圍可通過rigidbody.position來控制。學習了Mathf.Clamp(rigidbody.position.x, bound.xMin, bound.xMax)的用法。學習了系列化[System.Serializable]在視窗檢視中顯示。
6、建立一個GameObject,改名為Bolt,建立一個GameObject,改名為VFX,放到Bolt下,將fx_lazer_orange_dff拖到VFX Inspector中,將Shader改為Particles/Additive可將背景設定為透明。給Bolt加上RigidBody,去掉use Gravity.新增Mover指令碼:
using UnityEngine;
using System.Collections; public class Mover : MonoBehaviour {
    public float speed = 20f;
 // Use this for initialization
 void Start () {
        rigidbody.velocity = transform.forward * speed;
 } }
再新增一個Capsule Collider用於碰撞檢測,將Radius和height的值設為0.05,0.55.direction設為Z-Axis,現在報錯,是因為FBX的MeshRenderer和Bolt的Capsule Collider衝突,刪掉FBX的MeshRenderer。注意一點要想把改變的同步到Prefabs中就得點選應用。
開始新增子彈,建立新的GameObject,命名為SpawnPos放到Player下面。繼續編輯PlayerController指令碼。程式碼如下:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
   
    public float zMin = -3.3f;
    public float zMax = 14f;
    public float xMin = -6.5f;
    public float xMax = 6.5f;
}
public class PlayerController : MonoBehaviour {
    public float speed = 10f;
   
    public Boundary bound;
    public GameObject bolt;
    public Transform spawnPos;
    void Update()
    {
        if(Input.GetButton("Fire1"))
        {
            Instantiate(bolt, spawnPos.position, spawnPos.rotation);
        }
    }
    void FixedUpdate()
    {
       
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);
        rigidbody.velocity = speed * move;
        rigidbody.position = new Vector3(Mathf.Clamp(rigidbody.position.x, bound.xMin, bound.xMax), 0, Mathf.Clamp(rigidbody.position.z, bound.zMin, bound.zMax));
    }
}
問題:運行遊戲,發現遊戲的子彈是散的。將Bolt裡面的is Trigger勾選上可防止這種情況的出現。現在發現子彈的發射過於連續,新增程式碼:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
   
    public float zMin = -3.3f;
    public float zMax = 14f;
    public float xMin = -6.5f;
    public float xMax = 6.5f;
}
public class PlayerController : MonoBehaviour {
    public float speed = 10f;
   
    public Boundary bound;
    public GameObject bolt;
    public Transform spawnPos;
    public float fireRate = 0.1f;
    private float nextFire;
    void Update()
    {
        if(Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(bolt, spawnPos.position, spawnPos.rotation);
        }
    }
    void FixedUpdate()
    {
       
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);
        rigidbody.velocity = speed * move;
        rigidbody.position = new Vector3(Mathf.Clamp(rigidbody.position.x, bound.xMin, bound.xMax), 0, Mathf.Clamp(rigidbody.position.z, bound.zMin, bound.zMax));
    }
}
Bolt(clone)不會消失,現在開始製作子彈的邊界。
建立一個cube,改名為Boundary,將Box Collider的Size改為15 和 30,位置調節到與Background位置一致。刪除掉mesh collider等元件,新增指令碼DesdroyByBoundary。程式碼如下:
using UnityEngine;
using System.Collections; public class DestroyByBoundary : MonoBehaviour
{     void OnTriggerExit(Collider other)
    {
        Destroy(other.gameObject);
    }
}
現在開始建立隕石,建立新的GameObject,改名為Asteroid,將prop_asteroid_01拖入其中,新增Rigidbody和Capsule Collider,修改引數使Capsule Collider與物體差不多吻合。RigidBody去掉use Gravity.新增一個指令碼,RandomRotation,程式碼如下:
using UnityEngine;
using System.Collections; public class RandomRotation : MonoBehaviour {     public float tumble = 5;
 // Use this for initialization
 void Start () {
        rigidbody.angularVelocity = Random.insideUnitSphere * tumble;
 }
 
} Asteroid新增一個Mover,改Speed為-5。現在隕石可以向下旋轉且下落。
同理新增Asteroid2和Asteroid3.
新增一個指令碼,改名為DestroyByCantact,如下:
using UnityEngine;
using System.Collections; public class DestroyByCantact : MonoBehaviour {  void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Boundary")
        {
            return;
        }
        Destroy(other.gameObject);
        Destroy(this.gameObject);
    }
} 新增到各個Asteroid中。
DestroyByCantact新增程式碼:
using UnityEngine;
using System.Collections; public class DestroyByCantact : MonoBehaviour {
    public GameObject explossion;
    public GameObject playerExplosion;
 void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Boundary")
        {
            return;
        }
        if(other.gameObject.tag == "player")
        {
            Instantiate(playerExplosion, this.transform.position, this.transform.rotation);
        }
        Destroy(other.gameObject);
        Destroy(this.gameObject);
        Instantiate(explossion, this.transform.position, this.transform.rotation);
    }
} 將各個爆炸效果分別拖到指令碼中,應用到Prefabs當中。
接下來給GameCotroller指令碼加程式碼:
using UnityEngine;
using System.Collections; public class GameController : MonoBehaviour
{     public Vector3 spawnValues;
    public GameObject[] hazards;
    // Use this for initialization
    void Start()
    {     }     // Update is called once per frame
    void Update()
    {
        if (Random.value < 0.1)
        {
            Spawn();
        }
    }
    void Spawn()
    {
        GameObject o = hazards[Random.Range(0, hazards.Length)];
        Vector3 p = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
        Quaternion q = Quaternion.identity;
        Instantiate(o, p, q);
    }
}
將三個隕石分別新增到Hazards裡面。
繼續修改GameController程式碼:
using UnityEngine;
using System.Collections; public class GameController : MonoBehaviour
{     public Vector3 spawnValues;
    public GameObject[] hazards;
    public int numPerWave = 10;     public float startWait = 2f;   //出現小行星之前的等待時間
    public float spanWait = 1f;     //一波小行星內的間隔時間
    public float waveWait = 4f;   //每兩波時間間隔
    // Use this for initialization
    void Start()
    {
        StartCoroutine(SpawnWaves());
    }     // Update is called once per frame
    void Update()
    {
        //if (Random.value < 0.1)
        //{
         //   Spawn();
    //}
    }
    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < numPerWave; i++)
            {
                Spawn();
                yield return new WaitForSeconds(spanWait);
            }
            yield return new WaitForSeconds(waveWait);
        }
       
    }     private void WaitForSeconds(float spanWait)
    {
        throw new System.NotImplementedException();
    }
    void Spawn()
    {
        GameObject o = hazards[Random.Range(0, hazards.Length)];
        Vector3 p = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
        Quaternion q = Quaternion.identity;
        Instantiate(o, p, q);
    }
}
裡面涉及到技巧性。
新增音效,有幾個可以直接新增。對於player發射子彈的聲音可以在程式碼中新增。
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
   
    public float zMin = -3.3f;
    public float zMax = 14f;
    public float xMin = -6.5f;
    public float xMax = 6.5f;
}
public class PlayerController : MonoBehaviour {
    public float speed = 10f;
   
    public Boundary bound;
    public GameObject bolt;
    public Transform spawnPos;
    public float fireRate = 0.1f;
    private float nextFire;
    public AudioClip fire;
    void Update()
    {
        if(Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(bolt, spawnPos.position, spawnPos.rotation);
            audio.PlayOneShot(fire);
        }
    }
    void FixedUpdate()
    {
       
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);
        rigidbody.velocity = speed * move;
        rigidbody.position = new Vector3(Mathf.Clamp(rigidbody.position.x, bound.xMin, bound.xMax), 0, Mathf.Clamp(rigidbody.position.z, bound.zMin, bound.zMax));
    }
}
新增指令碼將爆炸效果去除
建立DestroyByTime,如下:
using UnityEngine;
using System.Collections; public class DestroyByTime : MonoBehaviour {     public float lifeTime = 2;
 // Use this for initialization
 void Start () {
        Destroy(this.gameObject, lifeTime);
 }
 
 // Update is called once per frame
 void Update () {
 
 }
}
新增分數,建立一個GUIText,修改y為1,修改程式碼GameController
using UnityEngine;
using System.Collections; public class GameController : MonoBehaviour
{     public Vector3 spawnValues;
    public GameObject[] hazards;
    public int numPerWave = 10;     public float startWait = 2f;   //出現小行星之前的等待時間
    public float spanWait = 1f;     //一波小行星內的間隔時間
    public float waveWait = 4f;   //每兩波時間間隔     public GUIText scoreText;
    private int score;
    // Use this for initialization
    void Start()
    {
        StartCoroutine(SpawnWaves());
        scoreText.text = "Score: " + score;
    }     // Update is called once per frame
    void Update()
    {
        //if (Random.value < 0.1)
        //{
         //   Spawn();
    //}
    }
    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < numPerWave; i++)
            {
                Spawn();
                yield return new WaitForSeconds(spanWait);
            }
            yield return new WaitForSeconds(waveWait);
        }
       
    }     private void WaitForSeconds(float spanWait)
    {
        throw new System.NotImplementedException();
    }
    void Spawn()
    {
        GameObject o = hazards[Random.Range(0, hazards.Length)];
        Vector3 p = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
        Quaternion q = Quaternion.identity;
        Instantiate(o, p, q);
    }
    public void AddScore(int v)
    {
        score += v;
        scoreText.text = "Score: " + score;
    }
}
將GameController物體的TAG改為GameController,修改DesdroyByContact指令碼:
using UnityEngine;
using System.Collections; public class DestroyByCantact : MonoBehaviour {
    public GameObject explossion;
    public GameObject playerExplosion;     private GameController gameController;
    public int score = 10;
    void Start()
    {
        GameObject gameControllerObject = GameObject.FindWithTag("GameController");
        if(gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();
        }
        if(gameControllerObject == null)
        {
            Debug.Log("TaoCheng");
        }
    }
 void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Boundary")
        {
            return;
        }
        if(other.gameObject.tag == "player")
        {
            Instantiate(playerExplosion, this.transform.position, this.transform.rotation);
        }
        if(other.gameObject.tag == "enemy")
        {
            return;
        }
        Destroy(other.gameObject);
        gameController.AddScore(score);
        Destroy(this.gameObject);
        Instantiate(explossion, this.transform.position, this.transform.rotation);
    }
}
做顯示遊戲結束和遊戲重新開始。
GameCotroller指令碼程式碼如下:
using UnityEngine;
using System.Collections; public class GameController : MonoBehaviour
{     public Vector3 spawnValues;
    public GameObject[] hazards;
    public int numPerWave = 10;     public float startWait = 2f;   //出現小行星之前的等待時間
    public float spanWait = 1f;     //一波小行星內的間隔時間
    public float waveWait = 4f;   //每兩波時間間隔     public GUIText scoreText;
    private int score;
    private bool gameOver;
    public GUIText gameOverText;
    public GUIText helpText;
    // Use this for initialization
    void Start()
    {
        StartCoroutine(SpawnWaves());
        scoreText.text = "Score: " + score;
        gameOverText.text = "";
        helpText.text = "";
    }     // Update is called once per frame
    void Update()
    {
        if(gameOver && Input.GetKeyDown(KeyCode.R))
        {
            Application.LoadLevel(Application.loadedLevel);
        }
        //if (Random.value < 0.1)
        //{
         //   Spawn();
    //}
    }
    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while(true)
        {
            for (int i = 0; i < numPerWave; i++)
            {
                Spawn();
                yield return new WaitForSeconds(spanWait);
            }
            yield return new WaitForSeconds(waveWait);
            if (gameOver)
                break;
        }  
    }     private void WaitForSeconds(float spanWait)
    {
        throw new System.NotImplementedException();
    }
    void Spawn()
    {
        GameObject o = hazards[Random.Range(0, hazards.Length)];
        Vector3 p = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
        Quaternion q = Quaternion.identity;
        Instantiate(o, p, q);
    }
    public void AddScore(int v)
    {
        score += v;
        scoreText.text = "Score: " + score;
    }
    public void GameOver()
    {
        gameOver = true;
        gameOverText.text = "Game Over!";
        helpText.text = "Press 'R' to Restart!";
    }
}
DestroyByContact指令碼程式碼如下:
using UnityEngine;
using System.Collections; public class DestroyByCantact : MonoBehaviour {
    public GameObject explossion;
    public GameObject playerExplosion;     private GameController gameController;
    public int score = 10;
    void Start()
    {
        GameObject gameControllerObject = GameObject.FindWithTag("GameController");
        if(gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();
        }
        if(gameControllerObject == null)
        {
            Debug.Log("TaoCheng");
        }
    }
 void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Boundary")
        {
            return;
        }
        if(other.gameObject.tag == "Player")
        {
            Instantiate(playerExplosion, this.transform.position, this.transform.rotation);
            gameController.GameOver();
            Debug.Log("Error");
        }
        if(other.gameObject.tag == "enemy")
        {
            return;
        }
        Destroy(other.gameObject);
        gameController.AddScore(score);
        Destroy(this.gameObject);
        Instantiate(explossion, this.transform.position, this.transform.rotation);
    }
}
設定一個GUIText組,分別新增到程式碼中。
SpaceShooter基本功能完成。