结对编程作业

https://github.com/1355158565/pigtail

姓名 具体分工 博客链接
林雨欣 原型设计、AI构想、博客撰写 https://www.cnblogs.com/Sweetxinxin/
林志锋 前端、AI实现、博客补充 https://www.cnblogs.com/linzhifengshinidie/

 

一、原型设计

 

1.结对作业的原型设计说明:

  • 原型作品链接https://orgnext.modao.cc/app/e5fd5a31ba60fb9eaf70b64fba10596ed03d02ae 《Pigtail》

  • 原型开发工具:墨刀(网页版)

  • 关于小程序。绘制该页面,可从此处进入小程序。

  • 开始页面。使用原型设计工具设计猪尾巴游戏的小程序,首先要关注到的点是整个游戏的整体需要包括哪些页面。由于微信小程序需要依赖微信,根据小程序的共性可以最先判断出需要一个进入小程序后的开始页面。页面设置有“点击进入游戏”按钮,当用户第一次使用该小程序时点击这个按钮则会请求获取用户信息(即跳转到用户信息获取页面)。

  • 用户信息获取页面。在以上基础上就需要绘制一个用户信息获取页面,并提供“取消”和“允许”两个按钮。点击“允许”即可完成用户信息的授权(添加允许按钮跳转到主页的事件),点击“取消”则相反。

  • 主页。对需求进行分析,该游戏可以选择游戏模式,据此绘制主页。主页需要包含“双人对战”、“人机对战”、“在线对战”三个按钮,由于已绘制获取用户信息的页面,该主页应显示有微信头像、昵称信息。一般游戏主页应配有设置功能,因此主页也包含“设置”按钮。考虑到横屏的排布会更适合牌类游戏,因而页面采用横屏绘制。
主页
  • 设置页面。上图中设置的功能为:1.是否开启游戏音乐(设置旁边的播放器用于播放游戏音乐);2.查看游戏规则。两种功能可通过设置界面的“音效设置”和“游戏设置”两个按钮(添加点击按钮后两个状态的相互跳转事件)来进行设置。将设置页面分为以下四个状态:

  • (1)设置音乐(开)状态。

主页
  • (2) 设置音乐(关)状态。
主页
  • (3) 游戏规则设置状态。
主页
  • (4) 游戏规则状态。
主页
  • 登录页面。由于在线对战需要使用vpn且需要用学号密码登录,需要绘制一个登录界面。

  • 创房、加入页面。在线对战前有两种选择,一是加入一个房间(输入uuid)进行对战,二是创建一个房间。

  • 匹配页面。若创建房间则显示等待对手加入,当匹配成功后显示学号和头像。

  • 加载页面。考虑到进入某种模式后通常需要加载进入游戏,绘制加载页面。页面绘制一个进度条来反映进入游戏的加载进度。
主页
  • 游戏页面。进入游戏后,呈现的是游戏页面。思路大致是屏幕在中间绘制放置区和卡组(显示牌的数量),卡组的牌背面朝上而放置区的牌正面朝上,两边分别为双方的手牌情况(为出牌方便将牌根据花色划分并显示每种花色的手牌数量)。页面需要呈现双方的头像、昵称信息。由于需求要求有托管功能,在游戏界面右下方设置“托管”按钮,同样有“设置”和“退出”按钮。
主页
  • 托管状态。处于游戏页面时,开启托管功能后出现“取消托管”按钮和托管中的动画。

  • 退出弹窗。在游戏页面中,点击右下方的退出按钮,出现以下弹窗。

主页
  • 结算界面。游戏结束后进入结算页面,游戏有输赢两种结果。绘制两种游戏的结算界面。结算页面显示双方剩余的手牌数量,窗口设有“再来一局”按钮(可再次跳转至加载页面,进入新游戏)和“回到主页”按钮(跳转回主页,可重新选择模式)

  • (1)胜利结算界面。

主页
  • (2)失败结算界面。
主页
  • (3)平局结算页面。
主页

2.遇到的困难及解决方法:

  • 困难描述:关于原型设计我认为困难就是在于之前从未接触过原型设计这个概念,不了解、未学习过就是原型设计最大的困难,从未听说可以先画一个能够简单交互的原型图,再进行进一步的前端实现,甚至不知道原型设计有哪些工具,什么工具比较适合初学者使用,这些都是接触原型设计时遇到的困难。

  • 解决过程:不了解的事物当然是利用好浏览器,搜索关于原型设计的内容。首先大致了解一下原型设计是做什么、需要什么,再了解原型设计有什么工具。这里是在网上看到一篇关于原型设计工具对比的文章,文章总结了各种原型设计工具的特点,在文章里了解到原型设计工具墨刀简洁、可云端保存、性价比高,于是选择用这个工具。在b站上学习墨刀原型设计的速成课后直接上手开始原型设计,在设计的过程中一点一点熟悉起来这个工具,学的不够熟练的地方再次看视频复习一下学着操作,最后还是比较顺利地解决了困难完成了原型设计。

  • 有何收获:最直接的收获大概就是从一点都不了解原型设计到了现在会一点基本的原型设计吧,学习了原型设计工具墨刀的使用方法完成了猪尾巴小程序的原型设计。学习过程最直观的感受是原型设计和Photoshop的图像处理有些相似,快捷键很多都是相通的,只是在有图层的基础上也拥有各种组件以及能够添加事件进行原型的交互预览,可以说有学习到了新的东西,收获还是蛮多的。

 

二、原型设计实现

1.代码实现思路:

(1)网络接口的使用

  • 微信小程序自带的wx.request可以很好的调用接口,请求的url格式为基础域名+后缀,注意要使用正确的请求方式,在刚开始做小程序的时候,发现就登录接口的时候成功了,调用其他的接口发现返回“鉴权失败”,后来发现是header头中添加token的时候格式不对,token因为经常需要调用,因此将其用wx.setStorage存入本地缓存中。调用不同的接口,有的时候需要提交不同的请求参数,这个需要注意。比如登录接口时需要以下参数:
  • 接口调用成功之后,基本返回的code码都为“200”,因此我们可以根据不同接口返回的code值与其本该返回的code值作比较来判断接口是否调用成功。

  • 在本次小程序中使用最多次的应该是获取上步操作的接口,并且我们需要用到其返回的各种参数来完成在线对战的操作,所以对于它们的类型与说明都需要认真看看。

  • 以下展示获取上步操作接口以及获取对局信息的接口的使用示例(获取对局信息接口用于判断对局是否结束):

 let url ='http://172.17.173.97:9000/api/game/' + this.data.uuid + '/last'
    let url1 = 'http://172.17.173.97:9000/api/game/' + this.data.uuid
    let that = this
    wx.request({
      url: url,
      method:'GET',
      header:{
        "Content-Type": 'application/json',
        'Authorization':wx.getStorageSync('token')
      },
      success(res){
        if(res.data.code == 200){
          if(res.data.data.your_turn != that.data.turn){
            console.log(res.data);
            that.data.value = res.data
            that.data.turn = res.data.data.your_turn
            that.Move_card()
          }
        }
        else{
          wx.request({
            url: url1,
            data: {
            },
            header: { 
              "Content-Type": 'application/json',
              "Authorization": wx.getStorageSync('token'),
            },
            method: 'GET',
            success(res) {
              if(res.data.code == "200"){
                that.Overgame()
              }
            },
          })
        }
      },
      fail(res){
        wx.showToast({
          title: '网络错误',
          icon:'error',
          duration:1500
        })
      }
    })

(2)代码组织与内部实现设计(类图)

  • 本小程序函数如下:

a.微信小程序自带的页面处理函数(生命周期函数)
onLoad函数在进入某页面时只调用一次,主要是对页面状态数据的初始化,是生命周期回调—监听页面加载。

onload(){
    this.setData({
      uuid:wx.getStorageSync('uuid'),
      id:wx.getStorageSync('id'),
      userInfo:wx.getStorageSync('userInfo')
    })
  },

b.Unload函数
当使用重定向方法wx.redirectTo(OBJECT)或关闭当前页返回上一页wx.navigateBack()调用,用于卸载监听。

 onUnload(){
    clearInterval(this.data.listen)
    this.setData({
      listen:null
    })
  },

c.自定义函数

  • 小程序登录界面
getUserProfile:进行用户信息授权
  • 客户端登录界面
Login:进行登录操作
  • 客户端大厅
Creategame:创建房间
Joingame:加入房间
  • 对局界面
Address_pile:由于微信小程序不能使用本地图片,需要使用网络图片,因此使用此函数提前赋值各图片的网络地址
Init_pile_stacks:初始化牌堆
Turnon_card:进行翻牌操作                                                                    Play_card:打出手牌
Move_card:将摸出或打出的牌放进判定区进行判定
Robot:进行AI托管操作
Wait:进行上步操作接口的监听
  • 函数图

(3)说明算法的关键与关键实现部分流程图

  • 对于人与人之间的操作来说,没有什么算法吧,就监听上步操作之后,进行相应的页面反馈,然后进行自己的操作,轮流交替,直到监听到对局结束。
  • 对于AI算法来说,由于个人水平有限,以及前期的怠惰,想了一个非常普通的策略:没手牌就摸牌,有手牌就将自己手牌中花色最多且不与判定区堆顶的牌色相同的牌打出。(其实这游戏也蛮看运气的,当然高质量的算法就能得到高胜率的回报!希望以后可以将AI算法完善,看到有些同学还用到了卷积神经网络,真是让人拜服!)
  • 算法:设置wait()定时器监听操作 -> 获取后端返回的摸牌结果或操作结果进行页面反馈 -> 如果此时为己方操作,则可以选择打开托管 ||打开:AI进行智能的摸牌以及出牌操作 不打开:就进行屏幕点击操作进行出牌或者摸牌.监听到对局结束信息后结束对局,计算结果。
  • 在线对战中算法关键的流程图:

(4)贴出你认为重要的/有价值的代码片段,并解释

  • 进行在线对战时,己方进行屏幕触碰操作时调用的函数,可以分辨出是打出自己的手牌还是摸牌,以此来自动以不同的参数来调用执行玩家操作接口,是进行在线对战中人人对战的关键部分。
Play_card(e){
    //判断是打牌还是摸牌
    let ans = false
    if(this.data.pile_total == 0){
      that.Overgame()
    }
    if(this.data.robottap==1){
      return
    }
    if(this.data.turn==true){
      let type = parseInt(e.currentTarget.dataset.type)
      let flowercolor = e.currentTarget.dataset.flowercolor
      let pile = ''
      if( flowercolor == 'C'){
        if(this.data.p1_club == 0){
          wx.showToast({
            title: '你没有梅花牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_club_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_club:this.data.p1_club - 1
          })
          if(this.data.p1_club == 0){
            this.setData({
              p1_club_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_club_top:this.Address_pile(this.data.p1_club_stacks[this.data.p1_club_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'D'){
        if(this.data.p1_diamond == 0){
          wx.showToast({
            title: '你没有方块牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_diamond_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_diamond:this.data.p1_diamond - 1
          })
          if(this.data.p1_diamond == 0){
            this.setData({
              p1_diamond_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_diamond_top:this.Address_pile(this.data.p1_diamond_stacks[this.data.p1_diamond_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'S'){
        if(this.data.p1_spade == 0){
          wx.showToast({
            title: '你没有黑桃牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_spade_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_spade:this.data.p1_spade - 1
          })
          if(this.data.p1_spade == 0){
            this.setData({
              p1_spade_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_spade_top:this.Address_pile(this.data.p1_spade_stacks[this.data.p1_spade_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'H'){
        if(this.data.p1_heart == 0){
          wx.showToast({
            title: '你没有红桃牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_heart_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_heart:this.data.p1_heart - 1
          })
          if(this.data.p1_heart == 0){
            this.setData({
              p1_heart_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_heart_top:this.Address_pile(this.data.p1_heart_stacks[this.data.p1_heart_stacks.length-1])
            })
          }
          ans = true
        }
      }
      if(ans){
        let that = this
        wx.request({
          url: 'http://172.17.173.97:9000/api/game/' + that.data.uuid,
          method:'PUT',
          header:{
            "Content-Type": 'application/json',
            'Authorization':wx.getStorageSync('token')
          },
          data:{
            'type':1,
            'card':pile
          },
          success(res){
            wx.showToast({
              title: '成功打出手牌',
              icon:'success',
              duration:1500
            })
          },
          fail(res){
            wx.showToast({
              title: '网络错误',
              icon:'error',
              duration:1500
            })
          }
        })
      }
      else if(ans==false && type == 0){
        let that = this
        wx.request({
          url: 'http://172.17.173.97:9000/api/game/' + that.data.uuid,
          method:'PUT',
          header:{
            "Content-Type": 'application/json',
            'Authorization':wx.getStorageSync('token')
          },
          data:{
            'type':0,
          },
          success(res){
            wx.showToast({
              title: '成功摸牌',
              icon:'success',
              duration:1500
            })
          },
          fail(res){
            wx.showToast({
              title: '网络错误',
              icon:'error',
              duration:1500
            })
          },
        })
      }
    }
    else{
      wx.showToast({
        title: '还没到你呢!',
        icon:'error',
        duration:1500
      })
    }
  },

 

  • AI算法,采用了比较稳扎稳打的策略(总是打出自己手牌中最多的花色牌且)通过触碰托管图标即可调用。
Robot(){
    if(this.data.p1_total == 0 || this.data.decision_total == 0){
      this.Robot_turnon_card()
      return
    }
    else{
      let top = this.data.decision_stacks[this.data.decision_stacks.length - 1][0]
      let temp = 0
      let color = ''
      if( top == 'C' ){
        if( this.data.p1_diamond == 0 && this.data.p1_spade == 0 && this.data.p1_heart == 0 ){
          this.Robot_turnon_card()
          return 
        }
        else{
          temp = this.data.p1_diamond
          color = 'D'
          if(this.data.p1_spade > temp){
            temp = this.data.p1_spade
            color = 'S'
          }
          if(this.data.p1_heart > temp){
            temp = this.data.p1_heart
            color = 'H'
          }
        }
        if(color == 'D'){
          this.Robot_play_card(color)
        }
        else if(color == 'S'){
          this.Robot_play_card(color)
        }
        else if(color == 'H'){
          this.Robot_play_card(color)
        }
        return
      }
      else if( top == 'D' ){
        if( this.data.p1_club == 0 && this.data.p1_spade == 0 && this.data.p1_heart == 0 ){
          this.Robot_turnon_card()
          return
        }
        else{
          temp = this.data.p1_club
          color = 'C'
          if(this.data.p1_spade > temp){
            temp = this.data.p1_spade
            color = 'S'
          }
          if(this.data.p1_heart > temp){
            temp = this.data.p1_heart
            color = 'H'
          }
        }
        if(color == 'C'){
          this.Robot_play_card(color)
        }
        else if(color == 'S'){
          this.Robot_play_card(color)
        }
        else if(color == 'H'){
          this.Robot_play_card(color)
        }
        return
      }
      else if( top == 'S' ){
        if( this.data.p1_diamond == 0 && this.data.p1_club == 0 && this.data.p1_heart == 0 ){
          this.Robot_turnon_card()
          return
        }
        else{
          temp = this.data.p1_diamond
          color = 'D'
          if(this.data.p1_club > temp){
            temp = this.data.p1_club
            color = 'C'
          }
          if(this.data.p1_heart > temp){
            temp = this.data.p1_heart
            color = 'H'
          }
        }
        if(color == 'D'){
          this.Robot_play_card(color)
        }
        else if(color == 'C'){
          this.Robot_play_card(color)
        }
        else if(color == 'H'){
          this.Robot_play_card(color)
        }
        return
      }
      else if( top == 'H' ){
        if( this.data.p1_diamond == 0 && this.data.p1_spade == 0 && this.data.p1_club == 0 ){
          this.Robot_turnon_card()
          return
        }
        else{
          temp = this.data.p1_diamond
          color = 'D'
          if(this.data.p1_spade > temp){
            temp = this.data.p1_spade
            color = 'S'
          }
          if(this.data.p1_club > temp){
            temp = this.data.p1_club
            color = 'C'
          }
        }
        if(color == 'D'){
          this.Robot_play_card(color)
        }
        else if(color == 'S'){
          this.Robot_play_card(color)
        }
        else if(color == 'C'){
          this.Robot_play_card(color)
        }
        return
      }
    }

(5)性能分析与改进

  • 由于微信小程序不能使用本地的图片,因此本小程序中所有图片的URL均是使用网络地址,造成访问网络链接的次数太多(还是在挂上了校园网vpn的情况下),因此访问的耗时很长,加载页面就会有卡顿。因为刚学前端,对于WXML以及WXSS的布局与样式不是很熟练,造成存在图片太大而有效显示区域较小的问题。

  • 因为在开发本小程序时,使用了COLORUI组件库,直接将其所有组件都导入了,造成了wxss覆盖率低的问题,以及定时器未跟随页面回收(不是很会用)。

(6)描述你改进的思路

  • 改进的思路:

    • 下次开发小程序时会使用云开发模式,将图片全部存储在云端,可以通过路径直接调用图片,应该会大大减少访问图片请求的时间,对于样式方面采用flex等布局。

    • 了解到定时器可以在生命周期函数中的监听页面卸载函数Unload中使用clearinterval函数清除定时器,在下次引用组件库时只引用所需要的wxss样式。

(7)展示性能分析图和程序中消耗最大的函数

  • 性能分析图:
  • 消耗最大的函数:
Play_card(e){
    //判断是打牌还是摸牌
    let ans = false
    if(this.data.pile_total == 0){
      that.Overgame()
    }
    if(this.data.robottap==1){
      return
    }
    if(this.data.turn==true){
      let type = parseInt(e.currentTarget.dataset.type)
      let flowercolor = e.currentTarget.dataset.flowercolor
      let pile = ''
      if( flowercolor == 'C'){
        if(this.data.p1_club == 0){
          wx.showToast({
            title: '你没有梅花牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_club_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_club:this.data.p1_club - 1
          })
          if(this.data.p1_club == 0){
            this.setData({
              p1_club_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_club_top:this.Address_pile(this.data.p1_club_stacks[this.data.p1_club_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'D'){
        if(this.data.p1_diamond == 0){
          wx.showToast({
            title: '你没有方块牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_diamond_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_diamond:this.data.p1_diamond - 1
          })
          if(this.data.p1_diamond == 0){
            this.setData({
              p1_diamond_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_diamond_top:this.Address_pile(this.data.p1_diamond_stacks[this.data.p1_diamond_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'S'){
        if(this.data.p1_spade == 0){
          wx.showToast({
            title: '你没有黑桃牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_spade_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_spade:this.data.p1_spade - 1
          })
          if(this.data.p1_spade == 0){
            this.setData({
              p1_spade_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_spade_top:this.Address_pile(this.data.p1_spade_stacks[this.data.p1_spade_stacks.length-1])
            })
          }
          ans = true
        }
      }
      else if( flowercolor == 'H'){
        if(this.data.p1_heart == 0){
          wx.showToast({
            title: '你没有红桃牌!',
            icon:'error',
            duration:1500
          })
          return
        }
        else{
          pile = this.data.p1_heart_stacks.pop()
          this.setData({
            p1_total:this.data.p1_total - 1,
            p1_heart:this.data.p1_heart - 1
          })
          if(this.data.p1_heart == 0){
            this.setData({
              p1_heart_top:'https://img2020.cnblogs.com/blog/1925175/202110/1925175-20211021225210340-773083698.jpg'
            })
          }
          else{
            this.setData({
              p1_heart_top:this.Address_pile(this.data.p1_heart_stacks[this.data.p1_heart_stacks.length-1])
            })
          }
          ans = true
        }
      }
      if(ans){
        let that = this
        wx.request({
          url: 'http://172.17.173.97:9000/api/game/' + that.data.uuid,
          method:'PUT',
          header:{
            "Content-Type": 'application/json',
            'Authorization':wx.getStorageSync('token')
          },
          data:{
            'type':1,
            'card':pile
          },
          success(res){
            wx.showToast({
              title: '成功打出手牌',
              icon:'success',
              duration:1500
            })
          },
          fail(res){
            wx.showToast({
              title: '网络错误',
              icon:'error',
              duration:1500
            })
          }
        })
      }
      else if(ans==false && type == 0){
        let that = this
        wx.request({
          url: 'http://172.17.173.97:9000/api/game/' + that.data.uuid,
          method:'PUT',
          header:{
            "Content-Type": 'application/json',
            'Authorization':wx.getStorageSync('token')
          },
          data:{
            'type':0,
          },
          success(res){
            wx.showToast({
              title: '成功摸牌',
              icon:'success',
              duration:1500
            })
          },
          fail(res){
            wx.showToast({
              title: '网络错误',
              icon:'error',
              duration:1500
            })
          },
        })
      }
    }
    else{
      wx.showToast({
        title: '还没到你呢!',
        icon:'error',
        duration:1500
      })
    }
  },

(8)展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路

  • 这部分由于时间有限暂时没有进行单元测试。

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

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

  • 林雨欣

    • 困难描述:这个结对编程的“编程”我确实没什么参与,困难除了没做过原型设计外就是浪费两周时间后带来的后果,那就是时间比较紧,没做过小程序,前端开发一无所知。

    • 解决过程:利用好浏览器和b站,学习原型设计网课和速成小程序开发(由于时间有限只学习了二十多节),然后找素材、抠图、修图,在线网站真的有好多挺神奇的东西,GIF的抠图不是我自己手动完成的,是利用网站智能抠除后下载的(当然效果好的是需要付费的),最后在结合队友的前端开发过程的需求进一步优化了原型设计。

    • 有何收获:收获了原型设计工具的使用经验,发现了几个对左图和转格式挺有帮助的网站,也学习一些些前端的知识。

  • 林志锋

    • 困难描述:最大的困难就是从来没学过小程序的开发,刚开始看到评测组给出的接口人都是懵逼的,根本不知道怎么用。作业布置的前期学习了十多节小程序前端开发的网课,但是结对编程作业ddl又迫在眉睫,此时还面临了其他课的考试、小测和实验报告提交,感觉太难了。另外在开发过程还遇到这些困难:1.小程序无法使用本地图片,若直接将图片放入小程序本地文件夹中,根据路径来调用会发生手机调试时,图片出不来的情况。2.调用接口时遇到了许多细节上的困难,比如在使用header请求头时,Authorization参数按照文档中Authorization: Bearer {{your_token}}的使用说明来,结果一直鉴权失败。3.在进行在线对战的操作设计时,弄清逻辑花了很多时间。

    • 解决过程:1.采用网络地址的方式给予url,可以上传相册或者黏贴在博客园随笔中,得到网络地址。2.问了同学才知道header的正确用法哈哈3.反复阅读了接口文档中获取上步接口的操作以及获取用户信息的操作,在草稿上画了流程图弄清逻辑之后再开始。在这次作业中,在ddl的驱动下,决定直接现学现卖,停下看网课的进度直接开始写代码,这个摸索的过程真的及其痛苦,写了一下午一个页面都写不明白,这就是知识储备不够的坏处吧。不过不得不说这样摸索式的实践不失为一种快速获取知识和编码经验的好方法,在开始初期经历了写几行代码debug好久后一点点地体会到规律,再到快起来和发现组件库以及学习组件库的使用方法,组件库的使用确实减少了我不少麻烦。前期由于按照主观的视觉效果胡乱排版,后来发现画面在手机上不正常的排布,于是又学习了flex布局,纠正了原先写的页面。在经历了写页面后面临的最大难题就是各种对战模式js文件的编写,太难了真的,这个对初学者也太不友好了,向同学请教、学习,再实践去一点一点排雷。爆肝了这么多天才完成了小程序。

    • 有何收获:学习了很多小程序前端开发的知识,学会了编写wxml、wxss和js代码、组件库的使用、接口如何调用、如何实现监听等,收获还是很丰富的,就是过程太痛苦了。

4.评价你的队友。

  • 林雨欣

    • 队友值得学习的地方:真的很认真很拼,爆肝熬到一两点写代码,从对小程序开发前端一无所知到独立完成前端页面绘制、调用接口、写各种对战模式的算法。总之一句话“大佬牛逼!”,以及“大佬式”霸道:“你可别写了",我:“.......”(团队请给我一打这么“不讲理”的队友),爱了爱了,那我只能“勉为其难”地摸鱼了。

    • 队友需要改进的地方:emmm把什么代码都写了那我无话可说了都,要说改进,我觉得应该是做这个作业的开始时间太晚了,所以才要爆肝....总之,还是早些规划、早些做比较好吧。

  • 林志锋

    • 队友值得学习的地方:关于原型设计部分队友完成得还不错,安排了原型设计工作,很快就付出行动去学习原型设计工具的使用方法并且在短时间完成了原型设计的初稿,行动和审美都值得肯定一下。

    • 队友需要改进的地方:队友给我的感觉是对写代码有很强烈的畏难情绪、害怕写出一些de不出来的bug导致情绪低落,对于这一点我觉得需要迎难而上,多看看、多写写代码,多学习学习debug,长久坚持应该可以有效克服。

5.提供此次结对作业的PSP和学习进度条(每周追加)

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

  • 林志锋

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 0 0 2 2 理清游戏规则、初步分工
2 0 0 8 10 学习小程序前端开发网课,初步了解wxml、wxss和js语言
3 220 220 14 24 开始前端几个简单页面的实现、安装组件库、学习组件库的使用方法
4 3597 3817 30 54 实现双人对战的js文件配合wxml、wxss文件,初步完成后测试并优化。学习登录页面、监听功能实现、调用接口,实现AI的算法,完成人机对战和在线对战模式的开发
  • 林雨欣
第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 0 0 2 2 大致梳理游戏有什么需求、需要学什么知识
2 0 0 3 5 构思AI算法(还是觉得太随机了,没想出什么好的打法)
3 0 0 20 25 查资料了解原型设计工具,学习墨刀网页版使用方法(看网课),初步构思原型设计,找素材(各种ps、抠图、转格式),学习小程序前端
4 0 0 14 39 为了找到好的打法于是自行打牌,顺带测试下本地双人对战(虽然没什么用就是了),优化了一下原型,补充原先的构思中缺失的部分,制作GIF,写博客

 

三、心得

 

  • 林雨欣

    • 刚看到这个结对编程作业感觉这也太难了,这难度都够别班当团队作业了,不愧是柯老板的班,作业就是恶心,两个人做一个小程序这是我从来没想过的。看看题目的需求,第一个我就没懂,原型设计那是听都没听过。一看完成时间,诶还有一个月,那真不戳,于是想着先快乐玩耍,反正还有这么长时间,最后十几天做应该也来得及。然后和我的队友快乐玩耍了两周觉得该开始了,于是灰溜溜地开始学原型设计,一口气刷完网课马上就上手画了个原型。但是吧,现实没有想象那么美好,不知道为什么十月开始作业就特别多,写都写不完,自从原型设计以后就忙着写其他课的作业,软工没开始竟然也没感觉心急如焚?我觉得大概我是老油条了。我本该承受快乐玩耍带来的代价,但是!!!但是我那聪明机智、活泼可爱、学习能力强、无私奉献、舍己为人的队友竟然为了我拥有一个不那么枯燥乏味且疲惫的大三生活以及拥有一个美好的软工实践体验决定独自承受一切,这真是让我感激涕零,“吃水不忘挖井人”,在此处我必须狠狠夸夸我那伟大的队友竟然把结对编程玩成了个人编程(大概是怕我写出一堆他de不出来的bug以至于加大工作量完不成作业),我还能给予他精神上的支持,这时候我就能感受到抱个大腿的重要性了。

    • 通过结对编程,我知道了事情不会像我想象的那么顺利,我也没想到准备开始的时候会有写不完的作业,自己学习小程序前端速成学了大约二十多节课(虽然说没实践上),加上队友爆肝写代码,我突然就觉得我不该看不起任何一个看起来很无聊的小程序,没想到一个小小的小程序竟然这么麻烦。我承认我的的确确是在摸鱼,不过总归还不算是一无所获。起码还学了个原型设计以及那二十多节课的前端知识,也不至于啥也没学到,感谢柯老板,感谢评测组,最感谢的还是我那舍己为人的队友。

  • 林志锋

    • 我就是那“舍己为人”的队友,我也很无奈,我不入地狱谁入地狱。我也同样觉得这作业太难了。以前没学过小程序开发,也没听过原型设计,更不会写前端。作业发布的前两周同样也在快乐打游戏玩耍,过了半个月发现游戏与软工皆失。剩最后一周的时候就得压迫一下自己。还真是不压迫一下自己都不知道自己潜力这么大,用这么短的时间写一个游戏连我自己也没有想到,果然“ddl是第一生产力”。说到这里我就要描述一下我这一周又肝又痛苦的写代码经历,ddl的前一周多学的前端网课的那点内容真的不够用啊,啥也不会就直接刚小程序前端,结果意料之中地写几行代码debug好久,心里苦,想着这就当是积累经验了,写到一半发现自己的页面在手机里的布局不太对劲,才中途又学习了flex布局重新排布了页面,心态炸了都。写到双人对战有人跟我说小程序开发用到的图片不能是本地的,还以为必须换成云开发,差点又心态炸了。写双人对战真的js也太长了,我真的写了挺长时间的,还有评测组提供的接口,一开始是真的看都看不懂、用也不会用,没有想到这些接口我最后都能用起来。监听是真的恶心我,我真的很认真地对比了自己和别的同学的写法,想了一天也没想明白到底哪里有问题,人都emo了,最后才发现竟然自己原来的写法就是正确的。

    • 好吧,尽管今天已经是ddl了,我还是在debug,我已经提前感受到当程序员的痛苦了,不希望以后做那么痛苦的工作。这次作业感觉是暴风吸入小程序的前端开发知识,收获很大,有一说一哈,软工实践作业挺磨练心性的,我只想好好睡觉好好活着。

posted @ 2021-10-24 22:57  星星落兜里了  阅读(100)  评论(0编辑  收藏  举报