你熟悉辩论么?如果不熟悉,“奇葩说”总是看过的吧?我们这次想聊聊辩论赛的“未来”。
说是“未来”,或许现在也同样适用。在疫情还未解除的情况下,很多线下活动都无法进行,辩论就是其中之一。不过已经有人将这个场景从线下搬到了线上。
我们 2019 年 RTC 创新编程挑战赛的三强之一,“辩之竹”团队实现了一套功能完整的辩论系统,能计时、在线辩论、裁判视频点评,还能统计票数等。而且,它已经应用于很多辩论比赛中。于是我们让“辩之竹”撰写分享了一下作品的初衷,还有核心功能的实现:
项目介绍
为解决传统辩论赛UI丑、各系统相互割裂、线下举办各类成本等问题,特开发此系统。该套系统完全体包括:
- 可定制的赛事计时器
- 赛程、评委和辩手 管理后台
- 赛程查看和无纸化提交分数的小程序
- 赛后即时点对点反馈个人表现的辩力提升系统
- 远程裁决的视频会议部分
项目初心
作为一个大学生,我们经常在大学举办各类辩论赛,但是在当前的辩论赛事中,长期存在着五大问题,即如图: image.png
- 日程查看不直观
- 计时器简陋不美观
- 统计票数麻烦且不环保
- 复盘数据获取困难
- 优质裁判资源稀少
除了以上问题,其实我们也发现现在的辩论赛非常受限于场地的局限,就算同一学校的两支辩论队也要解决申请教室、邀请评委、宣传吸引观众等问题,更别说如果是两所学校,两个城市,甚至两个国家的辩论队之间要举办比赛了。所以也想着能否把辩论赛整体搬迁到线上进行,正好接触到声网Agora 的黑客松大赛,了解到 SDK 每个月有 10000 分钟的免费额度,也看下了他支撑的语言框架和 SDK 集成文档很丰富,于是就想着参赛做个作品,能帮助解决相关问题的同时,提升锻炼自己的技术水平。
解决日程观看不直观
日程展示一直是个问题。传统赛事中,日程展示通常都是以发送 PDF 的形式呈现,当参赛人员和观赛人员想要查看日程时,往往需要[打开QQ->进入赛事群->点开群文件->打开PDF->人工寻找对应日期的日程]。
就算完成了上述步骤,日程文件往往也是一个字号小、信息繁复、不直观的 PDF 文件。寻找一个日程很麻烦且不适。为了让参赛人员和观赛人员更直观的看到日程,特在小程序内设置此页面。
数据储存使用了微信的云开发,并设计了 Web 端的后台页面,利用微信提供的API进行小程序端和 Web 端的数据统一来在 Web 端添加日程。
Web 端数据发送部分代码:
//./web/admin-battle-add.php
//这里调用了我自己写的微信云开发简易SDK,可以在源码中./web部分/lib/WeChat.php找到。具体信息可以在本文后部分具体介绍中找到。
else if($motion=="addBattle"){
$title = $_POST["title"];
$loc = $_POST["loc"];
$time = $_POST["time"];
$teamClaimId = $_POST["teamClaimId"];
$teamCounterClaimId = $_POST["teamCounterClaimId"];
$res = $wx->databaseAdd("db.collection(\"battle\").add({data:{env:\"{$GLOBALS["env"]}\",title:\"{$title}\",status:\"0\",loc:\"{$loc}\",time:\"{$time}\",data:{teamClaim:{Id:\"{$teamClaimId}\",point:\"\"},teamCounterClaim:{Id:\"{$teamCounterClaimId}\",point:\"\"}}}})");
redirect("./admin-battle-query.php?id=".$res["id_list"][0]);
}
复制代码
小程序部分代码:
//./小程序部分/Bam1/client/src/pages/schedule/schedule.js
componentWillMount () {
let {env} = this.$router.params;
Taro.setNavigationBarTitle({title:this.$router.params.cName});
Taro.setStorageSync("env",env);
let p = this;
let skpD = 0;
Taro.cloud.database({env:"factory-1"}).collection("other").where({
env: Taro.getStorageSync("env"),
isSkpD : true
}).get({
success: res1 => {
skpD = res1.data[0].skpD;
Taro.cloud.database({env:"factory-1"}).collection("battle").where({env: Taro.getStorageSync("env")}).skip(skpD).limit(12).get({
success:function (res) {
let idL = p.state.idList;
res.data.map((item,index)=>{
idL.push(item._id);
});
p.setState({
idList: idL
})
...
}
复制代码
备注:
-
小程序部分,
./Wechat Mini Program/Bam1/client/src/pages/schedule/schedule.js
这部分代码主要是调用微信云开发的相关函数获取比赛 ID 列表。 -
在这个文件外,
./Wechat Mini Program/Bam1/client/src/components/battle/battle.js
是每一个日程 card 的组件。通过 schedule 文件向内传入 ID 参数,在 battle component 内获取数据。 -
这里存在一个并不合理的获取数据方式。我后来思考,在主界面一次性获取所有数据后,将数据交由 battle component 渲染应该可以得到更高的性能。但是当时时间有限,就没有修改。
解决计时器简陋不美观
其次,计时器的不美观问题,传统计时器如图:
显然,这种计时器与任何现代的设计思维都背道而驰,完全无美感可言。我找朋友为我设计了计时器的页面,并利用 HTML + JavaScript 实现其功能。计时器展示如图:
JS 的主要实现部分在./web/admin-battle-add.php
该计时器主要实现了如下功能:
- 辩手姓名、图片展示
- 键盘操控
- 自定义环节 由于使用 HTML 开发,它有相比 exe 的更高跨平台能力。
解决统计票数问题
同时,响应无纸化潮流,我还设计了无纸化提交分数的裁判系统。它的另一个好处是避免了人工计算分数,防止人为误差的出现 + 计算分数的等待。 文件在:./Wechat Mini Program/Bam1/client/src/pages/user/judge.js
裁判界面的环节是与计时器同步的。
这里为了防止数据丢失,还使用Taro.setStorageSync()
,在每一次写完数据后,将数据存入临时数据。同样,当全部写入完毕,再使用微信云开发提交到服务器。
$obj = $wx->databaseQuery("db.collection(\"battle-judge-point-conclude\").where({env:\"{$GLOBALS["env"]}\",battleId:\"{$bid}\",judgeId:\"{$r["judgeId"]}\"}).get()");
复制代码
复盘数据获取更容易
个人辩力提升系统也是本系统的另一个亮点。在传统赛事中,辩手对自己的表现只能以输赢来衡量,很难量化出一个标准,往往会拖慢对个人的提升。
但是在最普遍的三轮投票制中,有一轮即分数票,评委会根据选手的表现给出对应的分数。在传统赛事里,因为统计麻烦,所以往往不会将这个分数反馈给辩手。这浪费了一个很重要的资源。所以此系统利用评委在云上的分数,将每个辩手的分数落实到每个辩手身上,这样可以直观量化出辩手的水平,为辩手的进一步提升提供帮助。
裁判远程视频
最后,目前在辩论赛上普遍存在一个问题:缺少优质裁判资源。倒不是说优质裁判特别特别少,而是请优质裁判所需要的高昂的食宿、交通费用往往给辩论组委会带来很大的经济困难。故利用Agora声网公司提供的视频会议SDK开发了远程视频裁决系统。
在前期开发中,其实并没有非常多的实际视频通话使用需求,声网提供的开发者免费时长(每月10000分钟免费),为我作为一个大学生的开发提供了很大便利。同时,声网便利的SDK及其文档介绍使我很快就将服务接入了已有系统,整体开发体验非常好。得益于声网的优质服务,在几次测试中,视频会议的稳定性非常之高,完全满足了使用需求。
文字直播:
$("#send").click(function () {
if(channel === null) {
showOnP("未加入频道,请登录。")
return;
}
channel.sendMessage({ text: $("#text").val() }).then(() => {
showOnP("直播发送:"+$("#text").val());
/* 频道消息发送成功的处理逻辑 */
}).catch(error => {
showOnP("直播发送失败:"+$("#text").val());
showOnP(error)
/* 频道消息发送失败的处理逻辑 */
});
})
client.on('ChannelMessage', ({ text }, senderId) => { // text 为收到的频道消息文本,senderId 为发送方的 User ID
console.log(text);
/* 收到频道消息的处理逻辑 */
showOnP("直播服务器收到信息:"+text+",直播员:"+senderId);
});
复制代码
接入RTC视频Channel:
rtc.client.init(option.appID, function () {
console.log("init success");
rtc.client.join(option.token ? option.token : null, option.channel, option.uid ? +option.uid : null, function (uid) {
Toast.notice("join channel: " + option.channel + " success, uid: " + uid);
console.log("join channel: " + option.channel + " success, uid: " + uid);
rtc.joined = true;
rtc.params.uid = uid;
// create local stream
rtc.localStream = AgoraRTC.createStream({
streamID: rtc.params.uid,
audio: true,
video: true,
screen: false,
microphoneId: option.microphoneId,
cameraId: option.cameraId
})
// init local stream
rtc.localStream.init(function () {
console.log("init local stream success");
// play stream with html element id "local_stream"
rtc.localStream.play("local_stream")
// publish local stream
publish(rtc);
}, function (err) {
Toast.error("stream init failed, please open console see more detail")
console.error("init local stream failed ", err);
})
}, function(err) {
Toast.error("client join failed, please open console see more detail")
console.error("client join failed", err)
})
}, (err) => {
Toast.error("client init failed, please open console see more detail")
console.error(err);
});
}
复制代码
此外,在上述展示出来的部分外,该系统还有完备的数据录入(指赛事信息录入),辩手信息和裁判信息录入系统。在这里不多展示。目前该系统已应用到实际环境多次,支撑多次辩论赛事的开展,欢迎大家使用,也欢迎大家可以一起参与到该项目的开发维护。
涉及技术
-
小程序: Taro, 微信云开发
-
网页端: Material Pro(样式), PHP, Agora SDK
开源与更多
- 作者于卓浩:重庆大学在读大一学生,喜欢学习新技术,兴致来了就写些自己喜欢的小项目。希望能在有限的时间多学习一些有用的知识~
- 个人 Github:github.com/pkmq24
- 开源地址:github.com/AgoraIO-Com…
RTC 2020 编程挑战赛春季赛已经开启报名了! 本次大赛从 3月10日 ~ 4月21日 进行报名、组队与开发,4 月 22 日至 4 月 24 日提交作品,4 月 25 日评奖,全程在线上进行。本次大赛准备了丰厚的大奖,获奖者更有机会进入声网 Agora 应聘快速通道,快拉上小伙伴报名吧!
来源:oschina
链接:https://my.oschina.net/u/4286012/blog/3209426