结对编程
姓名 | 分工 |
---|---|
韦宏麟 | 原型设计、素材提供、布局调整 |
黄新成 | 代码设计、代码实现、debug优化 |
一、原型设计
设计说明
猪yi巴的链接
1.原型开发工具:墨刀
2.总体设计思路
- 这款游戏启动后最先进入开始界面,通过该界面可以查看规则、自由选择进入三种游戏模式中的一个。如果进入单机模式或双人模式,可直接开始游戏。如果进入在线模式,则需进行登录,并选择加入或创建房间后才可开始游戏。
3.游戏架构、功能
-
开始界面
- 三个竖状按钮:点击后切换到对应的对战模式
- 圆形❓按钮:点击后弹出规则窗口
-
单机模式
-
具体细节:
- 为了游戏博弈的方便,双方手牌总数、卡组剩余牌数、放置区牌数等都有显示出来
- 为了使手牌分类更清晰明了,我们使用了四个‘盒子’来分别装四种花色的手牌,并在盒子中间显示出该花色牌的数量
- 为了简洁,我们抛弃了传统的多图案的扑克牌样式,采用自己绘制的简洁的中心对称卡牌
-
我们在游戏界面中添加了返回按钮,点击会出现弹窗可选择是否返回开始界面
-
投降按钮,点击会出现失败弹窗,并在两秒后返回开始界面
-
游戏结束后,根据胜利与否,会跳出胜利或失败的弹窗,并等待两秒后返回开始界面
-
-
在线模式
-
具体游戏细节与单机模式基本相同,但该模式在进入游戏界面前需先完成登录,并创建或加入房间
-
登录界面
-
房间界面
-
加入房间以及连接界面
-
创建房间以及等待玩家加入界面
-
成功进入游戏界面后
-
相比单机模式我们添加了托管功能,点击亮起后成功托管
-
-
双人模式
- 考虑到在玩家双方是在同一个手机上面对面进行游戏,并且不论是在对方的回合还是在自己的回合,玩家都需要看得到双方的牌数、卡组牌数等信息。而正因为是面对面进行游戏,所以必须将数字等信息都上下对称显示。但如果将所有信息都如此显示出来会过于繁乱,违背了简洁的原则,所以我们削减了界面一部分的显示,具体如下:
- 玩家1回合
- 玩家2回合
- 玩家1回合
- 可以看到,我们在每一个需要显示牌数的地方都添加了反方向的数字,目的是为了在不是自己的回合时,玩家也能了解场上ta需要了解的数据。
- 为了避免界面上充满了数字而导致观感不好,我们将不处于自己回合的玩家的手牌收起,换成两排字。等到了自己的回合才将手牌显露出来。
- 动图切换回合示范
- 考虑到在玩家双方是在同一个手机上面对面进行游戏,并且不论是在对方的回合还是在自己的回合,玩家都需要看得到双方的牌数、卡组牌数等信息。而正因为是面对面进行游戏,所以必须将数字等信息都上下对称显示。但如果将所有信息都如此显示出来会过于繁乱,违背了简洁的原则,所以我们削减了界面一部分的显示,具体如下:
遇到的困难以及解决方法
- 刚开始用的原型工具是微信小游戏原型工具,使用起来很容易入门,但是无法完成许多功能,导致原型设计完成缓慢。于是果断放弃该工具,经对比后使用墨刀,上手后原型完成速度加快许多。
- 在原型设计过程中有不少素材无法从墨刀中获得,最终发现可以自己绘制后再导入,于是解决了素材不足的问题。
二、原型设计实现
1.代码实现思路
(1)开发环境
我们用实现微信小游戏实现这个游戏,开发环境是微信开发者工具,使用语言为javascript
(2)网络接口的使用
在线模式对战需要的接口都使用到,用了微信里的wx.request函数请求接口
//登录接口
login(main){
let result
wx.request({
url: 'http://172.17.173.97:8080/api/user/login',
data:{
student_id:main.studentId,
password:main.password
},
method:'post',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
success (res) {
main.token = res.data.data.token
if(res.data.status == "200")
main.initInterface.bind(main)(5,main)
else
main.initInterface.bind(main)(0,main)
}
})
}
//创建对局
createGame(token,main,privacy = false){
wx.request({
url: 'http://172.17.173.97:9000/api/game',
method:'post',
header: {
'content-type': 'application/x-www-form-urlencoded',
'Authorization': token
},
data:{
"private": privacy
},
success (res) {
console.log(res.data.data.uuid)
main.uuid = res.data.data.uuid
if(res.data.code == "200")
main.initInterface.bind(main)(6,main)
else
main.initInterface.bind(main)(0,main)
}
})
}
//加入对局
joinGame(token,uuid,main){
if(!uuid) return
wx.request({
url: 'http://172.17.173.97:9000/api/game/'+uuid,
method:'post',
header: {
'content-type': 'application/x-www-form-urlencoded', // 默认值
'Authorization': token
},
success (res) {
main.apiRes = res
if(res.data.code == "200"){
main.initInterface.bind(main)(1,main)
main.initGame.bind(main)()
databus.mode = 2
databus.whoTurn = 1
console.log(databus.whoTurn)
}
else
main.initInterface.bind(main)(0,main)
}
})
}
//获取上步操作
getLastOp(token,uuid,main){
wx.request({
url: 'http://172.17.173.97:9000/api/game/'+uuid+'/last',
method:'get',
header: {
'content-type': 'application/x-www-form-urlencoded', // 默认值
'Authorization': token
},
success (res) {
main.apiRes = res
databus.requesting = false
}
})
}
//执行玩家操作
executeOp(main,token,uuid,type,pokerId){
wx.request({
url: 'http://172.17.173.97:9000/api/game/'+uuid,
method:'put',
header: {
'content-type': 'application/x-www-form-urlencoded', // 默认值
'Authorization': token
},
data:{
"type":type,
"card":pokerId
},
success (res) {
let lastOp = res.data.data.last_code.split(' ')
if(lastOp[1] == '0'){
main.players[0].uncover(lastOp[2])
}
else{
main.players[0].playCard(lastOp[2][0])
}
}
})
}
(3)代码组织与内部实现设计(类图)
- 代码的基础是Main类,里面定义了游戏运行必备的函数,其中loop()函数控制游戏全局的循环
- 每个界面都定义为一个界面类,界面中的按钮定义为按钮类
- 通过触摸检测逻辑来检测玩家是否触摸到按钮
- DataBus类为数据总线,游戏进行中的数据储存在其中
- 游戏开始后会初始化DataBus类,并初始化玩家类
- 玩家类中有玩家手牌类
- 游戏进行时主要监听玩家的手牌和卡组的第一张牌
(4)说明算法的关键与关键实现部分流程图
- AI算法
游戏AI会计算两名玩家的手牌数量和卡组、放置区的牌数量,估计一个风险值risk
当风险高的时候出牌概率较大、风险较低时翻牌概率较大
(5)贴出你认为重要的/有价值的代码片段,并解释
- 这是玩家的翻牌函数,先从卡组中选出第一张牌,然后与放置区第一张牌比较,如果相同就吃牌,将放置区所有牌加入玩家的手牌,如果不同就将该牌放到放置区顶部
(6)性能分析与改进
- 加载图片比较费时,有时候会产生卡顿
(7)描述你改进的思路
- 以后可能考虑将多张图片合成一张,从一张图中截取需要的部分,减少图片的加载量
(8)展示性能分析图和程序中消耗最大的函数
(9)展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
const ctx = canvas.getContext('2d')
const IMG_HERO = "imagesttons/openAI1.png"
const WIDTH = 80
const HEIGHT = 80
const X = 400
const Y = 100
const alpha = 0.9 //缩小比例
var img = new Sprite(IMG_HERO,WIDTH,HEIGHT,X,Y)
//const img2 = new Sprite(IMG_HERO,alpha*WIDTH,alpha*HEIGHT,(1-alpha)/2*WIDTH+X,(1-alpha)/2*HEIGHT+Y)
//img.drawToCanvas(ctx)
function touchStartHandler(e) {
e.preventDefault()
const x = e.touches[0].clientX
const y = e.touches[0].clientY
if(x >= img.x && x<= img.x+img.width && y >= img.y && y <= img.y+img.height){
img = new Sprite(IMG_HERO,alpha*WIDTH,alpha*HEIGHT,(1-alpha)/2*WIDTH+X,(1-alpha)/2*HEIGHT+Y)
}
}
function touchEndHandler(e) {
e.preventDefault()
img = new Sprite(IMG_HERO,WIDTH,HEIGHT,X,Y)
}
function loop(){
ctx.clearRect(0,0,canvas.width,canvas.height)
img.drawToCanvas(ctx)
//img2.drawToCanvas(ctx)
window.requestAnimationFrame(loop,canvas)
}
canvas.addEventListener('touchstart', touchStartHandler)
canvas.addEventListener('touchend',touchEndHandler)
loop()
- 按钮图片的坐标和大小根据手指的点击变化,并设置缩放比例,这是测试按钮伸缩效果的代码段
2.评价的你的队友
阿韦:队友抗压能力十分强,做事不怕困难,默默承受一切ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,点赞!点赞!
阿黄:队友很喜欢交流,讨论事情有条理,点赞!
3.psp和进度条
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 1200 | 1200 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 | 5 | 5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
· Design | · 具体设计 | 180 | 300 |
· Coding | · 具体编码 | 900 | 2120 |
· Code Review | · 代码复审 | 120 | 600 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | ||
· Test Repor | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 30 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 10 |
· 合计 | 2665 | 4475 |
第n周 | 新增代码(行) | 累计代码(行) | 本周学习耗时 | 累计学习耗时 | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 720 | 720 | 理解并掌握原型工具,做出了完整的原型,看懂了微信小游戏的示例代码 |
2 | 300 | 300 | 600 | 1440 | 代码写出了大概的框架、类 |
3 | 600 | 900 | 1000 | 2440 | 实现了简单的界面转化 |
4 | 450 | 1200 | 600 | 3160 | 单机的游戏基本实现了 |
5 | 200 | 1550 | 840 | 3760 | debug关于接口的debug各种debug |
三、心得
黄新成的心得:
- 本次作业我是比较感兴趣的,作业发布后就和队员一起想怎么实现。后来我们决定实现微信小程序,看了教程之后就下载了开发者工具。里面有示例代码,看了几天都没怎么看懂,本来打算放弃做微信小程序的。但在韦宏麟的鼓励下还是决定硬着头皮看,看了2天终于有点起色了。我们就开始想具体的实现方法,前两周主要是学习工具和语言以及思考实现方法。第三周实现了大部分类,本以为已经差不多了,但还是在最后一天晚上赶完QAQ。这次作业没能达到我的预期,没有考虑到后期的工作量,最后烂尾了。代码开始写之前我就尽可能保证可读性,但到后期还是变得有点臃肿。总之还会继续写代码没有完成的部分,争取能够发布
韦宏麟的心得:
- 这次的作业难度难,主要是素材的获取以及代码的实现。在第一周时经过试错比较选择墨刀做好原型之后专心给队友准备素材。脑海里想的原本是一起编程,奈何队友很棒,在我们约好碰面一起写代码前就已经写好了大部分的框架,于是我转变为纯纯的抱大腿型队友,专门提供牌、按钮、背景等各种图片素材以及对作品中个元素的位置风格的修改,最难的代码实现这一部分全交由队友来编写(向队友献上感谢敬佩)。不过由于我们误选择为微信小游戏而非小程序,教程难找只能自己摸索,导致完成时间大大拉长,到ddl时已经有点来不及最终只能实现得潦草了一些,没能实现原型中的一些设计。这次作业让我感受到团队的分工和合作是必须要明确的,并且一旦有一个人划水就会有人亚历山大,所以有需要合作的作业必须要每个人都尽全力。要最后没能帮上多少队友的忙感到十分难受可惜,再给队友和他的抗压能力点个大大的赞(๑•̀ㅂ•́)و✧👍