欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

碰撞、子弹路径、参考

程序员文章站 2024-03-16 19:46:28
...


/**
 * @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;//调试mc

    public 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);
    }
}

相关标签: h5