Phaser游戏制作

Game(游戏对象)

  • 所属类:Phaser.Game
  • this.xx.game === this

属性汇总

  • config:全局配置对象;Phaser.Core.Config
  • loop:主循环控制器;Phaser.Core.TimeStep
  • 各类管理器实例
  • canvas:游戏画布

config

type

  • AUTO:Phaser引擎自主判断使用CANVAS,还是WEBGL
  • CANVAS:性能低,兼容性高
  • WEBGL:性能高、兼容性低
  • HEADLESS:不渲染,只允许逻辑,适用于服务器端运行

scene

  • 有Object、Array<Phaser.Scene>、Phaser.Scene,三种配置类型
  • v3.85.PhaserJS中,默认进入config.scene数组中的第1个场景

other

  • fps:配置帧率
  • width、height:设置了world边界
  • parent:父盒子(HTML元素)的id
  • physics:有arcade(默认)、MatterJS两种引擎

loop

连续循环创建者

  • requestAnimationFrame(默认):帧率等于浏览器刷新帧率
  • setTimeout:需要配置config.fps.forceSetTimeout=true,帧率近似于config.fps.target

切换帧率

this.game.loop.stop()
Phaser.Core.TimeStep.call(this.game.loop,this.game,{
    target:120,
    forceSetTimeOut:true
})
this.game.loop.start(this.game.step.bind(this.game))

注意点

  • 每一帧的时间不一定相同
  •  采用setTimeout控制帧率,帧率与config.fps.target的偏差较大

Scene(场景对象)

  • 所属类:Phaser.Scene
  • this.xx.scene === this
  • this.physics.world.scene === this
  • this.physics.systems === this.sys

      属性汇总

      • add:简单游戏对象的工厂;Phaser.GameObjects.GameObjectFactory
      • sound:声音管理器实例;Phaser.Sound.HTML5AudioSoundManager(只举1例)
      • cameras:相机管理器实例;Phaser.Cameras.Scene2D.CameraManager
      • tweens:简单动画管理器实例;Phaser.Tweens.TweenManager
      • textures:纹理管理器实例;Phaser.Textures.TextureManager
      • anims:动画管理器实例:Phaser.Animations.AnimationManager
      • physics:物理对象实例;Phaser.Physics.Arcade.ArcadePhysics
      • events:事件对象实例;Phaser.Events.EventEmitter
      • input:输入事件对象实例;Phaser.Input.InputPlugin
      • game:游戏对象实例;Phaser.Game
      • load:加载对象实例;Phaser.Loader.LoaderPlugin
      • time:计时器对象实例;Phaser.Time.Clock
      • sys:系统对象实例;Phaser.Scenes.Systems
      • scene:场景插件对象实例;Phaser.Scenes.ScenePlugin

      sys

      • xx.displayList:管理场景中所有需要渲染的对象
      • xx.updateList:管理场景中所有需要更新的对象

      scene

      • xx.stop:结束当前场景
      • xx.start(sceneKey, initValue):结束当前场景,并开启新场景
      • xx.run:不结束当前场景,并开启新场景

      physics

      xx.add

      • 创建复杂游戏对象的工厂
      • 支持通过继承的方式定制化对象,并注册到工厂中

      xx.world.enableBody(gameObject)

      • 将对象添加到物理世界中
      • 为对象添加body属性

      xx.world.on("worldbounds",(body, 上, 下, 左, 右)=>{})

      • 上、下、左、右四个参数的类型是布尔值

      textures

      创建纹理Texture

      //方式1
      this.load.image(纹理的key,图片路径)
      
      //方式2
      const myGraphics=this.add.graphics()
      myGraphics.fillStyle(0xffffff)
      myGraphics.fillCircle(中心点水平坐标,中心点垂直坐标,班级)
      
      //从左上角顶点开始裁剪
      myGraphics.generateTexture(纹理的key,裁剪宽度,裁剪高度)
      
      //销毁myGraphics对象,销毁绘制的canvas,不会销毁已生成的纹理
      myGraphics.destroy()                                  

      input

      • keyboard:键盘对象
      • mouse:鼠标对象
      • on:监听鼠标事件、……
      • keyboard.on:监听键盘事件、……

      cameras

      功能

      1. 场景宽度大于游戏屏幕宽度(world的宽度)时
      2. 相机位置要随角色位置变化,以确保玩家视觉体验

      实现相机跟随角色

      • 自定义水平跟随:this.cameras.main.scrollX=this.player.x-this.game.config.width/2
      • 内置API跟随:this.cameras.main.startFollow(this.player)

      固定定位:[GameObject].setScrollFactor(0)

      anims

      1. 结合this.load.spritesheet/Atlas使用
      2. 通过图片的快速切换
      3. 实现角色的连贯动作

      load

      加载图片、JSON文件

      生命周期

      1. init(data):接受初始化参数
      2. preload:加载图片资源、加载音乐资源,不同场景preload加载的资源,是可以共享的
      3. create:注册事件回调,绘制游戏页面
      4. update(time,detla):每一帧开始时,会进行一次调用
      5. pause:主动设置暂停后
      6. resume:pause暂停中恢复前
      7. wake:后台切换的暂停中恢复前,如浏览器标签页切换
      8. shutdown:类实例销毁前
      9. time:游戏开始到当前帧开始的时间差
      10. detla:上一帧开始到当前帧开始的时间差

      其他常用对象

      GameObjects

      游戏对象的默认origin一般有两种(0,0),(0.5,0.5)

      静态对象

      • Graphics:图形,用法同canvas的ctx对象
      • Rectangle:矩形
      • Polygon:多边形
      • ellipse:椭圆
      • Text:文本
      • Image:图片

      Sprite(普通精灵)

      • 创建精灵方式:new创建、工厂创建
      • mySprite.destory():销毁精灵
      • mySprite.setAngle(90):顺时针旋转
      • mySprite.setScale():缩放
      • 生命周期方法:constructor、preUpdate

      ArcadeSprite

      • body属性赋值
      • 拥有速度、加速度、弹性、重力、碰撞检测
      • mySprite.setVelocity(水平速度,垂直速度):添加速度
      • mySprite.setCollideWorldBounds(true):限制在world的范围内
      • mySprite.setBounce(n):设置弹跳系数(0~1)
      • mySprite.disableBody():暂时禁用
      • mySprite.enableBoby():重新开启
      • mySprite.body.allowGravity = false:自身不受引力
      • mySprite.body.onWorldBounds=true:碰撞到world边界时,能触发对应事件

      Other

      • 动态组:group.remove(成员child,场景this,销毁精灵?bollean)
      • 静态组:staticGroup

      Physics(物理对象)

      • 控制游戏世界的物理规则
      • 有Arcade、Matterjs两大引擎

      Display(显示对象)

      控制游戏对象布局、透明度、显示/隐藏、颜色、上下层级

      Events(事件对象)

      • 定时事件:update事件、定时器事件
      • 交互事件:键盘输入、鼠标点击/滚动、触屏版滑动、手柄控制
      • 碰撞事件:静态对象和动态对象都能注册碰撞事件
      • 重合事件:静态对象和动态对象都能注册重合事件
      • 通信事件:on注册、emit触发,用于数据通信

      碰撞检测

      • 物理引擎依赖速度和加速度,来计算精灵的运动路径和碰撞检测
      • 直接修改物理坐标,不会触发物理引擎的碰撞检测,精灵会直接穿过物体

      两种键盘按键监听方式

      create(){
          this.input.keyboard?.on('keydown', ({ key }: { key: string }) => {
              console.log('keydown',key)
          })
      }
      
      update(time: number, delta: number): void {
          const keyW=this.input.keyboard?.addKey('w')
          console.log('update-balls',keyW?.isDown,keyW?.isUp)
      }
      

      一直按下 "w" 键时:

      • 会不断触发keydown事件
      • update回调中的keyW?.isDown,一直保持为true
      • 但是keydown事件触发的频率,不会和update的调用频率保持一致
      • 并且keydown事件触发的频率,通常会小于update的调用频率

      Math(计算工具类)

      • Angle.WrapDegrees(x)<=>x%360
      • DegToRad:角度转弧度

      Actions(动作序列)

      控制一组游戏对象的排列位置

      Geom(几何图形)

      • 创建几何图形描述对象
      • 结合GameObjects.Graphics,才能把图形绘制到页面上

      Atlas(图集)

      1. TexturePacker把一组图片合并成一张
      2. 合成的图片叫图集
      3. 还会额外生成一个JSON配置文件
      4. 配置文件记录着图集中每个图块的位置
      5. 配置文件记录着图集中每个图块的key

      Tilemap(砖块地图)

      使用方式1

      1. 用Tiled把图集切割成一组图块图片
      2. 用Tiled拖动图块图片构建砖块地图
      3. 导出描述砖块地图的JSON构建文件
      4. Phaser中导入构建文件和图集图片
      5. 生成图层并放入场景中

      {
             "compressionlevel": -1,
             "height": 50,
             "infinite": false,
             "layers": [
                    {
                           "data": [……],
                           "height": 50,
                           "id": 1,
                           "name": "layer1",
                           "opacity": 1,
                           "type": "tilelayer",
                           "visible": true,
                           "width": 150,
                           "x": 0,
                           "y": 0
                    }
             ],
             "nextlayerid": 2,
             "nextobjectid": 1,
             "orientation": "orthogonal",
             "renderorder": "right-down",
             "tiledversion": "1.11.0",
             "tileheight": 16,
             "tilesets": [
                    {
                           "columns": 8,
                           "firstgid": 1,
                           "image": "walls.png",
                           "imageheight": 135,
                           "imagewidth": 135,
                           "margin": 0,
                           "name": "wallTileset",
                           "spacing": 0,
                           "tilecount": 64,
                           "tileheight": 16,
                           "tilewidth": 16
                    }
             ],
             "tilewidth": 16,
             "type": "map",
             "version": "1.10",
             "width": 150
      }
      preload() {
          this.load.setPath('assets');
          this.load.image('wallImg','walls.png')
          this.load.tilemapTiledJSON('wallMap','walls.json')
      }
      
      create() {
          const map=this.make.tilemap({key:'wallMap'})
          const tiles=map.addTilesetImage('wallTileset','wallImg')
          map.createLayer(0,tiles,0,0)
      }

      使用方式2 

      1. Phaser导入图集图片
      2. 用Phaser来把图集图片切割成一组图块图片
      3. 每个图块图片都分配从0开始的数字编号
      4. 用数字数组表示图层每块区域对应的图块编号
      5. 生成图层并放入场景中

        Other

        Curves(曲线)、Cameras(相机对象)、Loader(加载对象)、Input(输入对象)、Time(计时器)、Tweens(简单动画)、Animations(精灵动画)、Scenes(场景对象)、Textures(纹理对象)、Sound(声音对象)

        技巧汇总

        遮罩层制作

        constructor(scene: Phaser.Scene, x: number, y: number, texture: string | Phaser.Textures.Texture, frame?: string | number) {
            ……
            this.maskGraphics = this.scene.add.graphics()
            this.maskGraphics.fillStyle(0x1677ff, 0.2)
            this.maskGraphics.moveTo(this.x - this.width / 2, this.y - this.height / 2)
            this.maskGraphics.lineTo(this.x + this.width / 2, this.y - this.height / 2)
            this.maskGraphics.lineTo(this.x + this.width / 2, this.y + this.height / 2)
            this.maskGraphics.lineTo(this.x - this.width / 2, this.y + this.height / 2)
            this.maskGraphics.closePath()
            this.maskGraphics.fill()
        
            this.cooldownMask = this.maskGraphics.createGeometryMask()
            this.setMask(this.cooldownMask)
        }
        
        protected preUpdate(time: number, delta: number): void {
            super.preUpdate(time, delta)
        
            if (this.cooldownTime > 0) {
                this.cooldownTime -= delta
                const progress = Phaser.Math.Clamp(1 - this.cooldownTime / this.cooldownDuration, 0, 1)
        
                this.maskGraphics.clear()
                this.maskGraphics.fillStyle(0x1677ff, 0.2)
                this.maskGraphics.moveTo(this.x - this.width / 2, this.y - this.height / 2)
                this.maskGraphics.lineTo(this.x + this.width / 2, this.y - this.height / 2)
                this.maskGraphics.lineTo(this.x + this.width / 2, this.y + this.height / 2 - this.height * progress)
                this.maskGraphics.lineTo(this.x - this.width / 2, this.y + this.height / 2 - this.height * progress)
                this.maskGraphics.closePath()
                this.maskGraphics.fill()
        
                this.maskGraphics.fillStyle(0x1677ff, 0)
                this.maskGraphics.moveTo(this.x - this.width / 2, this.y + this.height / 2 - this.height * progress)
                this.maskGraphics.lineTo(this.x + this.width / 2, this.y + this.height / 2 - this.height * progress)
                this.maskGraphics.lineTo(this.x + this.width / 2, this.y + this.height / 2)
                this.maskGraphics.lineTo(this.x - this.width / 2, this.y + this.height / 2)
                this.maskGraphics.closePath()
                this.maskGraphics.fill()
            }
        }
        
        startCooldown() {
            this.cooldownTime = this.cooldownDuration
        }

        Tiled

        语言设置

        布局设置

        创建地图

        新增图集

        创建图层

        绘制图层

        保存成JSON格式

        TexturePacker

        创建图集

        保存图集

        同步问题

        帧同步vs状态同步

        帧同步

        1. 服务端接收到所有客户端当前帧的操作后,广播给所有客户端
        2. 客户端依据接收的操作,跑出完整的游戏逻辑,实现游戏状态的更新

        状态同步

        1. 服务端接收到所有客户端当前帧的操作后,跑出完整的游戏逻辑
        2. 服务端把得到的游戏状态,发送给所有客户端
        3. 客户端拿到游戏的状态后,更新自身当前的游戏状态

        帧同步的不足(王者荣耀为例)

        • 断线重连的加载速度很慢,因为要跑完所有帧的操作
        • 观看王者荣耀回放时,无法进行回退

        状态同步的优点

        • 安全性非常高
        • 断线重连非常快
        • 只需创建和同步玩家视野内的角色

        使用帧编号

        1. 服务端生成帧编号:服务端会定期生成帧编号
        2. 服务端发送帧编号给客户端:每当服务端生成新的帧编号时,它会将此帧编号发送给所有连接的客户端。
        3. 客户端接收到帧编号后处理操作:每个客户端在接收到帧编号后,会把这个编号分配给自己接下来的操作。
        4. 客户端将带有帧编号的操作发送给服务端:客户端在此帧编号下进行的操作会被打包,并发送回服务端。
        5. 服务端接收并广播操作:服务端收到客户端的操作后,会将这些操作进行处理并广播给所有客户端

        帧操作的收集和存储

        create(){
            this.input.keyboard?.on('keydown', () => {……})
        }
        update(){
            const keyW=this.input.keyboard?.addKey('w')
            ……
        }
        • 建议收集update方法中的操作
        • 服务端存储所有帧操作
        • 客户端可以缓存100帧操作

        帧操作的发送

        快速反应游戏

        • 客户端发送帧操作时,建议逐帧发送
        • 服务端发送帧操作时,建议逐帧发送
        • 服务端帧编号发出后,用50~100ms收集客户端操作

        慢速反应游戏

        • 客户端发送帧操作时,可以批量发送
        • 服务端发送帧操作时,可以批量发送
        • 服务端帧编号发出后,用200~500ms收集客户端操作

        帧同步重连

        • 游戏渲染方式,切换为HEADLESS,减少性能消耗
        • 方式一:自定义myUpdate函数,批量处理帧操作,加速得出游戏的最新状态
        • 方式二:提高游戏帧率以及游戏对象的速度,加速得出游戏的最新状态

        魔兽技巧

        快捷键

        • F1:第1个购买的长老
        • F2:第2个购买的长老
        • F3:第3个购买的长老
        • Ctrl+1:选中的目标组成编队1
        • Shit+1:把选中的目标加入编队1
        • 单击1:选中编队1
        • 双击1:选中编队1&&把视角切换到编队1
        • Ctrl+目标A || 双击目标A:选中所有目标A
        • Alt+C:把视角切换到队伍栏选中的目标
        • 长按Shit:保持选中的队伍不变,可以通过鼠标 选中 或 取消选中 目标
        • 长按Alt:显示所有角色的血量
        • 退格:视角切到基地,合理分配建筑的位置,方便切回视角后找到
        • 士兵通用:patrol-巡逻;attack-攻击;stop-停止;hold-保持原地
        • 建造者通用:build-建造
        • 长老通用:open-打开
        • 编队1:射手队
        • 编队2:前排队
        • 编队3:飞鸟队 || 弩车队
        • 编队4:基地、兵工厂、祭坛
        • 编队5:兵营1、兵营2、兵营3、兵营4
        • 编队6~8:/
        • 编队9:精灵队
        • 编队0:建筑队,建泉水、兵工厂、大鸟之树、修补弩车

        精灵快捷键

        建筑物

        生命之树:T(tree)

        月亮泉:M(moon)

        保护者:P(protector)

        长者祭坛:A(alter)

        猎手大厅:H(hunter)

        战争古树:R(range-靶场)

        知识古树:L(loremaste-精通古代传说和历史的人,大师)

        风之古树:W(wind)

        熊德roar-咆哮、enhance-提高、form-变形
        丛林守护者

        缠绕的树根(Entangling Roots):快捷键为“E”,从地下召唤出树根缠绕住敌人

        自然之力(Force of Nature):快捷键F,召唤出树人。

        荆棘光环:快捷键R(return);让周围友军能返还伤害

        宁静(Tranquility):快捷键为T。在周围创造治疗之雨

        月之女祭司

        猫头鹰:快捷键C(call);召唤一只猫头鹰

        灼热之箭:快捷键R(reinforce);攻击时带有额外伤害,可以设置为自动施放

        强击光环:快捷键T(toughen)

        群星坠落(the stars fall):快捷键F

        隐形:快捷键I(invisibility)

        恶魔猎手

        法力燃烧(mana Burn) :快捷键B

        献祭(loss of life):快捷键L

        闪避(evasion):快捷键E

        变身(transfigure):快捷键T

        精灵发育阶段

        购买优先级:生命之树>知识古树>守卫之树>长老>小鹿>装备升级

        阶段一

        买7+2+2+1精灵+升级生命之树

        建祭坛+战争古树+4射手

        建3月亮泉+兵工厂

        阶段二

        生命之树升至二级

        生命之树学习技能

        建立生命之树

        建知识古树

        购买长老2、小鹿

        升级兽类装备

        阶段三

        生命之树建立

        兽类装备升至二级

        拥有两位长老

        兵力补齐

        升级生命之树

        建立知识古树+升级熊德技能+购买4熊德

        升级兽类装备

        阶段四

        兵工厂满级

        兵力满员

        购买长老3

        建风之古树+升级法德技能、购买2法德

        建大鸟之树、买3个奇美拉

        兵种分类

        兵工厂分类

        人类:弓箭手、豹子、弩车、小鸟弓箭手

        兽类:小鸟、奇美拉、小鹿、变身的熊德、变身的法德

        位置分类

        前排:豹子、变身的熊德

        后排:弓箭手、小鹿、法德

        空中:小鸟、小鸟弓箭手、奇美拉、变身的法德

        发育流

        1队:1麋鹿1老虎1刀客6小鹿

        2队:3熊德+树人+1山岭巨人

        3队:3奇美拉+3精灵龙

        速通流

        射手队:1麋鹿1老虎10弓箭手

        前排队:10豹子

        弩车队:5弩车

        欢乐斗地主

        八雀牌

        基础牌型

        • 两张:对子
        • 三张:三相同、同色顺子
        • 四张:四相同、同色顺子
        • 五张:五相同、同色顺子
        • 六张:六相同、同色顺子
        • 七张:七相同、同色顺子
        • 八张:八相同、同色顺子

        胡牌类型

        • 3/3/2
        • 4/4
        • 5/3
        • 6/2
        • 8

        推荐组合

        • 7张同色顺子
        • 5n-2m
        • 4n-3(n-1)

        篮球

        右手投球

        1. 右脚在前,身体下顿
        2. 右臂弯曲90度
        3. 举球至头顶右侧
        4. 起身并伸展手臂,最后阶段三指按压篮球
        5. 成功进球

        大脑/记忆/思想/躯体症状

                抑郁症通常认定为是精神类疾病,但大部分病人伴随着躯体症状,他们感受到的躯体刺痛是真实的吗?我的认为:是真实的,抑郁症患者的大腿刺痛感,与用千万根针刺入一身体,并无本质区别。

                精神痛苦和身体痛苦间的关系如何?我认为:两者紧密相关。如果我们持续进行俯卧撑,会感受到手臂传来的痛苦,如果我们长时间憋气,会感受到肺部传来的痛苦,如果我们听闻或经历一件不幸的事情,所感受到痛苦来自何方呢?是手臂?是大腿?是内脏?好像都不是,这种痛苦貌似是直接由大脑发出,并且影响全身各处,这种痛苦叫做精神痛苦。在我看来,人的大脑以实体的形式存储着大量记忆,人无法像脱去衣服、剪去头发、甚至砍断手臂一样,直接移去这些记忆。这些记忆如果不能以恰当的方式组合在一起,便会像骨头错位、大腿抽筋一样,实实在在的折磨着人体。我认为记忆的组合方式便是个体的思想,这份思想如果能够有序组合从出生到当下的记忆,并能够合理安置未来将要存入的记忆,那么这个个体的思想是健康的,或者说不会因为记忆的不合理存储对身体造成损失。反过来,个体记忆的组合方式不合理,现有记忆无法有序组合,未来将要存入的记忆无法合理安置,那么这个个体必然承受着大脑直接发出的痛苦,这份痛苦会传递到身体个人,这便是躯体症状。

                更加具体来说,如果一个学生极度渴望成为一个品学兼优好孩子,但他却抽烟酗酒烫头,打架骂人作弊样样不落,他极度懊悔自己的所做所为,发现这些经历的事情已经让他无法成为一个好孩子了,无疑,此时的他将会承受个体记忆无法有序组合造成的痛苦。但是,如果这个孩子信仰了上帝,通过神父向上帝阐述自己的罪行,并取得了原谅,同时身边的神父和教众都认可了自己,在思想上,这位同学认为自己是个好孩子,抽烟酗酒烫头,打架骂人作弊的过往记忆又添加上了上帝已原谅的标签,不会再导致记忆组合方式的不合理。那么这时,这位同学就切实摆脱了痛苦。

                吃好、穿好、睡好,可以确保身体的基本健康。个体思想无法有效处理大脑中存储的记忆,难以接纳未来外界输入的信息,身体同样会出现问题。

        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

        当前余额3.43前往充值 >
        需支付:10.00
        成就一亿技术人!
        领取后你会自动成为博主和红包主的粉丝 规则
        hope_wisdom
        发出的红包

        打赏作者

        stealPigs-youth

        你的鼓励将是我创作的最大动力

        ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
        扫码支付:¥1
        获取中
        扫码支付

        您的余额不足,请更换扫码支付或充值

        打赏作者

        实付
        使用余额支付
        点击重新获取
        扫码支付
        钱包余额 0

        抵扣说明:

        1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
        2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

        余额充值