2022K班结对编程任务

由于unity项目过于复杂已发送给测评组
Bilibili视频地址:
原型
游戏

一、结对探索

1.1 队伍基本信息

结对编号:32

队伍名称:Book私议队;

学号姓名作业博客链接具体分工
032002521梁佳莺博客unity编程学习,组件运动 , AI设计
032002519赖怡澄博客原型设计,组件摆放

1.2 描述结对的过程

舍友关系好,肥水不流外人田。

1.3 非摆拍的两人在讨论设计或结对编程过程的照片

在这里插入图片描述

二、原型设计

2.1 原型工具的选择

  • 采用的原型开发工具:墨刀
  • 原因:因为墨刀简单易上手,功能也非常齐全

2.2 遇到的困难与解决办法

  • 困难描述:最难的是设计,要如何设计一个交互性较强的原型
  • 解决尝试:借鉴其他游戏的设计,扩宽自己的思路,设计完成后,让队友参与游玩,交流感受,不断改进
  • 是否解决:大体解决
  • 有何收获:但思路卡顿的时候,不妨停下来去看看别人怎么完成的,集思广益而不是单单埋头苦干。

2.3 原型作品链接

墨刀

2.4 原型界面图片展示

(1)主界面
  • 因为是unity开发的游戏,因此进入主界面前会有加载界面
  • 用户进入页面,有【开始游戏】与【游戏说明】两个选项
    • 【开始游戏】设置抖动动画,激发用户点击欲望
    • 选择【开始游戏】,将直接进入加载界面
    • 选择【游戏说明】,将进入游戏说明界面
  • 页面设置有背景音乐,当音乐文件加载完毕便开始循环播放音乐
    在这里插入图片描述
(2)游戏说明界面
  • 介绍游戏规则,最下方有【开始游戏】选项,点击后直接跳转到加载页面
  • 页面设置有背景音乐
    在这里插入图片描述
(3) 加载界面
  • 用户进入页面,等待游戏开始,大约2s后自动跳转到游戏界面
  • 页面设置有背景音乐
    在这里插入图片描述
(4)游戏界面
  • 用户进入页面,将出现两个对战方框和中间一个骰子,点击骰子可以转出随机点数。右上角设有【暂停键】
    • 点击【暂停键】,进入到暂停页面
  • 页面设置有背景音乐
    在这里插入图片描述
    在这里插入图片描述
(5)暂停界面
  • 用户进入页面后,将有三个选项【继续游戏】、【重新开始】、【返回主菜单】

    • 选择【继续游戏】,将返回原来的游戏界面
    • 选择【重新开始】,将返回新的游戏界面
    • 选择【返回主菜单】,将返回主菜单界面
  • 页面设置有背景音乐
    在这里插入图片描述

(6)游戏结束界面
  • 根据游戏结果对应有【游戏成功】与【游戏失败】两个结局

    • 【游戏成功】界面中设有【再来一次】选项,点击将进入新的游戏界面,如果不想参加,点击任意位置,将返回主界面
    • 【游戏失败】界面中设有【重新挑战】选项,点击将进入新的游戏界面,如果不想参加,点击任意位置,将返回主界面
    • 【重新挑战】与【再来一次】设有抖动动画,激发用户点击欲望
  • 页面设置有背景音乐
    在这里插入图片描述
    在这里插入图片描述

三、编程实现

3.1 网络接口的使用

unity游戏开发引擎封装接口

interface ISampleInterface
{
    void SampleMethod();
}

class ImplementationClass : ISampleInterface
{
    // 显式接口成员实现:
    void ISampleInterface.SampleMethod()
    {
        // 方法实现
    }

    static void Main()
    {
        // Declare an interface instance.
        ISampleInterface obj = new ImplementationClass();

        //给会员打电话
        obj.SampleMethod();
    }
}

3.2 代码组织与内部实现设计

使用Tools在VS里调试unity
请添加图片描述

请添加图片描述
请添加图片描述

请添加图片描述

3.3 说明算法的关键与关键实现部分流程图

在这里插入图片描述

算法实现思路:

使用贪心算法:对问题求解时,总是做出在当前看来是最好的选择,在每次尝试放入之后选择计算分数,如果数据比上次放入的大,就保存数据,继续测试,最后记录的就是当前得分最大的数据,该位置可以作为要摆放的位置。

由于unity语法复杂,对c#语言了解不够透彻,写上去一直报错,所以还未投入游戏开发中,用java先打出思路:

public Integer nextStep(List<Integer> ownBoard, List<Integer> otherBoard, Integer figure) {
        int index = -1;
        int maxNum = -999;

        for(int j = 0;j < 9; j ++){//9个格子轮流匹配
            if(ownBoard.get(j) != 0) continue;//如果我们有输入数据,那就不放数据
            Integer[] aMap = ownBoard.toArray(new Integer[0]);//将自己棋盘转化成数组
            Integer[] bMap = otherBoard.toArray(new Integer[0]);//将对手棋盘转化成数组

            aMap[j] = figure;//我们的数据
            for (int i = j / 3 * 3; i < j / 3 * 3 + 3; i++) {//判断在第几行,从头判断到尾
                if (bMap[i] == figure) {//对手与我相同,可把对手消除
                    bMap[i]=0;//消除
                }
            }
            //计算总分
            int a_score = 0;
            int b_score = 0;

            for (int i = 0; i < 3; i++) {
                int a = aMap[i * 3];
                int b = aMap[i * 3 + 1];
                int c = aMap[i * 3 + 2];

                if (a == b && b == c) a_score += a * 9;
                else if (a == b) a_score += a * 4 + c;
                else if (a == c) a_score += a * 4 + b;
                else if (b == c) a_score += b * 4 + a;
                else a_score += (a + b + c);
            }
            for (int i = 0; i < 3; i++) {
                int a = bMap[i * 3];
                int b = bMap[i * 3 +1];
                int c = bMap[i * 3 + 2];

                if (a == b && b == c) b_score += a * 9;
                else if (a == b) b_score += a * 4 + c;
                else if (a == c) b_score += a * 4 + b;
                else if (b == c) b_score += b * 4 + a;
                else b_score += (a + b + c);
            }
            int temp = 0;
            temp = a_score - b_score;//计算分差
            if (temp > maxNum){
                index = j;
                maxNum = temp;//分差越大,效果越好
            }
        }

        return index;
    }

3.4 贴出重要的/有价值的代码片段并解释

实现思路:

随机次数,就算先看骰子随机了多少次就转多少次,然后每转一次就随机打开一个面,最后一次打开的面会被记录,是按for的i顺序放的,格子里面要有一个随机转动的骰子,随机转动的骰子每次转动又会是一个随机的面,里面的这个函数是关闭所有的面,因为前面打开的面没有到随机的最后一次的面时,都要关闭为下一个打开的面让出来位置,直到最后一个面打开,会被收录与记录,这样就能在unity里面实现骰子的转动。

public IEnumerator Spin()
    {
        playerCut = true;
        int randomNumber_s = Random.Range(5, 15);//循环5-15次
        for (int i = 0; i < randomNumber_s; i++)//循环5-15次
        {
            if (i < randomNumber_s)
            {
                Close_s();
            }

            randomNumber = Random.Range(0, diceImage.Length);//1-6随机一个面
            diceImage[randomNumber].gameObject.SetActive(true);//打开循环到的面
            
            dicesprite = diceImage[randomNumber].sprite;//将面赋值给dicesprite
            yield return new WaitForSeconds(0.1f);
        }

        for (int i = 0; i < playerGridBool.Length; i++)
        {
            if (playerGridBool[i] == false)
            {
               playerindex = i;
                break;
            }
        }

        for (int i = 0; i < playerindex+1; i++)
        {
            if (playerindex < 3)
            {


                if (playerGridBool[i] == false)
                {
                    playerGrid[i].sprite = dicesprite;//将 playerGrid[i].sprite替换为dicesprite
                    for (int ii = 0; ii < playerGrid.Length; ii++)
                    {
                        if (playernub[i] == 0)
                        {
                            playernub[i] = randomNumber + 1;//循环到的最后一个面的变量+1,因为从0开始,就是这个格子的数
                        }
                        for (int iii = 0; iii < enemyGrid.Length; iii++)
                        {
                            if (playernub[i] == enemynub[ii])
                            {
                                enemynub[iii] = 0;
                                enemyGridBool[iii] = false;
                                enemyGrid[iii].sprite = null;
                            }
                        }
                    }
                    playerGridBool[i] = true;
                    break;


                }
            }
            if (playerindex < 6)
            {

                print(i);
                if (playerGridBool[i] == false)
                {
                    
                    playerGrid2[x].sprite = dicesprite;//将 playerGrid[i].sprite替换为dicesprite
                    for (int ii = 0; ii < playerGrid.Length; ii++)
                    {
                        if (playernub[i] == 0)
                        {
                            playernub[i] = randomNumber + 1;//循环到的最后一个面的变量+1,因为从0开始,就是这个格子的数
                        }
                        for (int iii = 0; iii < enemyGrid.Length; iii++)
                        {
                            if (playernub[i] == enemynub[ii])
                            {
                                enemynub[iii] = 0;
                                enemyGridBool[iii] = false;
                                enemyGrid2[iii].sprite = null;
                            }
                        }
                    }
                    playerGridBool[i] = true;
                    x++;
                    break;


                }
            }
            if (playerindex < 9)
            {


                if (playerGridBool[i] == false)
                {
                    playerGrid3[x2].sprite = dicesprite;//将 playerGrid[i].sprite替换为dicesprite
                    for (int ii = 0; ii < playerGrid.Length; ii++)
                    {
                        if (playernub[i] == 0)
                        {
                            playernub[i] = randomNumber + 1;//循环到的最后一个面的变量+1,因为从0开始,就是这个格子的数
                        }
                        for (int iii = 0; iii < enemyGrid.Length; iii++)
                        {
                            if (playernub[i] == enemynub[ii])
                            {
                                enemynub[iii] = 0;
                                enemyGridBool[iii] = false;
                                enemyGrid3[iii].sprite = null;
                            }
                        }
                    }
                    playerGridBool[i] = true;
                    x2++;
                    break;


                }
            }
        }
        playerzf = 0;
        for (int i = 0; i < playernub.Length; i++)
        {


                playerzf  += playernub[i];
                playerzftext.text = playerzf +"";
        
        }
        enemybool = true;

    }

3.5 性能分析与改进

改进思路:能继续优化算法,用多个判断语句来增加特殊情况的判断,能够在一定程度上继续优化

消耗最大的函数:public IEnumerator Spin()(3.4有)用于投掷骰子并利用AI函数判断放置的位置

请添加图片描述
请添加图片描述

3.6 单元测试

测试思路:
由于UnityTest在Play Mode中的行为类似于协同程序,因此在编辑模式下可使用
与Assert类测试条件,使用yield跳过一帧,从而测试整个游戏

测试代码:

using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

public class MyTestScript
{
    // A Test behaves as an ordinary method
    [Test]
    public void MyTestScriptSimplePasses()
    {
        // Use the Assert class to test conditions
        Debug.Log(nameof(MyTestScriptSimplePasses));
    }

    // A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
    // `yield return null;` to skip a frame.
    [UnityTest]
    public IEnumerator MyTestScriptWithEnumeratorPasses()
    {
        // Use the Assert class to test conditions.
        // Use yield to skip a frame.
        yield return null;
        Debug.Log(nameof(MyTestScriptWithEnumeratorPasses));
    }
}

测试结果:请添加图片描述

3.7 贴出GitHub的代码签入记录,合理记录commit信息

由于在unity上开发,不是很会迁入github,中间并没有很多迁入的记录

四、总结反思

4.1 本次任务的PSP表格

PSP2.1Personal Software Process Stages预估耗时 (分钟)实际耗时 (分钟)
Planning计划4040
· Estimate· 估计这个任务需要多少时 间4040
Development开发2周2周半
· Analysis· 需求分析 (包括学习新技 术)4天1周
· Design Spec· 生成设计文档8080
· Design Review· 设计复审4040
· Coding Standard· 代码规范 (为目前的开发 制定合适的规范)2020
· Design· 具体设计120160
· Coding· 具体编码3天5天
· Code Review· 代码复审120120
· Test· 测试(自我测试,修改代 码,提交修改)7070
Reporting报告2天2天
· Test Report报告240240
· Size Measurement· 计算工作量3020
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改 进计划100100
· 合计3周3周

4.2 学习进度条(每周追加)

梁佳莺:

第N 周新增代码 (行)累计代码 (行)本周学习耗 时(小时)累计学习耗时 (小时)重要成长
1001515熟悉逍遥骰规则,研读题目与具体要求,确定开发环境
260602020学习unity游戏开发引擎的c#语言并编写部分代码
3643602040两个人共同实现原型设计,代码优化,写AI接口

赖怡澄:

第N 周新增代码 (行)累计代码 (行)本周学习耗 时(小时)累计学习耗时 (小时)重要成长
10099原型设计主题确定,收集素材
260601625设计原型,学习unity原型操作
36437032045两个人共同实现原型设计,代码优化

注:主要的模型设计都在unity里面生成,所有的东西都放在unity游戏开发引擎里,VS里的c#语言只是让他实现一些操作,达到一些操作的目的,比如骰子还有图形界面都是在unity里.总之就是相当于unity是一个方块,c#让方块运动,因此没有那么多代码, c#语言的每行代码都具有非常强大的功能。

4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?

  • 产品形态:一款能供大家娱乐的,具有人人模式与人机模式的投骰子小游戏
  • 原型设计:一款交互性强,体验感好的原型设计
  • 软件开发成果:一款具有投骰子,放置,对战等基本功能的半成品游戏APP

4.4 评价你的队友

梁佳莺:

  • 值得学习的地方:我的队友非常有想法和灵感,她的审美也很好,找图片还有背景音乐以及两个人一起研究unity的过程都很认真专注,做的原型设计也非常好看,效率很高
  • 需要改进的地方:我们都需要有耐心,不能生气不能生气

赖怡澄:

  • 值得学习的地方:我认为我的队友学习能力很强,这次游戏开发主要是使用unity,因为是从0开始,我上手的非常慢,而我的队友擅长学习钻研,因此我遇到的问题经常咨询她,并和她一起讨论,真的非常感谢♪(・ω・)ノ!
  • 需要改进的地方:越在难以攻克的问题上,越需要心平气和

4.5 结对编程作业心得体会

梁佳莺:

  • 这次作业难度较大,在课程紧密的同时需要拼凑出时间来完成这个小游戏任务,完成后也是成就感拉满,但其中也出现了很多问题,因此深有体会
    • 一个脚本实现一个功能,如果投入太多功能会错乱
    • 在VS上debugUnity时,代码编写不当,出现死循环,会死机,unity不动,选择修改后重启继续De
    • 安装环境,tools等过程需要耐心
    • 导出游戏时选择空的文件夹才能正确导出
  • 零基础学习 unity c#,太难了,真的看了非常多资料,终于学习了unity模型最最最最最基本的操作,unity自带很多功能强大的函数,并且能赋予每个组件脚本使其运作,感觉知识量大大超标,所以就取几个自己需要学习的部分去研究。感觉c#语言很多定义方式像c又像java,unity给它赋予了不同的定义,StartCoroutine( )协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的,是一个分部执行,遇到条件(yield)会挂起,直到条件满足才会被唤醒继续执行后面的代码,这也是实现这个游戏的重要一环,来完成人机交互过程中每一步的操作。
  • 第三周时的时候发现大家都是用网页什么的,做的非常漂亮又有很多功能,可以实现人人模式与人机模式,觉得大家都很厉害,而我们这次设计并没有很完善,unity中的服务器端没有写好,一直报错,因此人和机器下的很困难,在自己会的语法基础上完善了游戏,部分功能并没有实现,很废,其实是个半成品。但是看到made in unity游戏界面出来的时候还是很感动的,有种大型游戏的感觉。

赖怡澄:

  • 第一次开发游戏,难度真的好大o(╥﹏╥)o,又是从零开始的一次学习,当然完成后真的非常有成就感。但在完成中遇到了不少问题:

    • unity素材商店加载不出来,可以自己创文件夹上传素材,上传背景图时,可以通过Pixels Per Unit设置像素,调整大小

    • 快捷键可以非常大地提高效率,因此使用unity时可以提前了解它的快捷键

    • 背景音乐添加后,要选择apply all以及loop

  • 这次游戏设计真的非常困难,第一项用墨刀设计原型就把我难住了,不仅是从来没有使用过墨刀,而且对页游接触较少,不太明白该怎么设计交互性较强的原型,我的解决方法是去4399多玩几个游戏(绝对不是在摸鱼),玩着玩着就慢慢有思路。而墨刀其实算是比较好上手的工具,真正难点在于unity,虽然unity的功能非常强大,但它对于初学者来说其实是不太友好的,但是好在B站中有较多的unity游戏教程,因此跟着教程学可以少走一些弯路。这次游戏我们设计的较简单,因为自己的思路比较局限,不知道怎么把unity的功能和游戏结合,我曾将想给游戏添加键盘的功能,但是这个功能的添加不仅使游戏规则变得更加混乱,而且可玩性也降低了很多,因此就pass了这个想法。我认为这次游戏制作给我带来了全新的学习体验,它不仅提升了工具学习的能力,而且也开阔了我对思维,因为设计,你需要考虑的因素就很多,不单单是代码实现,还需要考虑它的观赏性与可玩性等等,我觉得受益匪浅。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值