# 【2D矩阵示例】

比如技能的矩形选择区域

Markdown 图片

export abstract class BaseShapeSelector {
    /** 位置 */
    position: Laya.Vector2 = new Laya.Vector2();

    /** 方向 */
    direction: Laya.Vector2 = new Laya.Vector2();

    /**
     * 设置Transform里的rotation 角度制
     * Transform里的rotation 值范围在 -360 ~ 360 之间
     *       180°
     *        ↑
     *   270°←→90°
     *        ↓
     *        0°
     */
    setRotation(rotation: number): void {
        if (rotation < 0) rotation = rotation + 360;
        if (rotation > 360) rotation = rotation - 360;
        rotation = (rotation / 180) * Math.PI; // 0 ~ 2PI
        this.direction.x = Math.sin(rotation);
        this.direction.y = Math.cos(rotation);
    }

    /** 检查指定位置是否在内部 */
    abstract checkInternal(target: Laya.Vector2): boolean;
}

/** 矩形区域选择器 */
export class RectShapeSelector extends BaseShapeSelector {
    /** 距离,同长度 */
    distance: number = 0;

    /** 宽度 */
    width: number = 60;

    private mat2D_T: Laya.Matrix3x3 = new Laya.Matrix3x3();
    private mat2D_R: Laya.Matrix3x3 = new Laya.Matrix3x3();
    private mat2D_TR: Laya.Matrix3x3 = new Laya.Matrix3x3();

    /** 目标点在矩形内的局部坐标 */
    private targetInRect: Laya.Vector2 = new Laya.Vector2();
    private resetPos: Laya.Vector2 = new Laya.Vector2();

    checkInternal(target: Laya.Vector2): boolean {
        this.direction.normalize();
        const rad = Math.atan2(this.direction.y, this.direction.x);
        this.resetPos.x = -this.position.x;
        this.resetPos.y = -this.position.y;

        Laya.Matrix3x3.createFromTranslation(this.resetPos, this.mat2D_T); // 平移到原点的矩阵
        Laya.Matrix3x3.createFromRotation(-rad, this.mat2D_R); // 旋转到水平的矩阵
        // 先平移到原点再旋转到水平,矩阵乘法操作是相反的,即旋转矩阵乘以平移矩阵
        Laya.Matrix3x3.multiply(this.mat2D_R, this.mat2D_T, this.mat2D_TR);
        // 把 target 世界坐标位置转换为矩形内的局部坐标。
        Laya.Vector2.transformCoordinate(target, this.mat2D_TR, this.targetInRect);
        // 判断局部坐标是否在矩形外,是则返回false
        if (this.targetInRect.x < 0) {
            return false;
        }
        if (this.targetInRect.x > this.distance) {
            return false;
        }
        if (this.targetInRect.y > this.width / 2) {
            return false;
        }
        if (this.targetInRect.y < -this.width / 2) {
            return false;
        }
        return true;
    }
}

/** 扇形区域选择器 */
export class FanShapeSelector extends BaseShapeSelector {
    /** 距离,同半径 */
    distance: number = 0;

    /** 扇形角度0~360 */
    angle: number = 60;

    /** position 指向 target 的向量 */
    positionToTarget: Laya.Vector2 = new Laya.Vector2();

    /** 检查指定位置是否在该扇形区域内部 */
    checkInternal(target: Laya.Vector2): boolean {
        const distance = Laya.Vector2.distance(this.position, target);
        if (distance > this.distance) {
            return false; // 距离超出了扇形
        }
        this.positionToTarget.x = target.x - this.position.x;
        this.positionToTarget.y = target.y - this.position.y;
        this.positionToTarget.normalize();
        this.direction.normalize();

        // 半角度
        const halfAngle = this.angle / 2;

        const dotVal = Laya.Vector2.dot(this.direction, this.positionToTarget);
        // 指向目标,与指向当前对象构成的夹角
        const angle2 = Math.acos(dotVal); // 0~2PI
        // angle2 转换成角度
        const angle2Degree = angle2 * (180 / Math.PI);

        if (angle2Degree > halfAngle) {
            return false; // 目标在扇形外
        }

        return true;
    }
}