Pixi的基本使用(4)--图形绘制与碰撞检测

1,436 阅读4分钟

前面三章已经讲述了如何显示精灵与对精灵的操作,本次讲述的是用pixi.js绘制各种图形,以及两个精灵之间的碰撞检测。

图形绘制

  • Pixi 也有自己的底层绘制工具,可以使用这些绘制工具制作矩形,形状,线,复杂的多边形和文本等
  • Pixi 的底层绘制工具使用了与 Canvas绘画API几乎相同的 API,与 Canvas 不同的是,Pixi 通过 WebGLGPU 上高性能的渲染和绘制形状
  • 所有形状的绘制,都先从创建 PixiGraphics 类实例开始
 const rectangle = new PIXI.Graphics();

矩形/圆角矩形

  • 使用drawRect方法绘制矩形
  • 绘制一个 x 坐标170y 坐标17064x64 像素大小,带红色边框的蓝色矩形
 rectangle.beginFill(0x66ccff); // 设置填充颜色
 rectangle.lineStyle(4, 0xff3300, 1); // 设置外边框
 rectangle.drawRect(100, 100, 100, 100); // x坐标为100,y坐标为100,宽高为100
 rectangle.endFill(); // 结束绘制
 this.app.stage.addChild(rectangle); // 将矩形添加都舞台

image-20230214121113710.png

  • 使用 drawRoundedRect 方法绘制圆角矩形
  • 它有5个参数分别为 x、y左上角坐标width、height宽高cornerRadius圆角大小
 const roundRect = new PIXI.Graphics();
 roundRect.beginFill(0xff9933);
 roundRect.lineStyle(5, 0x99ccff, 0.8);
 roundRect.drawRoundedRect(200, 10, 70, 80);
 roundRect.endFill();
 this.app.stage.addChild(roundRect);

image-20230214143241544.png

圆形/椭圆

  • 使用 drawCircle 方法绘制圆形,它有3个参数分别是 x、y(圆心)半径radius
 const circle = new PIXI.Graphics();
 circle.beginFill(0x996ff); // 设置填充色
 circle.drawCircle(50, 50, 20); // 圆心点为(50,50),半径为20
 circle.endFill(); // 结束绘制
 this.app.stage.addChild(circle);

image-20230214121834738.png

  • 使用 drawEllipse 方法来绘制椭圆,它有四个参数分别是 x、y(椭圆左上角)width、height(宽高)

    • 椭圆左上角: 假设椭圆被透明的矩形边界框包围,该框的左上角将代表椭圆的 x、y 锚点位置
    • 若椭圆宽高相等,则为圆形
 const ellipse = new PIXI.Graphics();
 ellipse.beginFill(0xffff00);
 ellipse.drawEllipse(200, 300, 100, 50);
 ellipse.endFill();
 this.app.stage.addChild(ellipse);

image-20230214122515844.png

线条/多边形

  • 使用 lineStyle 方法定义线的样式,使用 moveTolineTo 方法绘制线的起点和终点
  • 绘制一个10像素宽的线条
 const line = new PIXI.Graphics();
 line.lineStyle(10, 0xffffff, 1);
 line.moveTo(300, 0); // 起始坐标
 line.lineTo(600, 200); // 终点坐标
 line.position.set(0, 0);
 this.app.stage.addChild(line);

image-20230214144211407.png

  • 使用 drawPolygon 方法将线连接在一起,并用颜色进行填充,以制作复杂的形状
  • 它的参数是由位置 x、y 点确定一个位置组成的路径数组
  • 绘制一个绿色边框的三角形,默认绘制点为 (400,400)
 const triangle = new PIXI.Graphics();
 triangle.beginFill(0x66ff33);
 triangle.lineStyle(5, 0xcd0000, 0.7);
 triangle.drawPolygon([300, 200, 500, 200, 400, 400]); // 坐标(300,200)、(500,200)、(400,400)
 triangle.endFill();
 this.app.stage.addChild(triangle);

image-20230214144751191.png

显示文字

  • 使用 PIXI.Text 对象在舞台上显示文字,PixiText 对象继承自 Sprite 类,因此 Text 包含精灵类的全部属性,可以在舞台上调整文本的位置和大小
  • Pixi 通过使用 Canvas 绘图 API 将文本呈现到一个不可见的临时 canvas 元素来生成文本对象,然后将canvas 转换为 WebGL 纹理
 const text = new PIXI.Text("javaScript");
 text.position.set(200, 500);
 this.app.stage.addChild(text);

image-20230214145657861.png

  • 若想定义文本的样式,使用 PixiTextStyle 类对文本样式初始化
 const textStyle = new PIXI.TextStyle({
   fontFamily: "Arial", // 字体样式
   fontSize: 36, // 字号
   fill: "pink", // 填充颜色
   stroke: "#ff3300", // 线条颜色
   strokeThickness: 4, // 线条宽度
   dropShadow: true, // 是否有阴影
   dropShadowColor: "#ffffff", // 阴影颜色
   dropShadowBlur: 4, // 阴影模糊程度
   dropShadowAngle: Math.PI / 6, // 阴影角度
   dropShadowDistance: 6, // 阴影距离
 });
 const text = new PIXI.Text("javaScript", textStyle);

image-20230214150727077.png

  • 后续想更改文本内容,使用 text 属性更改
 myText.text = "typeScript";
  • 可以使用 style 属性重新定义样式
 myText.style.fill = "purple"; // 修改原有单个属性
 ​
 myText.style = { fill: "purple" }; // 覆盖所有属性
  • 可以对超长字符串换行

    • breakWords 设置为 true,英文单词是否能打断
    • wordWrap 设置为 true
    • wordWrapWidth 最大宽度像素值
    • align 设置多行文本的对齐方式(单行不生效)
 myText.style.breakWords = true;
 myText.style.wordWrap = true;
 myText.style.wordWrapWidth = 100;
 myText.style.align = "center";

碰撞检测

  • 可以使用一个名为 hitTestRectangle 的自定义函数,该函数检查是否有两个矩形的 Pixi 精灵正在接触
  • 如果两个精灵重叠,函数将返回 true
 hitTestRectangle(sprite1, sprite2)
  • 实现: 如果猫碰到了盒子,盒子就会变成红色,文本对象就会显示 Hit!
  • 判断原理:猫精灵与矩形中心点之间的距离,小于猫精灵与矩形的半宽和,则为碰撞

image-20230214162923552.png

// 碰撞检测函数
hitTestRectangle(moveSprite, otherSprite) {
  const { rectangle } = otherSprite;
  // 定义所需变量
  let hit = false; // 是否碰撞
  // 寻找每个精灵的中心点
  moveSprite.centerX = moveSprite.x + moveSprite.width / 2; // 移动精灵水平中心点
  moveSprite.centerY = moveSprite.y + moveSprite.height / 2; // 移动精灵垂直中心点
  rectangle.centerX = rectangle.x + rectangle.width / 2; // 矩形水平中心点
  rectangle.centerY = rectangle.y + rectangle.height / 2; // 矩形垂直中心点

  // 找出每个精灵的半高和半宽
  moveSprite.halfWidth = moveSprite.width / 2; // 移动精灵半宽
  moveSprite.halfHeight = moveSprite.height / 2; // 移动精灵半高
  rectangle.halfWidth = rectangle.width / 2; // 矩形半宽
  rectangle.halfHeight = rectangle.height / 2; // 矩形半高

  // 移动精灵和矩形之间的距离
  const gapX = moveSprite.centerX - rectangle.centerX;
  const gapY = moveSprite.centerY - rectangle.centerY;

  // 算出移动精灵和矩形半宽半高的总和
  const combineWidth = moveSprite.halfWidth + rectangle.halfWidth;
  const combineHeight = moveSprite.halfHeight + rectangle.halfHeight;
  // 检查x轴上是否有碰撞
  // 判断两个精灵中心点间的距离,是否小于精灵半宽和
  if (Math.abs(gapX) < combineWidth) {
    // 检查y轴是否有碰撞
    hit = Math.abs(gapY) < combineHeight;
  } else {
    hit = false;
  }
  return hit;
}
  • 运行游戏
playGame(moveSprite, otherSprite) {
  moveSprite.x += moveSprite.vx;
  moveSprite.y += moveSprite.vy;
  // 碰撞检测  
  const isHit = this.hitTestRectangle(moveSprite, otherSprite);
  if (isHit) {
    otherSprite.message.text = "hit!";
    otherSprite.rectangle.tint = 0xff3300;
  } else {
    otherSprite.message.text = "No Collision...";
    otherSprite.rectangle.tint = 0x66ccff;
  }
},

image-20230215100803504.png