UNPKG

pubtool4pixi

Version:

Usefultool for PIXI xes project

371 lines (352 loc) 15.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: hitTestContainer.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: hitTestContainer.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>import { Container, Point } from "pixi.js"; function defineProperty(name, defaultValue, getter = function (value) { return value; }, setter = function (value) { return value; }) { let value = defaultValue; let o = {}; o[name] = { get() { return getter(value); }, set(para) { value = setter(para); } }; Object.defineProperties(this, o); } /** * @class HitContainer * @extends PIXI.Container * @classdesc Hit test container * @property {Number} sampleCount - 采样个数,如360代表每隔一度采样一次 - 默认:360 * @property {Number} sampleMinAlpha - 采样像素点阈值,采样的数据为RGBA中的A 范围在0-255 - 默认:0 * @property {Number} sampleDist - 采样衰减距离 - 默认:1像素 * @example * var local = new HitContainer(); * local.sampleCount = 720; * local.sampleMinAlpha = 10; * var localSprite = new PIXI.Sprite(res["image_example1"].texture); * localSprite.anchor.set(0.5); * local.addChild(localSprite); * var target = new HitContainer(); * var targetSprite = new PIXI.Sprite(res["image_example2"].texture); * targetSprite.anchor.set(0.5); * target.addChild(targetSprite); * local.initBound(); * target.initBound(); * local.position.set(10,10); * console.log(local.hitTest(target)); * */ class HitContainer extends Container { constructor() { super(); defineProperty.call(this, "sampleCount", 360, undefined, function (value) { return parseInt(value); }); defineProperty.call(this, "sampleMinAlpha", 0); defineProperty.call(this, "sampleDist", 1); } _toRad(deg) { return (deg * Math.PI) / 180; } /** * @function _polarToPixel * @memberOf HitContainer * @description Caculate the position in Cartesian coordinate with given point in polar coordinate. * @param {Number} amplitude Amplitude of polar coordinate point. * @param {Number} phase Phase of polar coordinate point. * @return {Point} Point in Cartesian coordinate */ _polarToPixel(amplitude, phase) { return { x: amplitude * Math.cos(phase), y: amplitude * Math.sin(phase) }; } /** * @function _caculateBoundMatrix * @memberOf HitContainer * @description Caculate the matrix base on visible pixel in this container. * @return {Array} Two-dimensional array contains binnary numbers. 1 means visible and 0 means transparent. */ _caculateBoundMatrix() { var buffer = app.renderer.extract.pixels(this); let width = Math.floor(this.width); let count = width; let arr = []; for (let i = 0; i &lt; buffer.length; i += 4) { if (count === width) { arr.push([]); count = 0; } let alpha = buffer[i + 3]; arr[arr.length - 1].push(alpha > this.sampleMinAlpha ? 1 : 0); count++; } return arr; } /** * @function _caculateBound * @memberOf HitContainer * @description Caculate bound area for this in polor coordinate. * @param {Number} Two-dimensional array contains binnary numbers. * @return {Array} An array contains polor coordinate points. */ _caculateBound(buffer) { let bound = this.getBounds(); let globalP = this.getGlobalPosition(); let dx = bound.x - globalP.x; let dy = bound.y - globalP.y; let sampleAmplitudeMax = Math.floor( Math.sqrt(this.width * this.width + this.height * this.height, 2)/2 ); let arr = []; let me = this; let step = 360 / this.sampleCount; for (let j = 0; j &lt; 360; j += step) { for (let i = sampleAmplitudeMax; i > 0; i -= me.sampleDist) { let p = me._polarToPixel(i, me._toRad(j)); p.x -= dx; p.y -= dy; if (p.x > me.width || p.y > me.height || p.x &lt; 0 || p.y &lt; 0) { continue; } let indexX = Math.floor(p.x); let indexY = Math.floor(p.y); if (buffer[indexY] === undefined || buffer[indexY][indexX] === undefined || buffer[indexY][indexX] === 0) { continue; } else { arr.push({ amplitude: i, phase: j }); break; } } } return arr; } /** * @function _getBoundaryGraphics * @memberOf HitContainer * @description Draw a polygon based on given points in polor coordinate. * @para {Array} points in polor coordinate. * @return {PIXI.Graphics} Polygon graphic. */ _getBoundaryGraphics(bound) { let gra = new PIXI.Graphics(); gra.lineStyle(1, 0xff0000, 0); gra.beginFill(0xff00ff, 0); bound.forEach((element, index) => { let relP = this._polarToPixel(element.amplitude, this._toRad(element.phase)); if (index === 0) { gra.moveTo(relP.x, relP.y); } else { gra.lineTo(relP.x, relP.y); } }); gra.endFill(); return gra; } /** * @function initBound * @memberOf HitContainer * @description Prepare for hit test. This method must be done before hit test. If the boundary of container is changed. You must do it again. * @description 预处理碰撞检测,这个方法必须在碰撞检测前调用。如果容器的边界发生了变化,需要再次调用 */ initBound(boundMatrix,bound) { this._BoundMatrix = boundMatrix||this._caculateBoundMatrix(); this._Bound = bound||this._caculateBound(this._BoundMatrix); this._BoundGraphics &amp;&amp; this._BoundGraphics.destroy(); this._BoundGraphics = this._getBoundaryGraphics(this._Bound); this.addChild(this._BoundGraphics); } /** * @function isInBound * @memberOf HitContainer * @para {Object}A point. * @description Determine whether a point which defined in global is in the boundary. * @description 检测一个全局点是否在边界内 */ isInBound(point) { return this._BoundGraphics.containsPoint(point); } /** * @function hitTest * @memberOf HitContainer * @para {HitContainer}Target object. * @description Detect whether the container collides with the target area * @description 检测容器是否碰撞了目标区域 * @return {Boolean} Collision with target area or not */ hitTest(target) { let isHit = false; let p1 = this.getGlobalPosition(); let targetTestFun = target.isInBound || target.containsPoint; this._Bound.some((element, index) => { let relP = this._polarToPixel(element.amplitude, (element.phase * Math.PI) / 180); relP.x += p1.x; relP.y += p1.y; isHit = targetTestFun.call(target, relP); if (isHit) { return true; } }); if(!isHit){ p1 = target.getGlobalPosition(); target._Bound.some((element, index) => { let relP = target._polarToPixel(element.amplitude, (element.phase * Math.PI) / 180); relP.x += p1.x; relP.y += p1.y; isHit = targetTestFun.call(this, relP); if (isHit) { return true; } }); } return isHit; } /** * @function testHitArea * @memberOf HitContainer * @para {HitContainer}Target object. * @description Detect the proportion of hit area. * @description 检测碰撞面积比例 * @return {Number}The proportion of hit area. */ testHitArea(target) { if (!this.hitTest(target)) { return 0; } let targetBound = target.getBounds(); let localBound = this.getBounds(); let maxEmptyMatrix = this._generateMaxEmptyMatrix(targetBound, localBound); maxEmptyMatrix = this._addBoundToMatrix(maxEmptyMatrix, localBound); let maxEmptyMatrixTarget = this._generateMaxEmptyMatrix(targetBound, localBound); maxEmptyMatrixTarget = target._addBoundToMatrix(maxEmptyMatrixTarget, targetBound); let hitMatrix = this._multiplyMatrix(maxEmptyMatrix, maxEmptyMatrixTarget); let totalSurfaceArea = this._caculateSuerfaceArea(hitMatrix); let localSurfaceArea = this._caculateSuerfaceArea(this._BoundMatrix); return totalSurfaceArea / localSurfaceArea; } /** * @function _caculateSuerfaceArea * @memberOf HitContainer * @para {Array}A matrix which want to be measured. * @description Caculate the area based on pixel. * @return {Number} Area. */ _caculateSuerfaceArea(matrix) { let s = 0; for (let i = 0; i &lt; matrix.length; i++) { for (let j = 0; j &lt; matrix[i].length; j++) { if (matrix[i][j] !== 0 &amp;&amp; matrix[i][j] !== undefined) { s++; } } } return s; } /** * @function _multiplyMatrix * @memberOf HitContainer * @para {Array} Matrix 1. * @para {Array} Matrix 2. * @description Matrix 1 and Matrix 2 must have same shape. The method will multiply every item in matrix 1 and 2. This is not Matrix multiplication. * @return {Array} A new matrix which have a same shape with given matrix contains resault. */ _multiplyMatrix(Matrix1, Matrix2) { let newMatrix = []; for (let i = 0; i &lt; Matrix1.length; i++) { newMatrix.push([]); for (let j = 0; j &lt; Matrix1[i].length; j++) { newMatrix[i][j] = Matrix1[i][j] * Matrix2[i][j]; } } return newMatrix; } /** * @function _addBoundToMatrix * @memberOf HitContainer * @para {Array} Matrix 1. * @para {PIXI.Rectangle} localBound. * @description Add local pixel in to a Matrix. * @return {Array} The matrix after add the local pixel. */ _addBoundToMatrix(emptyMatrix, localBound) { let dx = Math.floor(localBound.x - emptyMatrix.bound.x); let dy = Math.floor(localBound.y - emptyMatrix.bound.y); let localX = 0; let localY = 0; for (let i = dy; i &lt; emptyMatrix.bound.height; i++) { for (let j = dx; j &lt; emptyMatrix.bound.width; j++) { if (this._BoundMatrix[localY] === undefined || this._BoundMatrix[localY][localX] === undefined) { continue; } emptyMatrix[i][j] += this._BoundMatrix[localY][localX]; localX++; } localX = 0; localY++; } return emptyMatrix; } /** * @function _generateMaxEmptyMatrix * @memberOf HitContainer * @para {Array} Matrix 1. * @para {Array} Matrix 2. * @description Caculate the smallest matrix which can contains both given matrix. * @return {Array} An empty matrix. */ _generateMaxEmptyMatrix(bound1, bound2) { let x = Math.min(bound1.x, bound2.x); let y = Math.min(bound1.y, bound2.y); let MaxX = Math.max(bound1.x + bound1.width, bound2.x + bound2.width); let MaxY = Math.max(bound1.y + bound1.height, bound2.y + bound2.height); let totalWidth = Math.floor(MaxX - x); let totalHeight = Math.floor(MaxY - y); let totalMatrix = []; for (let i = 0; i &lt; totalHeight; i++) { let aRow = []; for (let j = 0; j &lt; totalWidth; j++) { aRow.push(0); } totalMatrix.push(aRow); } let maxBound = new PIXI.Rectangle(x, y, totalWidth, totalHeight); totalMatrix.bound = maxBound; return totalMatrix; } } export default HitContainer;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Base.html">Base</a></li><li><a href="BaseContainer.html">BaseContainer</a></li><li><a href="BigMath_AniBtn.html">BigMath_AniBtn</a></li><li><a href="BigMath_GuideHand.html">BigMath_GuideHand</a></li><li><a href="BigMath_StarScoreBoard.html">BigMath_StarScoreBoard</a></li><li><a href="BigMath_Timer.html">BigMath_Timer</a></li><li><a href="HitContainer.html">HitContainer</a></li><li><a href="Preschool_aqiu.html">Preschool_aqiu</a></li><li><a href="Preschool_End.html">Preschool_End</a></li><li><a href="Preschool_HintBtn.html">Preschool_HintBtn</a></li><li><a href="Preschool_Start.html">Preschool_Start</a></li><li><a href="PUB_Mask.html">PUB_Mask</a></li></ul><h3>Events</h3><ul><li><a href="BaseContainer.html#.event:beforeHide">beforeHide</a></li><li><a href="BaseContainer.html#.event:beforeShow">beforeShow</a></li><li><a href="BaseContainer.html#.event:Destroyed">Destroyed</a></li><li><a href="BaseContainer.html#.event:onHide">onHide</a></li><li><a href="BaseContainer.html#.event:onShow">onShow</a></li><li><a href="BaseContainer.html#.event:ParentChange">ParentChange</a></li><li><a href="BigMath_AniBtn.html#.event:onClick">onClick</a></li><li><a href="BigMath_Timer.html#.event:onTimeRun">onTimeRun</a></li><li><a href="BigMath_Timer.html#.event:timeup">timeup</a></li><li><a href="Preschool_End.html#.event:gameOver">gameOver</a></li><li><a href="Preschool_Start.html#.event:startGame">startGame</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.2</a> on Wed Dec 18 2019 14:22:09 GMT+0800 (中国标准时间) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>