• 结对编程作业


    Github项目
    李晓芳博客
    江舒颖博客

    姓名 具体分工
    李晓芳 原型设计,游戏逻辑,AI算法
    江舒颖 原型设计,前端界面,接口连接

    一、原型设计

    原型作品

    设计说明

    • 设计逻辑:
      设计逻辑

    • 开发工具: 墨刀(MockingBot)

    • 原型介绍:

    进入小程序将会进入启动页,点击进入游戏会显示选择游戏模式的界面

    选择人机模式或者人人模式将不需要登陆,可直接进行游戏(以下以此为人人对战页面、人人对战带有托管页面、人机对战页面)

    选择在线模式时,如果未登录,将会需要登录,登录后可自己创建房间或者加入其他人已经组好的对局,如果已经登陆则可以直接查询对局

    游戏结束时将会有一个结束页面显示输赢情况,之后会有弹窗让你选择继续下一局返回主页面或者退出(以下依次为结束页面、退出登录页面)

    遇到的困难及解决方法

    • 困难描述: 原型的保真度选择问题。低保真原型设计耗时短,易于理解,便于修改。高保真原型交互性强,有利于后续原型的实现,对团队协作更为友好,但设计耗时长,修改成本高。
    • 解决过程: 综合考虑高保真度和低保真度的优缺点,最理想的解决方案是:前期采用低保真原型,基本功能实现的中期采用高保真原型。再考虑到了时间的因素,我们制定了最后的计划:在结对编程的第一周时间内,尽可能地完善原型设计,核心关键部分必须制作高保真的原型,其他部分至少做出低保真原型。
    • 收获: 通过所见即所得的制作原型的形式,我们对页面跳转的交互设计方法有了更为深刻的理解。同时,在实际选择原型保真度的过程中,我们两人进行得到了初步的磨合,这为后期的原型设计实现带来方便。

    二、原型设计实现

    代码实现思路

    • 网络接口的使用
    1. 登录接口一开始使用 'content-type':'application/json'一直显示参数错误,后来查了一些资料把它改成了'content-type': 'application/x-www-form-urlencoded'就可以成功接入了。
    2. 除了登录接口,所以接口都需要带'Authorization':token
    3. 一些接口存在频繁使用,对于这个问题,我将其封装为函数,减少代码的冗余度
    /*获取上步操作*/
    getLast : function(e){
      const token=wx.getStorageSync("token");
      wx.request({
        url: 'http://172.17.173.97:9000/api/game/'+e+'/last',
        data: {},
        header: {'content-type':'application/json',
        'Authorization':token},
        method: 'GET',
        success: (res) => {
         console.log(res.data)
         if(res.data.code==200){
           wx.redirectTo({
             url: '/pages/RandRgames/RandRgames'
           });    
         }
        },
        fail: () => {},
        complete: () => {}
      });
     }
    
    • 代码组织与内部实现设计

      • 生命周期函数
        onLoad 可获取来自其它页面的数据
        onShow 实现一进入页面就要开始的操作,例如页面初始化
      • 页面渲染函数
        imageUrl 实现不同牌的图片转换
        changeview 实现托管页面的改变
        iflook 实现卡组(即记牌功能)的显示与隐藏
      • 对局函数
        clickscard、clickhcard、clickccard、clickdcard 分别对应Player1对于四种花色牌的响应
        clickscard1、clickhcard1、clickccard1、clickdcard1 分别对应Player2对于四种花色牌的响应
        Shuffle 洗牌操作
        SetCard 设置卡组初值
        SelectGroup 卡组抽牌
        OpHand 处理手牌
        SelectHand 手牌出牌
        IsEat 吃牌判断
        EatHand 手牌吃牌
        IsWinner 胜利者判断
        Update 更新页面数据
    • 说明算法的关键与关键实现部分流程图
      人人对战、人机对战、在线对战都以人人对战的游戏逻辑为基础展开。而游戏逻辑算法的关键在于手牌出牌、卡组抽牌这两个地方,在玩家完成操作后,更新手牌、卡组和放置区。
      关键实现部分流程图

    • 贴出重要的/有价值的代码片段,并解释
      是对人人对战游戏逻辑的基本实现,首先是洗牌,之后等待点击事件的发生,根据玩家选择,进行卡组抽牌、手牌出牌不同的操作。值得注意的是,卡组抽牌、手牌出牌需要更新手牌、卡组和放置区的卡牌,并对不同花色进行归类。

    	Shuffle: function(){                     //  洗牌
        var that = this;
        var times = 5;                         //  默认洗牌5次
        for(var i = 0; i < times; i++){
          for(var j = 0; j < 52; j++) {
            var rvalue = Math.round(Math.random()*50+1);  //  随机生成1~51的一个整数
            var tempcard = that.data.onecard[j];
            that.data.onecard[j] = that.data.onecard[rvalue];
            that.data.onecard[rvalue] = tempcard;
          }
        }
      },
      SelectGroup: function(e){                //  卡组抽牌
        var that = this;
        that.data.placement_card[that.data.placement_num++] = that.data.group_card[that.data.group_num--];
        if(this.IsEat() == true){
          this.EatHand();
        }
        this.Update();
        if(that.data.group_num == -1){         //  卡组为空,结束对局
          that.data.finished = true;
          this.IsWinner();
        }
      },
      OpHand: function(e){                     //  处理手牌
        var that = this;
        var suit = that.data.card.substring(0, 1);
        var i = parseInt(that.data.turn);
        that.data.player[i].total++;
        switch (suit) {                         //  对应花色
          case "S":                             //  黑桃S
            that.data.player[i].hand[0][that.data.player[i].num[0]] =  that.data.card;
            that.data.player[i].num[0]++;
            break;
          case "H":                             //  红桃H 
            that.data.player[i].hand[1][that.data.player[i].num[1]] =  that.data.card;
            that.data.player[i].num[1]++;
            break;
          case "C":                             //  梅花C
            that.data.player[i].hand[2][that.data.player[i].num[2]] =  that.data.card;
            that.data.player[i].num[2]++;
            break;
          case "D":                             //  方块D
            that.data.player[i].hand[3][that.data.player[i].num[3]] =  that.data.card;
            that.data.player[i].num[3]++;
            break;
        }
      },  
      SelectHand: function(e){                  //  手牌出牌
        var that = this;
        var suit = that.data.card.substring(0, 1);
        var i = parseInt(that.data.turn);
        if(that.data.player[i].total != 0){
          switch(suit) {                      //  对应花色
            case "S": //黑桃S
              if(that.data.player[i].num[0] == 0){
                break;
              }
              that.data.player[i].total--;
              that.data.placement_card[that.data.placement_num++] = that.data.player[i].hand[0][--that.data.player[i].num[0]];
              break;
            case "H": //红桃H
              if(that.data.player[i].num[1] == 0){
                break;
              }
              that.data.player[i].total--;
              that.data.placement_card[that.data.placement_num++] = that.data.player[i].hand[1][--that.data.player[i].num[1]];
              break;
            case "C": //梅花C
              if(that.data.player[i].num[2] == 0){
                break;
              }
              that.data.player[i].total--;
              that.data.placement_card[that.data.placement_num++] = that.data.player[i].hand[2][--that.data.player[i].num[2]];
              break;
            case "D": //方块D
              if(that.data.player[i].num[3] == 0){
                break;
              }
              that.data.player[i].total--;
              that.data.placement_card[that.data.placement_num++] = that.data.player[i].hand[3][--that.data.player[i].num[3]];
              break;
          }
          if(this.IsEat() == true){
            this.EatHand();
          }
        }
        this.Update();
      },
      IsEat: function(e){//  吃牌判断
        var that = this;
        if(that.data.placement_num <= 1){
          return false;
        }
        var str1 = that.data.placement_card[that.data.placement_num-1].substring(0, 1);
        var str2 = that.data.placement_card[that.data.placement_num-2].substring(0, 1);
        if(str1 == str2){  //  花色相同
          return true;
        }
        return false;
      },
      EatHand: function(e){//  手牌吃牌
        var that = this;
        for(var i = 0; i < that.data.placement_num; i++){
          that.data.card = that.data.placement_card[i];
          this.OpHand();
        }
        that.data.placement_num = 0;
      },
      IsWinner: function(e){//  胜利者判断
        var that = this;
        if(that.data.player[0].total < that.data.player[1].total){
          that.data.winner = "0";
        }else{
          that.data.winner = "1";
        }
        this.setData({
          image_url0:'https://i.loli.net/2021/10/24/hFNM9dbDB1wCnak.png'
        })
         wx.navigateTo({
                url: '/pages/over/over?id=that.data.winner'
              });
      },
    
    
    • 性能分析与改进
      性能上,小程序的图片加载较慢,影响了对游戏玩法的体验感。通过去除冗余的界面,完善了界面逻辑,在这方面有了一定的改进。
      游戏逻辑方面,各个类的封装性做得不是很好,出现过相互扰乱的问题。游戏的反应不是很灵敏。

    • 描述改进的思路
      兼并人人对战、人机对战、在线对战公用的函数;不需要页面响应的数据不放在data中初始化;避免频繁触发的事件重度内存操作。尽可能减少小程序对运行内存的消耗。
      去除不必要的冗余界面,完善界面交互的逻辑。

    • 展示性能分析图和程序中消耗最大的函数
      性能分析图
      消耗最大的函数应该是启动函数。

    • 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
      测试的是生命周期函数--监听页面加载onLoad()。
      比对游戏参数在接口的返回和本地的记录是否一致,不一致的话查看出错的地方。信息通过 console.log()输出。

        wx.request({
          url: 'http://172.17.173.97:9000/api/game/?uuid',
          header: {'content-type':'application/json',
          'Authorization':token},
          method: 'GET',
          success: (res) => {
            //  核验信息是否一致
            console.log(res.data);
            console.log(that.data.player[0].hand);
            console.log(that.data.player[1].hand);
            if(res.data.winner!=that.data.winner){
              console.log("error");
            }
          },
          fail: () => {},
          complete: () => {}
        });
    

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

    Github代码签入记录

    遇到的代码模块异常或结对困难及解决方法

    • 困难描述: 小程序开发过程中,出现白屏问题。可能是运行内存过大、接口API超时。
    • 解决过程: 首先,减少小程序对运行内存的消耗。一方面,不需要页面响应的数据不放在data中初始化。另一方面,避免频繁触发的事件重度内存操作,比如上拉加载,下拉刷新时间增加节流。其次是,监督接口API是否超时。
    • 收获: 最大的收获是一个bug满满的小程序。但其实在制作小程序的过程中,我们已经解决了诸如小程序出现白屏等很多问题,这是一个成长的过程。

    评价你的队友

    李晓芳:

    • 队友值得学习的地方:
    1. 会主动督促项目进行,常常是push我学习和打代码。
    2. 有不错的前端编程基础,协作起来轻松愉快。
    3. 编程有一些需要交接的地方,例如参数传递、文件做出了改动,总是说得很清楚。
    • 队友需要改进的地方:
    1. 应该也是ddl驱动型队友,虽然我们一起扛ddl,但是交作业的时候太焦头烂额了。
    2. 注释跟空格强迫症看了有一点难受,希望能做改进。

    江舒颖:

    • 队友值得学习的地方:
    1. 在做事情前会先把思路理清,便于更好地展开工作。
    2. 善于把握大方向,会以整体为重,不会因小失大。
    3. 当不能把每个地方都做得很好时,能够比较准确地抓住比较重要的地方先做。
    4. 代码规范性很好,习惯于做注释,可读性强
    • 队友需要改进的地方:
    1. ddl驱动型队友,觉得可以更早一点开始完成作业,可以完成得更好。
    2. 沟通仍需加强,关于一些功能、接口参数在我们俩之间存在理解偏差,导致在编写代码过程中有些工作会突然没法进行。

    PSP和学习进度条

    PSP

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 30 50
    · Estimate · 估计这个任务需要多少时间 30 50
    Development 开发 930 1310
    · Analysis · 需求分析 (包括学习新技术) 100 180
    · Design Spec · 生成设计文档 20 30
    · Design Review · 设计复审 90 100
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
    · Design · 具体设计 120 150
    · Coding · 具体编码 360 600
    · Code Review · 代码复审 120 100
    · Test · 测试(自我测试,修改代码,提交修改) 90 120
    Reporting 报告 150 110
    · Test Repor · 测试报告 90 60
    · Size Measurement · 计算工作量 20 20
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 40 30
    · 合计 1110 1470

    学习进度条

    第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
    1 0 0 5 5 讨论作业要求,并确定要以哪种形式开发以及分工,查找了关于游戏开发的相关资料
    2 300 300 9 14 进行了游戏功能的讨论,确定页面跳转的逻辑,共同进行原型设计,学习了微信小程序的基本语法
    3 1100 1400 18 32 实现了部分页面的构建;人人对战界面的基本实现
    4 800 4200 18 50 实现了页面的动态渲染以及接口的连接,将页面之间的转换按逻辑连接; 完成其他对战模式,全部的游戏逻辑

    三、心得

    • 李晓芳:
    1. 原型制作是一个比较好玩的过程。一方面,所见即所得的制作方式比较轻松,另一方面,在讨论原型的过程中,我和队友进行了头脑风暴,达成一致的想法都是我们认为好看、有趣、好玩的。
    2. 每次软工作业都能学到新的东西,虽然学习的质量跟深度有待商榷,但确确实实为我推开很多扇大门。
    3. 团队之间的沟通十分重要。虽然我和队友经常展开讨论,并且当得出一致结论,但在讨论之后,仍然会对先前讨论的问题有疑惑,这是由于沟通并不十分有效。我们需要探寻更有效的沟通方式。
    • 江舒颖:
    1. 前期虽然查资料会花费很大一部分时间,因此没有编写代码,看起来像是工作毫无进展。但是这个时间一定要花,在完成一个自己没接触过的东西之前,需要去了解它,想清楚到底需要学什么东西,做好准备工作才能更有利于后期的进行。
    2. 和队友的沟通很重要,关于新事物的理解每个人都不同,所以需要和队友讨论确定一个正确的方向。
    3. 分工明确很重要,明确的分工才能明确地知道自己要做什么,各司其职,就可以达到1+1>2的效果。
  • 相关阅读:
    3个百度网盘下载实用小技巧
    decodeURIComponent 解码函数
    background属性怎么添加2个或多个背景图
    本地运行vue项目webpack提示 Compiled successfully
    var和let区别简述
    picture元素的使用
    博客园自定义鼠标icon
    background-size:100% 100% 和 background-size:cover的区别简述
    ScreenToGif——gif动图工具使用说明
    离职个人社保补缴——程序员也应该知道的社保基础知识科普
  • 原文地址:https://www.cnblogs.com/ponynice/p/15451783.html
Copyright © 2020-2023  润新知