egret-貝塞爾曲線實現游戲里物體的曲線移動


egret-貝塞爾曲線實現游戲里物體的曲線移動

1.Bezier曲線的原理

Bezier曲線是應用於二維圖形的曲線。曲線由頂點和控制點組成,通過改變控制點坐標可以改變曲線的形狀。

 

 

 

 

 

 一次Bezier曲線是由P0至P1的連續點,描述的一條線段

二次Bezier曲線公式

 

 

 

 

 

 

二次Bezier曲線是 P0至P1 的連續點Q0和P1至P2 的連續點Q1 組成的線段上的連續點B(t),描述一條拋物線。

 三次Bezier曲線公式:

 

 

 

 Bezier曲線二次跟三次的實現,其實就是根據幾個連續點,還有給定的t,繪制出點。

class BezierUtil {
    /**
     * 根據傳入的錨點組返回貝塞爾曲線上的一組點,返回類型為egret.Point[];
     * @param pointsData 錨點組,保存着所有控制點的x和y坐標,格式為[x0,y0,x1,y1,x2,y2...]
     * @param pointsAmount 要獲取的點的總個數,實際返回點數不一定等於該屬性,與范圍有關
     * @returns egret.Point[];
     */
    public static createBezierPoints(pointsData: number[], pointsAmount: number): egret.Point[] {
        let points = [];
        for (let i = 0; i < pointsAmount; i++) {
            const point = this.getBezierPointByFactor(pointsData, i / pointsAmount);
            if (point)
                points.push(point);
        }
        return points;
    }
    /**
     * 根據錨點組與取值系數獲取貝塞爾曲線上的一點
     * @param pointsData 錨點組,保存着所有控制點的x和y坐標,格式為[x0,y0,x1,y1,x2,y2...]
     * @param t 取值系數
     * @returns egret.Point
     */
    public static getBezierPointByFactor(pointsData: number[], t: number): egret.Point {
        let i = 0;
        let x = 0, y = 0;
        const len = pointsData.length;
        //根據傳入的數據數量判斷是二次貝塞爾還是三次貝塞爾
        if (len / 2 == 3) {
            //二次
            const x0 = pointsData[i++];
            const y0 = pointsData[i++];
            const x1 = pointsData[i++];
            const y1 = pointsData[i++];
            const x2 = pointsData[i++];
            const y2 = pointsData[i++];
            x = this.getCurvePoint(x0, x1, x2, t);
            y = this.getCurvePoint(y0, y1, y2, t);
        } else if (len / 2 == 4) {
            //三次
            const x0 = pointsData[i++];
            const y0 = pointsData[i++];
            const x1 = pointsData[i++];
            const y1 = pointsData[i++];
            const x2 = pointsData[i++];
            const y2 = pointsData[i++];
            const x3 = pointsData[i++];
            const y3 = pointsData[i++];
            x = this.getCubicCurvePoint(x0, x1, x2, x3, t);
            y = this.getCubicCurvePoint(y0, y1, y2, y3, t);
        }
        return egret.Point.create(x, y);
    }
    /**
     * 通過factor參數獲取二次貝塞爾曲線上的位置
     * 公式為B(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2 
     * @param value0 P0
     * @param value1 P1
     * @param value2 P2
     * @param factor t,從0到1的閉區間
     */
    public static getCurvePoint(value0: number, value1: number, value2: number, factor: number): number {
        const result = Math.pow((1 - factor), 2) * value0 + 2 * factor * (1 - factor) * value1 + Math.pow(factor, 2) * value2;
        return result;
    }
    /**
     * 通過factor參數獲取三次貝塞爾曲線上的位置
     * 公式為B(t) = (1-t)^3 * P0 + 3t(1-t)^2 * P1 + 3t^2 * (1-t) t^2 * P2 + t^3 *P3 
     * @param value0 P0
     * @param value1 P1
     * @param value2 P2
     * @param value3 P3
     * @param factor t,從0到1的閉區間
     */
    public static getCubicCurvePoint(value0: number, value1: number, value2: number, value3: number, factor: number): number {
        const result = Math.pow((1 - factor), 3) * value0 + 3 * factor * Math.pow((1 - factor), 2) * value1 + 3 * (1 - factor) * Math.pow(factor, 2) * value2 + Math.pow(factor, 3) * value3;
        return result;
    }

}

2.游戲實現

根據Bezier曲線的繪制的點擊,再幀循環改變物體的位置,就可以給出曲線移動的效果。

這里demo里的三個點分別為(100,100),(500,200),(300,800)

class BezierDemoView extends eui.Component {
    private ball: egret.Shape = new egret.Shape();//小球
    private points: egret.Point[] = [];//點集
    private pIndex: number = 0;//標記小球當前對應的點的下標
    constructor() {
        super();
        this.addEventListener(eui.UIEvent.COMPLETE, this.onUIComplete, this);
        this.skinName = "BezierDemoViewSkin";
    }
    private onUIComplete(): void {
        this.points = this.drawPoints();
        this.ball.graphics.beginFill(0xbc89f3);
        this.ball.graphics.drawCircle(0, 0, 10);
        this.ball.graphics.endFill();
        this.addChild(this.ball);
        this.addEventListener(egret.Event.ENTER_FRAME, this.onUpdate, this);
    }
    /** 繪制出點集 */
    private drawPoints(): egret.Point[] {
        let pointData: number[] = [100, 100, 500, 200, 300, 800];
        let points: egret.Point[] = BezierUtil.createBezierPoints(pointData, 60);
        let len: number = points.length;
        for (let i: number = 0; i < len; i++) {
            let point: egret.Point = points[i];
            let shp: egret.Shape = new egret.Shape();
            let gp: egret.Graphics = shp.graphics;
            gp.beginFill(0xfe0035);
            gp.drawCircle(point.x, point.y, 5);
            gp.endFill();
            this.addChild(shp);
        }
        return points;
    }
    /** 幀循環,更新小球位置 */
    private onUpdate(): void {
        let point: egret.Point = this.points[this.pIndex];
        if (point) {
            this.ball.x = point.x;
            this.ball.y = point.y;
            this.pIndex++;
        } else {
            this.removeEventListener(egret.Event.ENTER_FRAME, this.onUpdate, this);
        }
    }


}

效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM