与游戏世界交互作业

2023-11-17

一、编写一个简单的鼠标打飞碟(Hit UFO)游戏

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

 

首先,我们看到游戏的第一个要求是要我们使用一个带缓存的工厂模式管理不同飞碟的生产与回收,要实现这一目的,我们需要添加一个飞碟工厂。通过实现这个飞碟工厂,我们能够有效地利用已经构造好的游戏对象实现资源的重利用,以减少资源开销。具体实现如下:

我们再工厂中添加一个usde列表和free列表,分别表示已使用和未使用的飞碟对象。当free列表为空时,我们就需要创建新的对象。然后,我们对飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数等变量进行设置,并将获得的飞碟加入使用队列,回收的飞碟加入初始化队列。

using System.Collections.Generic;
using UnityEngine;

public class DiskFactory : MonoBehaviour {
    public List<GameObject> used = new List<GameObject>();
    public List<GameObject> free = new List<GameObject>();

    // Use this for initialization
    void Start () { }

    public void GenDisk()
    {
        GameObject disk;
        if(free.Count == 0)
        {
            disk = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/Disk"), Vector3.zero, Quaternion.identity);
        }
        else
        {
            disk = free[0];
            free.RemoveAt(0);
        }
        float x = Random.Range(-10.0f, 10.0f);
        disk.transform.position = new Vector3(x, 0, 0);
        disk.transform.Rotate(new Vector3(x < 0? -x*9 : x*9, 0, 0));
        float r = Random.Range(0f, 1f);
        float g = Random.Range(0f, 1f);
        float b = Random.Range(0f, 1f);
        Color color = new Color(r, g, b);
        disk.transform.GetComponent<Renderer>().material.color = color;
        used.Add(disk);
    }
    public void RecycleDisk(GameObject obj)
    {
        obj.transform.position = Vector3.zero;
        free.Add(obj);
    }
}

接下来是场景控制器。场景控制器由游戏对象生成,使用Singleton为每一个MonoBehavior子类创建一个对象实例,并通过调用动作管理器,实现每一个对象的相应动作,如飞盘的飞行、爆炸等。

public class FirstSceneController : MonoBehaviour, IUserAction, ISceneController{
    public CCActionManager actionManager;
    public GameObject disk;
    protected DiskFactory df;
    public int flag = 0;
    private float interval = 3;
    public int score = 0;
    public static int times = 0;

    private void Awake()
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentSceneController = this;
        this.gameObject.AddComponent<DiskFactory>();
        this.gameObject.AddComponent<CCActionManager>();
        this.gameObject.AddComponent<UserGUI>();
        df = Singleton<DiskFactory>.Instance;
        //director.currentSceneController.GenGameObjects();
    }
    private void Start()
    {
    }
    public void GenGameObjects ()
    {
    }
    public void Restart()
    {
        SceneManager.LoadScene("1");
    }
    public void Pause ()
    {
        actionManager.Pause();
    }
    public void Update()
    {
        if (times < 30 && flag == 0)
        {
            if (interval <= 0)
            {
                interval = Random.Range(3, 5);
                times++;
                df.GenDisk();
            }
            interval -= Time.deltaTime;
        }
    }
}

那么,被场景控制器调用的动作管理器是如何实现的呢?动作管理器这里就是设置一个动作列表,从而可以实现对多个飞碟的同时的飞行的控制。然后,我们调用userClickAction,可以实现通过鼠标点击控制对象的行为,实现飞碟一被点击就会爆炸的效果。

public class CCActionManager : SSActionManager, ISSActionCallback {
    public FirstSceneController sceneController;
    public List<CCMoveToAction> seq = new List<CCMoveToAction>();
    public UserClickAction userClickAction;
    public DiskFactory disks;
    
    protected new void Start()
    {
        sceneController = (FirstSceneController)SSDirector.getInstance().currentSceneController;
        sceneController.actionManager = this;
        disks = Singleton<DiskFactory>.Instance;
    }
    protected new void Update()
    {
        if(disks.used.Count > 0)
        {
            GameObject disk = disks.used[0];
            float x = Random.Range(-10, 10);
            CCMoveToAction moveToAction = CCMoveToAction.GetSSAction(new Vector3(x, 12, 0), 3 * (Mathf.CeilToInt(FirstSceneController.times / 10) + 1) * Time.deltaTime);
            seq.Add(moveToAction);
            this.RunAction(disk, moveToAction, this);
            disks.used.RemoveAt(0);
        }
        if (Input.GetMouseButtonDown(0) && sceneController.flag == 0)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitGameObject;
            if (Physics.Raycast(ray, out hitGameObject))
            {
                GameObject gameObject = hitGameObject.collider.gameObject;
                if (gameObject.tag == "disk")
                {
                    foreach(var k in seq)
                    {
                        if (k.gameObject == gameObject)
                            k.transform.position = k.target;
                    }
                    userClickAction = UserClickAction.GetSSAction();
                    this.RunAction(gameObject, userClickAction, this);
                }/*
                else if (gameObject.transform.parent.name == "boat" && sceneController.boatCapacity < 2 && (moveToAction == null || !moveToAction.enable))
                {
                    moveToAction = CCMoveToAction.GetSSAction(-gameObject.transform.parent.transform.position, 10*Time.deltaTime);
                    this.RunAction(gameObject.transform.parent.gameObject, moveToAction, this);
                }*/
            }
        }
        base.Update();
    }
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
        disks.RecycleDisk(source.gameObject);
        seq.Remove(source as CCMoveToAction);
        source.destory = true;
        if (FirstSceneController.times >= 30)
            sceneController.flag = 1;
    }
    public void CheckEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
    }
    public void Pause()
    {
        if(sceneController.flag == 0)
        {
            foreach (var k in seq)
            {
                k.enable = false;
            }
            sceneController.flag = 2;
        }
        else if(sceneController.flag == 2)
        {
            foreach (var k in seq)
            {
                k.enable = true;
            }
            sceneController.flag = 0;
        }
    }
}

最后是UserGUI的实现,每当用户成功点击一个飞碟使其爆炸,在分数一栏其得分就加一;每当用户漏点一个飞碟,在生命一栏其生命就减一。点击start开始游戏,若游戏失败,点击restart可重新开始游戏。

if (game_start)
        {
            //用户射击
            if (Input.GetButtonDown("Fire1"))
            {
                Vector3 pos = Input.mousePosition;
                action.Hit(pos);
            }

            GUI.Label(new Rect(5, 5, 200, 50), "Score:", text_style);
            GUI.Label(new Rect(100, 5, 200, 50), action.GetScore().ToString(), score_style);

            GUI.Label(new Rect(Screen.width - 280, 5, 50, 50), "Life:", text_style);
            //显示当前血量
            for (int i = 0; i < life; i++)
            {
                GUI.Label(new Rect(Screen.width - 200 + 30 * i, 5, 50, 50), "★", bold_style);
            }
            for (int i = life; i < 6; i++)
            {
                GUI.Label(new Rect(Screen.width - 200 + 30 * i, 5, 50, 50), "★", wait_style);
            }

            if (action.GetCoolTimes() >= 0 && action.GetRound() == 1) {
                        //    GUI.Label(new Rect(Screen.width / 2 - 100, 60, 200, 50), "", wait_style);
                    if (action.GetCoolTimes() == 0) {
                        GUI.Label(new Rect(Screen.width / 2 - 200, 150, 200, 50), "ROUND 1", time_style);
                    }else
                        GUI.Label(new Rect(Screen.width / 2 - 30, 150, 200, 50), action.GetCoolTimes().ToString(), time_style);


            }
            if (action.GetRound() == 2 && action.GetCoolTimes2() > 0) {
                    GUI.Label(new Rect(Screen.width / 2 - 200, 150, 200, 50), "ROUND 2", time_style);

            }
            if (action.GetRound() == 3 && action.GetCoolTimes3() > 0) {
                    GUI.Label(new Rect(Screen.width / 2 - 200, 150, 200, 50), "ROUND 3", time_style);

            }


            //游戏结束
            if (life == 0)
            {
                high_score = high_score > action.GetScore() ? high_score : action.GetScore();
                GUI.Label(new Rect(Screen.width / 2 - 100, 110, 100, 100), "Game Over!", over_style);
                GUI.Label(new Rect(Screen.width / 2 - 80, 200, 50, 50), "Highest Score:", text_style);
                GUI.Label(new Rect(Screen.width / 2 + 50, 200, 50, 50), high_score.ToString(), text_style);

                if (GUI.Button(new Rect(Screen.width / 2 - 60, 300, 100, 50), "Restart"))
                {
                    life = 6;
                    action.ReStart();
                    return;
                }
                action.GameOver();
            }
        }
        else
        {
            GUI.Label(new Rect(Screen.width / 2 - 110, 110, 100, 100), "鼠标打飞碟", over_style);
       
            if (GUI.Button(new Rect(Screen.width / 2 - 60, 200, 100, 50), "Start"))
            {
                game_start = true;
                
                action.BeginGame();
            }
        }
    }
    public void ReduceBlood()
    {
        if(life > 0)
            life--;
    }

游戏场景如下:

 

二 、编写一个简单地自定义Component

我们可以编写一个旋转脚本,使得UFO可以进行转动。在旋转脚本中,我们设计不同的速度与转轴,并实现预制:

public class Rotate : MonoBehaviour
{

    public float v;
    public float ang1, ang2;

    void Update()
    {
        Vector3 axis = new Vector3(0, ang1, ang2);
        this.transform.RotateAround(new Vector3(0, 0, 0), axis, v * Time.deltaTime);
        this.transform.Rotate(Vector3.up * 100 * Time.deltaTime);
    }

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与游戏世界交互作业 的相关文章

  • 详述Java中的异常

    我是目录 一 异常的解决方案 二 异常的基本用法 三 Java异常体系 四 自定义异常 五 面试阐述 所谓 异常 指的就是程序在 运行时 出现错误时通知调用者的一种机制 我们平时把 System out println 拼写错了 写成了 s
  • 第一章 pandas基础-练习题

    第一章 pandas基础 练习题 首先要导入对应的模块 import pandas as pd import numpy as np Ex1 口袋妖怪数据集 现有一份口袋妖怪的数据集 下面进行一些背景说明 代表全国图鉴编号 不同行存在相同数
  • QT基础(三)之添加资源文件及界面美化

    QT基础之添加资源文件及界面美化 QT可以做出非常炫酷的图形界面 通过添加一些资源文件可以对我们的界面进行美化 下面以添加图片资源 美化标签为例 对QT Creator添加资源文件进行学习 一 添加资源文件 1 给工程添加一个新的资源文件
  • C++之标准库(STL)容器List的用法

    文章目录 list说明 list定义 list使用 list赋值操作 list数据元素插入和删除操作 list数据存取 list大小操作 list反转排序 list访问 list说明 链表是一种物理存储单元上非连续 非顺序的存储结构 数据元
  • QtCreator 打不开UI文件

    最近遇到了ubuntu下的QtCreator打开ui 文件时 QT Creator 界面变黑色 程序崩溃 然后自动退出 这儿软件我用了1年了 莫名其妙出现了这个问题 很是费解 重装了QtCreator和QtDesigner 还是不行 在网上
  • Endnote 导入参考文献的时候,格式错误太多了。et.al错误,国标GBT7714在endnote中的详细配置教程,适用于本科,硕士论文文献插入的模板

    文章目录 一 Endnote文献GBT7714下载 0 起因与发展 1 1进行chinese GBT7714 2015的下载 二 Endnote格式配置及参数的设置 2 1 开始修改配置 重要配置详细分解 英文期刊 中文期刊 三 实战插入文
  • 用matlab进行拉普拉斯滤波,matlab拉普拉斯算子锐化滤波

    一 本文主要是在给出拉普拉斯锐化算子公式的情况下 在matlab上实现代码设计 拉普拉斯算子是最简单的各向同性微分算子 有几种常用的滤波模板 本文使用的是八邻域模板 如下所示 image png 其对应的计算公式为 image png 因此
  • 应用程序,操作系统,驱动程序的关系

    硬件和软件 计算机资源分为硬件资源和软件资源 硬件资源包括cpu 内存 显卡 网卡 声卡 硬盘等等 软件资源包括各种程序 每个硬件完成特定的功能 比如显卡完成在显示设备上显示图形 声卡实现声音的处理 再比如 你用qq发送一段文字给一个同学
  • VUE3使用JSON编辑器

    1 先看看效果图 可以自行选择展示效果 2 这是我在vue3项目中使用的JSON编辑器 首先引入第三方插件 npm install json editor vue3 yarn add json editor vue3 3 引入到项目中 导入
  • 强化学习实践二 :理解gym的建模思想

    David Silver的强化学习公开课有几个特点 个人感觉首要的一个特点是偏重于讲解理论 而且有时候为了讲清楚一个理论的来龙去脉 也顺带讲了很多不常用的理论 还有一个特点是小例子很多 这些例子有时候不仅是为了讲清楚一个复杂的算法 而且通过
  • [Redis] Redis 安装部署

    Redis Redis 安装部署 简介 Redis是一个开源的使用ANSI C语言编写 遵守BSD协议 支持网络 可基于内存亦可持久化的日志型 Key Value 数据库 并提供多种语言的API 它通常被称为数据结构服务器 因为值 valu
  • 查看Postgresql的连接状况

    今天遇到一个问题 就是pg一直报错 说有太多的客户端连接到数据库上面 但现在不知道是什么程序连接 pg默认的max connection是100 我并没有修改过 以为平时公司内部用 应该够了 但现在貌似这100个连接都被消耗掉 在网上goo
  • 【CSS】如何用css做一个爱心

    摘要 HTML的标签都比较简单 入门非常的迅速 但是CSS是一个需要我们深度挖掘的东西 里面的很多样式属性掌握几个常用的便可以实现很好看的效果 下面我便教大家如何用CSS做一个爱心 前期预备知识 明白正方形的画法 明白圆形的画法 明白什么是
  • Android下基于Http协议的网络摄像机开发

    这段时间在做Android平台下的网络摄像机的兼容 摄像机的通讯采用Http1 1协议 现将遇到的问题简单总结一下 1 Http协议中需要用到身份认证部分 不同厂家的摄像机所采取的方案可能有所不同 但是大体无外乎都是将摄像机的用户名和密码简
  • 江科大自化协STM32学习笔记(部分C语言知识、STM32简介和GPIO口的使用)

    本篇文章是根据B站UP主江科大自化协的教学视频STM32入门教程 2023持续更新中 在了解 学习与实操后整理的学习笔记 内容部分来自UP主的课程资料 并包含了一些个人的理解 如有谬误欢迎指正 详细知识点可以观看UP主的视频进行了解 希望大
  • php如何读取解析eml文件以及生成网页的示例分享

    这篇文章主要介绍了PHP读取 解析eml文件及生成网页的方法 结合实例形式分析了PHP操作eml文件的读取 解析 转换等相关实现技巧与注意事项 并附带demo源码供读者下载参考 需要的朋友可以参考下 本文实例讲述了PHP读取 解析eml文件
  • Linux及Windows下编译exosip和osip2源码

    eXosip库及编译流程简介 1 eXosip库的简介 1 1 osip简介 osip2是一个开放源代码的sip协议栈 是开源代码中不多使用C语言写的协议栈之一 它具有短小简洁的特点 专注于sip底层解析使得它的效率比较高 但缺点也很明显
  • 前端通过FormData上传文件到服务器端

    前端代码 html
  • Win 11 打开未知文件/打开方式 该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置”页面中创建关联。

    问题 鼠标右键选中文件 打开方式 或者选择其他应用 弹窗提示 该文件没有与之关联的应用来执行该操作 请安装应用 若已经安装应用 请在 默认应用设置 页面中创建关联 打开注册表 查看是否存在以下路径 HKEY CLASSES ROOT Unk
  • Visual Studio Code如何打开多个tab标签

    原创 Visual Studio Code如何打开多个tab标签 SweetTool的专栏 CSDN博客 在打开文件夹预览的模式下VS Code默认单击打开文件时仅保存一个tab 例如当前window打开一个tabA 然后点击另外一个文件B

随机推荐

  • 带你了解ES6 Module

    1 commonJS 在说 es6 模块以前 我们先来看一下后端普遍使用的打包方式 commonJS的一些特性 同步加载 也就是串行执行 后面的任务要等到前面任务执行完才能继续执行 语法 commonJS中使用 require 引入 mod
  • [Paper-CV] ECCV 2012 papers 1

    http applesun0757 blog 163 com blog static 18737419220126702145274 Paper CV ECCV 2012 papers 1 2012 07 07 00 21 45 分类 Pa
  • linux中替换命令详解,linux中tr命令详解 (替换,删除d,缩减s)

    tr用来从标准输入中通过替换或删除操作进行字符转换 tr主要用于删除文件中控制字符或进行字符转换 特别要注意一点 tr 只能进行字符的替换 缩减和删除 不能用来替换字符串 最常用选项的tr命令格式为 tr c d s string1 to
  • C# 网络编程之Tcp实现客户端和服务器聊天

    最近使用Socket网络套接字编程中 在同步与异步通讯中客户端与服务器总是无法响应 但在学习Tcp协议编程中完成了通讯聊天功能 下面简单讲讲我最近学到的及Tcp聊天的源代码及详细注释 Tcp协议是一个传输层的协议 在Tcp协议编程中它通常使
  • OpenGL超级宝典 纹理(一)

    文章目录 纹理 创建并且初始化纹理 更新纹理数据 从着色器中读取数据 采样器类型 控制纹理数据的读取方式 创建采样器对象和绑定到纹理单元 纹理过滤 设置过滤器 加载纹理 完整代码展示 shader vertex shader fragmen
  • mysql 授权管理和设置

    1 给指定数据库增加所有权限 所有库即 GRANT ALL PRIVILEGES ON TO 用户名 IDENTIFIED BY 密码 WITH GRANT OPTION 设置完之后更新权限表 FLUSH PRIVILEGES 2 给用户增
  • C++11模板元编程-std::enable_if示例详解

    文章目录 1 限制模板函数的参数类型 2 模板类型偏特化 传送门 gt gt AutoSAR实战系列300讲 糖果Autosar 总目录 C 11中引入了std enable if函数 函数原型如下 template lt bool B c
  • AI+数据安全,探索数据安全防护新手段

    随着 4G 正式商用 带宽将不再是数据传输的瓶颈 人类社会真正意义的进入了以手持终端 各类传感器为代表的移动互联网 万物互联 人工智能时代 我们将不再受限于地理位置 可尽情享受着手机购物 电子支付 媒体社交 个性化推送 VR等各种便捷和个性
  • 计算机图形学十五:基于物理的渲染(蒙特卡洛路径追踪)

    蒙特卡洛路径追踪 摘要 1 蒙特卡洛积分 Monte Carlo Integration 2 蒙特卡洛路径追踪 Monte Carlo Path Tracing Reference 本篇文章同步发表于知乎专栏 https zhuanlan
  • PHP与JSON的一些常用操作

    PHP把数据写入JSON文件 PHP读取JSON数据
  • C++ 抽象类

    抽象类 接口 接口描述了类的行为和功能 而无需完成类的特定实现 C 接口时通过抽象类实现的 设计抽象类的目的 是为了给其他类提供一个可以继承的适当的基类 抽象类本类不能被用于实例化对象 只能作为接口使用 注意 如果试图实例化一个抽象类的对象
  • 对象的初始化和清理

    对象的初始化和清理 构造函数和析构函数 对象的初始化和清理也是两个非常重要的安全问题 一个对象或者变量没有初始状态 对其使用后果是未知 同样的使用完一个对象或变量 没有及时清理 也会造成一定的安全问题 c 利用了构造函数和析构函数解决上述问
  • visual studio2019创建解决方案,并在一个解决方案中包含多个项目

    系列文章目录 文章目录 系列文章目录 前言 一 使用步骤 前言 之前一直使用visual studio2019一直都是一个解决方案 下面包含一个工程 这次写一个网络同步的模块 具体使用boost的asio模块 我们需要建立一个解决方案 一个
  • 使用slickedit调试开源代码

    slickedit linux下的神器啊 阅读代码堪比 source insight 调试代码堪比 visual studio nginx优秀的web服务器 因为其具有多进程 后台进程的特点 因此本文选择以此为例讲解slickedit如何对
  • Java中的排序算法

    冒泡排序 核心思想 冒泡排序 核心思想 冒泡排序 Bubble Sort 又被称为气泡排序或泡沫排序 它是一种较简单的排序算法 它会遍历若干次要排序的数列 每次遍历时 它都会从前往后依次的比较相邻两个数的大小 如果前者比后者大 则交换它们的
  • LeetCode题解——394. 字符串解码

    题目相关 题目链接 LeetCode中国 https leetcode cn com problems decode string 注意需要登录 题目描述 给定一个经过编码的字符串 返回它解码后的字符串 编码规则为 k encoded st
  • 昨晚做梦面试官问我三色标记算法

    本文已收录至GitHub 推荐阅读 Java随想录 微信公众号 Java随想录 原创不易 注重版权 转载请注明原作者和原文链接 文章目录 三色标记算法 增量更新 原始快照 某天 爪哇星球上 一个普通的房间 正在举行一场秘密的面试 面试官 我
  • Sql server 存储过程加密

    本方法可用于加密SQL存储过程 函数或者触发器 使用 WITH ENCRYPTION 选项 WITH ENCRYPTION 子句对用户隐藏存储过程的文本 例子 IF OBJECT ID N Pro Encrypt Test IS NOT N
  • PySide6-控件教程-005-QLabel标签控件-内边距、缩放、伙伴关系

    QLabel 标签控件 本文摘录自我的开源教程 PySide6 代码式教程 QLabel CSDN 平台仅做镜像 答疑 纠错请至 GitHub 提交 issue 内边距 QLabel还可以调整内边距 启用内容缩放 以更细致地调节显示效果 s
  • 与游戏世界交互作业

    一 编写一个简单的鼠标打飞碟 Hit UFO 游戏 游戏内容要求 游戏有 n 个 round 每个 round 都包括10 次 trial 每个 trial 的飞碟的色彩 大小 发射位置 速度 角度 同时出现的个数都可能不同 它们由该 ro