我在Phaser3中的世界提到,随着Phaser.Game的初始化,一个Game World便随之被构建出来,然后我们可以通过canvas去窥探这个世界的人事物。随着进一步的学习,我发现其实我们是通过camera对这个世界进行观察,camera对着哪里,我们就可以看到哪里,这时canvas更像是camera的显示屏。
Camera
// camera的构造函数
// x - viewport的横坐标
// y - viewport的纵坐标
// width - viewport的宽度
// height - viewport的高度
Camera(x, y, width, height)
camera有两个重要属性,viewport跟scroll。我们可以把一个camera想象成生活中常见的电子监控,其中viewport可以看成是监控屏幕,我们可以从这里看到摄像头正对着的地方,而scroll则用来控制摄像头地视角,通过修改这个值,我们可以实现把摄像头朝上下左右四个方向进行调整。
viewport
viewport用来向我们展示camera正在观察地东西,它有两个重要的属性,分别是position跟size,其中position表示我们要把“屏幕”放在canvas的哪个位置,而size则表示我们的“屏幕”有多大。我们可以通过setPosition(x, y)和setSize(width, height)来调整camera的viewport。实际上camera构造函数的四个参数就是在设置其viewport。
scroll
scroll用于调整相机的视角,我们可以使用setScroll(scrollX, scrollY)方法,来调整我们想观察的位置,我们的可见范围是以(scrollX, scrollY)为左上角,宽度与高度分别为viewport.width与viewport.height的矩形。初始情况下,scrollX与scrollY都为0。
main camera
camera的任务是负责渲染游戏中的对象,换句话说,不使用camera我们就无法在canvas上看到任何东西。那么之前的代码我们也没有创建camera,为什么可以看到那些矩形呢?
因为,在游戏初始化的过程中,Phaser会自动创建一个默认的camera,其viewport的大小与配置的canvas大小一致,position设置为(0, 0)(canvas左上角),并且scroll的位置也是(0, 0)。于是我们在scene中创建的所有位于画布范围内的东西都可以看到(在Phaser3中的世界中画的红色矩形),我们可以通过以下代码访问这个默认创建的camera,
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
create ()
{
const mainCamera = this.cameras.main;
console.log(`main camera viewport position: (${mainCamera.x}, ${mainCamera.y})`);
console.log(`main camera viewport size: ${mainCamera.width} * ${mainCamera.height}`);
console.log(`main camera scroll at (${mainCamera.scrollX}, ${mainCamera.scrollY})`)
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
打开控制台就可以看到各个数据
main camera viewport position: (0, 0)
main camera viewport size: 800 * 600
main camera scroll at (0, 0)
操纵相机视角
在Phaser3中的世界文末的例子中,绿色矩形完全不可见,蓝色矩形有一半在可见范围外。我们可以尝试修改代码,通过方向键控制camera的视角,从而看到原先看不到的绿色矩形。
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
create ()
{
this.drawRectange(0, 0, 0xff0000); // 红色
this.drawRectange(-100, 0, 0x00ff00); // 绿色
this.drawRectange(750, 0, 0x0000ff); // 蓝色
const mainCamera = this.cameras.main;
this.input.keyboard.on('keydown-LEFT', () => {
mainCamera.scrollX -= 10;
});
this.input.keyboard.on('keydown-RIGHT', () => {
mainCamera.scrollX += 10;
});
this.input.keyboard.on('keydown-UP', () => {
mainCamera.scrollY -= 10;
});
this.input.keyboard.on('keydown-DOWN', () => {
mainCamera.scrollY += 10;
});
}
drawRectange(x, y, color)
{
// setOrigin将矩形定位原点设置为左上角
return this.add.rectangle(x, y, 100, 100, color).setOrigin(0);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
通过方向键的上下左右调整视角之后,我们就可以看到隐藏在canvas外的绿色矩形了。当我们一直按左方向键的时候,我们可以看到,左边慢慢出现绿色矩形,而右边的蓝色矩形在慢慢消失。
bounds
默认情况下,camera的视角是没有边界的,我们可以任意改变scrollX和scrollY为任何值。我们也可以通过setBounds(x, y, width, height, [centerOn])方法,给camera设置边界,从而限制其可视范围,换句话说,scrollX <= bounds.x + bounds.width - viewport.width。
viewport细节
如果我们用过监控系统,就可以很直观的感受到viewport是什么,它跟我们平时看到的监控画面很像,我们可以放大或者缩小画面,也可以将其移动到屏幕的任何一个位置。
比如上面这个图,我们可以把canvas看成监控室里用来显示监控画面的电视(感觉挺合理的,电视跟canvas都是有大小限制的),有四个camera在观察着不同的场景,它们将拍到的画面传送到监控室,接着我们设置四个等大的viewport,分别展示拍到的监控画面。
但是Phaser3中的camera与现实的监控系统不太一样,现实生活中的摄像头是有视野盲区的,上图的四个监控画面,我们不可能通过拉长画面从而看到更多的东西,比如右下角的手扶梯监控画面,竖着拉长并不能看到电梯的两端,除非我们调整摄像头的视角。
Phaser3中的camera的视野范围是以(scrollX, scrollY)为左上角顶点,右下角在无穷远处的矩形,换句话说,将(scrollX, scrollY)作为直角坐标系的原点,那么camera的视野范围是整个第四象限,我们可以通过增大viewport的size,从而看到更多的画面。
改变viewport大小
接下来,通过编写代码来直观感受一下上面说的那些概念。
Phaser3 Sandbox 提供了一个在线编程的环境,可以很方便的验证一些想法。
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
// 图片大小是640 × 514
this.load.image('einstein', 'assets/pics/ra-einstein.png');
}
create ()
{
const image = this.add.image(0, 0, 'einstein').setOrigin(0);
const cam = this.cameras.main;
const gui = new dat.GUI();
gui.addFolder('Main Camera');
gui.add(cam, 'width');
gui.add(cam, 'height');
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example,
// 将canvas大小设置成跟图片一样大
width: 640,
height: 514
};
const game = new Phaser.Game(config);
我们可以通过在右上角的面板输入不同的width跟height来调整viewport,当我们将width缩小到320的时候,我们只能看到爱因斯坦的左半边脸,随着我们将width增大,我们可以慢慢地看到整张脸。这一点就跟现实中的监控画面不太一样,我们可以通过放大viewport从而看到更多的画面。
改变viewport位置
接着尝试改变viewport的位置,这里有一个要点,我在一开始学习的时候就弄混了,viewport自己的位置跟它所观察的位置完全是两码事。
还是用上文的代码加以说明,我们创建了一个与图片等大的canvas,然后将图片刚好放在上面,这时候我们缩小viewport的大小为300 x 300,这时候我们只能看到图片左上角,
然后,我尝试将viewport挪到它眼睛的位置,以为就可以看到他的眼睛。其实并不能,不管我移动到哪里,viewport里的画面都保持不变。
因为决定viewport所显示的画面是通过camera的scroll决定的,我们虽然改变了viewport的position,但是其scroll并没有改变。附上验证的代码
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
// 图片大小是640 × 514
this.load.image('einstein', 'assets/pics/ra-einstein.png');
}
create ()
{
const image = this.add.image(0, 0, 'einstein').setOrigin(0);
const mainCamera = this.cameras.main;
mainCamera.setSize(300, 300);
this.input.keyboard.on('keydown-LEFT', () => {
mainCamera.x -= 10;
});
this.input.keyboard.on('keydown-RIGHT', () => {
mainCamera.x += 10;
});
this.input.keyboard.on('keydown-UP', () => {
mainCamera.y -= 10;
});
this.input.keyboard.on('keydown-DOWN', () => {
mainCamera.y += 10;
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example,
// 将canvas大小设置成跟图片一样大
width: 640,
height: 514
};
const game = new Phaser.Game(config);
我们可以通过方向键移动viewport的位置,但是viewport中的画面并没有改变。
总结
通过用现实生活中的监控来类比camera,会发现它们之间其实有挺多共同之处的(当然也有差异),这种类比方式可以帮助我们更好地去理解一些概念,会比较生动形象。关于camera的两个重要属性,感觉基本也就这些内容了,详细的资料可以通过camera的api文档查看。