pixiJS界面渲染与交互动作
最开始做网页用H5和js做了一些简单的游戏,主要是练习使用js,其界面非常简陋,并且没有专门对移动端进行适配,导致按键盘交互的游戏无法体验。现在逐渐地熟悉了网页开发,想对一些早期的游戏进行重构。前端的游戏框架当然很多,先从2D游戏开始,多方面调查后选择了pixi作为游戏引擎进行开发。
关于pixi
Pixi是一个超快的2D渲染引擎。这意味着什么呢?这意味着它会帮助你用JavaScript或者其他HTML5技术来显示媒体,创建动画或管理交互式图像,从而制作一个游戏或应用。它拥有语义化的,简洁的API接口并且加入了一些非常有用的特性。比如支持纹理贴图集和为精灵(交互式图像)提供了一个简单的动画系统。它也提供了一个完备的场景图,你可以在精灵图层里面创建另一个精灵,当然也可以让精灵响应你的鼠标或触摸事件。
项目地址: https://github.com/pixijs/pixi.js
中文教程: https://github.com/Zainking/learningPixi
开始
创建Application
Pixi拥有一个Pixi应用
对象来帮助你创建它。它会自动创建一个<canvas>
HTML标签并且计算出怎么去让你的图片在这个标签中显示 。 这个舞台
对象将会被当作根容器而使用,它将包裹所有你想用Pixi显示的东西。
PIXI.Application
算出了应该使用Canvas还是WebGL去渲染图象,它取决于你正在使用的浏览器支持哪一个。它的参数是一个被称作options
的对象。在这儿例子中,它的width
和 height
属性已经被设置了,它们决定了canvas的宽和高(单位是像素)。你能够在options
对象中使用更多的属性设置 。
如果你需要在你创建canvas标签之后改变它,可以 app.renderer
对象进行设置。
<!doctype html> <meta charset="utf-8"> <title>Displaying the canvas</title> <body> <script src="../pixi/pixi.min.js"></script> <script> //Create a Pixi Application let app = new PIXI.Application({ width: 256, height: 256, antialiasing: true, transparent: false, resolution: 1 } ); //Add the canvas that Pixi automatically created for you to the HTML document document.body.appendChild(app.view); //If you want to make the canvas fill the entire window, you can apply this //CSS styling: /* app.renderer.view.style.position = "absolute" app.renderer.view.style.width = window.innerWidth + "px"; app.renderer.view.style.height = window.innerHeight + "px"; app.renderer.view.style.display = "block"; */ //The `renderer.view` is just an ordinary `<canvas>` element. //Here's how you can reference to add an optional dashed //border around the canvas app.renderer.view.style.border = "1px dashed black"; //To resize the canvas app.renderer.resize(512, 512); //To change the background color app.renderer.backgroundColor = 0x061639; </script> </body>
放置对象
所有你想在画布上显示的东西必须被加进一个被称作 stage
的Pixi对象中。你能够像这样使用舞台对象 。 Pixi用WebGL和GPU去渲染图像,所以图像需要转化成GPU可以处理的版本。可以被GPU处理的图像被称作 纹理 。在显示图片之前,需要将普通的图片转化成WebGL纹理。 加载后用stage.addChild
方法把它放到Pixi的stage
上面去。
//load an image and run the `setup` function when it's done PIXI.loader .add("images/cat.png") .load(setup); //This `setup` function will run when the image has loaded function setup() { //Create the cat sprite let cat = new PIXI.Sprite(PIXI.loader.resources["images/cat.png"].texture); //You can also create the `cat` sprite from the texture, like this: //let cat = new PIXI.Sprite(PIXI.TextureCache["images/cat.png"]); //Add the cat to the stage app.stage.addChild(cat); //If you ever need to, here's how you can clean out WebGL's GPU //memory manually /* Object.keys(TextureCache).forEach(function(texture) { TextureCache[texture].destroy(true); }); */ }
这样,路径为images/cat.png
的图片就可以加载到Application上显示出来。如果想把cat移走,可以使用app.stage.removeChild(cat)
, 但是通常,我们都把精灵的visible
属性设置成false
来让它简单的隐藏。
要调整它的位置,可以使用cat.position.set(x, y)
,也可以为cat.x
与cat.y
赋值。宽高则分别用width和height属性,或者用scale.x,scle.y的方法按比例调整宽高,也可以使用cat.scale.set(x,y)
更改。
cat.rotation
可以指定旋转角度,cat.anchor.set(x,y)
可以指定旋转锚点,锚点以0到1的小数表示,cat.anchor.set(0.5,0.5)
即为默认值,以图像中心为锚点旋转。相应地,用cat.pivot
则以指定像素的方式指定旋转锚点。
使用别名
可以对你使用频繁的Pixi对象和方法设置一些简略的可读性更强的别名
//Aliases let Application = PIXI.Application, loader = PIXI.loader, resources = PIXI.loader.resources, Sprite = PIXI.Sprite; //Create a Pixi Application let app = new Application({ width: 256, height: 256, antialias: true, transparent: false, resolution: 1 } ); //Add the canvas that Pixi automatically created for you to the HTML document document.body.appendChild(app.view); //load an image and run the `setup` function when it's done loader .add("images/cat.png") .load(setup); //This `setup` function will run when the image has loaded function setup() { //Create the cat sprite let cat = new Sprite(resources["images/cat.png"].texture); //Add the cat to the stage app.stage.addChild(cat); }
预加载的进度条
Pixi的加载器有一个特殊的progress
事件 ,可以这样绑定到相应函数上:PIXI.loader.on("progress", loadProgressHandler);
function loadProgressHandler(loader, resource) { //Display the file `url` currently being loaded console.log("loading: " + resource.url); //Display the percentage of files currently loaded console.log("progress: " + loader.progress + "%"); //If you gave your files names as the first argument //of the `add` method, you can access them like this //console.log("loading: " + resource.name); }
用css sprite加载
所谓css sprite就是将所有素材都放在一张大图上,而使用的素材则是这个大图的一块区域,这样只要大图加载完成,那么所有素材也就加载完成。如下图示,为jQuery-ui的图标
Pixi内置了一个通用的Rectangle
对象 (PIXI.Rectangle
),他是一个用于定义矩形形状的通用对象。他需要一些参数,前两个参数定义了x
和y
轴坐标位置,后两个参数定义了矩形的width
和 height
。
Pixi的纹理中有一个叫做frame
的很有用的属性,它可以被设置成任何的Rectangle
对象。frame
将纹理映射到Rectangle
的维度 。
loader .add("images/ui-icons_cd0a0a_256x240.png") .load(setup); function setup() { let rectangle = new PIXI.Rectangle(0, 0, 16, 16); let texture = TextureCache["images/ui-icons_cd0a0a_256x240.png"]; texture.frame = rectangle; let theIcon = new Sprite(texture); app.stage.addChild(theIcon); }
纹理贴图集
要向游戏中一次添加多个对象,一种比较快速有效的方法就是纹理贴图集。,
一个纹理贴图集就是一个JSON数据文件,它包含了匹配的PNG雪碧图的子图像的大小和位置。如果你使用了纹理贴图集,那么想要显示一个子图像只需要知道它的名字就行了。你可以任意的排序你的排版,JSON文件会保持他们的大小和位置不变。这非常方便,因为这意味着图片的位置和大小不必写在你的代码里。如果你想要改变纹理贴图集的排版,类似增加图片,修改图片大小和删除图片这些操作,只需要修改那个JSON数据文件就行了,你的游戏会自动给程序内的所有数据应用新的纹理贴图集。你没必要在所有用到它代码的地方修改它。 pixi兼容TexturePacker的JSON格式,类似于如下的json文件:
{"frames": { "cat.png": { "frame": {"x":2,"y":2,"w":64,"h":64}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, "sourceSize": {"w":64,"h":64}, "pivot": {"x":0.5,"y":0.5} }, "hedgehog.png": { "frame": {"x":68,"y":2,"w":64,"h":64}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, "sourceSize": {"w":64,"h":64}, "pivot": {"x":0.5,"y":0.5} }, "tiger.png": { "frame": {"x":134,"y":2,"w":64,"h":64}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":64,"h":64}, "sourceSize": {"w":64,"h":64}, "pivot": {"x":0.5,"y":0.5} }}, "meta": { "app": "http://www.codeandweb.com/texturepacker", "version": "1.0", "image": "animals.png", "format": "RGBA8888", "size": {"w":200,"h":68}, "scale": "1", "smartupdate": "$TexturePacker:SmartUpdate:52586866875309c357a59ef94cc3e344:67b70cfeefc06c04b551ab33c8f1fc7a:b00d48b51f56eb7c81e25100fcce2828$" } }
用Pixi的loader
来加载纹理贴图集 。setup中创建它们,可以使用TextureCache,也可以用resources["images/treasureHunter.json"].textures["frameId.png"]
,为了方便一般给它起别名然后在方括号中再索引它:
let id = PIXI.loader.resources["images/treasureHunter.json"].textures; frameId = new Sprite(id["frameId.png"]);
移动物体
为app的ticker注册函数,它将会每秒执行60次。
app.ticker.add(delta => gameLoop(delta)); function gameLoop(delta){ //Move the cat 1 pixel cat.x += 1; }
delta
的值代表帧的部分的延迟 ,可以把它添加到cat的位置,让cat的速度和帧率无关 ,它往往只在你的动画没法跟上60帧的速率时候出现(比如游戏运行在很老旧的机器上) 。
也可以用requestAnimationFrame
像这样创建
function gameLoop() { //Call this `gameLoop` function on the next screen refresh //(which happens 60 times per second) requestAnimationFrame(gameLoop); //Move the cat cat.x += 1; } //Start the loop gameLoop();
属性cat.vx
与cat.vy
能够设定物体移动速度,例如可以这样编写让小猫头碰到边界就反弹的效果:
var cat; loader .add("images/cat.png") .load(setup); function setup() { let texture = TextureCache["images/cat.png]; cat = new Sprite(texture); cat.width=30; cat.height=30; cat.vx = 1; cat.vy = 1; app.stage.addChild(cat); app.ticker.add(delta=>gameLoop(delta)); } function gameLoop(delta) { cat.x += cat.vx; if(cat.x+cat.width>app.view.width || cat.x<0) { cat.vx *=-1; } cat.y += cat.vy; if(cat.y+cat.height>app.view.height || cat.y<0) { cat.vy *=-1; } }
键盘动作
编写这样一个函数,为相应keyCode的键位构造一个key对象。在使用时,只需要指定press和releas方法即可非常方便的设计键盘动作
function keyboard(keyCode) { let key = {}; key.code = keyCode; key.isDown = false; key.isUp = true; key.press = undefined; key.release = undefined; //The `downHandler` key.downHandler = event => { if (event.keyCode === key.code) { if (key.isUp && key.press) key.press(); key.isDown = true; key.isUp = false; } event.preventDefault(); }; //The `upHandler` key.upHandler = event => { if (event.keyCode === key.code) { if (key.isDown && key.release) key.release(); key.isDown = false; key.isUp = true; } event.preventDefault(); }; //Attach event listeners window.addEventListener( "keydown", key.downHandler.bind(key), false ); window.addEventListener( "keyup", key.upHandler.bind(key), false ); return key; }
键盘对象也有 isDown
和 isUp
的布尔值属性,你可以用它们来检查每个按键的状态。
鼠标动作
1.鼠标左键触发事件:
- click:点击事件
- mousedown:鼠标按下
- mousemove:鼠标移动
- mouseout:鼠标移出
- mouseover:鼠标经过
- mouseup:鼠标松开
- mouseupoutside:鼠标按下,移出对象松开
2.鼠标右键触发事件:
- rightclick:点击事件
- rightdown:鼠标按下
- rightup:鼠标松开
- rightupoutside:鼠标按下,移出对象松开
3.触摸屏触发事件:
- touchcancel:触摸系统cancels键
- touchend:触摸结束
- touchendoutside:触摸开始,移出对象松开
- touchmove:触摸移动
- touchstart:触摸开始
4.兼容鼠标和触摸屏的共同触发:
- pointercancel:触发系统cancels键
- pointerdown:触发按下
- pointermove:触发移动
- pointerout:触发移出
- pointerover:触发经过
- pointertap:触发点击
- pointerup:触发松开
一般在应用时,最好是做兼容鼠标和触摸屏的方式,毕竟现在移动端可能市场更大一些。要使对象能响应鼠标动作,需要指定其interactive
为true
.
例如,编写一个点击猫头让猫头变大的鼠标动作:
var cat; loader .add("images/cat.png") .load(setup); function setup() { let texture = TextureCache["images/cat.png"];; cat = new Sprite(texture); cat.x=50; cat.y=50; cat.width=30; cat.height=30; cat.on('pointerdown',OnPointerDown) .on('pointerup',OnPointerUp); cat.interactive = true; app.stage.addChild(cat); } var expandRate = 0.2,absoluteWidth,absoluteHeight; function OnPointerDown() { absoluteWidth = this.width*expandRate; absoluteHeight = this.height*expandRate; this.x-=absoluteWidth/2; this.y-=absoluteHeight/2; this.width+=absoluteWidth; this.height+=absoluteHeight; } function OnPointerUp() { this.x+=absoluteWidth/2; this.y+=absoluteHeight/2; this.width-=absoluteWidth; this.height-=absoluteHeight; }
另外,对物体的拖动也是非常重要的鼠标交互事件。编写一个拖动猫头中心的鼠标动作:
var cat; loader .add("images/cat.png") .load(setup); function setup() { let texture = TextureCache["images/cat.png"];; cat = new Sprite(texture); cat.x=50; cat.y=50; cat.width=30; cat.height=30; cat.on('pointerdown', onDragStart) .on('pointerup', onDragEnd) .on('pointerupoutside', onDragEnd) .on('pointermove', onDragMove); cat.interactive = true; app.stage.addChild(cat); } function onDragStart(event) { this.data = event.data; this.alpha = 0.5; this.dragging = true; } function onDragEnd(event) { this.alpha = 1; this.dragging = false; this.data = null; } function onDragMove(event) { if(this.dragging) { var newPosition = this.data.getLocalPosition(this.parent); //获取鼠标移动的位置 this.position.x = newPosition.x-this.width/2; this.position.y = newPosition.y-this.height/2; } }
以上就是使用pixi进行简单地交互所需要的知识。更多更详细的内容可以看pixi的教程。另外,如果要制作复杂的交互游戏或应用,可能仅使用Pixi还不够,这时就可以利用其它的库来丰富交互体验。