pixiJS界面渲染与交互动作

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.xcat.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的图标

图片256×240,每个图标为16×16

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.vxcat.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:触发松开

一般在应用时,最好是做兼容鼠标和触摸屏的方式,毕竟现在移动端可能市场更大一些。要使对象能响应鼠标动作,需要指定其interactivetrue.

例如,编写一个点击猫头让猫头变大的鼠标动作:

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还不够,这时就可以利用其它的库来丰富交互体验。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注