花了一天的时间,照着官方的说明将spaceshoot这个DEMO撸了一遍,实现了一个简单飞行射击游戏应有的小功能,本想着用Unet这个组件把这个游戏做成一个联网的,无奈只能实现飞机运动之间的同步,其余部分画面都是不同步的。等经过进一步的学习之后我会再把这个DEMO进行完善。
接下来就分析一下这个DEMO的各个游戏对象与组件的关系。
1.场景:这个飞行射击游戏只有需要一个场景,一张用图片来做背景的星空图,一些粒子效果来模拟星星。
2.游戏对象:1.1玩家飞机:用WASD来进行控制,移动有范围限制,鼠标左键开火,撞到子弹,陨石,敌机会爆炸并附带音效;
1.2 陨石:在玩家上方随机生成并向下移动并具有旋转,离开边界范围会自动销毁,被子弹击中会爆炸,撞击到玩家飞机会爆炸并附带音效
1.3 敌机:在玩家上方随机生成并向下移动,并且可以开火,被子弹击中会爆炸,与玩家飞机相撞会爆炸,超出边界范围会销毁
1.4 子弹:可以由玩家和敌机生成,在Z轴上具有一定的速度,遇见玩家,陨石,敌机,进行爆炸并附带音效,超过生命周期会销毁
3.UI界面:需要一个计分板,玩家死亡后的结束游戏提示,和按R重新开始提示
脚本:
3.1玩家控制脚本
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
[System.Serializable]public class Boundary
{
public float xMin,zMin,xMax,zMax;//这是设定玩家运动的边界范围
}
public class playercontroller : MonoBehaviour {
public float speed;//移动速度
public Boundary boundary;//边界
public float tilt = 4;//旋转速度
public float fireRate = 0.2f;//开或延迟
public GameObject shot;//子弹对象
public Transform shotspawn;//子弹生成地点
private float nextfire = 0;//开火间隔
// Use this for initialization
void Update () {
//if (isLocalPlayer) {
if (Input.GetButton ("Fire1") && Time.time > nextfire) {//按下鼠标左键并且时间大于开火间隔
nextfire = Time.time + fireRate;//更新最近开火时间
Instantiate (shot, shotspawn.position, shotspawn.rotation); //生成子弹对象
GetComponent ().Play ();//播放射击音效
}
//}
}
// Update is called once per frame
void FixedUpdate () {
//if (isLocalPlayer) {
float moveHorizontal = Input.GetAxis ("Horizontal");//获得水平输入值
float moveVertical = Input.GetAxis ("Vertical");//获得竖直方向上的输入值
Vector3 movement = new Vector3 (moveHorizontal, 0, moveVertical);//定义一个三维变量用于储存玩家进行的移动操作
Rigidbody rb = GetComponent ();
if (rb != null) {
rb.velocity = movement * speed;//设定刚体在某方向上的速度
rb.position = new Vector3 (Mathf.Clamp (rb.position.x, boundary.xMin, boundary.xMax), 0, Mathf.Clamp (rb.position.z, boundary.zMin, boundary.zMax));//更新位置,并且处在设定范围内
rb.rotation = Quaternion.Euler (0, 0, rb.velocity.x * -tilt);//计算旋转,因为Z轴上的旋转逆时针为正值,所以假设向左移动,X轴上的速度为负值,这时需要乘以一个负的旋转速度来实现
}
}
//}
}
3.2敌对物体移动与旋转位置随机化脚本
//设定移动速度
using UnityEngine;
using System.Collections;
public class Mover : MonoBehaviour {
public float speed;
void Start ()
{
GetComponent().velocity = transform.forward * speed;//设定Z轴上的速度
}
}
//随机旋转速度
using UnityEngine;
using System.Collections;
public class RandomRatator : MonoBehaviour {
public float tumble;//旋转速度
void Start () {
GetComponent ().angularVelocity = Random.insideUnitSphere * tumble;//随机化欧拉角的速度
}
}
3.3销毁游戏对象的方法脚本:超出边界,碰撞,超过生命周期
//按时销毁
using UnityEngine;
using System.Collections;
public class DestroyByTime : MonoBehaviour {
public float lifeTime=2.0f;//设定生命周期为2S
void Start () {
Destroy (gameObject, lifeTime);//指定时间后销毁游戏对象
}
}
//超过边界销毁
using UnityEngine;
using System.Collections;
public class DestoyByBoundary : MonoBehaviour
{
void OnTriggerExit (Collider other) //离开触发器
{
Destroy(other.gameObject);//销毁游戏对象
}
}
//碰撞销毁
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class DestroyByContact : MonoBehaviour {
public int scoreValues;//本物体被击杀分数
private GameController gameController1;//游戏控制对象
public GameObject explosion;//爆炸粒子效果
public GameObject playerExplosion;//玩家爆炸粒子效果
void OnTriggerEnter(Collider other)//进入触发器
{
if (other.tag == "Boundary" || other.tag == "Enemy")//如果是边界或敌机就不爆炸,避免误伤
return;
Instantiate (explosion, transform.position, transform.rotation);
if (other.tag == "Player") {//撞到玩家
Instantiate (playerExplosion, other.transform.position, other.transform.rotation);//播放玩家的爆炸动画
gameController1.gameOver ();//游戏结束,进入重置画面
}
gameController1.addScore (scoreValues);//增加分数
Destroy (other.gameObject);//删除对方游戏对象
Destroy (gameObject);//自我删除
}
void Start ()
{
GameObject gameControllerObject = GameObject.FindGameObjectWithTag ("GameController");//获得游戏控制器对象
if (gameControllerObject != null)
{
gameController1 = gameControllerObject.GetComponent ();//初始化游戏控制组件
}
if (gameController1 == null)
{
Debug.Log ("Cannot find 'GameController' script");
}
}
}
//玩家爆炸脚本
using UnityEngine;
using System.Collections;
public class player_explosion : MonoBehaviour {
private GameController gameController1;
public GameObject playerExplosion;
// Use this for initialization
void Start () {
GameObject gameControllerObject = GameObject.FindGameObjectWithTag ("GameController");
if (gameControllerObject != null)
{
gameController1 = gameControllerObject.GetComponent ();
}
if (gameController1 == null)
{
Debug.Log ("Cannot find 'GameController' script");
}
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Boundary"||other.tag=="Player")//遇到边界或其他玩家不爆炸
return;
if (other.tag == "Bullet") {//遇见子弹,因为陨石和敌机已经写了遇见玩家爆炸两者一起销毁的代码,所以只需要写子弹的
Instantiate (playerExplosion, other.transform.position, other.transform.rotation);//播放爆炸动画
gameController1.gameOver();//游戏结束
}
Destroy (other.gameObject);
Destroy (gameObject);
}
}
3.4游戏控制脚本
public class GameController : NetworkBehaviour {
public Text scoreText;//计分板
public Text gameOverText;//游戏结束提示
public Text restartText;//重新开始提示
public bool restart;//是否重置
public bool gameover;//是否结束
public int score;//初始分数
public float starWait;//开始之前的等待时间
public float waveWait;//每一波敌人之间的间隔
public float spawnwait;//每个物体生成之间的间隔
public float hazardCount;//一波敌人的数量
public GameObject[] hazards;//敌对物体游戏对象数组
public Vector3 spawnValues;//出生地点
private Vector3 spawnPosition = Vector3.zero;
private Quaternion spawnRotation;//出生旋转速度
public void addScore(int newScoreValues){
score += newScoreValues;
UpdateScore ();//更新分数
}
public void gameOver()
{
gameover = true;
gameOverText.text="游戏结束";
}
public void UpdateScore()
{
scoreText.text = "得分:" + score;
}
IEnumerator spawnWaves()//使用了协程的函数写法
{
yield return new WaitForSeconds (starWait);//游戏开始前的间隔
while(true){
if (gameover) {
restartText.text="按R键重新开始游戏";
restart = true;
break;
}
for (int i = 0; i < hazardCount; i++) {
GameObject hazard = hazards [Random.Range (0, hazards.Length)];
Vector3 spawnPosition = new Vector3 (Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);//随机化物体X轴上的位置
spawnRotation = Quaternion.identity;//没旋转之前的初始角度
Instantiate (hazard, spawnPosition, spawnRotation);//生成物体
yield return new WaitForSeconds (spawnwait);//生成下个物体之间的间隔
}
yield return new WaitForSeconds (waveWait);//每波敌人之间的间隔
}
}
void Start () {
gameOverText.text = "";
restartText.text = "";
gameover = false;
restart = false;
score = 0;
UpdateScore ();//初始化各项数据
StartCoroutine (spawnWaves ());//启动协程
}
void Update () {
if (restart) {
if (Input.GetKeyDown (KeyCode.R))
Application.LoadLevel (Application.loadedLevel);//重新加载场景
}
}
}
3.5敌机移动与开火脚本
//敌机子弹射击
using UnityEngine;
using System.Collections;
public class enamyFire : MonoBehaviour {
public GameObject shot;//子弹
public Transform shotSpawn;//子弹生成地点
public float delay;//函数延迟
public float fireRate;//开火延迟
void Start () {
InvokeRepeating ("Fire", delay, fireRate);//重复调用一个函数,第一个参数是被调用函数名字。后面参数的意思是首先在delay延迟后调用这个函数,以后以fireRate的时间间隔调用这个函数
}
void Fire () {
Instantiate (shot, shotSpawn.position, shotSpawn.rotation);
GetComponent ().Play ();//生成子弹并播放音效
}
}
//敌机飞行轨迹
using UnityEngine;
using System.Collections;
public class enemyMove : MonoBehaviour {
public Done_Boundary boundary;//边界
public float tilt;//旋转毒素
public float dodge;//闪避速度
public float smoothing;//平滑度
public Vector2 startWait;//改变轨迹之前的时间间隔
public Vector2 maneuverTime;//闪避时间
public Vector2 maneuverWait;//闪避之间的间隔
private float currentSpeed;//当前速度
private float targetManeuver;//闪避目标
void Start ()
{
currentSpeed = GetComponent().velocity.z;//获得Z轴上的速度
StartCoroutine(Evade());//启动协程
}
IEnumerator Evade ()//协程的写法
{
yield return new WaitForSeconds (Random.Range (startWait.x, startWait.y));//等待给定区间内的随机时间
while (true)
{
targetManeuver = Random.Range (1, dodge) * -Mathf.Sign (transform.position.x);//判断飞机的X轴正负,正或0返回1,负的放回-1
yield return new WaitForSeconds (Random.Range (maneuverTime.x, maneuverTime.y));//随机等待时间,旋转时间
targetManeuver = 0;//旋转速度清零
yield return new WaitForSeconds (Random.Range (maneuverWait.x, maneuverWait.y));
}
}
void FixedUpdate ()
{
float newManeuver = Mathf.MoveTowards (GetComponent().velocity.x, targetManeuver, smoothing * Time.deltaTime);//第一个参数是当前值,第二值是要移向的位置,第三个是最大速度,不会超过它
GetComponent().velocity = new Vector3 (newManeuver, 0.0f, currentSpeed);//设定当前移动速度
GetComponent().position = new Vector3
(
Mathf.Clamp(GetComponent().position.x, boundary.xMin, boundary.xMax),
0.0f,
Mathf.Clamp(GetComponent().position.z, boundary.zMin, boundary.zMax)//计算移动后的位置,在一个闭区间内,不能移出边界
);
GetComponent().rotation = Quaternion.Euler (0, 0, GetComponent().velocity.x * -tilt);//设定旋转速度
}
}