Unity太空大战制作初试

经过不到一周的学习,制作完成了太空大战,游戏虽然简陋,但是在制作的过程中学习到了hin多的知识

游戏截图:

游戏源码:

1.player

//player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


//可以自定义脚本在菜单栏中的位置
[AddComponentMenu("脚本/MyGame/Player")]

public class Player : MonoBehaviour {

    //定义飞船的生命值
    public float HP = 100;
    //定义飞船移动的速度
    public float movespeed = 6f;
    //获取子弹的预构体
    public Transform rocket;
    //子弹的发射计时器
    public float rocketTimer;
    //加载声音,从面板拖入到当前脚本组件上来添加
    public AudioClip m_shootClip;
    //声音源组件,用于播放声音
    protected AudioSource m_audio;
    //爆炸特效
    //注意爆炸特效是一个预构体,需要用Transform的类接受
    public Transform m_explosionFX;
    //定义目标位置
    // protected Vector3 m_targetPOS;
    //鼠标射线碰撞层
    // public LayerMask m_inputMask;
	void Start () {
        //在初始化的时候获取音效组件
        m_audio = this.GetComponent<AudioSource>();
        //m_targetPOS = this.transform.position;
    }
    void Update () {
        //MoveTo();
        //每次进行刷新的时候的移动侦测
        //在每次都先重置位置
        float movex = 0;
        float movez = 0;
        //上下左右移动
        if (Input.GetKey(KeyCode.W))
        {
            //上
            movez -= movespeed * Time.deltaTime;
        }
        if (Input.GetKey(KeyCode.S))
        {
            //下
            movez += movespeed * Time.deltaTime;
        }
        if (Input.GetKey(KeyCode.A))
        {
            //左
            movex += movespeed * Time.deltaTime;
        }
        if (Input.GetKey(KeyCode.D))
        {
            //右
            movex -= movespeed * Time.deltaTime;
        }
        //移动
        transform.Translate(new Vector3(movex, 0f, movez));
        //发射子弹
        //if (Input.GetMouseButton(0))
        //{
        //   Instantiate(rocket, transform.position, transform.rotation);
        //}
        //连续发射的太快,于是要修改发射频率
        rocketTimer -= Time.deltaTime;
        if (rocketTimer<=0)
        {
            rocketTimer = 0.1f;
            if (Input.GetMouseButton(0))
          {
                //实例化rocket的prefab
             Instantiate(rocket, transform.position, transform.rotation);
                //在实例化之后还可以加上声音
                m_audio.PlayOneShot(m_shootClip);
          }
        }
    }
    //碰撞器
    //一定要注意 碰撞器一定要和刚体组件一起使用
    void OnTriggerEnter(Collider other)
    {
        //定义主角在发生碰撞时的事件
        if (other.tag.CompareTo("PlayerRocket")!=0)
        {
            if (other.tag.CompareTo("Enemy")==0)
            {
                HP -= 5;
            }
            else if(other.tag.CompareTo("SuperEnemy")==0)
            {
                HP -= 15;
            }
            else if (other.tag.CompareTo("EnemyRocket")==0)
            {
                HP -= other.GetComponent<Enemy_rocket>().damage;
            }
            //在UI中显示更改后的生命
            GameManager.Instance.HP_display(HP);
            if (HP <= 0)
            {
                //在销毁之前添加爆炸特效,使用实例化来添加特效
                //Quaternion.identity就是不作旋转
                Instantiate(m_explosionFX, transform.position,Quaternion.identity);
                Destroy(this.gameObject);
            }
        }
    }
    //获取鼠标射线
    //void MoveTo()
    //{
    //    if (Input.GetMouseButton(0))
    //    {
    //        Vector3 ms = Input.mousePosition;
    //        Ray ray1 = Camera.main.ScreenPointToRay(ms);
    //        RaycastHit hitinfo;
    //        bool iscast = Physics.Raycast(ray1, out hitinfo, 1000, m_inputMask);
    //        if (iscast)
    //        {
    //            //如果射中目标,记录射线碰撞点
    //            m_targetPOS = hitinfo.point;
    //        }
    //        //使用Vector3向量提供的MoveTowards函数,获取朝目标移动的位置
    //        Vector3 pos = Vector3.MoveTowards(transform.position, m_targetPOS, 5*movespeed * Time.deltaTime);
    //        //更新位置
    //        this.transform.position = pos;
    //    }
    //}
}

2.rocket

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/Rocket")]
public class rocket : MonoBehaviour {
    //定义子弹的属性
    public float movespeed = 10f;
    public float livetime = 1;
    public float damage = 10f;

	void Start () {
        Destroy(gameObject, livetime);
	}
	
	void Update () {
        transform.Translate(new Vector3(0,0, -movespeed * Time.deltaTime));
	}

    //一定要注意 碰撞器一定要和刚体组件一起使用
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag.CompareTo("Enemy")==0 || other.tag.CompareTo("SuperEnemy")==0)
        {
            Destroy(this.gameObject);
        }
    }
}

3.Enemy_rocket


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/Enemy_rocket")]
public class Enemy_rocket : rocket {

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag.CompareTo("Player") != 0)
        {
            //print("不是玩家");
            return;
        }
       // print("是玩家");
        Destroy(this.gameObject);
    }
}

4.Enemy

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/Enemy")]
public class Enemy : MonoBehaviour {

    //初级敌人的一些参数
    public float movespeed;
    public float HP;
    private int enemypoint=2;
    public Transform m_explosionFX;
	void Start () {
        renderer1 = this.GetComponent<Renderer>();
    }
    //刷新中进行的操作
	void Update () {
        UpdateMove();
        //每一帧都侦测是否移动到了屏幕外部
        if (isActiv && !this.renderer1.isVisible)
        {
            Destroy(this.gameObject);
        }
        //让敌人旋转
        //transform.Rotate(new Vector3(0,transform.position.y,0), SpinAngle);
	}
    //为了将来的扩展功能,将UpdateMove写成一个虚函数,继承使用的时候更改更加方便
    protected virtual void UpdateMove()
    {
        //随事件变化的左右随机移动
        float rx = Mathf.Sin(Time.time) * Time.deltaTime;
        //前进和左右随机移动共同呈现了敌人的移动
        transform.Translate(new Vector3(movespeed * Time.deltaTime, 0,rx ));
    }
    //一定要注意 碰撞器一定要和刚体组件一起使用
    private void OnTriggerEnter(Collider other)
    {
        //如果敌人撞到子弹
        if (other.tag.CompareTo("PlayerRocket")==0)
        {
            rocket rocket = other.GetComponent<rocket>();
            if (HP!=0)
            {
                HP -= rocket.damage;
                if (HP<=0)
                {
                    Destroy(this.gameObject);
                    Instantiate(m_explosionFX, transform.position, Quaternion.identity);
                    //如果死亡,就传给GameManager相应增加分数的函数score_up应该增加的函数
                    GameManager.Instance.score_up(enemypoint);
                }
            }
        }
        //如果敌人撞到主角
        else if (other.tag.CompareTo("Player")==0)
        {
            HP = 0;
            Destroy(this.gameObject);
            Instantiate(m_explosionFX, transform.position, Quaternion.identity);
        }
    }
    //侦测是否进入屏幕,并且进行操作
    //首先要定义两个组件
    //1.在初始化Update中获得模型渲染组件renderer
    internal Renderer renderer1;
    //2.决定是否激活的组件
    internal bool isActiv = false;
    private void OnBecameVisible()
    {
        isActiv = true;
    }
}

5.SuperEnemy

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/SuperEnemy")]
public class SuperEnemy : Enemy
{
    //模型旋转角度
    public float SpinAngle;
    private int enemypoint_super = 7;
    //获取音效资源
    public AudioClip m_audioClip;
    //获取音效组件
    protected AudioSource m_audio;
    //获取敌人子弹的预构体
    public Transform Enemy_rocket;
    protected float ShootTime = 2f;
    //出生的时候查找主角
    protected Transform player;
    private void Awake()
    {
        GameObject gameObject = GameObject.Find("Player");
        if (gameObject != null)
        {
            //获取主角的位置
            player = gameObject.transform;
        }
        //注意在初始化的时候记得获取音效组件
        m_audio = GetComponent<AudioSource>();
    }

    //重新定义移动
    protected override void UpdateMove()
    {
        ShootTime -= Time.deltaTime;
        //射击时间间隔为2s
        if (ShootTime <= 0)
        {
            ShootTime = 2;
            if (player != null)
            {
                //使用向量计算子弹在飞出时的朝向---向量减法
                Vector3 relativePOS = transform.position - player.position;
                //实例化敌人子弹并且设定朝向为玩家方向
                Instantiate(Enemy_rocket, this.transform.position, Quaternion.LookRotation(relativePOS));
                //实例化后可以输出射击音效
                m_audio.PlayOneShot(m_audioClip);
            }
        }
        //自身不断向前移动
        transform.Translate(new Vector3(0, 0,-movespeed * Time.deltaTime));
    }
    //重写高级敌人撞到子弹的判断函数
    private void OnTriggerEnter(Collider other)
    {
        //如果敌人撞到子弹
        if (other.tag.CompareTo("PlayerRocket") == 0)
        {
            rocket rocket = other.GetComponent<rocket>();
            if (HP != 0)
            {
                HP -= rocket.damage;
                if (HP <= 0)
                {
                    Destroy(this.gameObject);
                    Instantiate(m_explosionFX, transform.position, Quaternion.identity);
                    //如果死亡,就传给GameManager相应增加分数的函数score_up应该增加的函数
                    GameManager.Instance.score_up(enemypoint_super);
                }
            }
        }
        //如果敌人撞到主角
        else if (other.tag.CompareTo("Player") == 0)
        {
            HP = 0;
            Destroy(this.gameObject);
            Instantiate(m_explosionFX, transform.position, Quaternion.identity);
        }
    }
}

6.EnemySpawn (敌人生成器)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/EnemySpawn")]
public class EnemySpawn : MonoBehaviour {

    //需要调用协程
    //IEnumerator是协程函数的标识符,它的功能有些类似线程
    //注意将敌人生成器制作成两个,一个是普通敌人,另一个是高级敌人,但是两者的脚本一样

    //获取敌人的预构体
    public Transform m_enemy;

	void Start () {
        //初始化的时候调用协程
        StartCoroutine(SpawnEnemy());
	}
	
    IEnumerator SpawnEnemy ()
    {
        //协程一定要有yield return 返回
        yield return new WaitForSeconds(Random.Range(5f, 30f));
        //在生成器的位置实例化一个个敌人
        //使用协程可以在不堵塞主线程的情况下,让函数进行等待,每隔几秒创建一个敌人,然后循环这个过程
        //调用协程函数一定要使用 StartCoroutine(协程函数()) 函数

        Instantiate(m_enemy, transform.position, transform.rotation);
        //循环进行协程操作,不断产生敌人
        StartCoroutine(SpawnEnemy());
    }
    //用来显示敌人生成器图标的函数,这样方便摆放和设置
    private void OnDrawGizmos()
    {
        Gizmos.DrawIcon(transform.position,"item.png",true);
    }
}

7.GameManager

using System.Collections;
using System.Collections.Generic;
//需要调用Unity中的UI类库
using UnityEngine.UI;
//切换场景需要调用Unity中的SceneManagement类库
using UnityEngine.SceneManagement;
using UnityEngine;
[AddComponentMenu("脚本/MyGame/GameManager")]
public class GameManager : MonoBehaviour {
    //静态实例,保存这个组件到instance中,这样所有的脚本都可以通过GameManager这个类来访问他
    //在Start函数中指向自身,这样可以方便在其他类的对象中引用 GameManager 实例
    //这种做法通常只用于只有一个游戏对象的时候
    public static GameManager Instance;
    //显示分数的UI界面
    public Transform m_canvas_main;
    //显示游戏失败的游戏界面
    public Transform m_canvas_gameover;
    //得分UI文字
    public Text m_text_score;
    //最高分UI文字
    public Text m_text_best;
    //生命UI文字
    public Text m_text_HP;

    //游戏得分
    protected int m_score=0;
    //最高分
    public static int m_bestscore=0;
    //游戏主角
    protected Player m_player;

    //背景音乐
    public AudioClip m_background_music;
    //背景音乐声音源
    public AudioSource m_background_source;

    void Start () {
        //获取实例
        Instance = this;
        //使用代码来添加音效组件
        m_background_source = this.gameObject.AddComponent<AudioSource>();
        m_background_source.clip = m_background_music;
        m_background_source.loop = true;
        m_background_source.Play();

        //获取主角
        //这里获取的Player组件是 Player脚本组件
        m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
        //获取UI
        m_text_score = m_canvas_main.Find("Text_Score").GetComponent<Text>();
        m_text_best = m_canvas_main.Find("Text_Best").GetComponent<Text>();
        m_text_HP = m_canvas_main.Find("Text_HP").GetComponent<Text>();
        //初始化UI显示
        m_text_score.text = string.Format("分数   {0}",m_score);
        m_text_best.text = string.Format("最佳   {0}", m_bestscore);
        m_text_HP.text = string.Format("HP   {0}", m_player.HP);

        //var是隐式定义
        //获取重新开始按钮
        var restart_button = m_canvas_gameover.transform.Find("Button").GetComponent<Button>();
        //按钮事件回调,重新开始关卡
        //监听事件,定义delegate委托类型来传递方法
        restart_button.onClick.AddListener(delegate(){
            //中间是传递的方法
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        });
        //在游戏开始的时候默认游戏结束界面是隐藏的,即m_canvas_gameover的游戏对象组件默认是不显示的
        m_canvas_gameover.gameObject.SetActive(false);
    }

    //设置游戏分数增加的函数
    public void score_up(int point) {
        m_score += point;
        //每次都更新高分记录
        if (m_score > m_bestscore)
        {
            m_bestscore = m_score;
        }
        //刷新输出分数和最高分
        //通过不断更改Text中的文本就可以实现
        m_text_score.text = string.Format("分数    {0}", m_score);
        m_text_best.text = string.Format("最佳    {0}", m_bestscore);
    }

    //设置更改生命显示的函数
    public void HP_display(float life)
    {
        //不断更新生命值
        m_text_HP.text = string.Format("HP     {0}", m_player.HP);
        if (m_player.HP <= 0)
        {
            //通过设置游戏结束场景为显示就可以切换到游戏结束界面
            m_canvas_gameover.gameObject.SetActive(true);
        }
    }
}

8.IndexScene (欢迎界面)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
[AddComponentMenu("脚本/MyGame/IndexScece")]

public class IndexScene : MonoBehaviour {
    
	void Start () {
		
	}
	void Update () {
		
	}
    private void OnGUI()
    {
        //文字大小
        GUI.skin.label.fontSize = 50;
        //UI中心对齐
        GUI.skin.label.alignment = TextAnchor.LowerCenter;
        //显示标题
        GUI.Label(new Rect(0, 30, Screen.width, 100), "太空大战");
        //开始游戏按钮
        if (GUI.Button(new Rect(Screen.width * 0.5f - 100, Screen.height * 0.7f, 200, 30), "开始游戏"))
        {
            SceneManager.LoadScene("level1");
        }
    }
}

9.ProcessModel (制作过程中的Prefab自动生成器--以超级敌人为例)

//自动创建Prefabs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//添加UnityEditor类库,可以使用Unity的编辑器相关API
using UnityEditor;

//继承自AssetPostprocessor , 专门用来处理资源导入。
public class ProcessModel : AssetPostprocessor
{
    //只需要一个加载模型的函数,定义这个载入的模型是input
    void OnPostprocessModel(GameObject input)
    {
        //只处理高级敌人的模型
        if (!input.name.Contains("SuperEnemy"))
        {
            return;
        }
        //设置导入模型的tag
        input.tag = "SuperEnemy";
        //查找导入模型的碰撞模型
        foreach (Transform obj in input.GetComponentInChildren<Transform>())
        {
            if (obj.name.CompareTo("碰撞模型") != 0)
            {
                //要是没有碰撞模型,指定Renderer脚本
                //obj.gameObject.AddComponent<EnemyRenderer>();
            }
            else
            {
                //如果有就取消碰撞模型的显示
                MeshRenderer r = obj.GetComponent<MeshRenderer>();
                r.enabled = false;
                //添加Mesh碰撞体
                obj.gameObject.AddComponent<MeshCollider>();
                //设置碰撞体的tag
                obj.tag = "SuperEnemy";
            }
        }

        //设置刚体
        Rigidbody rigidbody = input.AddComponent<Rigidbody>();
        rigidbody.useGravity = false;
        rigidbody.isKinematic = true;

        //取得导入模型的相关信息
        ModelImporter importer = assetImporter as ModelImporter;
        //从工程中将该模型读出来
        GameObject tar = (GameObject)AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(GameObject));
        //将获取到的模型创建为prefab,保存在相应路径下
        GameObject prefab = (GameObject)PrefabUtility.CreatePrefab("Assets/prefabs/Superenemy.prefab", tar);

        //给Prefab添加音效组件
        prefab.AddComponent<AudioSource>();
        //在相应路径获取子弹的prefab对象
        GameObject enemyrocket = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/prefabs/Enemy_rocket.prefab", 
            typeof(GameObject));

        //获取爆炸效果的prefab
        var fx = (GameObject)AssetDatabase.LoadAssetAtPath("Assets/FX/explosion.prefab", typeof(GameObject));
        //获取射击音效
        AudioClip audiocilp = (AudioClip)AssetDatabase.LoadAssetAtPath("Assets/FX/shoot.WAV", typeof(AudioClip));
        //给高级敌人prefab添加相应的SuperEnemy脚本
        SuperEnemy enemy = prefab.AddComponent<SuperEnemy>();
        //设置脚本的默认参数
        enemy.movespeed = 0.2f;
        enemy.HP = 140;
        enemy.SpinAngle = 0;
        enemy.Enemy_rocket = enemyrocket.transform;
        enemy.m_explosionFX = fx.transform;
        enemy.m_audioClip = audiocilp;
     }
}

在制作的过程中,除了看书上的注意事项之外,还遇到了一些错误,现在整理解决方法如下:

1. 尽量不要使用和类型名称几乎重名的变量名

https://answers.unity.com/questions/778335/warning-cs0108-playerphysicscollider-hides-inherit.html

2. Object reference not set to an instance of an object

https://blog.csdn.net/slwbcsdn/article/details/80791336

3. Invalid AABB inAABB

https://blog.csdn.net/Htlas/article/details/79077009

https://blog.csdn.net/Zafir6453/article/details/78571702

4. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition)会报错 空引用

http://blog.51cto.com/chenyuanyu/1380837

游戏的主要部分包括C#代码已经上传到了GitHub:

https://github.com/dzzhyk/SpaceWar

猜你喜欢

转载自blog.csdn.net/weixin_43826242/article/details/85224287
今日推荐