/*** @description 一些和显示相关的函数放到这里* @author (pdh)* @date 2020-11-05* @class ShowUtil*/
class ShowUtil {private static instance: ShowUtil = null;private hurtColorFilter = null;private shadowColorFilter = null;private shadeColors: number[] = [];private shadeMap: {} = {};private constructor() {}public static getInstance() {if (!this.instance) {this.instance = new ShowUtil();}return this.instance;}/*** @description 沿着中心翻转* @author (pdh)* @date 2020-11-05* @param {egret.DisplayObject} entity* @param {boolean} [flipX=false]* @param {boolean} [flipY=false]* @memberof ShowUtil*/public centerFlip(entity: egret.DisplayObject, flipX: boolean = false, flipY: boolean = false): void {if (!entity) {return;}if (entity.anchorOffsetX === 0 && flipX) {let offsetX = entity.width / 2.0;entity.anchorOffsetX = offsetX;entity.x += offsetX;}if (flipX) {entity.scaleX = entity.scaleX * (-1);}if (entity.anchorOffsetY === 0 && flipY) {let offsetY = entity.height / 2.0;entity.anchorOffsetY = offsetY;entity.y += offsetY;}if (flipY) {entity.scaleY = entity.scaleY * (-1);}}/*** @description 更改锚点。更改后图标显示不移位,仍然按照原来位置显示,只是锚点更改了。* @author (pdh)* @date 2020-11-06* @param {egret.DisplayObject} entity* @param {number} anchorX* @param {number} anchorY* @memberof ShowUtil*/public setAnchor(entity: egret.DisplayObject, anchorX: number, anchorY: number): void {let oldAnchorX = entity.anchorOffsetX;let oldAnchorY = entity.anchorOffsetY;entity.anchorOffsetX = anchorX;entity.anchorOffsetY = anchorY;entity.x += (anchorX - oldAnchorX);entity.y += (anchorY - oldAnchorY);}/*** @description 角度转弧度* @author (pdh)* @date 2020-11-09* @param {number} angle* @returns {number}* @memberof ShowUtil*/public angle2radian(angle: number): number {return angle * Math.PI / 180;}/*** @description 弧度转角度* @author (pdh)* @date 2020-11-09* @param {number} radian* @returns {number}* @memberof ShowUtil*/public radian2angle(radian: number): number {let angle = radian * 180 / Math.PI; //弧度转角度,方便调试return angle;}/*** @description 将坐标控制在0~360度之间* @author (pdh)* @date 2020-11-09* @private* @param {number} angle* @returns {number}* @memberof ShowUtil*/public getRealAngle(angle: number): number {let shang = parseInt(String(angle / 360));angle = angle - shang * 360;if (angle > 360) {angle -= 360;} else if (angle < 0) {angle += 360;}return angle;}/*** @description 两条线段是否相交。如果有相交点放在{@resultPoint}中返回。*              只有return为true时,{@resultPoint}才有意义。* @author (pdh)* @date 2020-11-08* @param {egret.Point} line1Start 线段1的起点* @param {egret.Point} line1End 线段1的终点* @param {egret.Point} line2Start 线段2的起点* @param {egret.Point} line2End 线段2的终点* @param {egret.Point} resultPoint  相交点* @returns {boolean}  是否相交* @memberof ShowUtil*/public crashLine(line1Start: egret.Point,line1End: egret.Point,line2Start: egret.Point,line2End: egret.Point,resultPoint: egret.Point): boolean {let pt = resultPoint;// line1's cpmponent  let x1 = line1End.x - line1Start.x;//b1  let y1 = line1End.y - line1Start.y;//a1  // line2's cpmponent  let x2 = line2End.x - line2Start.x;//b2  let y2 = line2End.y - line2Start.y;//a2  // distance of 1,2  let x21 = line2Start.x - line1Start.x;let y21 = line2Start.y - line1Start.y;// determinant  let D = y1 * x2 - y2 * x1;// a1b2-a2b1  //   if (0 != D) {// cross point  pt.x = (x1 * x2 * y21 + y1 * x2 * line1Start.x - y2 * x1 * line2Start.x) / D;// on screen y is down increased !   pt.y = -(y1 * y2 * x21 + x1 * y2 * line1Start.y - x2 * y1 * line2Start.y) / D;// segments intersect.  if ((Math.abs(pt.x - line1Start.x - x1 / 2) <= Math.abs(x1 / 2)) &&(Math.abs(pt.y - line1Start.y - y1 / 2) <= Math.abs(y1 / 2)) &&(Math.abs(pt.x - line2Start.x - x2 / 2) <= Math.abs(x2 / 2)) &&(Math.abs(pt.y - line2Start.y - y2 / 2) <= Math.abs(y2 / 2))) {return true;}}return false;}/*** @description 计算夹角,沿X轴正向为0,逆时针增大* @author (pdh)* @date 2020-11-06* @param {number} fromX* @param {number} fromY* @param {number} toX* @param {number} toY* @returns * @memberof ShowUtil*/public getAngle(fromX: number, fromY: number, toX: number, toY: number) {let x = toX - fromX;let y = toY - fromY;//egret中左上角为原点,要做一下转换let radian = Math.atan2(y, x);let angle = this.radian2angle(radian); //弧度转角度,方便调试angle = this.getRealAngle(angle);return angle;}/*** @description 以baseX、baseY为原点,旋转rotateAngle的角度,获得beforeX、beforeY旋转后的新坐标* @author (pdh)* @date 2020-12-20* @param {number} baseX* @param {number} baseY* @param {number} beforeX* @param {number} beforeY* @param {number} rotateAngle* @param {egret.Point} resultPos* @returns {egret.Point}* @memberof ShowUtil*/public rotateByBasePoint(baseX: number, baseY: number,beforeX: number, beforeY: number,rotateAngle: number,resultPos: egret.Point): egret.Point {let baseP: egret.Point = ObjectPool.pop(ObjectType.Point);baseP.setTo(baseX, baseY);let beforeP: egret.Point = ObjectPool.pop(ObjectType.Point);beforeP.setTo(beforeX, beforeY);let distance = egret.Point.distance(beforeP, baseP);let beforeAngle = this.getAngle(baseX, baseY, beforeX, beforeY);let afterAngle = this.getRealAngle(beforeAngle + rotateAngle);let radian = this.angle2radian(afterAngle);resultPos.x = Math.cos(radian) * distance + baseX;resultPos.y = Math.sin(radian) * distance + baseY;ObjectPool.push(baseP);ObjectPool.push(beforeP);return resultPos;}/*** @description 炮弹射程1500就够了* @author (pdh)* @date 2020-11-08* @static* @returns {number}* @memberof ShowUtil*/public getMaxDistance(): number {let w = GDatas.getStageWidth();let h = GDatas.getStageHeight();let distance = Math.sqrt(w * w + h * h);return distance + 100;}/*** @description 判断点是否落在矩形内。 注意注意:矩形顶角坐标传入次序不要搞错* @author (pdh)* @date 2020-12-20* @param {egret.Point} checkPoint  要检测的点* @param {egret.Point} rectLT        左上角* @param {egret.Point} rectRT       右上角* @param {egret.Point} rectRB       右下角* @param {egret.Point} rectLB       左下角* @returns {boolean}* @memberof ShowUtil*/public isPointInRect(checkPoint: egret.Point, rectLT: egret.Point, rectRT: egret.Point, rectRB: egret.Point, rectLB: egret.Point): boolean {let isIn = false;let dot: egret.Point = ObjectPool.pop(ObjectType.Point);dot.setTo(checkPoint.x, checkPoint.y);let lt: egret.Point = ObjectPool.pop(ObjectType.Point);lt.setTo(rectLT.x, rectLT.y);let rt: egret.Point = ObjectPool.pop(ObjectType.Point);rt.setTo(rectRT.x, rectRT.y);let rb: egret.Point = ObjectPool.pop(ObjectType.Point);rb.setTo(rectRB.x, rectRB.y);let lb: egret.Point = ObjectPool.pop(ObjectType.Point);lb.setTo(rectLB.x, rectLB.y);let angle = this.getAngle(lt.x, lt.y, rt.x, rt.y);//以左上角lt为原点,将要判断的各个点转换为正常坐标系this.rotateByBasePoint(lt.x, lt.y, rt.x, rt.y, -angle, rt);this.rotateByBasePoint(lt.x, lt.y, rb.x, rb.y, -angle, rb);this.rotateByBasePoint(lt.x, lt.y, lb.x, lb.y, -angle, lb);this.rotateByBasePoint(lt.x, lt.y, dot.x, dot.y, -angle, dot);let x = lt.x < rb.x ? lt.x : rb.x;let y = lt.y < rb.y ? lt.y : rb.y;let width = Math.abs(rb.x - lt.x);let height = Math.abs(rb.y - lt.y);if ((dot.x >= x && dot.x < (x + width))&& (dot.y >= y && dot.y < (y + height))) {isIn = true;}ObjectPool.push(lt);ObjectPool.push(rt);ObjectPool.push(rb);ObjectPool.push(lb);ObjectPool.push(dot);return isIn;}/*** @description 检测闭合的两个多边形是否碰撞。注意注意:只判断边线相交,不判断两个entity互相包含的情况*                   判断entity互相包含,请参考{@link ShowUtils.isPointInRect}* @author (pdh)* @date 2020-11-14* @param {egret.Point[]} srcShape* @param {egret.Point[]} dstShape* @returns {boolean}* @memberof ShowUtil*/public crashEdges(srcShape: egret.Point[], dstShape: egret.Point[]): boolean {let isCrash: boolean = false;let START = "start";let END = "end";let srcLines = [];for (let sIndex = 0; sIndex < srcShape.length; sIndex++) {let srcStart = srcShape[sIndex];let srcEnd = srcShape[0];if ((srcShape.length - 1) != sIndex) {srcEnd = srcShape[sIndex + 1];}srcLines.push({ [START]: srcStart, [END]: srcEnd });}for (let dIndex = 0; dIndex < dstShape.length; dIndex++) {let dstStart = dstShape[dIndex];let dstEnd = dstShape[0];if ((dstShape.length - 1) != dIndex) {dstEnd = dstShape[dIndex + 1];}for (let line of srcLines) {let srcStart = line[START];let srcEnd = line[END];let resultPoint: egret.Point = ObjectPool.pop(ObjectType.Point);resultPoint.setTo(0, 0);isCrash = this.crashLine(srcStart, srcEnd, dstStart, dstEnd, resultPoint);ObjectPool.push(resultPoint);if (isCrash) {return true;}}}return isCrash;}/*** @description 判断两个矩形是否相撞* @author (pdh)* @date 2020-12-21* @param {egret.Point} srcStageLT* @param {egret.Point} srcStageRT* @param {egret.Point} srcStageRB* @param {egret.Point} srcStageLB* @param {egret.Point} dstStageLT* @param {egret.Point} dstStageRT* @param {egret.Point} dstStageRB* @param {egret.Point} dstStageLB* @returns {boolean}* @memberof ShowUtil*/public crashRect(srcStageLT: egret.Point,srcStageRT: egret.Point,srcStageRB: egret.Point,srcStageLB: egret.Point,dstStageLT: egret.Point,dstStageRT: egret.Point,dstStageRB: egret.Point,dstStageLB: egret.Point,isExact: boolean = false): boolean {let isCrash: boolean = false;let srcPoints: egret.Point[] = [srcStageLB, srcStageRB, srcStageRT, srcStageLT];let diameterDst: number = Math.abs(egret.Point.distance(dstStageLB, dstStageRT));let diameterSrc: number = Math.abs(egret.Point.distance(srcStageLB, srcStageRT));//两个对象间任意两点间的距离都可以let distance: number = Math.abs(egret.Point.distance(srcStageLB, dstStageLB));if (distance <= (diameterDst + diameterSrc)) {//两物体间距小于对角线长度之和,则要检测是否碰撞let dstPoints: egret.Point[] = [dstStageLB, dstStageRB, dstStageRT, dstStageLT];isCrash = ShowUtil.getInstance().crashEdges(srcPoints, dstPoints);if (isExact && !isCrash) {//TODO: pdh 太耗费性能,部分手机FPS掉到7帧,先屏蔽// 检测一个物体将另一个物体完全包住,各条边都没有碰撞的情况let srcArea = Math.abs((srcStageRB.x - srcStageLT.x) * (srcStageRB.y - srcStageLT.y));let dstArea = Math.abs((dstStageRB.x - dstStageLT.x) * (dstStageRB.y - dstStageLT.y));if (dstArea >= srcArea) {for (let point of srcPoints) {if (this.isPointInRect(point, dstStageLT, dstStageRT, dstStageRB, dstStageLB)) {isCrash = true;break;}}} else {for (let point of dstPoints) {if (this.isPointInRect(point, srcStageLT, srcStageRT, srcStageRB, srcStageLB)) {isCrash = true;break;}}}}}return isCrash;}/*** @description 检查两个IQuickCheckCrash是否碰撞,请确保前面调用过 CrashObject.refreshPositions();* @author (pdh)* @date 2020-12-21* @param {IQuickCheckCrash} srcEntity* @param {IQuickCheckCrash} dstEntity* @param {boolean} [isExact=true] 是否精确检查* @returns {boolean}* @memberof ShowUtil*/public checkCrashObject(srcEntity: IQuickCheckCrash, dstEntity: IQuickCheckCrash, isExact = true): boolean {let srcLT = srcEntity.stageCrashLeftTop;let srcRT = srcEntity.stageCrashRightTop;let srcRB = srcEntity.stageCrashRightBottom;let srcLB = srcEntity.stageCrashLeftBottom;let dstLT = dstEntity.stageCrashLeftTop;let dstRT = dstEntity.stageCrashRightTop;let dstRB = dstEntity.stageCrashRightBottom;let dstLB = dstEntity.stageCrashLeftBottom;let isCrash: boolean = this.crashRect(srcLT, srcRT, srcRB, srcLB, dstLT, dstRT, dstRB, dstLB, isExact);return isCrash;}/*** @description 检查两个物体是否相撞,或者互相包含* @author (pdh)* @date 2020-11-22* @param {egret.DisplayObject} objSrc* @param {egret.DisplayObject} objDst* @param {boolean} isExact 是否精确检测. 精确检测更费时间* @returns {boolean}* @memberof ShowUtil*/public checkCrashDisplayObject(objSrc: egret.DisplayObject, objDst: egret.DisplayObject, isExact: boolean = false): boolean {let isCrash = false;let srcLB: egret.Point = ObjectPool.pop(ObjectType.Point);//左下角let srcRB: egret.Point = ObjectPool.pop(ObjectType.Point);//右下角let srcRT: egret.Point = ObjectPool.pop(ObjectType.Point);//右上角let srcLT: egret.Point = ObjectPool.pop(ObjectType.Point);//左上角let dstLB: egret.Point = ObjectPool.pop(ObjectType.Point);let dstRB: egret.Point = ObjectPool.pop(ObjectType.Point);let dstRT: egret.Point = ObjectPool.pop(ObjectType.Point);let dstLT: egret.Point = ObjectPool.pop(ObjectType.Point);let srcW = objSrc.width;let srctH = objSrc.height;objSrc.localToGlobal(0, srctH, srcLB);objSrc.localToGlobal(srcW, srctH, srcRB);objSrc.localToGlobal(srcW, 0, srcRT);objSrc.localToGlobal(0, 0, srcLT);let dstW = objDst.width;let dstH = objDst.height;objDst.localToGlobal(0, dstH, dstLB);objDst.localToGlobal(dstW, dstH, dstRB);objDst.localToGlobal(dstW, 0, dstRT);objDst.localToGlobal(0, 0, dstLT);isCrash = this.crashRect(srcLT, srcRT, srcRB, srcLB, dstLT, dstRT, dstRB, dstLB, isExact);ObjectPool.push(dstLB);ObjectPool.push(dstRB);ObjectPool.push(dstRT);ObjectPool.push(dstLT);ObjectPool.push(srcLB);ObjectPool.push(srcRB);ObjectPool.push(srcRT);ObjectPool.push(srcLT);return isCrash;}/*** @description 判断一个物品是否在另一个物品内部* @author (pdh) * @date 2020-05-24* @param {CrashObject} srcEntity* @param {CrashObject} dstEntity* @returns {boolean}* @memberof ShowUtil*/public checkSrcInDst(srcEntity: CrashObject, dstEntity: CrashObject): boolean {let dstLT = dstEntity.stageCrashLeftTop;let dstRT = dstEntity.stageCrashRightTop;let dstRB = dstEntity.stageCrashRightBottom;let dstLB = dstEntity.stageCrashLeftBottom;let srcPoints: egret.Point[] = [srcEntity.stageCrashLeftTop,srcEntity.stageCrashRightTop,srcEntity.stageCrashRightBottom,srcEntity.stageCrashLeftBottom];let isIn: boolean = true;for (let point of srcPoints) {if (!this.isPointInRect(point, dstLT, dstRT, dstRB, dstLB)) {isIn = false;break;}}return isIn;}/*** @description 判断一个矩形是否在另一个矩形内部* @author (pdh)* @date 2020-05-24* @param {egret.Point} srcLT* @param {egret.Point} srcRT* @param {egret.Point} srcRB* @param {egret.Point} srcLB* @param {egret.Point} dstLT* @param {egret.Point} dstRT* @param {egret.Point} dstRB* @param {egret.Point} dstLB* @returns {boolean}* @memberof ShowUtil*/public checkRectInRect(srcLT: egret.Point, srcRT: egret.Point,srcRB: egret.Point, srcLB: egret.Point,dstLT: egret.Point, dstRT: egret.Point,dstRB: egret.Point, dstLB: egret.Point): boolean {let srcPoints: egret.Point[] = [srcLT, srcRT, srcRB, srcLB];let isIn: boolean = true;for (let point of srcPoints) {if (!this.isPointInRect(point, dstLT, dstRT, dstRB, dstLB)) {isIn = false;break;}}return isIn;}/*** @description 鱼被打中就涂红色* @author (pdh)* @date 2020-02-12* @returns * @memberof HurtComponent*/public getHurtColor() {if (!this.hurtColorFilter) {let color: number = 0xff0000;let spliceColor = (color) => {let result = { r: -1, g: -1, b: -1 };result.b = color % 256;result.g = Math.floor((color / 256)) % 256;result.r = Math.floor((color / 256) / 256);return result;}let result = spliceColor(color);let colorMatrix = [1, 0, 0, 0, 0,0, 1, 0, 0, 0,0, 0, 1, 0, 0,0, 0, 0, 0.5, 0];colorMatrix[0] = result.r / 255;colorMatrix[6] = result.g / 255;colorMatrix[12] = result.b / 255;this.hurtColorFilter = [];let filter = new egret.ColorMatrixFilter(colorMatrix);this.hurtColorFilter.push(filter);}return this.hurtColorFilter;}/*** @description 黑色影子* @author (pdh)* @date 2020-03-04* @returns * @memberof ShowUtil*/public getShadowColor() {if (!this.shadowColorFilter) {let color: number = 0x000001;let spliceColor = (color) => {let result = { r: -1, g: -1, b: -1 };result.b = color % 256;result.g = Math.floor((color / 256)) % 256;result.r = Math.floor((color / 256) / 256);return result;}let result = spliceColor(color);let colorMatrix = [1, 0, 0, 0, 0,0, 1, 0, 0, 0,0, 0, 1, 0, 0,0, 0, 0, 1, 0];colorMatrix[0] = result.r / 255;colorMatrix[6] = result.g / 255;colorMatrix[12] = result.b / 255;colorMatrix[18] = 0.3;this.shadowColorFilter = [];let filter = new egret.ColorMatrixFilter(colorMatrix);this.shadowColorFilter.push(filter);}return this.shadowColorFilter;}public getRedFilter() {return [new egret.GlowFilter(0xFF0000, 1, 1, 1, 6, 1, false, false)];}/*** @description 颜色渲染* @author (pdh)* @date 2020-03-04* @returns * @memberof ShowUtil*/public getShadeColor(aColor: number, force: boolean = false) {if (aColor === undefined) {return undefined;} else if (!aColor) {aColor = 0;}if (this.shadeColors.length <= 0) {let interval = 0xff / 10;let b = 0x00;while (b < 0xff) {this.shadeColors.push(b);b += interval;}let g = 0x00;while (g < 0xff) {this.shadeColors.push(g << 8);g += interval;}let r = 0x00;while (r < 0xff) {this.shadeColors.push(r << 16);r += interval;}this.shadeColors.push(0xffffff);}let nearColor = this.shadeColors[0];if (!force) {for (let i = 1; i < this.shadeColors.length; i++) {let c = this.shadeColors[i];if (aColor <= 0xff && c > 0xff) {continue;} else if (aColor > 0xff && aColor <= 0x00ff00 && (c < 0xff || c > 0X00FF00)) {continue;} else if (aColor > 0x00ff00 && c <= 0x00ff00) {continue;}let now = Math.abs(c - aColor);let last = Math.abs(nearColor - aColor);if (now < last) {nearColor = c;}}} else {nearColor = aColor;}let shade = this.shadeMap[nearColor];if (!shade) {let color: number = nearColor;let spliceColor = (color) => {let result = { r: -1, g: -1, b: -1 };result.b = color % 256;result.g = Math.floor((color / 256)) % 256;result.r = Math.floor((color / 256) / 256);return result;}let result = spliceColor(color);let colorMatrix = [1, 0, 0, 0, 0,0, 1, 0, 0, 0,0, 0, 1, 0, 0,0, 0, 0, 1, 0];colorMatrix[0] = result.r / 255;colorMatrix[6] = result.g / 255;colorMatrix[12] = result.b / 255;colorMatrix[18] = 0.7;shade = [];let filter = new egret.ColorMatrixFilter(colorMatrix);shade.push(filter);}return shade;}}

参考播放动画

/*** @description 只管绘制身体,其实是帧动画* @author (pdh)* @date 2020-10-31* @class AnimComponent* @extends {Component}*/
class AnimComponent extends Component {private _container: egret.DisplayObjectContainer;private mc: FSDefaultMovieClip;// private mcMap: { [key: string]: DefaultMovieClip };private property: AAnimEntityProperty;private adjustAnchor: boolean = false;//锚点设置在中心点private debugMc: boolean = false;//调试mcpublic constructor() {super();}public start(): void {super.start();this.property = <AAnimEntityProperty>(this.entity.property);this.mc = ObjectPool.pop(ObjectType.FSDefaultMovieClip);this.mc.setCompleteAction(this.completeAction, this);this._container = ObjectPool.pop(ObjectType.DisplayObjectContainer);this._container.x = 0;this._container.y = 0;this._container.anchorOffsetX = 0;this._container.anchorOffsetY = 0;this._container.removeChildren();this._container.addChild(this.mc);this.entity.addChild(this._container);this.startLoad();}public stop(): void {if (this._container) {this._container.x = 0;this._container.y = 0;this._container.anchorOffsetX = 0;this._container.anchorOffsetY = 0;App.DisplayUtils.removeFromParent(this._container);this._container.removeChildren();ObjectPool.push(this._container);this._container = null;}if (this.mc) {this.mc.destroy();this.mc = null;}this.adjustAnchor = false;super.stop();}public update(advancedTime: number): void {super.update(advancedTime);if (this.property.curAction !== this.mc.getCurrAction()) {this.mc.gotoAction(this.property.curAction, 1);} else {this.mc.runAction(advancedTime);}//NOTE: 销毁的时候 this.entity === null还会往下跑if (this.entity) {let containerX: number = this._container.x;let containerY: number = this._container.y;let containerAnchorX: number = this._container.anchorOffsetX;let containerAnchorY: number = this._container.anchorOffsetY;let nWidth: number = containerX - containerAnchorX + this.mc.x + this.mc.width;let nHeight: number = containerY - containerAnchorY + this.mc.y + this.mc.height;if (nWidth > 0) {this.entity.width = nWidth;}if (nHeight > 0) {this.entity.height = nHeight;}//至少要播放一帧动画后才能得到entity的有效尺寸,获得有效尺寸后才能设置锚点if (!this.adjustAnchor || this.property.anchorCenter) {let container = this._container;ShowUtil.getInstance().setAnchor(container, container.width / 2, container.height / 2);// let entity = this.entity;// ShowUtil.getInstance().setAnchor(entity, entity.width / 2, entity.height / 2);let width = this.entity.width;let height = this.entity.height;this.entity.anchorOffsetX = width / 2;this.entity.anchorOffsetY = height / 2;this.adjustAnchor = true;// egret.log("anchorX = ", this._container.anchorOffsetX, ", anchorY = ", this._container.anchorOffsetY, ", wid = ", container.width, ", height = ", container.height);}this.entity.drawDebugShape();}}/*** @description 一个动作播放完毕后,要么重复播放,要么就销毁* @author (pdh)* @date 2019-01-17* @private* @memberof AnimComponent*/private completeAction(): void {if (this.property.eActionType === EActionType.destroyAfterOnce) {//NOTE: pdh 注意,销毁的entity回收要这样写,//否则在调用destroy——>components.stop函数时会将 this.entity置为null导致无法回收ObjectPool.pushAndDestroy(this.entity);} else if (this.property.eActionType === EActionType.onceCallBack) {if (this.property.completeCallback) {this.property.completeCallback();// this.mc.gotoAction(this.property.curAction, 1);} else {this.property.curAction = ResActionDefine.normal;this.property.eActionType = EActionType.forever;this.mc.setCurrAction(null);// this.mc.gotoAction(this.property.curAction, 1);}} else {//NOTE: 正常应该是鱼被击中播放受伤动画,然后在这里转成normal动画.//但是现在受伤动作用红边代替,那就不用切换动画this.mc.gotoAction(this.property.curAction, 1);}}/*** @description 为了碰撞检测更准确,鱼的碰撞会用mc来做检测* @author (pdh)* @date 2019-01-17* @returns {DefaultMovieClip}* @memberof AnimComponent*/public getMc(): FSDefaultMovieClip {return this.mc;}/*** @description 客户提出鱼要一遍沿路径游动,一边有节奏地摇晃,增加一个用于摇晃的container* @author (pdh)* @date 2019-02-12* @returns {egret.DisplayObjectContainer}* @memberof AnimComponent*/public getContainer(): egret.DisplayObjectContainer {return this._container;}private startLoad(): void {this.isRuning = true;this.onLoadComplate();}private onLoadComplate(): void {if (!this.isRuning) {return;}let anim: string = this.property.animPrefix;let mcName: string = this.property.mcName;let mcData: egret.MovieClipData = McDataCaches.getInstance().getMcData(anim, mcName);this.mc.setMcData(mcData);this.mc.$animSpeedScale = this.property.animSpeedScale * 30 / 24;}
}

计算子弹路径

/*** @description 当前坐标及朝向* @author (pdh)* @date 2020-11-09* @class FlyPoint*/
class FlyPoint implements IReset {public x: number = 0;public y: number = 0;public angle: number = 0;public milliSecond: number = 0;//飞到该点所耗费时间public point: egret.Point = new egret.Point();public constructor() {}public init(x: number = 0, y: number = 0, angle: number = 0, milliSecond: number = 0) {this.x = x;this.y = y;this.angle = angle;this.milliSecond = milliSecond;}public getXY(): egret.Point {this.point.setTo(this.x, this.y);return this.point;}public reset() {this.x = 0;this.y = 0;this.angle = 0;this.milliSecond = 0;this.point.setTo(0, 0);}
}/*** @description 炮弹飞行,边飞行边检查碰撞* @author (pdh)* @date 2020-11-07* @class BulletFlyComponent* @extends {Component}*/
class BulletFlyComponent extends Component {private _bullet: BulletEntity;private _property: BulletProp;private _tween: egret.Tween = null;private _MAX_DISTANCE: number = 1500;//子弹射一条直线最长距离//存放子弹路径private _flyPoints: FlyPoint[];public constructor() {super();}public start(): void {super.start();this._bullet = <BulletEntity>this.entity;this._property = this._bullet.getProperty();this._MAX_DISTANCE = ShowUtil.getInstance().getMaxDistance();//使用瞄准道具的子弹要追踪指定的鱼,在this.trail()中做特殊处理if (this._property.eFireStatus === EFireStatus.TrailFire) {this._bullet.rotation = this.flyToBulletAngle(this._property.angle);} else {this._flyPoints = [];this.calculateBouncePoints(this._property.fromX,this._property.fromY,this._property.angle,this._property.bounceCount,EEdgeID.None,this._MAX_DISTANCE,this._property.tripMilliSec,this._flyPoints);//TODO: pdh可能还是在update中算比较好this._tween = egret.Tween.get(this._bullet);for (let i = 0; i < this._flyPoints.length - 1; i++) {let fromPoint = this._flyPoints[i];let toPoint = this._flyPoints[i + 1];let angle = this.flyToBulletAngle(fromPoint.angle);this._tween.to({ rotation: angle }, 1);this._tween.to({ x: toPoint.x, y: toPoint.y }, toPoint.milliSecond);}this._tween.call(() => {if (this._bullet) {ObjectPool.pushAndDestroy(this._bullet);this._bullet = null;}});}let battery: BatteryEntity = this._bullet.getBattery();// egret.log("BulletFlyComponent battery=", this._property.batteryIndex, ", status = ", this._property.eFireStatus, ", battery.angle=", battery.angle, ", bullet.angle=", this._property.angle, ", trailFish = ", battery.getTrailDynamicId())App.SoundManager.playEffect(ResSoundDefine.s_fire_mp3);let fps = GDatas.getFrameRate();// egret.log("bullet battery id = ", this._property.batteryIndex, ", fireStatus = ", this._property.eFireStatus);// this.dealInterval = 1 / (fps / 2);//两帧检测一次碰撞。否则差手机浏览器上有点卡。}public stop(): void {if (this._flyPoints) {for (let k in this._flyPoints) {let point = this._flyPoints[k];point.reset();ObjectPool.push(point);}this._flyPoints.splice(0, this._flyPoints.length);this._flyPoints = null;}if (this._bullet) {egret.Tween.removeTweens(this._bullet);this._bullet = null;}super.stop();}public update(delta: number): void {super.update(delta);if (this.entity) {let bullet: BulletEntity = <BulletEntity>(this.entity);//note: pdh 新UI不需要缩放// if (bullet.width > 0) {//     bullet.scaleX = 60 / bullet.width;//     bullet.scaleY = bullet.scaleX;// }}this._bullet.refreshPositions();this.trail(delta);this.checkHit();}/*** @description 使用道具的子弹要做特殊处理* @author (pdh)* @date 2020-12-07* @param {number} deltaTime 两帧之间间隔几毫秒* @memberof BulletFlyComponent*/public trail(deltaTime: number) {if (this._property.eFireStatus === EFireStatus.TrailFire) {let fish: FishEntity = null;let battery: BatteryEntity = this._bullet.getBattery();if (battery) {fish = battery.getTrailFish();}if (fish) {let inScreen: boolean = ShowUtil.getInstance().checkCrashObject(fish, this._bullet.gameView.bg, true);if (!inScreen) {//鱼已经不在屏幕内,则忽略fish = null;}}let toX: number = 0;let toY: number = 0;let maxDistance: number = this._MAX_DISTANCE;let interval: number = maxDistance * deltaTime / this._property.tripMilliSec;let angle: number = 0;if (fish) {//追着鱼打angle = ShowUtil.getInstance().getAngle(this._bullet.relativeCrashAnchor.x, this._bullet.relativeCrashAnchor.y, fish.relativeCrashAnchor.x, fish.relativeCrashAnchor.y);this._bullet.rotation = this.flyToBulletAngle(angle);} else {//没有鱼则沿当前子弹方向飞出屏幕angle = this.bulletToFlyAngle();}let radian = ShowUtil.getInstance().angle2radian(angle);toX = Math.cos(radian) * interval + this._bullet.relativeCrashAnchor.x;toY = Math.sin(radian) * interval + this._bullet.relativeCrashAnchor.y;this._bullet.x = toX;this._bullet.y = toY;this._bullet.refreshPositions();}}/*** @description 检测子弹与鱼的碰撞* @author (pdh)* @date 2020-12-07* @returns * @memberof BulletFlyComponent*/public checkHit() {let view = <FSBattleView>App.ViewManager.getView(ViewConst.FishBattle);if (!view || !view.fishLayer) {return;}let inScreen: boolean = ShowUtil.getInstance().checkCrashObject(this._bullet, this._bullet.gameView.bg, true);if (!inScreen) {//子弹飞出屏幕后不再与鱼做碰撞检测ObjectPool.pushAndDestroy(this._bullet);return;}let battery: BatteryEntity = this._bullet.getBattery();if (battery) {let batProp: BatteryProp = battery.getProperty();if (!batProp || batProp.isEmpty) {return;}//炮弹在本炮台范围内,不要检查鱼的碰撞let inCannon = ShowUtil.getInstance().checkCrashObject(this._bullet, battery, true);if (inCannon) {return;}}let fishCount = view.fishLayer.fishs.length;for (let i: number = fishCount - 1; i >= 0; i--) {let fish: FishEntity = view.fishLayer.fishs[i];if (!fish.$isAlive) {continue;}//本子弹已经撞过的鱼就不用检测了if (this._property.eFireStatus === EFireStatus.TrailFire) {let battery: BatteryEntity = this._bullet.getBattery();if (battery) {//使用瞄准追踪道具时,只检测瞄准的鱼是否碰撞let aimId = battery.getTrailDynamicId();let fishPro: FishProp = fish.getProperty();if (fishPro && aimId !== fishPro.dynamicId) {continue;}}}let isCrash: boolean = ShowUtil.getInstance().checkCrashObject(this._bullet, fish);if (isCrash) {let bulletPos: egret.Point = this.entity.stageCrashAnchor;let fishMc: FSDefaultMovieClip = fish.getMc();if (fishMc && bulletPos) {//TODO: pdh 升级引擎版本之后碰撞检测很卡,先屏蔽掉。以后引擎速度快了再恢复// isCrash = fishMc.hitTestPoint(bulletPos.x, bulletPos.y, false);isCrash = fishMc.hitTestPoint(bulletPos.x, bulletPos.y, true);if (isCrash) {fish.hurt(this._bullet);//NOTE: andew 目前策划撞到一只鱼炮弹就爆炸销毁let pBomb: egret.Point = egret.Point.create(0, 0);let pCrash: egret.Point = this._bullet.stageCrashAnchor;fish.getNearestBombPoint(pCrash.x, pCrash.y, pBomb);//发送打中鱼协议let view = <FSBattleView>App.ViewManager.getView(ViewConst.FishBattle);if (view && view.batteryLayer) {let fishIds: number[] = [];fishIds.push(fish.getProperty().dynamicId);let battery = view.batteryLayer.getBattery(this._bullet.getProperty().batteryIndex);if (battery.getProperty().isYou) {FSGameSocket.instance().sendBulletHitFishReq(fishIds, this._bullet.getProperty().clientKey);}fishIds.splice(0, fishIds.length);fishIds = null;}ObjectPool.pushAndDestroy(this._bullet);egret.Point.release(pBomb);break;}}}}}/*** @description 计算子弹路径。放在{@flyPoints}中返回 * @author (pdh)* @date 2020-12-07* @param {number} fromX* @param {number} fromY* @param {number} angle* @param {number} bounceCount* @param {number} fromWhichEdge* @param {number} tripDistance* @param {number} tripMillSec* @param {FlyPoint[]} flyPoints* @returns {FlyPoint[]}* @memberof BulletFlyComponent*/public calculateBouncePoints(fromX: number, fromY: number, angle: number, bounceCount: number, fromWhichEdge: number, tripDistance: number, tripMillSec: number, flyPoints: FlyPoint[]): FlyPoint[] {let MAX_DISTANCE = tripDistance;//炮弹射程let MAX_TIME = tripMillSec;//DefaultDefine.BulletTripMilliSec;//炮弹打完一个射程的时间angle = ShowUtil.getInstance().getRealAngle(angle);let scrW = GDatas.getStageWidth();let scrH = GDatas.getStageHeight();let start = "start";let end = "end";let radian = ShowUtil.getInstance().angle2radian(angle);let toX = Math.cos(radian) * MAX_DISTANCE + fromX;let toY = Math.sin(radian) * MAX_DISTANCE + fromY;let pStart: FlyPoint = ObjectPool.pop(ObjectType.FlyPoint);let pEnd: FlyPoint = ObjectPool.pop(ObjectType.FlyPoint);pStart.reset();pEnd.reset();pStart.init(fromX, fromY, angle, MAX_TIME);pEnd.init(toX, toY, angle, MAX_TIME);//第一个撞击点。炮口发射点也要加到路径中if (0 == flyPoints.length) {flyPoints.push(pStart);}//反弹结束,子弹直接飞出屏幕if (bounceCount <= 0) {flyPoints.push(pEnd);return flyPoints;}//获得屏幕的四条边沿let lbPoint = egret.Point.create(0, scrH);let rbPoint = egret.Point.create(scrW, scrH);let rtPoint = egret.Point.create(scrW, 0);let ltPoint = egret.Point.create(0, 0);let lineBottom = { [start]: lbPoint, [end]: rbPoint };let lineRight = { [start]: rbPoint, [end]: rtPoint };let lineTop = { [start]: rtPoint, [end]: ltPoint };let lineLeft = { [start]: ltPoint, [end]: lbPoint };let screenEdge = [lineBottom, lineRight, lineTop, lineLeft];//检查与哪条边相撞,撞击点在哪里let edgeIndex: EEdgeID = null;let collisionPoint: FlyPoint = null;for (let i = 0; i < screenEdge.length; i++) {//子弹从哪条边发来的,则哪条边就不用检查。 不在screenEdge中的边也不用检查if (fromWhichEdge === i || !screenEdge[i]) {continue;}let edge = screenEdge[i];let point = egret.Point.create(0, 0);let crashed = ShowUtil.getInstance().crashLine(edge[start], edge[end],pStart.getXY(), pEnd.getXY(),point);//相撞了    if (crashed) {edgeIndex = i;collisionPoint = ObjectPool.pop(ObjectType.FlyPoint);collisionPoint.init(point.x, point.y, angle);egret.Point.release(point);break;}egret.Point.release(point);}egret.Point.release(lbPoint);egret.Point.release(rbPoint);egret.Point.release(rtPoint);egret.Point.release(ltPoint);lbPoint = null;rbPoint = null;rtPoint = null;ltPoint = null;//撞到屏幕一条边沿则反弹if (collisionPoint && null != edgeIndex) {switch (edgeIndex) {//撞到底边case EEdgeID.Bottom: {angle = ShowUtil.getInstance().getRealAngle(0 - angle);break;}//撞到右边case EEdgeID.Right: {angle = ShowUtil.getInstance().getRealAngle(180 - angle);break;}//撞到上边case EEdgeID.Top: {angle = ShowUtil.getInstance().getRealAngle(0 - angle);break;}//撞到左边case EEdgeID.Left: {angle = ShowUtil.getInstance().getRealAngle(180 - angle);break;}default: {egret.error("calculateBouncePoints: impossible. edgeIndex = ", edgeIndex);break;}}collisionPoint.angle = angle;collisionPoint.milliSecond = 0;let distance = egret.Point.distance(collisionPoint.getXY(), pStart.getXY());distance = Math.abs(distance);if (distance > 0) {collisionPoint.milliSecond = (distance / MAX_DISTANCE) * MAX_TIME;}flyPoints.push(collisionPoint);bounceCount--;return this.calculateBouncePoints(collisionPoint.x, collisionPoint.y, angle, bounceCount, edgeIndex, MAX_DISTANCE, MAX_TIME, flyPoints);}return flyPoints;}/*** @description 飞行角度与子弹贴图角度有偏差* @author (pdh)* @date 2020-12-07* @private* @returns {number}* @memberof BulletFlyComponent*/private bulletToFlyAngle(): number {let angle = this._bullet.rotation + 270;return ShowUtil.getInstance().getRealAngle(angle);}/*** @description 飞行角度与子弹贴图角度有偏差* @author (pdh)* @date 2020-12-07* @private* @param {number} flyAngle* @returns {number}* @memberof BulletFlyComponent*/private flyToBulletAngle(flyAngle: number): number {return ShowUtil.getInstance().getRealAngle(flyAngle - 270);}
}

碰撞、子弹路径、参考相关推荐

  1. 剑指Offer_12_矩阵中的路径(参考问题:马踏棋盘)

    题目描述  请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵 ...

  2. appnode php,环境软件路径参考

    [TOC] ## Nginx * 主程序路径:/usr/sbin/nginx * 配置文件路径:/etc/nginx.conf * 服务控制命令: * CentOS 6:service nginx s ...

  3. 路径规划的二次规划方法-知识总结

    路径规划的二次规划方法-知识总结 关于路径规划的优化方法,常见的有二次规划QP,样条二次规划SQP,这里以Apollo的优化方法为例子,作为一个笔记的记录,记录一些个人的问题. 二次规划基础知识 二次 ...

  4. 基于遗传算法的移动机器人路径规划

      之前在网上找基于遗传算法的移动机器人路径规划资料的时候,没有找到理想的开源代码.最后参照论文,用matlab写了代码.最近开了公众号--Joe学习笔记,会不定期更新一些文章,主要是自己平时学到的知 ...

  5. 【路径规划】基于matlab遗传优化模拟退火算法避障路径规划【含Matlab源码 889期】

    ⛄一.简介 路径规划主要是让目标对象在规定范围内的区域内找到一条从起点到终点的无碰撞安全路径.路径规划中有静态路径规划以及动态路径规划,本文所讨论的问题仅针对静态路径规划.具体问题描述如下: 给定起点 ...

  6. unity3d 脚本参考-技术文档

    unity3d 脚本参考-技术文档 核心提示:一.脚本概览这是一个关于Unity内部脚本如何工作的简单概览.Unity内部的脚本,是通过附加自定义脚本对象到游戏物体组成的.在脚本对象内部不同志的函数被 ...

  7. Apollo星火计划学习笔记——Apollo路径规划算法原理与实践

    文章目录 前言 1. 路径规划算法总体介绍 1.1 Task: LANE_CHANGE_DECIDER 1.2 Task: PATH_REUSE_DECIDER 1.3 Task: PATH_BORR ...

  8. 彻底解决web开发中遇到的路径问题(上)

    注:本文部分引用了网络上的文章,以及动力节点老师的讲解内容,感谢老师,嘻嘻. 为了举例方便,我新建了pathTest项目: 关于tomcat的配置,eclipse访问项目的路径一般是localhost ...

  9. mysql 分区指定路径_[数据库]MySQL 指定各分区路径

    [数据库]MySQL 指定各分区路径 0 2016-11-08 18:00:44 介绍 可以针对分区表的每个分区指定各自的存储路径,对于innodb存储引擎的表只能指定数据路径,因为数据和索引是存储在 ...

最新文章

  1. 如何区别一幅图像是否是黑白图像
  2. 机器学习 —— 基础整理(三)生成式模型的非参数方法: Parzen窗估计、k近邻估计;k近邻分类器...
  3. 美国教授北大演讲:并不是每个人都适合做学术
  4. 【操作系统】分段内存管理
  5. 机器人码垛搬运编程程序_码垛机器人市场进一步扩张,解放人力搬运跑不了了...
  6. python写二进制大文件,如何将文件写入二进制文件,或在大文件中编辑单行– Python...
  7. UVA722 LA5359 Lakes【DFS】
  8. ASP.NET MVC中的控制器激活与反射之间的联系(帮助理解)
  9. 入门级XML学习(三)
  10. 1005 地球人口承载力估计
  11. 认知无线电----能量检测法原理介绍及MATLAB实现
  12. 【场景化解决方案】OA审批与用友U9数据集成
  13. DELL D630 显卡门事件 终于碰上了~
  14. jbx添加加mysql驱动
  15. 百度地图 自定义结果面板+分页+图层标注(标注点+搜索)
  16. mysql cast 整数_Mysql-CAST/CONVERT 类型转换函数之 整型
  17. 想做跨境电商,这几种收款方式必须知道!!
  18. 用Winrar打造永不被杀的免杀捆绑器!
  19. JAVA应用程序转换为Applet
  20. 推荐最适合IT人自学的视频网站和社区网站

热门文章

  1. 【ROSE】1. Rational Rose简介
  2. python数据分析 - numpy | ndarray数组 | numpy常用函数
  3. 用计算机做图画ppt,用计算机画图课件.ppt
  4. 【总结】1296- 总结 12 个常见移动端 H5 与 Hybrid 开发问题
  5. c语言程序题在哪儿搜,国家二级计算机考试c语言题库
  6. 餐饮行业的营销策略是什么?
  7. win10开机桌面图像获取
  8. 方便地边看便翻译原版pdf文章(wps)
  9. TikTok搬运视频怎么才会不被限流?
  10. 华为云——开发者技能测评