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();
}
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();
}
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基本功能完成。