碰撞、子弹路径、参考
程序员文章站
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);
}
}