写在前面


印象中第一次看NBA是在1999年的夏天吧,当跟着学校校队巡演招生什么的,住在一个破宾馆里,一群人围着一台黑白电视机,当时还小,不理解为什么队里的大哥哥们看个比赛鬼吼鬼叫上蹿下跳的。记得那天是公牛打76人,乔丹对位艾佛森。

2002年姚明当选NBA的状元秀,国人开始关注到NBA这项充满魅力的体育赛事,关注这位承载东方篮球希望的小巨人,和他所在的休斯顿火箭队。真正开始看火箭队的比赛是上高中的时候吧,诺瓦克绝杀,22连胜,季后赛魔咒,整个高中时期的记忆,不是考试,不是校花,而是藏在一堆课本下的摩托罗拉,3G手机门户的NBA文字直播,和主播阿星的“BOOOOOOOM BABY!!!”。

转眼20年就过去了,工作后已经很久都没有机会完整的看一场比赛了,大年初二在朋友圈看到科比离开的消息一直不敢相信,刷了一天的新闻。

感觉青春,和一个时代就这么终结了。



Keep going


由于疫情出不了门,无意间又翻到了一个朋友在科比退役时画的图。

人气插画师:冬眠的熊 作品(lofter:xiangxdx.lofter.com/、weibo:weibo.com/u/217898786…)

于是想写一个简单的投篮游戏致敬一下,找朋友重新画了一版素材。

这就是我们今天要做的游戏:



游戏原理


首先我们了解一下物理游戏的开发原理。

一个物理游戏是由2个部分组成的:

物理计算通常我们需要借助一些现成的物理引擎,如Box2dP2.jsmatter.js等。

图形渲染可以用原生canvas,也可以用更强大的2d渲染引擎Pixi.js、CreateJS等。

物理引擎通常只负责模拟物理运动计算,而不涉及图形渲染。以此减小包的体积,同时更灵活地结合不同渲染方式完成游戏开发。

单纯使用物理引擎,你在界面上是看不到任何东西的。物理引擎实际上跟大家常用的缓动函数计算库Tween.js是一类东西。


基本概念


我们选择MatterJS + PixiJS组合来实现这个游戏。

MattterJS:brm.io/matter-js/

Pixi.js:pixijs.io/

两个库的使用方法参照官方的示例DEMO,这里就不详细介绍了。

了解几个基本的概念就可以开始进行游戏开发了。

Matter会创建一个叫World的东西,用来模拟仿真物理世界,添加到World中的物体,就具备了重力、摩擦力、碰撞等物理属性,这些物体我们称之为Rigidbody刚体),在Matter中,刚体分为2种,动态刚体(Dynamic Body)与静态刚体(Static body)。

PIXI会创建一个叫Stage的东西,用于图形渲染,实体就是DOM中的canvas节点,而添加到画布里的图形,我们称之为Sprite(贴图)。

物理游戏就是由物理引擎(虚拟环境)到渲染引擎(可视环境)的映射。

需要确保物理引擎与渲染引擎使用同一坐标系,有些物理引擎(如P2)与Pixi坐标系不同,要先进行全局的坐标变换



开始开发


初始化物理环境与渲染环境

var Engine = Matter.Engine.create()
var world = Engine.world                 // 物理环境var canvas = document.getElementById('canvas')
var App = new PIXI.Application({width: window.innerWidth,height: window.innerHeight,transparent: true
})
var stage = App.stage                   // 渲染环境
canvas.appendChild(App.view)App.ticker.add((delta) => {// update()                         // 启动刷新器
});

前面提到了物理引擎通常不负责图形渲染,而Matter提供了一个简单的渲染器用于调试,让我们在没有接入渲染引擎时也能方便的以线框的形式看到物理引擎创建的刚体。

var Render = Matter.Render
var render = Render.create({element: document.getElementById('physic'),engine: Engine,options: {width: window.innerWidth,    height: window.innerHeight}
})

创建一个圆形刚体

var body = Bodies.circle(x, y, radius, {restitution: 0.7,    // 弹性系数density:0.05,        // 密度firction: 1          // 摩擦力
})

添加到World

World.add(world, body)

启动引擎与渲染器

Matter.Engine.run(engine)
Matter.Render.run(render)

这里我们就完成了物理引擎与渲染引擎的初始化,并且向物理世界中添加了第一个圆形刚体DEMO。

创建贴图

用PIXI创建一个篮球贴图。

var sprite = PIXI.Sprite.from('...ball.png');
sprite.anchor.set(.5)                             // 改变锚点至中心位置,方便定位
stage.addChild(sprite)                            // 贴图添加到渲染环境

刚体与贴图同步

在刷新器中同步绑定贴图与刚体位置、旋转角度。

function update () {sprite.position.x = body.position.xsprite.position.y = body.position.y    sprite.rotation   = body.angle}

完成同步后,就可以把刚体的测试线框去掉了

var body = Bodies.circle(x, y, radius, {...render: { visible: false }   // 关闭线框渲染
})

创建篮框

虽然我们的素材有一点点的透视,但我们实现的是2D视角,所以篮框的碰撞区域只需要在横切面设置2个静态刚体即可。

这里注意把篮框拆为2部分,让篮球可以从篮框中间穿过。


添加篮网

Matter中有一些预设的复合体挂载在Composites中,比如softbody(软体)、car(汽车)、bridge(桥)等,我们用softbody来模拟篮网。

// 参数说明参照 https://brm.io/matter-js/docs/classes/Composites.html
var nets =  Composites.softBody(800, 240, 8, 5, 0, 0, false, 3.2, {firction: 1,frictionAir: 0.08,render: { visible: false },collisionFilter: { group: Body.nextGroup(true) }
}, {render: { lineWidth: 2, strokeStyle: "#fff" },stiffness: 1.4
})

发射篮球

给篮球设置一个线速度与角速度

// 操作刚体的方法挂载在Matter.Body类上
Body.setVelocity(body, { x: 1, y: -1 });     // 设置线速度
Body.setAngularVelocity(body, -0.1);         // 设置角速度,使球轻微后旋

到这里一个简单的2D投篮游戏DEMO就完成了。

加入人物

接下来我们用动画软件的骨骼系统制作一个科比的投篮动画。

具体动画的制作过程这里就不详细介绍了,做的比较渣。

接着导出png序列图。

拉到TexturePack里合成精灵图,选择PIXI框架格式导出,得到一个精灵图和JSON配置文件。

PIXI加载精灵图

App.loader.add('.../kobe.json').load(function(){const frames = [];for (let i = 1; i <= 37; i++) {const val = i < 10 ? `0${i}` : i;frames.push(PIXI.Texture.from(`kobe00${val}.png`));}var Kobe = new PIXI.AnimatedSprite(frames);Kobe.animationSpeed = 0.4;Kobe.loop = false
})

通过Kobe.play()播放动画

关键问题

写到这里的时候遇到了一个问题,持球到起跳出手的过程,篮球是一个跟随手掌写死的动画,而投出后又是一个动态刚体,这两部分虚实如何衔接?

一开始我想到的方案是,动画序列里不包含篮球,只有一个空投的动作,创建一个篮球刚体跟随投篮动画中手掌的位置,尝试后发现很难同步,持球过程不是一个简单的线性轨迹,投篮动画的播放速率与Tween控制的篮球运动速率也是不可控的。

第二个方案是在动画序列中完成前半部分持球跳投动作,在篮球投出瞬间留出一帧空白帧,在篮球消失位置瞬间创建一个一摸一样的篮球刚体,以此实现虚实衔接。

关键代码

Kobe.onFrameChange = (e) => {// 动画帧回调,e为当前播放帧索引if (e === 4) {// 第5帧篮球消失瞬间,创建刚体var body = Bodies.circle(...)World.add(world, body)        Body.setVelocity(body, { x: 10, y: -10 });  // 创建瞬间将球投出...}
}

完整版

最后再加上一些音效、字幕、分数、引导,完善一下交互细节。

完整版地址:guowc.github.io/mamba/

最后

Heros comes and go,

but legends are forever.

你们懂的,读者三部曲 

你见过凌晨4点的洛杉矶吗?写一个简单的投篮游戏相关推荐

  1. 一个简单的神经网络,三种常见的神经网络

    BP人工神经网络方法 (一)方法原理人工神经网络是由大量的类似人脑神经元的简单处理单元广泛地相互连接而成的复杂的网络系统.理论和实践表明,在信息处理方面,神经网络方法比传统模式识别方法更具有优势. 人 ...

  2. 我们讨论的是《战争之城》的一个简单版本。如果地图上只有空旷的空间,河流,钢墙和砖墙。你的任务是尽快得到奖金,假设没有敌人会打扰你(见下图) 你的坦克不能穿过河流和墙壁,但它可以通过射击摧毁砖墙。当你击

    我们讨论的是<战争之城>的一个简单版本.如果地图上只有空旷的空间,河流,钢墙和砖墙.你的任务是尽快得到奖金,假设没有敌人会打扰你(见下图) 你的坦克不能穿过河流和墙壁,但它可以通过射击摧毁 ...

  3. 定时每天凌晨一点在linux系统上执行一个autobuild.sh脚本如何实现?

    定时每天凌晨一点在linux系统上执行一个autobuild.sh脚本如何实现? 可以使用linux的计划任务功能crontab来实现定时执行脚本. 具体步骤如下: 编辑crontab计划任务列表: ...

  4. 你见过凌晨四点破解密码的john吗?

    " 凌晨四点 " , 一个很有意义的代名词!这几个字最初是在科比的训练场上留下来的话!很励志.很艰辛,同时我们也为科比的不幸而感到惋惜~ 前人给我们留下的不只是回忆,更多的是精神上 ...

  5. 所见即搜,3分钟教你搭建一个服装搜索系统!

    摘要:用MindSpore+Jina,基于Fashion-MNIST Dataset搭建的服装搜索系统. 引言 各位算法萌新们,是不是经常训练了模型却不知道如何部署和应用?或者只会调参但不会前端后端所 ...

  6. 已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。 注:参赛球队列表见附录 注2:使用Math.random 来产生随机数。(也可以使用其它方法) 2. 2

    /*** Created by whp on 2018/7/30.*/ public class Test {public static void main(String[] args) {Strin ...

  7. 已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。注:参赛球队列表见附录注2:使用Math.random 来产生随机数。(也可以使用其它方法)

     方式一: import java.util.ArrayList; import java.util.List; import java.util.Random;public class Test1 ...

  8. 看我用LabVIEW写一个情人节的小礼物!!!(源码见最后链接)

    写在最前: 我是汽车NVH工程师,但我的生活也离不开代码,那么就用我最熟悉的"(编程)语言",趁着情人节主题征文活动,送各位一个小礼物吧~ 今年的情人节在春节中度过,不知道是好是坏 ...

  9. python俄罗斯方块算法详解_用 Python 写一个俄罗斯方块游戏 (

    @@ -2,34 +2,34 @@ > * 原文作者:[Dr Pommes](https://medium.com/@pommes) > * 译文出自:[掘金翻译计划](https://g ...

  10. Python 写一个俄罗斯方块游戏

    使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例 ...

最新文章

  1. 神经网络与机器学习 笔记—多层感知器(MLP)
  2. 类的初始化(构造函数)
  3. 2021年,Java开发者值得学习的13项技能
  4. 线性汇编总结和函数说明
  5. P6348 [PA2011]Journeys 线段树优化建图 区间连区间
  6. Go语言(golang)开源项目大全
  7. idea关联mysql数据库具体操作
  8. 8187L芯片真的比雷凌3070芯片好吗?各有什么特点?
  9. 百度地图根据缩放级别显示不同的marker点和窗口内容
  10. win10下office2013命令激活参考
  11. 小猪短租陈驰:共享经济不是简单的资本游戏
  12. twitter跳过手机验证_twitter跳过手机验证_twitter手机验证不了_攻略
  13. 被裁员后:三个月含泪啃完了1111道面试解析,再入职腾讯年薪45万
  14. 八千字,带你看示波器的发展史。
  15. c语言的标识符可分为哪3种字符,c语言标识符有哪三类?
  16. 医号馆建设医联体的核心优势(互联网医疗解决方案)
  17. 利用声音传感器控制led灯功能_树莓派.利用声音传感器制作声控灯
  18. 欢乐狼人服务器维护,欢乐狼人杀6人局怎么玩 欢乐狼人杀6人玩法攻略 如何玩欢乐狼人杀...
  19. Node.js:nodemailer发送163邮件
  20. SPSS(五)SPSS之相关分析与线性回归模型(图文+数据集)

热门文章

  1. DirectAdmin+阿里云免费SSL证书 面板里安装SSL
  2. Android 版本4.12 微信,安卓4.12微信下载
  3. PE装到移动硬盘的资料寻回办法
  4. C++练习实例———控制台实现坦克大战小游戏
  5. OFF文件格式_拔剑-浆糊的传说_新浪博客
  6. android卡在开机画面,请教:用迅为4412开发板开机时卡在Android静止画面
  7. panabit连接控制
  8. Flowable 流程实例
  9. pink老师 JS p61课后作业
  10. 网络聊天室项目说明书