Cocos Creator入门实战:桌球小游戏
本文作者:BigBear
多年游戏行业研发经验
精通Unreal、CocosCreator游戏引擎
参与过多款手游、端游项目的研发
Cocos Creator入门实战:桌球小游戏
本篇主要是希望能够通过Cocos Creator实现一个桌球小游戏,从而能够让大家更好的了解以及运用Creator的物理系统
由于游戏比较简单,同时代码量也极少,因此就集中在一篇文章里面了。因此会长一些,有兴趣的同学麻烦耐心食用
开始之前
在开始桌球小游戏之前,我们需要对creator有一定了解以及熟悉,对js语法有一定的了解。在开始同样还是希望大家能够仔细的阅读一遍官方文档,以便理解。
所涉及到的知识点参考:
物理系统
UI系统
监听和发射事件
动作列表
预制体Prefab
图集资源
ps:本项目所使用的Cocos Creator版本为v2.0.9
开启物理系统
为了尽量达到真实的效果,我们对于桌球的运动均采用物理模拟来实现。在Cocos Creator中物理系统是默认关闭的,我们可以通过下面的代码来开启物理系统
let physicsManager = cc.director.getPhysicsManager();
physicsManager.enabled = true;
同时我们也可以通过设置debugDrawFlags来开启一些调试信息的显示,方便我们开发预览
cc.director.getPhysicsManager().debugDrawFlags = cc.PhysicsManager.DrawBits.e_aabbBit |cc.PhysicsManager.DrawBits.e_pairBit |cc.PhysicsManager.DrawBits.e_centerOfMassBit |cc.PhysicsManager.DrawBits.e_jointBit |cc.PhysicsManager.DrawBits.e_shapeBit;
这里我建议将物理系统的开启以及各个属性的设置统一开发为一个组件,这样可以方便我们在属性面板中管理物理系统的属性以及各个调试开关,同时这样的一个物理系统组件比较独立,在日后其他需要使用到物理系统的场景或者是其他项目中,都可以很方便的进行复用。
创建一个叫做"PhysicsManager.js"的脚本,代码如下:
/*** 物理引擎管理组件*/cc.Class({extends: cc.Component,properties: {active: {default: true,tooltip: '是否启用物理引擎',},aabb:{default: true,tooltip: '是否显示包围盒',},pair: {default: true,},centerOfMass: {default: true,tooltip: '是否显示中心点'},joint: {default: true,tooltip: '是否显示关节连接线'},shape: {default: true,tooltip: '是否填充形状'},gravity: {default: cc.v2(0,-960),tooltip: '重力'}},onEnable() {//开启或关闭物理系统let physicsManager = cc.director.getPhysicsManager();if (physicsManager.enabled && this.active) {cc.warn('The physical system is enabled!');}physicsManager.enabled = this.active;if (!this.active) {return;}//设置物理系统的重力属性physicsManager.gravity = this.gravity;//设置调试标志let drawBits = cc.PhysicsManager.DrawBits;if (CC_PREVIEW) {physicsManager.debugDrawFlags =(this.aabb && drawBits.e_aabbBit) |(this.pair && drawBits.e_pairBit) |(this.centerOfMass && drawBits.e_centerOfMassBit) |(this.joint && drawBits.e_jointBit) |(this.shape && drawBits.e_shapeBit);} else {physicsManager.debugDrawFlags = 0;}},onDisable() {let physicsManager = cc.director.getPhysicsManager();physicsManager.debugDrawFlags = 0;physicsManager.enabled = false;}
});
编写完脚本后,我们将这个组件挂载到Canvas节点下。在属性面板中我们可以看到:
可视化的编辑,非常的方便。勾上Active开启物理系统,这里调试开关我们开启Shape即可,由于我们这个桌游游戏demo是一个俯视的视角,因此Gravity重力我们设置为(0,0),让小球不会受到重力的影响向下坠去。
桌面节点
在Canvas下面创建一个子节点作为我们的桌面节点。
给它添加一个cc.Sprite组件并且给予它显示的spriteFrame
添加RighdBody组件
由于桌面是静止不动的,因此我们将Type设置为Static
去掉AwakeOnLoad,因为我们并不需要它处于唤醒状态
添加四个边的碰撞组件
注意不是使用碰撞组件,而是物理组件中的Collider组件
勾选Editing选项可在场景编辑器中改变碰撞体的大小和位置
添加六个袋口的碰撞组件
这里用的碰撞体为CircleCollider
袋口碰撞体的tag我们设置为1
这里是为了在碰撞回调中与四周的边区分开来,便于判断
最终的节点碰撞体应调整为这样的效果:
小球
搞定完桌面我们来制作小球。同样,小球也是需要使用到物理系统的
红球
摆放好后我们可以将这10个小球节点都放到一个空节点下,这样我们就可以将这个10个小球做成一个prefab。方便我们动态生成。
创建一个带Sprite组件的节点,给予红球的图片显示
添加一个球形碰撞组件(CircleCollider)并调整大小
勾选Bullet属性,由于小球是有可能进行高速移动的,因此勾选上这个属性可以禁止它穿过其他同样正在进行高速移动的其他物体(其他的的小球)
由于小球是需要移动的,并非静止不动,因此刚体的Type选择为Dynamic
AllowSleep一定要勾选上
Linear Damping设置为1.2,Angular Damping设置为0.8,通过这两个值我们可以让小球在没有其他外力或者碰撞时从运动中慢慢的停止下来
ok,到这里我们的红球就做好了。我们可以将红球节点在场景中复制10个出来,摆放为金字塔的形状,注意摆放时红球与红球之间不要发生碰撞,否则游戏一开始红球就会散开。
白球与红球基本是一样的,只需要将spriteframe更换为白球的纹理即可。
球杆
在白球节点下创建一个子节点“Cue”,“Cue”节点为一个空节点。
在这个空节点下再创建一个子节点,并添加Sprite组件,使用球杆的问题。调整节点的x坐标,使得球杆与白球的位置如下
这样设计节点的层级关系是为了方便进行球杆的角度计算,当然会有其他不同的做法,这里只是其中的一种。
同样的,我们将白球和球杆做成一个预制体,方便我们后面的动态创建
做到这里整个节点树应该是如下的一个结构
接下来我们就要在这个基础上开发我们的游戏逻辑的脚本组件了
球杆脚本Cue.js
球杆脚本我们挂到“Cue”节点下,它的功能我们主要需要实现这几个:
监听鼠标事件,通过鼠标的移动控制球杆的方向
监听鼠标左键的按下以及抬起事件,通过按下的时间控制球杆的力度
鼠标左键按下时,球杆做向后的移动,松开鼠标左键,球杆做向前击打白球的动作
球杆击打时,向白球发送自定义事件,使得白球进行运动
接受白球发送过来的自定义事件,使得白球运动过程中,球杆不能操作并隐藏显示
明确了需求,并对需求进行功能拆分后,我们可以开始做手编码了。
首先要做的就是在start方法中对上面提到的一些事件进行监听的注册
start () {//鼠标移动系统事件cc.Canvas.instance.node.on(cc.Node.EventType.MOUSE_MOVE, this.onMouseMove, this);//鼠标左键按下系统事件cc.Canvas.instance.node.on(cc.Node.EventType.MOUSE_DOWN, this.onMouseDown, this);//鼠标左键抬起系统事件cc.Canvas.instance.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp, this);//白球停止的自定义事件cc.Canvas.instance.node.on("wball-sleep", this.onwballSleep, this);},
注册完事件后,我们定一个cc.Node类型的属性,将cue的子节点,也就是显示球杆纹理的那个节点引用进来
properties: {//显示球杆纹理的节点cue : cc.Node},
接下来我们挨个实现这些事件的回调。
鼠标移动的事件回调
onMouseMove (event) {//按下鼠标时,球杆方向不再移动。球杆隐藏时操作无效if (this._mouseDown || this.node.opacity != 255) {return;}//获取鼠标的当前位置坐标var loc = event.getLocation();this._mousePosition = loc;//将坐标转换到父节点的坐标系下loc = this.node.parent.convertToNodeSpaceAR(loc);//计算与(-1,0)向量的夹脚,改夹脚即为球杆需要转动的角度var angle = loc.signAngle(cc.v2(-1,0));angle = cc.misc.radiansToDegrees(angle);//设置球杆的角度this.node.rotation = angle;},
通过这段代码,我们即可让球杆跟随鼠标进行转动,从而控制击球的方向
鼠标左键按下的事件回调
onMouseDown (event) {//球杆隐藏时操作无效if (this.node.opacity != 255) {return;}//将按下鼠标的标记设置为truethis._mouseDown = true;//使球杆向后移动,每秒向后移动50个像素//这里可以将-50这个值提升为组件属性,暴露到属性面板中方便配置调试this.cue.runAction(cc.repeatForever(cc.moveBy(1, cc.v2(-50, 0))));},
这里的速度属性值可以提取出来,在属性面板中进行配置。这样做会好一些,我这里完全是偷懒写死了
鼠标左键抬起的事件回调
onMouseUp (event) {//球杆隐藏时操作无效if (this.node.opacity != 255) {return;}//计算球杆向后移动的像素,通过这个值来计算击球的力度var force = this.cue.x - 182;//停止球杆向后移动的动作this.cue.stopAllActions();//使用序列动作,先执行this.cue.runAction(cc.sequence(cc.moveTo(0.1, cc.v2(-182,0)).easing(cc.easeSineOut()),cc.callFunc(() => {//将按下鼠标的标记设置为falsethis._mouseDown = false;//创建自定义事件"cue",并派发出去//事件有两个参数,一个是force,通过这个值,白球可以计算击球力度//另一个值为cue,是一个cc.vec2坐标,记录按下时的鼠标位置,这是提供给白球进行角度计算的var customEvent = new cc.Event.EventCustom("cue", true);customEvent.force = force;customEvent.cue = this._mousePositionthis.node.dispatchEvent(customEvent);//隐藏球杆this.node.opacity = 0;})));},
这里force计算中的- 182
也是可以提取为参数进行配置的,这里再次偷懒写死
白球停止的自定义事件回调
onwballSleep () {//白球停止时,显示球杆this.node.opacity = 255;},
ok,这样子我们就完成了球杆Cue.js脚本的代码编写
白球脚本 wball.js
白球需要做的事情就相对比较简单了:
监听击球事件“cue”,通过击球力度和坐标计算出白球被击打后的线速度
当白球停止运动时,也就是刚体的awake状态为false时,发送事件,告知球杆节点显示并可以击球了
因为要监听击球事件,因此首先是在start中对事件进行注册
start () {//监听击球事件“cue”this.node.on("cue", this.onCue, this);//白球是否停止的标记,主要是用于使停止事件只发送一次//白球的初始状态是停止的,因此设置为truethis._sleep = true;},
接下来是击球事件的回调实现
onCue (event) {if (this && this.node.parent) {//白球没有停止,因此_sleep为falsethis._sleep = false;//计算白球运动的方向向量var direction = this.node.parent.convertToNodeSpaceAR(event.cue);direction = direction.sub(this.node.position);direction = direction.normalize();//根据方向和力度,计算并给予白球线速度this.node.getComponent(cc.RigidBody).linearVelocity = direction.mul(-Math.pow(1.016, Math.abs(event.force)));}},
最后是白球停止时的事件派发
update (dt) {if (!this.node.getComponent(cc.RigidBody).awake && !this._sleep) {//白球从运动到停止,状态切换时,标记设置为true,并发送白球停止的事件this._sleep = true;cc.Canvas.instance.node.emit("wball-sleep");}},
ok,搞定。基本上到这里,你已经可以控制白球去撞击其他的红球了。
这里有一个小技巧,可以看到白球停止的事件在球杆中我们是注册到了Canvas节点上。由于Cocos Creator目前的事件传递的机制是冒泡传递,因此会有一些情况下我们的事件不太好进行派发。而这样通过Canvas节点,我们就可以实现在场景中很方便的进行事件的监听处理以及派发。
需要注意的是,通过Canvas节点的话,意味着如果有场景切换的话,事件监听就会被注销掉,因此如果这个事件监听需要跨场景存在的话,可以自定义一个全局的cc.EventTarget对象用于事件的监听以及派发
球桌脚本 table.js
最后的脚本是我们的球桌脚本,这个脚本主要负责几个事情:
球入袋的逻辑,红球入袋的记分以及胜负判定;白球入袋的重置白球位置逻辑
开始游戏或是重新开始的游戏逻辑:重置记分以及重置白球红球的数量位置
最主要的代码是实现入袋的逻辑,这一部分需要我们实现onBeginContact方法 onBeginContact在组件所属节点的碰撞体发生碰撞时会进行调用 这个方法有三个参数 contact, selfCollider, otherCollider 第一个参数contact,是碰撞的信息 第二个参数selfCollider,是当前组件所属节点被碰撞到的Collider组件 第三个参数otherCollider,是非this节点,也就是发生碰撞另一个节点的Collider组件 代码如下:
onBeginContact(contact, selfCollider, otherCollider) {//如果Collider的组件tag为1时,也就是小球碰撞到代表袋口的碰撞体时if (selfCollider.tag === 1) {//如果是与白球发生碰撞if (otherCollider.node.name === "wball") {//发送通知重新生成白球,重置位置this.node.emit("wball", otherCollider.node);}else {//红球数减1this._ballNum--;this.ballLabel.string = "剩余" + this._ballNum + "球";//红球数小于等于0时,获得胜利if (this._ballNum <= 0) {this.gameUI.active = false;this.winUI.active = true;}}//将小球节点从场景树上移除otherCollider.node.removeFromParent(true);}},
至于重新生成白球和红球的逻辑就更简单了,这里就不详细说了,届时你会利用上之前我们所做的白球,红球的prefab
至此,我们的桌球demo基本上算是完成了核心的部分了,剩下的就是一些UI上的逻辑了。比较简单就不一一叙述了。
希望通过这个小游戏的制作,可以帮助大家了解Creator以及了解Creator物理系统的应用
小游戏的完整代码以及工程,请在公众号回复【桌球】获取!
CreatorPrimer 30篇教程汇总
我的B站视频开通啦!
从“新手引导”到“自动化测试”
KUOKUO的趣味教程 | 进击的小怪诞生(1)
KUOKUO的趣味教程 | 小怪物的视野(2)
KUOKUO的趣味教程 | 小怪物也思考(3)
大神驾到 |「大掌教」Cocos3D组件详解
CreatorPrimer|组件编码心得(上)
CreatorPrimer|组件编码心得(中)
CreatorPrimer|组件编码心得(下)
Cocos Creator入门实战:桌球小游戏相关推荐
- Cocos Creator 入门篇-描摹小游戏(描摹数字,描摹英语字母)
cocos creator写游戏还是很友好的,官方文档给的也挺全.今天带大家来用cocoscreator实现描摹数字/描摹英文字母/描摹直线曲线的功能. 先看下效果图 实现思路 这里我们已描摹数字2为 ...
- Cocos Creator 入门篇-描摹小游戏(cocos描摹数字,描摹英语字母,描摹图形)
cocos creator写游戏还是很友好的,官方文档给的也挺全.今天带大家来用cocoscreator实现描摹数字/描摹英文字母/描摹直线曲线的功能. 实现思路 这里我们已描摹数字2为例,来讲下具体 ...
- Cocos Creator入门实战:桌球小游戏
Cocos Creator入门实战:桌球小游戏 转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/90035153 本篇主要是希望能够通过C ...
- Cocos Creator 开发实战——篮球物理游戏
Cocos Creator 开发实战--篮球物理游戏 知识点 教程 基础背景搭建 物理系统 篮网制作 篮球 结语 Cocos Creator 开发实战--篮球物理游戏 本篇文章教大家实现篮球物理游戏最 ...
- cocos creator 3.7微信小游戏开发云函数和云托管部署
cocos creator开发跨平台游戏能力不错,可以一次开发适用于多个平台,如andriod,ios原生,网页,各类小程序等等.就实际情况而言,能够跨1-2个平台已经不容易了,这是因为在跨平台开发中 ...
- Cocos Creator 3D发布QQ小游戏并打APK包
Cocos Creator 3D版本 : 1.2.0 Visual Studio版本:2017,typeScripts. 1. 构建QQ小游戏 1. 在菜单栏的项目里打开构建发布 因为QQ跟微信的平台 ...
- 基于Cocos creator 实现坦克大战小游戏【100010131】
疯狂坦克 引言 坦克大战是一款曾经风靡一时的小游戏,本次的实训,就是要力图让经典历久弥新.因此在老师的帮助下,我们将尽力开发一款在原有基础上有突破的"疯狂坦克2",加入新的元素,融 ...
- Cocos Creator 发布到微信小游戏的资源管理
环境:Cocos Creator v1.9.1: 微信web开发者工具 v1.02.0808300,线上基础调试库 1.9.92. 以下,ccc指cocos creator.资源均指Texture.A ...
- cocos creator JS 实现微信小游戏体力倒计时恢复的代码
写微信小游戏时写的,功能可能还有待改进.目前这里只是在逻辑层获取了当前的系统时间,所以离开页面倒计时会停止(所以想要完成场景跳转之后仍在继续倒计时的功能,必须在逻辑层取回离开某个场景时的时间来做条件运 ...
最新文章
- 数据结构与算法(C++)– 堆排(Heap Sort)
- java arraylist的初始化和数组的相互转化
- PAT1001 A+B Format (20 分)
- [转载]最完整PHP.INI中文版
- android意图传递参数返回结果(六)
- 服务器如何修复dll,Windows10系统修复KernelBase.dll错误的解决方法
- 有人说赚钱靠项目,也有人说赚钱靠技术
- 真正智能的语音识别系统离我们还有多远
- mysql - binlog主从复制
- 【TSP】基于matlab自重启伪遗传改良算法求解旅行商问题【含Matlab源码 1510期】
- miui tv android,MIUI for TV 3.0上线 应用远程安装 手机反控电视
- 【Get深一度】信号处理(三)——3db带宽
- Win10账户已被锁定解决方法
- 4月书讯 | 一大波好书来袭,最美华章四月天
- ktt算法 约化_深度学习面试题
- python在每个字符后加上逗号_Python将逗号添加到数字字符串中
- npm install xxx 出现Unexpected token < in
- 一个高颜值宝藏开源软件,跨平台终端神器 Tabby
- 广告商、影视剧和晚会用的流行歌曲,版权都是怎么买的?
- Python + Selenium + Chrome Driver 自动化点击+评论+刷弹幕(仅供学习)
热门文章
- idea引入maven依赖飘红解决方法
- 2020年中国电影市场票房全球最高;辛芷蕾成为瑞士莲亚太区品牌代言人 | 美通企业日报...
- 解密m3u8/ts视频流
- android手机性能,手机性能排行榜2018前十名(安卓)
- buuctf-pwn write-ups (6)
- 窥探React - 源码分析
- Ubuntu18.0.4仿Mac界面
- 如何灵活使用ASCII编码
- Exception in thread main java.lang.NoSuchMethodError: scala.Predef$.$conforms()Lscala/Predef$$less
- 限行的处罚标准是什么