设置默认tts引擎_Phaser3物理引擎的基本用法 (一)
Phaser3中提供了两种物理引擎,分别为Arcade与Matter。它们在Phaser中的命名空间分别为Phaser.Physics.Arcade与Phaser.Physics.Matter。Arcade相对简单,物体的形状只支持矩形与圆形两种;Matter则要强大很多,能模拟更多的物理效果。我们还是先通过Phaser提供的工程模板来创建工程# 克隆模板工程git clone --de...

Phaser3中提供了两种物理引擎,分别为Arcade与Matter。它们在Phaser中的命名空间分别为Phaser.Physics.Arcade与Phaser.Physics.Matter。Arcade相对简单,物体的形状只支持矩形与圆形两种;Matter则要强大很多,能模拟更多的物理效果。
我们还是先通过Phaser提供的工程模板来创建工程
# 克隆模板工程git clone --depth=1 git@github.com:photonstorm/phaser3-project-template.git study-phaser-physics# 进入文件夹cd study-phaser-physics/# 删除原有git数据rm -rf .git# 安装依赖yarn# 启动工程yarn start
我们分两个场景来分别研究Arcade与Matter。方便起见我们把场景以类的形式来写,在src下新建scenes文件夹,然后在该文件夹下分别创建arcade.js与matter.js。
其中arcade.js的初始代码如下
import { Scene } from 'phaser'export class StudyArcade extends Scene { constructor() { super({ key: 'StudyArcade', // 物理引擎设置 physics: { default: 'arcade', arcade: { // 重力 gravity: { y: 10 }, } } }) } /** * 加载资源 */ preload() {} /** * 创建内容 */ create() { console.log('I am Arcade') } /** * 更新内容 */ update() {}}export default StudyArcade
matter.js的初始内容与arcade.js除了配置物理引擎的地方外都一样
import { Scene } from 'phaser'export class StudyMatter extends Scene { constructor() { super({ key: 'StudyMatter', physics: { default: 'matter', matter: { gravity: { y: 10 }, } } }) } /** * 加载资源 */ preload() {} /** * 创建内容 */ create() { console.log('I am Matter') } /** * 更新内容 */ update() {}}export default StudyMatter
然后修改默认的src/index.js文件内容如下
import Phaser from "phaser";import StudyArcade from "./scenes/arcade";import StudyMatter from "./scenes/matter";const config = { type: Phaser.AUTO, parent: "phaser-example", width: 800, height: 600, // 配置场景,第一个为默认场景 scene: [StudyArcade, StudyMatter],};// 创建游戏实例const game = new Phaser.Game(config);
刷新页面后,在控制台可以看到StudyArcade场景的日志信息

说明场景加载成功了。下面先来看看Arcade的用法。
Arcade
先在OpenGameArt上找几个图片用于测试,图片如下

资源加载很简单,直接用this.load即可
// arcade.js // ... preload() { this.load.image('ball', require('./assets/ball.png')) this.load.image('rock', require('./assets/rock.png')) this.load.image('ship', require('./assets/ball.png')) } // ... 世界
首先我们需要理解物理引擎中的“世界”,可以把它理解成是一个盒子,大小默认与游戏画布相同。也就是说物理引擎的世界是有范围的,所有的模拟都是在世界范围内,世界以外的地方不会考虑。
场景会自动创建物理引擎实例,使用this.physics即可使用arcade引擎实例。如果要修改世界的相关属性,可以直接使用this.physics.world来操作。如
// 修改世界的边界this.physics.world.setBounds(x, y, width, height)
Body
body是用于描述物理系统中物体的对象,比如速度、质量、重力、弹力等都是body的属性。一个普通的游戏对象是没有body的,但可以通过给普通游戏对象添加body使之拥有物理属性。
body分为静态与动态两种。静态body(Arcade.STATIC_BODY)用于模拟如大地或建筑物这种不会移动的物体;动态body(Arcade.DYNAMIC_BODY)用于模拟会因速度与加速度(受力)而移动的物体。
一个物理对象可以直接通过body来操作相关属性,如
// rock是一个物理对象,修改其body的角速度rock.body.setAngularVelocity(100)
添加物理对象
通常在场景中添加普通的游戏对象是这样的
// 添加一个图片对象const ball = this.add.image(400, 100, 'ball')
此时这个ball并没有物理body对象,即其不会参与到Arcade的物理系统中,如果想让它加入到物理系统中,需要这样
// 为ball创建body对象,即加入到了物理系统中this.physics.world.enable(ball)
此时ball上的body会自动将ball的尺寸与位置等信息进行同步。如果只是使用image生成物理对象的话,这样略显繁琐,实际上可以直接像下面这样添加一个物理对象
// arcade.js// ...create() { // 通过物理引擎添加一个图片对象到场景中 const ball = this.physics.add.image(400, 100, 'ball')}
sprite对象也是一样的,代码中的“image”改成“sprite”即可。
this.physics.add与this.add一样都是工厂,提供了相应的创建对象并添加到场景的便捷方法。
重力、与世界边界碰撞
用上述代码添加一个球后效果是这样的

可以看到,下落的非常缓慢,而且直接落到下边界以外了。
下落慢应该就是重力设置的问题,在场景的构造函数中调整physics设置的gravity值,改到600后感觉相对舒服些

掉出边界是因为没有设置与世界边界碰撞,添加如下设置
// 设置球与边界发生碰撞ball.setCollideWorldBounds(true)
效果如下

上面的球落到下边界后静止不动了。我们希望球落下后可以反弹,可以通过设置弹跳来实现
// 设置弹跳系数,值越大,反弹越强ball.setBounce(0.9)
效果如下

可以正常弹跳了。现在我们希望球最初可以做抛物线运动,而不是自由落体,可以为其设置水平初速度来实现
// 设置水平速度ball.setVelocityX(200)
效果如下

但是当球弹跳结束后,出现了在地面平移现象

没有滚动,也没有正常停下,这很可能是因为没有设置摩擦力的原因。球可以设置摩擦力,但物理世界的边界是没有摩擦力的,这个稍后再解决。
碰撞
下面我们添加个大石块看看效果
const rock = this.physics.add.image(100, 100, 'rock')// 石块的反弹设置低一点rock.setBounce(0.1)// 当然也设置为与世界外界发生碰撞rock.setCollideWorldBounds(true)

石块稳稳地落在下面,但到后面我们发现,球与石块并没有正常碰撞。这是因为默认情况下,物体之间并不碰撞。这好理解,因为碰撞检测是会消耗性能的,所以需要明确设置哪些物体之间需要发生碰撞,如下
// 添加碰撞检测对象this.physics.add.collider(rock, ball)
这样就可以让石块与球发生碰撞了

有时当碰撞发生时我们需要做些事情,如增加分数或者game over,可在创建碰撞对象时传入一个回调函数来实现,如
// 当碰撞发生时执行doSomething方法this.physics.add.collider(rock, ball, doSomething)
有时我们希望当两个物体接触或者说重叠时做些事情,但不希望它们有碰撞效果,可以这样
// 只检测碰撞是否发生,但不模拟碰撞效果this.physics.add.overlap(rock, ball, doSomething)
debug模式
我们给石块与球都设置一下摩擦力,看看会怎么样
// 设置摩擦力系数rock.setFriction(0.6)// ...// 设置摩擦系数ball.setFriction(0.9)
很奇怪,与没设置时的效果很接近,球也依然没有滚动。
原来是我忽略了一点,默认情况下物理对象是矩形的,所以这里的球实际上一直是矩形的状态。把debug状态打开看看
// 构造函数的physics配置physics: { default: 'arcade', arcade: { gravity: { y: 600 }, // 开启debug模式 debug: true, }}
效果如下

紫色的框表示物体的边界,绿线表示物体速度的方向及大小。可见球是一个矩形的,自然很难滚动起来。
我们把球设置为圆形
// 设置为圆形ball.body.isCircle = true
Arcade太初级了
然后……依然没有滚动!哪怕我们想让石块倾斜也无法做到,如下图

它就这样不动了。
好吧,实际上是因为Arcade太过简单,它对物体受力的模拟是没有力矩的。也就是说,当一个物体受力时,对力的计算只分x轴方向与y轴方向以及大小,对于受力点是不关心的。
如下图,矩形左右两边分别受到拉力F1与F2(灰色),正常应该会旋转,因为受力点不在同一个位置

而Arcade在模拟时会简化,相当于两个力都作用在矩形中心点(红色),所以它不会旋转。水平方向也是同样的道理。
这样的确比较简陋,但优点是速度快,我们应当根据具体情况来选择是否使用它。
组Group
前面有提到,如果希望物体有碰撞需要明确声明。只有两三个物体时还好办,当物体比较多时似乎就比较麻烦了,而且物体经常是在游戏过程中动态生成的,那样岂不是更麻烦。
这时就需要用到组的功能了。我们可以简单的把组理解成标签,一个物体可以在多个组(有多个标签),一个组自然也可以有多个物体。
组有两种,一个是Phaser的游戏对象组Phaser.GameObjects.Group,我们姑且称之为“对象组”;一个是Arcade的组Phaser.Physics.Arcade.Group,我们姑且称之为“物理组”。
对象组不关心其成员是否为物理对象,就是纯粹的分组功能;物理组除了分组外,还能提供一些使游戏对象物理化的便捷功能,即添加到物理组的对象如果没有物理body则会被自动添加上body。
// 创建一个物理组const shipGroup = this.physics.add.group()// 创建一个物理对象并添加到shipGroupshipGroup.create(400, 100, 'ship')
静态物理组
与普通物理组一样,只是由静态物理组创建的物理对象也都静态的,可以用它方便的创建与管理静态对象。
// 创建一个静态物理组const platforms = this.physics.add.staticGroup()// 通过静态物理组直接创建一个静态物理对象platforms.create(600, 200, 'rock')// 让球与静态物理组中的对象可以碰撞this.physics.add.collider(ball, platforms)

// 创建一个静态物理组,并自动生成10个“砖块”const platforms = this.physics.add.staticGroup({ // 使用石块图片 key: 'rock', // 一共生成10个(第一次创建后再重复创建9次) repeat: 9, // 设置缩放 setScale: { x: 0.1, y: 0.1, }, // 设置位置 setXY: { // 首次创建对象时的坐标 x: 500, y: 300, // 每次重复生成时 x坐标的增量 stepX:25, }})// 由于静态物体body不会自动同步对象的大小与位置等信息,所以需要手动刷新一下platforms.children.iterate(child => child.refreshBody())// 让球与静态物理组中的对象可以碰撞this.physics.add.collider(ball, platforms)这里需要注意child.refreshBody()方法。由于静态body不会自动同步游戏对象的尺寸与位置信息,所以需要手动同步;对于动态body则无需手动调用该方法。

好了,今天先到这,下次继续研究Matter。附
OpenGameArt地址:
https://opengameart.org/
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)