Kode Sports Club
Kode Sports Club 是开罗新城一家私人体育俱乐部的线上导览。俱乐部当时还在施工,巴黎工作室 Merci-Michel 就把它做成了一座可以走进去的卡通三维小岛。访客给自己捏一个角色,在岛上点到哪里就走到哪里,和各个场馆的工作人员聊天,玩两个小游戏。访客点一下屏幕下方的小地图,镜头就从角色身后升到高空,整座岛变成一张标着场馆名字的导览图,再点一个场馆,镜头又落回地面。
这个站在 2020 年 11 月上线,代替了一场因为疫情没能举办的线下发布活动。它拿到了 Awwwards 2020 年 11 月的月度最佳和那一年的年度最佳开发(Developer Site of the Year),也拿到了 FWA 的当日最佳。

01概念
Kode 是开发商 Hassan Allam Properties 在开罗新城的 SwanLake Residences 社区里建的核心俱乐部,由 Blue Ribbon 运营。这个站要解决的问题,是在俱乐部开门之前让人先认识它。Merci-Michel 在一篇讲制作过程的 Medium 文章里说,客户原本打算办一场线下发布活动,疫情让活动办不成,于是要一个好玩、独特的线上体验,让人感受到俱乐部的气质和理念,也能先看一眼还在施工的场馆。团队和客户一起决定,把整座俱乐部用卡通的方式重新搭一遍,里面放上可以聊天的工作人员、俱乐部建筑的真实效果图和几位知名运动员的访谈视频,让一次参观变成一场有任务、有成就的游戏。
访客的路径从一个白色的加载页开始。页面中间是深蓝色的 KODE 标志和一个圆形进度环,进度走满后,进度环变成一个写着 ENTER 的按钮。访客点下去以后,先看到整座岛的俯瞰,镜头随后降到入口的喷泉边,一位接待员用五句话介绍这座俱乐部和玩法,最后问访客是否想先打扮一下自己的角色。换装界面在角色两边放了三排左右箭头,分别换头饰、身体配色和鞋袜,换好以后访客就可以自由地在岛上走动。


岛上的内容分成 11 个区域,从游泳池、网球和板式网球场、足球和橄榄球场,到会所、健身中心和运动表现中心。每个区域都站着一位工作人员,访客走到身边点一下,对方就会用两到七句话介绍这个场馆,中间几句的上方会弹出一张圆形的建筑效果图,让访客看到真实建成后的样子,最后一句通常是一条和这项运动有关的冷知识。这样排的道理在于,网站把一本介绍场馆的宣传册拆成了几十句对话,散落在岛上各处,访客要走过去才能读到,读完一个区域的所有人,这个区域在地图上的红色感叹号就会消失。

对话之外,岛上还有几样让访客多停一会儿的东西。足球场和跑道边的两位工作人员各发起一个限时一分钟的小游戏,足球是按准时机射门,骑车是按节奏点击加速。另外四位工作人员各介绍一段视频,分别是俱乐部的品牌短片、它的手机应用,以及壁球世界冠军 Raneem El Weleily、短跑运动员 Derek Redmond 和游泳名将 Dara Torres 的访谈。岛上还藏着可以收集的水瓶、会员卡和星星,一共 30 个。

这些活动最后都汇总到成就和奖杯上。顶部导航里的 Dashboard 列出 16 项成就,比如参观三个区域、走满三公里、看完品牌短片、换一次装、每种收集物各集满八个。成就上面是三个奖杯,对应俱乐部写在理念里的三句口号。Better Beats Best 奖励不断刷新自己成绩的人,Virtue Makes Victory 留给在骑车比赛中途停下来帮助一位受伤跑者的人,Passion Powers Progress 则要求解锁全部成就。成就的说明和实际的门槛有一处对不上,Yellow Jersey 写着在骑车游戏里超过 0.8 km 就能解锁,实际要骑满 5 km,一局骑出 1.7 km 时它仍然锁着。第二个奖杯只有一种拿法,骑车时跑道上会躺着一位举着红色 HELP 气泡的跑者,访客要放弃这一局的成绩,停下来和对方说话。俱乐部的价值观因此变成了访客亲手做出的选择,访客读到的就不只是一句口号。所有进度都存在浏览器里,访客下次再来,会直接回到岛上,进度也还在。

通往会员的路一直留在页面顶部。导航右边始终有一个白色的 Become a member 按钮,About us 则是一页按传统方式往下滚动的宣传页,讲俱乐部的理念和运动项目,最后提供一份介绍项目的 PDF。这样分工以后,岛上负责让人记住这座俱乐部,宣传页和会员按钮负责把有兴趣的人带到下一步。
这次拆的是站点现在的线上版本,地址是 kodeclubs.com,服务器上的文件在 2025 年 5 月重新部署过。现在的首页和网页存档里 2020 年 11 月 29 日的首页逐行比对,页面结构和全部文案都对得上,改动只有几处。俱乐部已经开业,所以所有写着 opening in Q2 2022 的地方都删掉了。顶部的 Become a member 原来打开站内的会员码弹窗,现在改成跳到外部的报名链接。页脚的电话号码换成了 WhatsApp 链接,原来分享到 Facebook 的按钮也换成了下载手机应用的按钮。
02设计
整个站的视觉分成两层,底下是一座低多边形、颜色柔和的三维玩具岛,上面是一层扁平的二维界面,只用深蓝、品红和白色。岛上的东西都没有写实的质感,草地是平整的亮绿色,树是一团团圆润的薄荷绿,跑道是粉色和淡紫色的两条色带,每样东西的边缘都带着一圈淡淡的白光,阴影也很柔和。这种画法让一座还没盖好的俱乐部看起来像一个精心做好的模型,访客不会拿它和真实照片去比,工作人员拿出来的效果图则负责交代真实的样子。
二维界面的规矩可以直接复用。主色是深蓝 #1c228c,用在 KODE 标志、标题和主要按钮上。强调色是品红 #f50359,用在对话里的关键词、成就标题和会员按钮的文字上。俱乐部的旗帜和会员卡用紫色 #7d0e8b,个别图形用橙色 #ff4000,其余都是白色。卡片统一是白底、圆角 15 像素。Awwwards 收录页列出的三种代表色是 #2779a7、#49c5b6 和 #FF9398,大致对应岛上的天空和海、成片的树和粉色的跑道。
字体只用 Antique Olive 一个家族的两款。窄而粗的 Antique Olive Compact 全部用大写,负责标志、标题、按钮和地图上的场馆标签。常规宽度的 Antique Olive 负责对话和说明文字。全站的字号都以根字号为单位,根字号随屏幕宽度线性变化。屏幕宽度小于 1024 像素时,根字号从大约 10 像素涨到 14 像素,1024 像素以上从大约 16 像素慢慢涨,1600 像素起固定为 18 像素。所以在 1920 宽的屏幕上,加载页的 KODE 标志是 10 倍根字号,也就是 180 像素,到手机上缩到大约 87 像素,各级字号之间的比例始终不变。
界面部件也用同一套语言,而且多数部件都有明确的状态。按钮是圆角矩形,深蓝底上压着浅色的 K 字花纹,表示继续。离开对话的按钮是绿色,表示结束。对话卡片下方有一个小尖角指向说话的人,正文是深蓝,关键词是品红。地图上的场馆标签平时是白底深蓝字,光标停上去时变成深蓝底白字,场馆四周同时亮起一圈描边,还没逛完的场馆则带一个红色感叹号。捡到东西或拿到奖杯时,弹出的卡片顶端都有一个深蓝色的圆形徽章,徽章里放着那件东西的小动画。成就面板用颜色区分状态,已解锁的是深蓝图标加品红标题,没解锁的整项变浅灰。
角色的造型同样是为了让任何年龄的访客都愿意玩。所有人都是一根没有脖子的彩色胶囊,两点眼睛,一条会变形的嘴,配上帽子、发型、护腕和球鞋。团队在文章里说,他们希望角色能同时讨好孩子和大人。
About us 是岛以外最主要的一页,它把同一套规矩放进了一张传统的、往下滚动的宣传页。背景是一整片天蓝,每一节左右交替排着标题和插画,标题前面有一个深蓝色的小标签(OUR PHILOSOPHY、SPORTS PROGRAM 等),身后衬着一个巨大的浅色编号 01 到 04。插画是岛上那些胶囊角色的高清渲染图,守门员蹲在球门前,另一位举着哑铃,让宣传页和岛上的游戏看起来出自同一个世界。

到了手机上,三维岛原样保留,界面按竖屏重新排了一遍。顶部的三个导航链接收进右上角的圆形菜单按钮,菜单打开后是一整屏从品红到紫色的渐变,中间的圆圈里放着整座岛的图片,圆圈外绕着一圈弧形排列的 BACK TO THE CLUB,下面是三个大号的白色链接。对话卡片撑满屏幕宽度,两个回答按钮并排放在卡片下方。地图在竖屏上同样能用,访客点一下场馆标签就能传送过去。手机横过来时,整个页面会被一张写着 Please rotate your device 的提示盖住,站点只接受竖着拿的手机。

03动效
这个站的动效分成两套节奏。界面上的卡片、按钮和对话框用同一条先慢后快、再长长收住的曲线,时长大多是半秒。镜头的大移动则用一条两头都很慢、中间很快的曲线,时长两秒。界面的动作因此显得轻快,镜头的动作显得从容,两者不会混在一起。
访客点下 ENTER 之后的第一个镜头,就是这条两秒曲线的完整示范。访客点击后大约一秒内,白色的加载页退去,岛的俯瞰在原地慢慢转动。接着镜头开始下降,在这两秒里,开头约半秒几乎没动,绝大部分路程挤在中间大约 0.6 秒里飞完,最后贴着喷泉慢慢停稳,停在接待员的正前方。这条路线让访客先看清整座岛的布局,再落到具体的一个人面前。
小地图那一跳用的是同一个动作的倒放。访客点 NAVIGATE 后,底部的界面先收起,大约一秒后镜头从角色身后升起,同样用两秒飞到岛的高空,岛上所有人物在上升途中一起缩小,到高处时已经看不见了,只剩场馆和标签。高空的镜头自己绕着岛缓慢转圈,访客按住拖动可以转到想看的角度,松手后镜头停在新的角度。光标停在场馆上时标签变色、四周亮起描边,点下去后镜头立刻按同样的曲线落向那个场馆,角色已经站在那里。
在地面上走路是另一种手感。访客按住地面的某一点,角色就朝那里走,按住不放就一直跟着光标走,单击则让角色朝那个方向走一小段。角色起步时速度在几帧之内逐渐加上去,离目标还有大约四个单位时开始减速,最后轻轻停在目标点上。转向也是逐帧追上去的,不会一下子掉头。镜头跟在角色身后,角色转弯时,镜头会慢慢绕回它的背后,脚下留下一串小小的尘土。手机上的操作完全相同,手指按住哪里,角色就走向哪里,第一次进岛时屏幕中间会出现一张画着手指点按的提示卡,第一次触屏后就消失。键盘只用三个键,上方向键前进,左右方向键横移。
和工作人员说话时,镜头和界面一起换成对话的样子。访客点下工作人员以后,镜头从角色身后降低、转到侧面,把两个人放在画面左右两边,大部分移动在一秒左右完成,之后一直轻轻漂着,像一台手持的摄影机。对话卡片在半秒内从屏幕下方升上来,0.25 秒后卡片的小尖角弹出来,文字一个字母接一个字母地出现,每 10 毫秒出现一个,所以一句话通常不到一秒就打完。文字打完后,回答按钮依次升上来,每个比前一个晚 0.1 秒。工作人员说话时嘴型会跟着变,开口时还会挥手。
岛上的小东西也都在动,让一座静止的模型看起来是活的。可收集的水瓶和星星一边缓慢上下浮动一边自转,访客走到大约 25 个单位以内时,它从无到有地放大出现,起势很快,最后慢慢收住,走远了又缩回去。蝴蝶扇着翅膀飞过,天上偶尔划过几道白色的风线,路人沿着小路慢跑。解锁成就时,屏幕左下角会探出接待员的半个身子,旁边弹出一个写着 Achievement unlocked 的气泡。
两个小游戏都把一个简单的时机判断做成了手感。足球游戏里,角色头顶有一排白色的瞄准标记在左右摆动,访客按下鼠标时瞄准停住,按得越久,蓄的力越足、球飞得越高,松开时角色抬脚射门,球按物理规律飞向球门里那块会换位置的圆形靶子。骑车游戏里,底部横条上的白点左右来回摆动,白点落进中间的 TAP 圈时点击,车就加速,点偏了就减速,而车骑得越快,白点摆得也越快,节奏越来越难跟。镜头也会跟着车速变化,骑得越快,它追赶角色的速度越快,所以高速时画面跟得更紧。两局都以一张压暗背景的结果卡结束,卡上写着进球数或里程,以及再玩一次和退出的按钮。
这些动作在 1920 宽的屏幕上都很流畅。在岛上走动和停在地图上时,帧率都保持在每秒 60 帧,没有出现掉帧。
04工程
画面全部交给 three.js,用 WebGL2 画在一块铺满窗口的画布上,整座岛由三类东西组成。第一类是 8 个大块(Block),地面、道路、球场、泳池和建筑这些每样只出现一次的东西,按它们在地图上的位置合并成 8 块,镜头看不到的那几块就不画。第二类是所有重复出现的东西,树、棕榈、汽车、路灯、栏杆和旗子,团队在导出时先把它们换成只记录位置、旋转和缩放的空物体,在浏览器里再还原,最后合并成一个大网格,一次画完。第三类是 26 个带骨骼动画的角色。
团队在文章里解释了为什么最后选择把重复物体合并。他们先试过按不同方式分组的实例化绘制,还试过按镜头视野分组的实例化,但为了让岛显得丰富,物体种类太多,绘制调用还是太多。而且他们要让地图和第三人称之间的镜头过渡不被打断,所以干脆把所有重复物体并成一个网格。这样整座岛每帧只需要 40 到 60 次绘制调用。

资源的做法同样以小为先。团队说,地图的几何体一开始有 12 MB,把重复的物体换成空物体之后省下约 4 MB,再用 Cesium 的 gltf-pipeline 做 Draco 压缩,整张地图只剩 2 MB。整张地图的颜色来自一张贴图,这张贴图在下载时其实是一个 9.5 KB 的 SVG 色板,上面是一格一格的平涂色块,浏览器拿到后再把它画成 2048 像素的贴图,所有模型的贴图坐标都指向某一个色块。同一个物体要换颜色时,团队没有改贴图坐标,而是用贴图的透明通道当遮罩来调整颜色。角色贴图也是一个 SVG,左半边是默认颜色,右半边是红、绿、蓝三色的遮罩,着色器把同一张贴图按左右两半各读一次,红色区域染成主色、绿色区域染成副色、蓝色区域保留原色,五套配色就靠一个数值切换。真正占体积的是烘焙好的环境光遮蔽(AO)贴图,8 个大块各在两张 2048 像素的图集里有一块,加上角色和其他物体用的一张,三张 PNG 合计约 2.9 MB,是下载里最大的图片。

光照没有用后期处理,靠几层便宜的做法叠出来。半球光给场景一层从上到下的颜色渐变,团队在 three.js 的标准材质里加了一圈边缘光,物体轮廓因此会微微发白,看起来像上了一层釉的玩具,再叠上一点 MatCap 的光照变化和烘焙好的 AO。阴影只用一张阴影贴图,团队改了 three.js 的光照代码,让阴影在这张贴图的覆盖范围边缘逐渐淡出,范围以外的物体就不会出现一道生硬的阴影边界。他们放弃了烘焙阴影和级联阴影,前者数据太大、效果也不够好,后者会增加太多绘制调用。代码里还搭了一套泛光、移轴和暗角的后期处理,但正式版的开关是关着的,每一帧都直接渲染场景,画布也关掉了抗锯齿,像素比最高只到 1.5。
镜头在两种状态之间过渡,靠的是一个从 0 到 1 的数值。0 是跟在角色身后的跟随镜头,1 是绕着全岛转的地图镜头,GSAP 用两秒、expo.inOut 的曲线补间这个数值,镜头位置在两种状态之间按它混合。同一个数值还同时控制三件事。所有角色的大小等于 1 减去这个数值,所以镜头升高时人物会一起缩小到看不见,不会在地图上变成一堆闪烁的小点。阴影贴图覆盖的范围跟着镜头和角色的距离变化,在地面上只覆盖角色周围三十多个单位,到地图高度时扩大到整座岛,团队在文章里也提到地图模式下阴影相机会放大。镜头的近裁剪面和远裁剪面也随这个数值从 0.1 和 250 变到 5 和 500,让近处和远处的深度精度都够用。进门、打开地图、从地图落回地面和 About 页的升空,都是同一个补间。

走路和物理都尽量少算。控制方式在开发中改过三次,团队在文章里讲了经过。最早做的是点击地面后沿导航网格寻路,他们先后用 Blender 和 Unity 生成导航网格,寻路用的是 Don McCurdy 的库,但网格太复杂,寻路结果时常出错,于是放弃了。他们接着改成摇杆,又发现掉头太难,加第二个摇杆对不玩游戏的人太复杂,固定镜头虽然好控制,却看不到环境的开阔感。最后他们回到点击,但不再寻路。
现在的版本里,点击只投射到一个看不见的水平面上,得到目标点,角色直接朝它走。角色的高度另外用一张简化的导航网格来求,网格的三角面按中心点分进 10×10 的格子,每一帧只对角色所在那一格里的面做射线检测,岛上的高低起伏和桥都靠它站得稳。物理交给 cannon.js,同样的分格思路也用在碰撞体上。站点把岛分成 5×5 的格子,只有角色附近 5 个单位以内的物体才会创建碰撞体,离开后就停用。足球游戏里的球也是 cannon.js 的刚体,射门时按瞄准方向和蓄力算出初速度,蓄力只决定球往上飞的那一部分。
几处小地方也用了同样省事的做法。每个角色的所有嘴型和奖牌都在同一个网格里,顶点上标着它属于哪一种形状,着色器把当前不用的形状直接移到远处,说话时只要改一个数值。蝴蝶的翅膀在顶点着色器里按时间来回翻折,不需要骨骼动画。手机和平板上,菜单打开后三维场景会停止渲染,省下显卡的工作。
站点把加载压成两个包,再按需要补几个小文件。团队自己的构建工具把文件打成两个二进制包,app.pack 装着成就和奖杯的动画序列帧,canvas.pack 装着模型、贴图、字体和 42 段音效,每个文件在包里的位置和长度写在首页的清单里,下载完以后再在浏览器里切开。访客第一次打开时一共要下载约 7.6 MB,访谈视频不算在内,这和团队文章里说的 7.6 MB 一致,其中 canvas.pack 压缩后约 5.3 MB,是最大的一项。屏幕上第一次出现内容只要 0.3 秒左右,但两个包要下完,加载页的进度环才会走满,在 1920 宽的屏幕上大约五六秒后才变成 ENTER。
声音也分成两条路加载。三个循环播放的背景声(环境声、平时的音乐和小游戏的音乐)不打包,用音频元素边下边播。音效则从包里解码,在三维场景里按位置播放,脚步、角色的说话声和射门声各有两到四个版本轮流使用,风声和自行车的声音会跟着速度变大。
这个站只能用鼠标或触屏完整地玩。键盘可以在页眉、页脚和地图入口之间移动,也能用方向键走路,但和工作人员说话、点场馆传送、换装和捡东西都必须用指针,读屏软件也读不到三维世界里的任何内容。站点不读取系统的减少动态效果设置,镜头的大幅飞行在任何设置下都会照常播放。
05评分与团队
| 奖项 | 出处 | 日期 | 分数 |
|---|---|---|---|
| FWA of the Day | The FWA | 2020-11-17 | 88 POINTS,DAYS 1 |
| Site of the Day | Awwwards | 2020-11-26 | 8.30。Design 8.21 / Usability 8.10 / Creativity 8.76 / Content 8.32 |
| Developer Award | Awwwards | 2020-11-26 | 8.25 |
| Site of the Month | Awwwards | 2020 年 11 月 | 月度最佳,不另打分 |
| Developer Site of the Year | Awwwards | 2020 年 | 年度最佳开发,不另打分 |
Kode Sports Club 先在 FWA 拿到 88 分的当日最佳,九天后又拿到 Awwwards 的当日最佳,随后被选为 Awwwards 2020 年 11 月的月度最佳。在 Awwwards 的四个分项里,Creativity 最高,有 8.76,这和它把一座俱乐部的介绍做成一场可以玩的寻宝游戏有关。Usability 是最低的一项,但也有 8.10,可能是因为点哪走哪的操作和随时可用的地图让第一次来的访客不容易迷路。开发评委给 Animations 打了 9.33,是六项里最高的,Accessibility 只有 6.67,对应前面说的键盘和读屏软件进不了三维世界。在 Awwwards 2020 年的年度评选里,它拿的是年度最佳开发这个分项,不是那一年的年度最佳(Site of the Year)。它还拿到了 CSS Design Awards 的 Website of the Day。
| 署名 | 身份 | 战绩 |
|---|---|---|
| Merci-Michel | 巴黎的互动设计和数字制作工作室,Awwwards 账号的介绍是 digital gamification studio。在这个项目里负责用户体验和界面设计、美术指导、动态图形、功能原型和技术开发 | Awwwards 作品 64,SOTY 2,SOTM 6,SOTD 44,HM 62。FWA 80,2026 年入选 FWA 名人堂 |
| Josette | 声音制作,Merci-Michel 的项目页把它列为这个项目的声音署名 | — |
这个站从设计到开发都由 Merci-Michel 一家完成,只有声音另外请了 Josette 制作。Merci-Michel 自 2012 年起拿过 80 次 FWA 奖,FWA 介绍它时说,这家工作室擅长把网站做成精心打磨、真正可以玩的世界。团队在文章里说,项目是 2020 年 4 月底法国封城期间接到的比稿,他们只有一周时间准备方案,最后赢下了项目。
06档案
- 获奖日期
- 2020-11-26
- 奖项
- Awwwards SOTD 2020-11-26,Awwwards DEV 2020-11-26,Awwwards SOTM 2020-11,Awwwards Developer SOTY 2020,The FWA FOTD 2020-11-17
- 站点
- https://www.kodeclubs.com/
- 署名
- Merci-Michel,声音 Josette
- 设计取向
- spatial
- 技术路线
- realtime
- Awwwards
- 8.3 / 10。Design 8.21 / Usability 8.10 / Creativity 8.76 / Content 8.32。开发奖 8.25。https://www.awwwards.com/sites/kode
- The FWA
- 88 POINTS,DAYS 1。https://thefwa.com/cases/kode
- 技术栈
- three.js,cannon.js 0.6.2,GSAP,Draco,自建的资源打包格式,Google Cloud Storage
- 资产总计
- 约 7.6 MB,其中模型、贴图和音效打成的 canvas.pack 约 5.3 MB
- 首屏与帧率
- FCP 0.30–0.34 秒,LCP 0.40–0.99 秒(加载页的标志),CLS 0.005。岛上走动和地图 60 fps