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
功能
- 场景宽度大于游戏屏幕宽度(world的宽度)时
- 相机位置要随角色位置变化,以确保玩家视觉体验
实现相机跟随角色
- 自定义水平跟随:this.cameras.main.scrollX=this.player.x-this.game.config.width/2
- 内置API跟随:this.cameras.main.startFollow(this.player)
固定定位:[GameObject].setScrollFactor(0)
anims
- 结合this.load.spritesheet/Atlas使用
- 通过图片的快速切换
- 实现角色的连贯动作
load
加载图片、JSON文件
生命周期
- init(data):接受初始化参数
- preload:加载图片资源、加载音乐资源,不同场景preload加载的资源,是可以共享的
- create:注册事件回调,绘制游戏页面
- update(time,detla):每一帧开始时,会进行一次调用
- pause:主动设置暂停后
- resume:pause暂停中恢复前
- wake:后台切换的暂停中恢复前,如浏览器标签页切换
- shutdown:类实例销毁前
- time:游戏开始到当前帧开始的时间差
- 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(图集)
- TexturePacker把一组图片合并成一张
- 合成的图片叫图集
- 还会额外生成一个JSON配置文件
- 配置文件记录着图集中每个图块的位置
- 配置文件记录着图集中每个图块的key
Tilemap(砖块地图)
使用方式1
- 用Tiled把图集切割成一组图块图片
- 用Tiled拖动图块图片构建砖块地图
- 导出描述砖块地图的JSON构建文件
- Phaser中导入构建文件和图集图片
- 生成图层并放入场景中
{
"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
- Phaser导入图集图片
- 用Phaser来把图集图片切割成一组图块图片
- 每个图块图片都分配从0开始的数字编号
- 用数字数组表示图层每块区域对应的图块编号
- 生成图层并放入场景中
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状态同步
帧同步
- 服务端接收到所有客户端当前帧的操作后,广播给所有客户端
- 客户端依据接收的操作,跑出完整的游戏逻辑,实现游戏状态的更新
状态同步
- 服务端接收到所有客户端当前帧的操作后,跑出完整的游戏逻辑
- 服务端把得到的游戏状态,发送给所有客户端
- 客户端拿到游戏的状态后,更新自身当前的游戏状态
帧同步的不足(王者荣耀为例)
- 断线重连的加载速度很慢,因为要跑完所有帧的操作
- 观看王者荣耀回放时,无法进行回退
状态同步的优点
- 安全性非常高
- 断线重连非常快
- 只需创建和同步玩家视野内的角色
使用帧编号
- 服务端生成帧编号:服务端会定期生成帧编号
- 服务端发送帧编号给客户端:每当服务端生成新的帧编号时,它会将此帧编号发送给所有连接的客户端。
- 客户端接收到帧编号后处理操作:每个客户端在接收到帧编号后,会把这个编号分配给自己接下来的操作。
- 客户端将带有帧编号的操作发送给服务端:客户端在此帧编号下进行的操作会被打包,并发送回服务端。
- 服务端接收并广播操作:服务端收到客户端的操作后,会将这些操作进行处理并广播给所有客户端
帧操作的收集和存储
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)
篮球
右手投球
- 右脚在前,身体下顿
- 右臂弯曲90度
- 举球至头顶右侧
- 起身并伸展手臂,最后阶段三指按压篮球
- 成功进球
大脑/记忆/思想/躯体症状
抑郁症通常认定为是精神类疾病,但大部分病人伴随着躯体症状,他们感受到的躯体刺痛是真实的吗?我的认为:是真实的,抑郁症患者的大腿刺痛感,与用千万根针刺入一身体,并无本质区别。
精神痛苦和身体痛苦间的关系如何?我认为:两者紧密相关。如果我们持续进行俯卧撑,会感受到手臂传来的痛苦,如果我们长时间憋气,会感受到肺部传来的痛苦,如果我们听闻或经历一件不幸的事情,所感受到痛苦来自何方呢?是手臂?是大腿?是内脏?好像都不是,这种痛苦貌似是直接由大脑发出,并且影响全身各处,这种痛苦叫做精神痛苦。在我看来,人的大脑以实体的形式存储着大量记忆,人无法像脱去衣服、剪去头发、甚至砍断手臂一样,直接移去这些记忆。这些记忆如果不能以恰当的方式组合在一起,便会像骨头错位、大腿抽筋一样,实实在在的折磨着人体。我认为记忆的组合方式便是个体的思想,这份思想如果能够有序组合从出生到当下的记忆,并能够合理安置未来将要存入的记忆,那么这个个体的思想是健康的,或者说不会因为记忆的不合理存储对身体造成损失。反过来,个体记忆的组合方式不合理,现有记忆无法有序组合,未来将要存入的记忆无法合理安置,那么这个个体必然承受着大脑直接发出的痛苦,这份痛苦会传递到身体个人,这便是躯体症状。
更加具体来说,如果一个学生极度渴望成为一个品学兼优好孩子,但他却抽烟酗酒烫头,打架骂人作弊样样不落,他极度懊悔自己的所做所为,发现这些经历的事情已经让他无法成为一个好孩子了,无疑,此时的他将会承受个体记忆无法有序组合造成的痛苦。但是,如果这个孩子信仰了上帝,通过神父向上帝阐述自己的罪行,并取得了原谅,同时身边的神父和教众都认可了自己,在思想上,这位同学认为自己是个好孩子,抽烟酗酒烫头,打架骂人作弊的过往记忆又添加上了上帝已原谅的标签,不会再导致记忆组合方式的不合理。那么这时,这位同学就切实摆脱了痛苦。
吃好、穿好、睡好,可以确保身体的基本健康。个体思想无法有效处理大脑中存储的记忆,难以接纳未来外界输入的信息,身体同样会出现问题。