游戏规则:游戏有三个关卡,每个关卡有四次发射机会,每次发射的飞碟大小颜色速度方向位置都不同,有50分初始分,每打中一个加10分,掉落一个在地减10分,第一二关都是90分过关,第三关70分过关,第一关每次发射一个飞碟,第二关两个,第三关三个。空格发射飞碟,鼠标左键射击飞碟。如图(虽然UI还没怎么优化过,将就一下了,还有没有录第三关也是为了让gif短一点,因为上传的gif有内存限制):

下面进入正题,怎么制作呢?

回收与再利用:首先这种涉及到类似于子弹啊,雨滴等物体的游戏,这种资源最好能回收与再利用,这样就可以节约资源,省的一直创建新对象,别看创建一个对象对游戏影响不怎么大,在一些大型游戏中这是很重要的思想的,因为一个场景往往涉及到成千上万的这种小物体,如果每一个都要创建一遍才能用,会导致卡顿,速度跟不上。

工厂模式:物体的生成都由一个工厂管理,需要什么只需要跟工厂说出你要什么,它会自己实现生成给你,里面如何生成不用你操心,实际上也有抽象的意思。就像这次的游戏,我数据设置好了,预设也传给了工厂,所以在需要加载飞碟是,只用调用一个getDisk()就可以了,不需要操心怎么生成。当然回收与再利用也是在工厂中实现的,也就是回收仓库也是在工厂里。

射线碰撞:用鼠标点击屏幕,如何获取我所点到的物体以及我的鼠标坐标呢?就是用从摄像机中发射出射线,然后鼠标点的位置作为方向,这样就可以选中第一个被射击中的物体,同时也可以记录出鼠标所点位置。

下面是这游戏制作代码的架构图:

SceneController类:负责把各个类之间对接,作为内部设置与用户界面的连接。

GameModel类:管理飞碟的基础数据。

RoundController类:管理关卡数据,每一个关卡对应需要不同的飞碟数据,在这里会给飞碟根据不通关卡加载不同属性。

Recorder类:管理记录加分减分。

Userface类:管理用户界面以及界面逻辑。

DiskFactory类:利用传进来的飞碟预设根据需要生产飞碟存于仓库,回收与再利用飞碟。

下面是代码:

工厂类(看注释应该就能看懂):

public class DiskFactory : System.Object
    {
        private static DiskFactory _instance;
        private GameObject prefabs_disk;
        public static List<GameObject> used = new List<GameObject>();
        public static List<GameObject> unused = new List<GameObject>();
        public static DiskFactory getInstance()             //单例化工厂
        {
            if (_instance == null)
            {
                _instance = new DiskFactory();
            }
            return _instance;
        }
        public void SetObject(GameObject x)     //把预设传入工厂
        {
            prefabs_disk = x;
        }
        public GameObject getDisk() //提供从工厂获取飞碟的借口
        {
            GameObject t;
            if (unused.Count == 0)
                t = GameObject.Instantiate(prefabs_disk);
            else
            {
                t = unused[0];
                unused.Remove(t);
            }
            used.Add(t);
            return t;
        }
        public void free(GameObject t)  //提供从外界回收飞碟到工厂的接口
        {
            t.GetComponent<Rigidbody>().velocity = Vector3.zero;
            t.transform.localScale = prefabs_disk.transform.localScale;
            t.SetActive(false);
            used.Remove(t);
            unused.Add(t);
        }
    }

接下来是SceneController类,它的工作实际上就是把各个类的功能合并起来,需要什么功能就它就调用某个类的功能来实现:

public class SceneController : System.Object, IQuery, IUserInterface
    {
        private static SceneController _instance;
        private RoundController _RC;
        private GameModel _gameModel;
        private Recorder _Recorder;
        public static SceneController getInstance()         //单例化
        {
            if (_instance == null)
            {
                _instance = new SceneController();
            }
            return _instance;
        }
        public void setdisk(GameObject t)               //把飞碟预设传入工厂
        {
            DiskFactory.getInstance().SetObject(t);
        }
        public int getRound()
        {
            return _RC.round;
        }
        public int getScore()
        {
            return _Recorder.score;
        }
        public void setGameModel(GameModel obj) { _gameModel = obj; }
        internal GameModel getGameModel() { return _gameModel; }
        public void setRecorder(Recorder obj) { _Recorder = obj; }
        internal Recorder getRecorder() { return _Recorder; }
        public void setRoundController(RoundController obj) { _RC = obj; }
        internal RoundController getRoundController() { return _RC; }
        public void emitDisk() { _gameModel.emitReady(); }
        public bool isShooting() { return _gameModel.shooting; }
        public void nextRound(int i)            //进入下一关卡
        {
            if (i == 1 || i == 2)
            {
                _Recorder.reset();
                _RC.round++;
                _RC.loadRoundData();
            }
            else if (i == 0) _RC.round = 0;
        }
    }

两个用户与SceneController的接口(在SceneController实现):

public interface IUserInterface         //Scenecontroller与用户界面的接口
    {
        void emitDisk();
        void setdisk(GameObject t);
    }
    public interface IQuery         //Scenecontroller与用户界面的接口
    {
        bool isShooting();
        int getRound();
        int getScore();
    }

以上三个部分我都放在一个自定义的namespace里面(name_game);

Userface用户界面实现UI以及提供用户接口逻辑:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using name_game;
public class UserInterface : MonoBehaviour {
    public int changes = 4;         //发射机会
    public Text roundText;          //显示目前关卡数
    public Text scoreText;          //显示目前分数
    public Text mainText;          //主显示板,显示规则以及提示
    public Text changeText;         //显示目前剩余发射机会
    public Camera ca;
    private int states = 0;      //0为初始界面,1为规则界面,2为游戏中 
    private IUserInterface userInt;     // 用户接口  
    private IQuery queryInt;      // 查询接口  
    public GameObject disktem;
    public ParticleSystem _exposion;    //爆炸粒子
    void Start()
    {
        userInt = SceneController.getInstance() as IUserInterface;
        queryInt = SceneController.getInstance() as IQuery;
        _exposion = GameObject.Instantiate(_exposion) as ParticleSystem;
        changes = 4;
        states = 0;
        userInt.setdisk(disktem);
        mainText.text = "欢迎来到飞碟世界,请按空格进入游戏";
    }
    void Update()
    {
        if ((queryInt.getRound() == 1 || queryInt.getRound() == 2 )&& changes == 0 && !queryInt.isShooting()) //判断一二关的结果
        {
            if (queryInt.getScore() >= 90)
            {
                mainText.text = "You win!(空格进入下一关)";
                if (Input.GetKeyDown("space"))
                {
                    states = 1;
                    changes = 4;
                    SceneController.getInstance().nextRound(1);
                }
            }
            else
            {
                mainText.text = "You lose!(空格重新开始)";
                if (Input.GetKeyDown("space"))
                {
                    states = 0;
                    changes = 4;
                    SceneController.getInstance().nextRound(0);
                }
            }
        }
        if (queryInt.getRound() == 3 && changes == 0 && !queryInt.isShooting()) //判断第三关的结果
        {
            if (queryInt.getScore() >= 70) mainText.text = "You win!You have passed all arounds(空格重新开始游戏)";
            else mainText.text = "You lose!(空格重新开始)";
            if (Input.GetKeyDown("space"))
            {
                states = 0;
                changes = 4;
                SceneController.getInstance().nextRound(0);
            }
        }
        changeText.text = "Changes: " + changes.ToString();
        if (changes>0&&Input.GetKeyDown("space")) {
            if(states==0)
            {
                states = 1;
                if(queryInt.getRound()==0) SceneController.getInstance().nextRound(1);
                mainText.text = "你有50初始分,每一关有四次发射机会,每次发射射出的飞碟数量以及速度还有大小颜色都不同,每一轮发射中每一个飞碟掉落在地都会扣10分,打中一个加10分,第一二关90分以上过关第三关70分以上过关,空格发射飞碟,鼠标左键射击,请按空格进入第一关";
            } else if(states==1)
            {
                states = 2;
                mainText.text = "";
            } else if(states==2)            //这个状态下才能发射
            {
                if ((!queryInt.isShooting())&&changes!=0)
                {
                    userInt.emitDisk();
                    changes--;
                }
            }
        }
        if(queryInt.isShooting()&&Input.GetMouseButtonDown(0))      //判断是否射击中目标,并且实现爆炸粒子效果
        {
            Ray ray = ca.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "disk")
            {
                _exposion.transform.position = hit.collider.gameObject.transform.position;
                _exposion.GetComponent<Renderer>().material.color = hit.collider.gameObject.GetComponent<Renderer>().material.color;
                _exposion.Play();
                hit.collider.gameObject.SetActive(false);
            }
        }
        roundText.text = "Round: " + queryInt.getRound().ToString();
        scoreText.text = "Score: " + queryInt.getScore().ToString();
    }
}

Recorder类:

public class Recorder : MonoBehaviour {
    public int score = 50;          //记录当前所得分数
    void Awake () {
        SceneController.getInstance().setRecorder(this);
    }
    public void scoreADisk()        //得分
    {
        score += 10;
    }
    public void reset()             //重置
    {
        score = 50;
    }
    public void failADisk()         // 失分  
    {
        score -= 10;
    }
}

RoundController类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class RoundController : MonoBehaviour {
    private Color color;
    private Vector3 emitPos;
    private Vector3 emitDir;
    private float speed;
    public int round = 0;
    void Awake()
    {
        SceneController.getInstance().setRoundController(this);
    }
    void Start () {
        emitPos = new Vector3(0f, 5f, -6f);
        emitDir = new Vector3(10f, 15f, 70f);
        speed = 15;
    }
    public void loadRoundData()                 //加载每一关卡飞碟数据
    {
        if(round==1)
        {
            color = Color.black;
            SceneController.getInstance().getGameModel().setting(3, color, emitPos, emitDir.normalized, speed, 1);
        } else
            if(round==2)
        {
            color = Color.red;
            SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 2);
        } else if(round==3)
        {
            color = Color.green;
            SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 3);
        }
    }
}

GameModel类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class GameModel : MonoBehaviour
{
    public bool shooting=false;             //标记飞碟飞行期间的状态
    private int diskScale;                  //飞碟大小
    private Color diskColor;                //飞碟颜色
    private Vector3 emitPosition;           // 发射位置  
    private Vector3 emitDirection;          // 发射方向  
    private float emitSpeed = 0;                // 发射速度  
    private int emitNumber;                 // 发射数量  
    private bool emitEnable;
    private List<GameObject> disksToEmit = new List<GameObject>();
    void Awake()
    {
        SceneController.getInstance().setGameModel(this);
    }
    public void setting(int scale, Color color, Vector3 emitPos, Vector3 emitDir, float speed, int num)
    {
        diskScale = scale;
        diskColor = color;
        emitPosition = emitPos;
        emitDirection = emitDir;
        emitSpeed = speed;
        emitNumber = num;
    }
    public void emitReady()             //判断是否可以发射
    {
        if (!shooting)
        {
            emitEnable = true;
        }
    }
    void emitDisks()                //从工厂生产或者取出飞碟并且装入实现发射逻辑
    {
        for (int i = 0; i < emitNumber; ++i)    //每一个飞碟发射前都随机一次方向和发射位置
        {
            disksToEmit.Add(DiskFactory.getInstance().getDisk());
            disksToEmit[i].transform.localScale *= diskScale;
            disksToEmit[i].GetComponent<Renderer>().material.color = diskColor;
            disksToEmit[i].transform.position = new Vector3(Random.Range(emitPosition.x-1f, emitPosition.x+1f), emitPosition.y, emitPosition.z);
            disksToEmit[i].SetActive(true);
            emitDirection = new Vector3(Random.Range(emitDirection.x-0.3f, emitDirection.x + 0.3f), emitDirection.y, emitDirection.z);
            disksToEmit[i].GetComponent<Rigidbody>().velocity = emitDirection * Random.Range(emitSpeed, emitSpeed+5f);
        }
    }
    void freeDisk(GameObject adisk)             //调用工厂来回收飞碟
    {
        DiskFactory.getInstance().free(adisk);
        disksToEmit.Remove(adisk);
    }

void FixedUpdate()
    {
        if (emitEnable)
        {
            emitDisks(); // 发射飞碟  
            emitEnable = false;
            shooting = true;
        }
    }
    void Update()
    {
        for (int i = 0; i < disksToEmit.Count; ++i)         //实现判断是否射击中
        {
            if (!disksToEmit[i].activeInHierarchy)
            {  // 飞碟不在场景中  
                SceneController.getInstance().getRecorder().scoreADisk();  // 得分  
                freeDisk(disksToEmit[i]);
            }
            else if (disksToEmit[i].transform.position.y < 0)
            {    // 飞碟在场景中但落地  
                SceneController.getInstance().getRecorder().failADisk();   // 失分  
                freeDisk(disksToEmit[i]);
            }
        }
        if (disksToEmit.Count == 0)
        {
            shooting = false;
        }
    }
}

还有一点就是要给飞碟的预设就如刚体组件,在Add Component->physics->Rigidbody就可以了

Text的话可以create一个Canvas,在里面创建并且调整各个提示框的位置以及大小

粒子系统就直接create->Particle System创建,然后样式再根据自己需要调一下。这次的游戏就大功告成了~~~

Unity3D学习:射击小游戏——飞碟世界相关推荐

  1. Unity3D 2D射击小游戏瞄准线的实现

    Unity3D 2D射击小游戏瞄准线的实现 0. 前言 本片文章是对 https://www.bilibili.com/video/bv1sY411V7tx 视频中瞄准线是如何实现的一个讲解教程,欢迎 ...

  2. 基于Unity3D的AR小游戏开发【100011412】

    本科毕业设计(论文) GRADUATION DESIGN(THESIS) 基于 Unity3D 的增强现实游戏程序 摘要 增强现实(AR)作为一项新兴技术近年来被越来越多的人群所获知,AR 也渐渐走进 ...

  3. 坦克射击小游戏(后续将会升级功能)

    坦克射击小游戏(后续将会升级功能) 总体框架 关键技术 未来版本 我的Github 游戏源码在我的Github,感兴趣的的帮忙给颗星呀,并加入我们吧~ 这是我跟着b站韩顺平老师学的一个很锻炼基础的小项 ...

  4. Unity3D休闲射击类游戏《Survival Shooter》完整源码

    Unity3D休闲射击类游戏<Survival Shooter>完整源码分享给大家学习,这个对于那些想要制作u3d射击类游戏有很大帮助. 运行环境是:Unity5.3.1 下载地址: ht ...

  5. Java面向对象(6) —— 射击小游戏

    这里做一个射击小游戏,作为对前面Java学习的一个小结. 1.设计 2.知识点总结 主要是在主程序ShootGame.java中引入了一些新知识,swing画图.定时器.鼠标事件处理.其他程序则主要体 ...

  6. Android 自定义View实现炮弹射击小游戏

    先看效果图: 这是一个比较简易的射击小游戏,后期可以将圆球,炮筒用其它图片来替换,应该可以变得好看一些.我实现这个效果,主要是为了学习和巩固自定义View的一些知识点.下面我来讲述一下本游戏的设计思路 ...

  7. 射击小游戏c语言实验报告,C++实现简单射击小游戏

    使用c++制作简单的横板射击小游戏,供大家参考,具体内容如下 #include #include #include class Bullet; class Tank; class E_Bullet; ...

  8. 基于pygame的射击小游戏制作(一)让飞船动起来

    基于pygame的射击小游戏制作(一)让飞船动起来 一.文件结构 alien_invasion.py 是整个系统的主文件,用来创建游戏中的一系列对象,ai_settings存储设置.screen存储显 ...

  9. Python学习—2048小游戏等4个小练习

    Python学习-2048小游戏等4个小练习 转载于:https://www.cnblogs.com/ChangAn223/p/10627777.html

最新文章

  1. GUI(概述和布局)
  2. factorybean 声明_阿里面试题:BeanFactory与FactoryBean的区别
  3. 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流
  4. 性价比高台式计算机,2021年台式电脑什么牌子好,性价比高?
  5. python可以做计量分析吗_技术分享 - python数据分析(2)——数据特征分析(上)...
  6. IDEA 启动、编译、clean、安装maven等、报错Process terminated
  7. java子类和父类实例_java中父类与子类之间的转换示例
  8. U盘文件系统无法识别,数据怎么恢复?
  9. 【LeetCode】79-单词搜索
  10. (17)FPGA速度和面积互换原则
  11. VS2010下如何改变Button颜色、背景、边框
  12. python英文版怎么改成中文版_“PyQt的Qt Designer菜单是中文版的,怎么改成英文的“python的qt designer教程...
  13. 电脑计算机无法安3.5,Windows无法安装以下功能:.NET Framework 3.5(包括 .NET 2.0 和 3.0)解决方法...
  14. SQL Server触发器更新原理
  15. 浅谈ThreadPoolExecutor线程池底层源码
  16. 国企计算机岗位职称申请,在国企工作如何评定职称
  17. 图片转ascii字符画C语言,将图片转为ASCII字符画
  18. Android中的传感器之---光线传感器
  19. 学以致用——微博文章内容统计分析之一(Excel+GraphLab)
  20. python:实现丢番图方程算法(附完整源码)

热门文章

  1. Quartus||仿真图设计
  2. 删除你的所有计算机文件的英文,删除Download和DataStore文件夹中的所有文件
  3. 试题 算法训练 生活大爆炸版石头剪刀布
  4. updating java index_myeclipse右下角的updating indexes 是什么意思?
  5. 小米note2 支付宝指纹支付 -10008
  6. 使用思博伦Avalanche进行HTTP双向吞吐量测试 _双极未来
  7. java ant配置环境变量
  8. jsp文字上下居中显示_怎么把jsp的文字居中
  9. 【定位不准的烦心事系列】第1篇:谈谈卫星定位的位置干扰
  10. mysql中的dual