转载知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具
项目地址:赛车小游戏-基于Cocos Creator 3.5版本实现: 课程的源码,基于Cocos Creator 3.5版本实现
上一张您已经对Cocos的坐标系有了了解。这一章我们将让小车能够自动的向您触控的点运动过去。所以为了达到这个目的,我们要接收触摸屏幕事件,再对小车的刚体组件施加一个由小车的坐标点指向触摸点的力。具体步骤如下:
一、实现一个触控类:
1.请在script文件夹下新建一个名为TouchInput的脚本文件,我们将会在这里完成对触控事件的接收。新建脚本后效果如下:
2.打开并初步编辑脚本文件
双击这个脚本文件,VS Code将会为您打开并编辑这个脚本文件,因为这个脚本文件只负责处理触控事件,不会挂载到某个节点上,所以它无需具备组件的特性,所以我们可以删掉他的“extend Componment”属性。而您要实现的游戏是个单人游戏,也就是说触控接收器只为由玩家操控的汽车节点提供触控事件处理服务,不会为别的节点例如电脑提供的NPC车辆提供服务,所以以单例模式运行即可。综上,TouchInput的初步代码为:
3.接收触摸事件
cocos中采用input常量中的on方法来注册事件,on方法一共有两个参数:第一个参数是要注册的事件类型,事件类型的枚举值都Input.EventType下;第二个参数是一个方法,当接收到对应事件后这个方法将会执行,这个方法的入参是框架已经为您封装好的事件的具体值,例如点击的是哪个坐标点等。现在您要实现的是点击屏幕来控制小车的运动,所以在控制器里首先您需要注册开始点击、结束点击、点击时手指移动以及点击时手指移动到触控区域外这四个函数,不过在处理时,我们可以把点击时手指移动到触控区域外也视为结束点击的逻辑。其次我们还需要记录当前是否玩家处于点击状态以及当前的触控点的坐标。具体代码如下:
import { _decorator, Component, Node, input, Input, Vec2 } from 'cc'; const { ccclass, property } = _decorator; @ccclass('TouchInput') export class TouchInput { private static singleiInstance: TouchInput = new TouchInput(); touchX: number = 0; touchY: number = 0; inTouch: boolean = false static Instance() { return TouchInput.singleiInstance; }; private constructor() { input.on(Input.EventType.TOUCH_MOVE, (event)=>{ this.inTouch = true var pos = new Vec2(event.getLocation()) this.touchX = pos.x this.touchY = pos.y console.log('移动中 点击的X坐标: '+this.touchX+ ' 点击的Y坐标:'+this.touchY + ' 点击状态:'+this.inTouch) }) input.on(Input.EventType.TOUCH_START,(event)=>{ this.inTouch = true var pos = new Vec2(event.getLocation()) this.touchX = pos.x this.touchY = pos.y console.log('开始点击 点击的X坐标: '+this.touchX+ ' 点击的Y坐标:'+this.touchY+ ' 点击状态:'+this.inTouch) }) input.on(Input.EventType.TOUCH_CANCEL,(event)=>{ this.inTouch = false console.log('点击结束 点击的X坐标: '+this.touchX+ ' 点击的Y坐标:'+this.touchY+ ' 点击状态:'+this.inTouch) }) input.on(Input.EventType.TOUCH_END,(event)=>{ this.inTouch = false console.log('点击结束 点击的X坐标: '+this.touchX+ ' 点击的Y坐标:'+this.touchY+ ' 点击状态:'+this.inTouch) }) } }
我们可以看到,我们在实例化类时自动执行的构造函数中注册了对应的事件,并且将是否处于触控状态以及点击的坐标保存到了TouchInput的成员变量中。
4.测试触摸接收事件:
保存好文件,然后点击运行按钮,在浏览器运行后打开开发者工具,然后在屏幕上点击任意位置,我们将会在控制台中看到对event的打印:
也许你可能会有疑问,为何感觉console.log打印出来的坐标和上一章说的无论是世界坐标系还是本地坐标系都匹配不上?这是因为点击事件返回的点是根据触控坐标系计算出来的,您可能会对又引入了一个坐标系感到很Angry,但是不要紧,只需要使用相机来转化一下就能把触控坐标转化为世界坐标,这个具体过程我们会在后面实现。
5.在控制汽车节点的CarControl脚本中引入并使用TouchInput:
双击CarControl来让VS Code编辑它,我们先清空下update方法中的语句,然后把添加一个TouchInput类型的成员变量,并且在start函数中让这个成员变量指向TouchInput的单例。具体效果如下:
6.分析小车向触控点运动的实现:
我们要实现的效果是小车向玩家点击屏幕的位置移动,所以在代码中的逻辑就是:在每一帧的update的函数中,先获取touchInput的inTouch字段来判断下是否处于触摸状态,如果处于触摸状态,就获取到触摸点,然后将触摸点转换为世界坐标,最后再给小车的刚体组件施加一个由当前小车刚体所在的节点的世界坐标点指向触摸点的世界坐标点的力。
7.引入camera组件:
上一步骤中说到,获取到触摸点后要将触摸点转换为世界坐标才能参与后续计算,camera类中提供了screenToWorld方法来完成了这种转换。所以我们要引入camera。和引入汽车节点的刚体组件时一样,我们先声明一个Camera类型的gameCamera成员变量并且为他添加上给Cocos Creator看的注解。也许你会问我为什么不直接叫camera而是叫gameCamera,这是因为后期要显示UI,所以可能不只有一个摄像头,所以这个就不采用比较笼统的camera的名字:
然后回到Cocos Creator中,指定这个变量指向Game场景下的Canvas下的Camera:
8.实现步骤6的逻辑:
逻辑非常简单,把6的步骤给翻译成代码就是了,直接上CarControl的代码:
import { _decorator, Component, Node, RigidBody2D, Vec3, Vec2, math, Camera } from 'cc'; import { TouchInput } from './TouchInput'; const { ccclass, property } = _decorator; @ccclass('CarControl') export class CarControl extends Component { @property({type: RigidBody2D}) carRigidBody: RigidBody2D touchInput: TouchInput @property({type: Camera}) gameCamera: Camera start() { console.log('------------start-----------------') this.touchInput = TouchInput.Instance() } update(deltaTime: number) { var inTouching: boolean = this.touchInput.inTouch if(!inTouching){ return } var twPos: Vec3 = new Vec3(this.gameCamera.screenToWorld(new Vec3(this.touchInput.touchX,this.touchInput.touchY))) var nwPos: Vec3 = this.carRigidBody.node.getWorldPosition() var force: Vec2 = new Vec2(-nwPos.x+twPos.x,-nwPos.y+twPos.y) this.carRigidBody.applyForceToCenter(force,true) } }
9.在浏览器中运行并查看结果:
VS Code和Cocos Creator中各自保存好,然后点击运行,在浏览器中查看运行。因为目前没有设置小车的阻力,所以如果一直点击的话持续受力会越来越快,如果只点击一下则会做向点击方向的匀速直线运动。但是不管怎么了,只要点击一下发现小车向着您点击的点做匀速直线运动了就表明没问题了,效果:
10、debug
虽然我们已经看到了小车的受力已经没问题了,但是小车的根节点,也就Car节点并没有随着刚体发生移动:
这是为什么呢,因为在前面的章节中我们在挂载刚体时按照常理给挂载到了小车的CarSprite节点上,而现在我们看到,因为刚体受力而发生位移时只对刚体所在的节点及其子节点生效,所以,刚体应该挂载到Car节点上才对。所以现在需要您删除掉CarSprite上的刚体:
然后按照相同方式在Car上面新建一个刚体:
最后记得让Car节点挂载的CarControl节点的carRigidBody变量指向Car的刚体:
11.重新测试
保存好,然后点击运行按钮,在浏览器中观察效果:
我们可以看到,Car节点不再一直停在起始位置了。