打飞碟

文章目录

  • 打飞碟
    • 游戏规则与要求
    • 项目地址与演示视频
    • 具体实现
    • 总结
    • 参考资料

游戏规则与要求

  • 游戏内容要求:

    1. 游戏有 n 个 round,每个 round 都包括10 次 trial;
    2. 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
    3. 每个 trial 的飞碟有随机性,总体难度随 round 上升;
    4. 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
  • 游戏的要求:
    • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
    • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离

项目地址与演示视频

项目地址 ->传送门?

视频连接 -> 传送门?

具体实现

动作管理的大部分代码延用上一次作业,需要实现的就只有一个飞碟的飞行动作。

  • FlyActionManager

    飞碟的动作管理类,当场景控制器需要发射飞碟时就调用DiskFly使飞碟飞行。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;public class FlyActionManager : SSActionManager {public DiskFlyAction fly;  public FirstController scene_controller;           protected void Start() {scene_controller = (FirstController)SSDirector.GetInstance().CurrentScenceController;scene_controller.action_manager = this;     }//飞碟飞行public void DiskFly(GameObject disk, float angle, float power) {int lor = 1;if (disk.transform.position.x > 0) lor = -1;fly = DiskFlyAction.GetSSAction(lor, angle, power);this.RunAction(disk, fly, this);}
    }
  • DiskFlyAction

    通过位置变换和角度变换模拟飞碟的飞行,也可以使用刚体组件(Rigidbody)实现。当飞碟的高度在摄像机观察范围之下时则动作停止。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;public class DiskFlyAction : SSAction {public float gravity = -5;                                 //向下的加速度private Vector3 start_vector;                              //初速度向量private Vector3 gravity_vector = Vector3.zero;             //加速度的向量,初始时为0private Vector3 current_angle = Vector3.zero;              //当前时间的欧拉角private float time;                                        //已经过去的时间private DiskFlyAction() { }public static DiskFlyAction GetSSAction(int lor, float angle, float power) {DiskFlyAction action = CreateInstance<DiskFlyAction>();if (lor == -1) {action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;}else {action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;}return action;}public override void Update() {time += Time.fixedDeltaTime;gravity_vector.y = gravity * time;transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;transform.eulerAngles = current_angle;//如果物体y坐标小于-10,动作就做完了if (this.transform.position.y < -10) {this.destroy = true;this.callback.SSActionEvent(this);      }}public override void Start() { }
    }

然后是游戏对象部分,本次游戏中的对象就只有飞碟,为了节约资源需要使用工厂模式对飞碟进行管理。

  • Disk

    Disk中保留飞碟类型type,分数score,颜色color。然后原本还有一个控制缩放的public Vector3 scale = new Vector3(1, 0.25f, 1),我认为没有实际用处,因为碰撞检测器的大小会随着拉伸到比飞碟更大。

下图为拉伸之后的飞碟:

public class Disk : MonoBehaviour {public int type = 1;public int score = 1;                               public Color color = Color.white;
}
  • DiskFactory

    维护两个列表,一个是正在使用的飞碟,一个是空闲飞碟。当场景控制器需要获取一个飞碟时,先在空闲列表中寻找可用的空闲飞碟,如果找不到就根据预制重新实例化一个飞碟。回收飞碟的逻辑为遍历使用列表,当有飞碟已经完成了所有动作,即位置在摄像机之下,则回收。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;public class DiskFactory : MonoBehaviour {private List<Disk> used = new List<Disk>();private List<Disk> free = new List<Disk>();public GameObject GetDisk(int round) {GameObject disk_prefab;//寻找空闲飞碟,如果无空闲飞碟则重新实例化飞碟if (free.Count>0) {disk_prefab = free[0].gameObject;free.Remove(free[0]);} else {disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk"), new Vector3(0, -10f, 0), Quaternion.identity);disk_prefab.GetComponent<Renderer>().material.color = disk_prefab.GetComponent<Disk>().color;disk_prefab.transform.localScale = disk_prefab.GetComponent<Disk>().scale;}used.Add(disk_prefab.GetComponent<Disk>());disk_prefab.SetActive(true);return disk_prefab;}public void FreeDisk() {for(int i=0; i<used.Count; i++) {if (used[i].gameObject.transform.position.y <= -10f) {free.Add(used[i]);used.Remove(used[i]);}}          }public void Reset() {FreeDisk();}
    }
    
  • UserGUI

    比较简单,将分数,Round,Trial显示出来,有按钮控制游戏开始和重新开始即可。比较重要的一点时使用Input.GetButtonDown("Fire1")检测鼠标左键的点击。

     if (Input.GetButtonDown("Fire1")) {Vector3 pos = Input.mousePosition;action.Hit(pos);}
    
  • FirstController

    最重要的部分是场景控制器,游戏开始之后,设置一个定时器,每隔一定时间从飞碟工厂中获取一个飞碟并发射,检测用户点击发送的射线是否与飞碟发生碰撞,有则通知记分员加分并且通知工厂回收飞碟。部分重要函数代码如下。

    Update函数每一帧检测鼠标点击,并根据round调整规则。

    void Update () {if(running) {count++;//检测鼠标点击if (Input.GetButtonDown("Fire1")) {//Debug.Log("sdfsdfdf");Vector3 pos = Input.mousePosition;Hit(pos);}//rulerswitch (round) {case 1: {if (count >= 150) {count = 0;SendDisk(1);trial += 1;if (trial == 10) {round += 1;trial = 0;}}break;}case 2: {if (count >= 100) {count = 0;if (trial % 2 == 0) SendDisk(1);else SendDisk(2);trial += 1;if (trial == 10) {round += 1;trial = 0;}}break;}case 3: {if (count >= 50) {count = 0;if (trial % 3 == 0) SendDisk(1);else if(trial % 3 == 1) SendDisk(2);else SendDisk(3);trial += 1;if (trial == 10) {running = false;}}break;}default:break;} disk_factory.FreeDisk();}
    }
    

    SendDisk从工厂中拿飞碟并根据种类设置发射参数,然后调用动作管理器执行动作。师兄博客中提及的点一下会执行两次加分是因为将检测鼠标点击,即Input.GetButtonDown("Fire1")在UserGUI的OnGUI中实现,OnGUI再通知场景控制器执行Hit,调试了很久发现点一下调用了两次OnGUI,所以调用了两次Hit。并且我认为检测碰撞不应该由GUI实现所以在场景控制器中检测。

    private void SendDisk(int type) {//从工厂中拿一个飞碟GameObject disk = disk_factory.GetDisk(type);//飞碟位置float ran_y = 0;float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;//飞碟初始所受的力和角度float power = 0;float angle = 0;//根据飞碟种类不同设置不同的发射位置和速度if (type == 1) {ran_y = Random.Range(1f, 5f);power = Random.Range(5f, 7f);angle = Random.Range(25f,30f);}else if (type == 2) {ran_y = Random.Range(2f, 3f);power = Random.Range(10f, 12f);angle = Random.Range(15f, 17f);}else {ran_y = Random.Range(5f, 6f);power = Random.Range(15f, 20f);angle = Random.Range(10f, 12f);}disk.transform.position = new Vector3(ran_x*16f, ran_y, 0);action_manager.DiskFly(disk, angle, power);
    }
    

    Hit函数检测射线与飞碟是否碰撞,如碰撞则计分并回收飞碟。

    public void Hit(Vector3 pos) {Ray ray = Camera.main.ScreenPointToRay(pos);RaycastHit[] hits;hits = Physics.RaycastAll(ray);for (int i = 0; i < hits.Length; i++) {RaycastHit hit = hits[i];if (hit.collider.gameObject.GetComponent<Disk>() != null) {score_recorder.Record(hit.collider.gameObject);hit.collider.gameObject.transform.position = new Vector3(0, -10, 0);}}
    }
    

总结

本次作业主要使用了工厂模式复用已经实例化的对象节约资源,以及鼠标点击事件的检测,射线与游戏对象的碰撞。

参考资料

师兄博客1

师兄博客2

官方文档

Unity3D项目五:简单打飞碟相关推荐

  1. Unity3d制作一个简单粗暴的五子棋项目工程源码

    Unity3d制作一个简单粗暴的五子棋 最终效果 项目源码 绘制棋盘 绘制构思 绘制代码 效果图 放置棋子 功能和效果 功能 效果 制作棋子 定义类和类型 棋子类型 棋盘格类 实现功能 初始棋盘格数据 ...

  2. Unity3D游戏编程-鼠标打飞碟

    Unity3D游戏编程-鼠标打飞碟 文章目录 Unity3D游戏编程-鼠标打飞碟 一.作业要求 二.项目配置 三.项目演示 视频演示 项目下载 文字说明 项目截图 四.前置内容 MVC模式 动作管理器 ...

  3. c语言1000内亲密对数,《C语言程序的设计上机指导》项目五函数及其应用.pptx

    <C语言程序的设计上机指导>项目五函数及其应用.pptx 项目五 函数及其应用 本章重点 文本 C语言程序结构. 函数定义与函数声明. 函数的调用. 变量的存储属性. 任务一 关于函数应用 ...

  4. 工业机器人安装调试与维护课程试卷_工业机器人安装、调试与维护教学课件作者阙正湘项目五工业机器人机械结构件的维修...

    工业机器人安装.调试与维护教学课件作者阙正湘项目五工业机器人机械结构件的维修 工业机器人与智能制造/智能硬件/嵌入式与物联网/电子信息工程 工业机器人安装调试与维护保养 项目五 工业机器人机械结 构件 ...

  5. 深入浅出聊Unity3D项目优化:从Draw Calls到GC (难度2 推荐5)

    原文出处: 慕容小匹夫的博客(@慕容小匹夫) 前言: 刚开始写这篇文章的时候选了一个很土的题目...<Unity3D优化全解析>.因为这是一篇临时起意才写的文章,而且陈述的都是既有的事实, ...

  6. Unity3D项目程序加密1——在Unity3D里使用自己的dl

    原文地址:http://liweizhaolili.blog.163.com/blog/static/1623074420144313825921/ 如果稍微关注过这方面知识的朋友,应该知道Unity ...

  7. Unity3D项目加密-精锐5加密锁解决方案

    Unity3D项目加密 Unity3D项目研发完成,发布前开发者需要对代码及资源进行加密,防止代码被反编译,防止资源被盗窃,造成不必要的损失.如果不做加密,可能很快就会出现一堆破解版游戏. Unity ...

  8. 安卓开发项目(微信简单界面)

    安卓项目----微信简单界面(1) 一.开发所需软件:Android studio 二.功能说明 在安卓studio中建立一个新的项目后,我们可以看到在项目中存在两个不一样的未经修改的原文件 acti ...

  9. SSM+MyBatis-Plus+EasyExcel+腾讯云tianai滑动验证码接入项目搭建+简单实现增、删、改、查、导入、滑动验证码功能

    SSM+MyBatis-Plus+EasyExcel+腾讯云&tianai滑动验证码接入项目搭建+简单实现增.删.改.查.导入.滑动验证码功能 文章末尾附源码 一.什么是SSM框架 SSM框架 ...

  10. 暗月渗透测试项目-五(上)

    暗月渗透测试项目五 准备工作 环境搭建 使用的是暗月提供的环境 直接虚拟机运行即可 设置网络环境 网卡配置的时候为了方便直接使用的是暗月的vm19的网卡配置的内网环境 环境搭建完成 如果使用的Mac的 ...

最新文章

  1. Java语言的循环控制结构
  2. Lambda 表达式到底有何用处?如何使用?
  3. Django 发布时间格式化
  4. 【C 语言】指针数据类型 ( 指针类型变量 与 指针指向的内存块 概念区别 | 指针赋值 | 指针运算 | 内存赋值 | 内存取值 | 内存修改注意事项 )
  5. java标志清理_JVM内存管理之GC算法精解(五分钟让你彻底明白标记/清除算法)...
  6. [翻译]opengl扩展教程2
  7. 2013计算机大纲,2013计算机应用基础考试大纲
  8. docker容器内存和CPU使用限制
  9. 深入理解JVM虚拟机-Ubuntu中安装openJDK
  10. Java 图片处理解决方案:ImageMagick 快速入门教程
  11. JAVA实现上传,下载,jxl操作Excel和邮件发送
  12. C++项目学习(机器人方向)
  13. linux usb ic读卡器,在Linux下使用ACS ACR1252U USB NFC读卡器
  14. 基于Java springmvc+mybatis酒店信息管理系统设计和实现
  15. android popWindow组件微信式实现(较完整版)
  16. No fallbackFactory instance of type class 问题处理
  17. 步步高彭雄:弄潮“互联网+零售”从端到端流程开始
  18. 论文阅读——MobileNetV2: Inverted Residuals and Linear Bottlenecks
  19. 银河麒麟系统开启root用户登录
  20. 《可复制的领导力》读书笔记

热门文章

  1. 最新VMware16pro虚拟机的下载与安装
  2. UltraEdit 注册机使用激活方法 更新:暴力破解
  3. android手机的mqtt测试工具,sIoT及安卓app实现mqtt实验
  4. 中国移动加快自主创新推动我国主导4G标准引领国际
  5. neo4j图数据库导入scv文件
  6. BPC电波授时信号的“零成本”伪造
  7. matlab正太分布的反函数,怎样用matlab求标准正态分布函数的反函数函数值
  8. 嵌入式BI助力ISV厂商决胜大数据时代
  9. 计算机与酒店管理大学论文,酒店管理系统的设计与实现
  10. LED的基本操作(138译码器 573锁存器)