github传送门:https://github.com/dongzizhu/unity3DLearning/tree/master/hw4/Disk

视频传送门:https://space.bilibili.com/472759319

打飞碟小游戏

这次的代码架构同样采用了MVC模式,与之前的牧师与魔鬼基本相同,这里就不重复叙述了,感兴趣的可以看上上篇博文。

这里主要还是介绍一下firstController的变化以及新应用的工厂模式和真正负责飞碟移动的Emit类。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using MyGame;
using UnityEngine.SceneManagement;public class FirstController : MonoBehaviour, ISceneController, IUserAction {public ActionManager MyActionManager { get; set; }public DiskFactory factory { get; set; }public RecordController scoreRecorder;public UserGUI user;    void Awake() {Director diretor = Director.getInstance();diretor.sceneCtrl = this;                              }// Use this for initializationvoid Start() {Begin();}// Update is called once per framevoid Update () {}public void Begin() {MyActionManager = gameObject.AddComponent<ActionManager>() as ActionManager;scoreRecorder = gameObject.AddComponent<RecordController>();user = gameObject.AddComponent<UserGUI>();user.Begin();}public void Hit(DiskController diskCtrl) {   // 0=playing 1=lose 2=win 3=cooling     if (user.game == 0) {            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit)) {//hit.collider.gameObject.SetActive(false);Debug.Log("Hit");factory.freeDisk(hit.collider.gameObject);hit.collider.gameObject.GetComponent<DiskController>().hit = true;scoreRecorder.add(hit.collider.gameObject.GetComponent<DiskController>());}}}public void PlayDisk() {MyActionManager.playDisk(user.round);}public void Restart() {SceneManager.LoadScene("scene");}public int Check() {return 0;}
}

FirstController同样是负责着所有其他的controller和userGUI,这都和之前相同;新加入的DiskFactory我们一会儿再介绍。这里主要讲一下Hit函数。所谓ScreenPointToRay就是从Camera出发连接到鼠标点击位置的一条射线,然后如果射线经过了我们目标的GameObject,就算是击中了。当一个飞碟被击中时,我们首先将这个Object的Active设为False,从而将击中的消息传回给Action;然后FreeDisk是将这个实例从放到free列表中等待下一次调用(其实在FreeDisk中我们已经有了设置Active的操作,这里将其注释在这里是为了提醒我们它的重要性)。不知道free列表是什么东西没关系,我们继续看DiskFactory的代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;public class DiskFactory : MonoBehaviour {private static DiskFactory _instance;public FirstController sceneControler { get; set; }GameObject diskPrefab;public DiskController diskData;public List<GameObject> used;public List<GameObject> free;// Use this for initializationpublic static DiskFactory getInstance() {return _instance;}private void Awake() {if (_instance == null) {_instance = Singleton<DiskFactory>.Instance;_instance.used = new List<GameObject>();_instance.free = new List<GameObject>();diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk"), new Vector3(40, 0, 0), Quaternion.identity);}}public void Start() {sceneControler = (FirstController)Director.getInstance().sceneCtrl;sceneControler.factory = _instance;      }public GameObject getDisk(int round) { // 0=playing 1=lose 2=win 3=coolingif (sceneControler.scoreRecorder.Score >= round * 4) {if (sceneControler.user.round < 3) {sceneControler.user.round++;sceneControler.user.num = 0;sceneControler.scoreRecorder.Score = 0;}else {sceneControler.user.game = 2; // 赢了return null;}}else {if (sceneControler.user.num >= 10) {sceneControler.user.game = 1; // 输了return null;}            }GameObject newDisk;RoundController diskOfCurrentRound = new RoundController(sceneControler.user.round);        if (free.Count == 0) {// if no free disk, then create a new disknewDisk = GameObject.Instantiate(diskPrefab) as GameObject;newDisk.AddComponent<ClickGUI>();diskData = newDisk.AddComponent<DiskController>();}else {// else let the first free disk be the newDisknewDisk = free[0];free.Remove(free[0]);newDisk.SetActive(true);}diskData = newDisk.GetComponent<DiskController>();diskData.color = diskOfCurrentRound.color;//Debug.Log(diskData);newDisk.transform.localScale = diskOfCurrentRound.scale * diskPrefab.transform.localScale;newDisk.GetComponent<Renderer>().material.color = diskData.color;used.Add(newDisk);return newDisk;}public void freeDisk(GameObject disk1) {used.Remove(disk1);disk1.SetActive(false);free.Add(disk1);return;}public void Restart() {used.Clear();free.Clear();}
}

这就是所谓的工厂模式了。当游戏对象的创建与销毁成本较高,且游戏涉及大量游戏对象的创建与销毁时,必须考虑减少销毁次数,比如这次的打飞碟游戏,或者像其他类型的射击游戏,其中子弹或者中弹对象的创建与销毁是很频繁的。工厂模式将已经创建好正在使用的实例存在一个used列表中,然后当使用完成(被击中)就将其放在free列表中,等待下一次调用;当我们需要一个新的实例的时候,首先检查free列表,当其中没有限制的实例时我们才创建一个新的。getDisk和freeDisk就实现了上面所叙述的逻辑,是核心的代码。

最后是Emit类。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyGame;public class Emit : SSAction
{public FirstController sceneControler = (FirstController)Director.getInstance().sceneCtrl;public Vector3 target;     public float speed;     private float distanceToTarget;    float startX;float targetX;float targetY;public override void Start() {speed = sceneControler.user.round * 5;GameObject.GetComponent<DiskController>().speed = speed;startX = 6 - Random.value * 12;if (Random.value > 0.5) {targetX = 36 - Random.value * 36;targetY = 25 - Random.value * 25;}else {targetX = -36 + Random.value * 36;targetY = -25 + Random.value * 25;}this.Transform.position = new Vector3(startX, 0, 0);target = new Vector3(targetX, targetY, 30);//Debug.Log(target);distanceToTarget = Vector3.Distance(this.Transform.position, target);}public static Emit GetSSAction() {Emit action = ScriptableObject.CreateInstance<Emit>();return action;}public override void Update() {Vector3 targetPos = target;if(!GameObject.activeSelf){this.destroy = true;return;}//facing the targetGameObject.transform.LookAt(targetPos);//calculate the starting angel  float angle = Mathf.Min(1, Vector3.Distance(GameObject.transform.position, targetPos) / distanceToTarget) * 45;GameObject.transform.rotation = GameObject.transform.rotation * Quaternion.Euler(Mathf.Clamp(-angle, -42, 42), 0, 0);float currentDist = Vector3.Distance(GameObject.transform.position, target);//Debug.Log("****************************");//Debug.Log(startX);//Debug.Log(target);//Debug.Log("****************************");GameObject.transform.Translate(Vector3.forward * Mathf.Min(speed * Time.deltaTime, currentDist));if (this.Transform.position == target) {sceneControler.scoreRecorder.miss();Debug.Log("here in miss!!");GameObject.SetActive(false);GameObject.transform.position = new Vector3(startX, 0, 0);sceneControler.factory.freeDisk(GameObject);this.destroy = true;this.Callback.ActionDone(this);}}
}

我们在Start函数中保证了飞碟出现的位置和目标方向的随机性。然后在Update函数中首先计算移动的角度,然后根据速度给出当前的位移,然后进行一次判断,如果当前位置已经是终点了,那么我们首先setActive告诉外层的actionManager之前的运动可以取消了,然后将当前的实例free掉。在Update函数开始返回前也需要设置一下destroy是为了在hit后也可以告诉actionManager取消当前运动,与后面那个并不是重复操作。

最后我们来看一眼actionControl的核心ActionMananger,其他的就不全部贴上来了。

public class ActionManager : SSActionManager {public FirstController sceneController;public DiskFactory diskFactory;public RecordController scoreRecorder;public Emit EmitDisk;public GameObject Disk;int count = 0;protected void Start() {sceneController = (FirstController)Director.getInstance().sceneCtrl;diskFactory = sceneController.factory;scoreRecorder = sceneController.scoreRecorder;sceneController.MyActionManager = this;}protected new void Update() {if (sceneController.user.round <= 3 && sceneController.user.game == 0) {count++;if (count == 60 * sceneController.user.round) {playDisk(sceneController.user.round);sceneController.user.num++;count = 0;}base.Update();}}public void playDisk(int round) {EmitDisk = Emit.GetSSAction();Disk = diskFactory.getDisk(round);this.AddAction(Disk, EmitDisk, this);Disk.GetComponent<DiskController>().action = EmitDisk;}}

Update实现了每60帧*round后发出一个飞碟。之所以这样设计是因为最后一轮的飞碟速度太快,这样能够适当降低游戏难度。playDisk函数就是从工厂中获取一个飞碟,然后和下一个应该出现的飞碟的移动方向和特征一起传给AcitionManager。

另外为了有空战的感觉,我还加入了在AssetStore下载的StarField天空盒,最终的效果如下图所示。

【游戏开发】unity教程4 打飞碟小游戏相关推荐

  1. Python游戏开发,Python实现贪吃蛇小游戏与吃豆豆 附带源码

    开发工具 Python版本: 3.6.4 相关模块: pygame模块: 以及一些Python自带的模块. 环境搭建 安装Python并添加到环境变量,pip安装需要的相关模块即可. 原理简介 贪吃蛇 ...

  2. 7小时Unity3D游戏开发培训教程

    获取地址:7小时Unity3D游戏开发培训教程 中文名: 7小时Unity3D游戏开发培训教程 英文名: Over 7 hours of Unity Training Videos 资源格式: 光盘镜 ...

  3. 如何开发与设计一个爆款小游戏

    小游戏是今年游戏行业中又一个火爆的类型.它所指的不是微信小游戏,而是可以运行于微信.浏览器的H5游戏,也是能以Native形式运行的小游戏.它小而美,即点即玩,社交属性更强.在这次开发者巡回沙龙中,游 ...

  4. 【游戏开发实战】使用Unity 2019制作仿微信小游戏飞机大战(七):主角飞机碰撞与爆炸

    文章目录 零.教程目录 一.前言 二.本篇目标 三.飞机机碰撞组件:BoxCollider2D.Rigidbody2D 四.添加Tag:Enemy 五.主角飞机碰撞处理:OnTriggerEnter2 ...

  5. Unity 创建2D平台游戏开发学习教程

    了解如何使用C#在Unity中创建您的第一款2D平台游戏 你会学到什么 使用Unity创建2D奥运会 使用可脚本化的对象和单一模式 使用良好的编程实践 创造武器和射弹 使用可脚本化的对象和委托模式创建 ...

  6. C#和Unity编码和游戏开发学习教程

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:110节课(26小时25分钟)|大小解压后:18.6 ...

  7. Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍

    Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍 本书主要讲解的是,如何使用Unity开发2D游戏.但在开始讲解之前,最好先熟悉一下Unity这个工具.本章会首先介绍Unity的下 ...

  8. Unity 4.x 2D游戏开发基础教程大学霸第一更

    Unity 4.x 2D游戏开发基础教程 大学霸 第1章  Unity及其组成的介绍 本书主要讲解的是,如何使用Unity开发2D游戏.但在开始讲解之前,最好先熟悉一下Unity这个工具.本章会首先介 ...

  9. Unity 游戏实例开发集合 之 FlyPin (见缝插针) 休闲小游戏快速实现

    Unity 游戏实例开发集合 之 FlyPin (见缝插针) 休闲小游戏快速实现 目录 Unity 游戏实例开发集合 之 FlyPin (见缝插针) 休闲小游戏快速实现 一.简单介绍 二.FlyPin ...

最新文章

  1. onethink php7.1,海豚PHP开发框架下载
  2. 参加中国十大IT杰出博客
  3. JdbcTemplate中queryForObject方法返回空结果或不正确结果数量的解决方法
  4. 每日一皮:重构时总会出现的惊喜.......
  5. note-删除Visual Studio recent Projects list
  6. Konstrukt PHP REST框架 教程二
  7. Exchange 2013学习(六),脱机通讯簿
  8. 2018.3.24 struct
  9. 微信彩色个性昵称 游戏取名 九宫格切图 多图拼接工具箱微信小程序源码
  10. 图像处理(十三)LBP特征提取
  11. linux下编译jrtplib和jthread,Linux下编译jrtplib和jthread(转)
  12. MindManager 2021授权许可密钥思维导图软件
  13. 出售永磁同步电机(pmsm)模型预测控制(MPC)matlab/simulink仿真模型,转速控制,电流控制,转矩控制,直接预测控制(有限集模型预测控制)(这单矢量和双矢量,三矢量),无差拍,foc矢
  14. 计算机等级考试三级信息安全知识整理
  15. PYTHON处理年月日的英文转换
  16. android 获取控件在屏幕中位置
  17. m4a怎么转换成mp3
  18. 每日一个小技巧:今天告诉你拍照识别文字的软件有哪些
  19. 图的计算(1):图的矩阵表示
  20. 使用 Android Studio 查看模拟器和手机根目录方法Android Device Monitor

热门文章

  1. 在Mac上安装使用Charles
  2. logback异步输出日志详解
  3. JAVA实现简单“伪植物大战僵尸“游戏
  4. 《算法导论》第九章.中位数和顺序统计量
  5. 腾讯Java一面失利,四月凭借这份offer,直接进厂
  6. icloud安装错误怎么办_给你细说win7系统icloud win7安装失败的修复办法
  7. c语言窗口插件,C语言插件开发形式
  8. Msc.Marc安装和使用过程中遇到证书错误——处理办法
  9. Spring整合swagger
  10. 蓝桥杯算法很美笔记—排序实现题