閒聊js14: 實現一個關鍵的,最小化的,非場景圖型別的精靈系統(下)

weixin_33890499發表於2017-08-18

閒聊js: 實現一個關鍵的,最小化的,非場景圖型別的精靈系統(上)一文中完成了精靈系統,但是還缺乏空間變換和動畫runLoop的支援,本篇完成上述需求!

本篇目的:

  • 為BLFSprite新增空間變換相關的成員變數
  • 為BLFSprite新增beginRender/endRender方法,有利於變換狀態的操作
  • 增加兩個精靈類:BLFGridSprite和BLFDemoSprite
  • 在引擎中封裝requestAnimationFrame操作,讓引擎動起來(遇到了一點問題,花了點時間,還是this指標導致的問題!)
  • 動起來,轉起來

精靈系統最大的作用是封裝動畫和渲染操作,讓我們新增空間變換方面的內容吧!

  1. BLFSprite中暫時先新增平移,旋轉操作:
class BLFSprite {
    constructor(name = '') {
        this.typeName = "BASE";
        this.name = name;
        this.color = "rgba(255,0,0,1)";

        //當前精靈的位置座標
        this.x = 0;
        this.y = 0;

        //當前精靈的旋轉角度
        this.rotation = 0;

        //用於移動原點偏移量
        //例如rectSprite的原點在左上角,而我們想旋轉的時候以rect中心點進行,此時需要通過使用centerX,centerY進行操作
        this.centerX = 0;
        this.centerY = 0;
    }
}
  1. BLFSprite中新增beginRender/endRender用於控制空間變換操作和狀態:
 beginRender(render) {
        //將渲染器的變換矩陣歸一化
        render.resetTransform();
        
        //設定精靈的原點
        if (this.x != 0 || this.y != 0)
            render.translate(this.x, this.y);

        //旋轉中心點的設定
        if (this.rotation != 0)
            if (this.centerX != 0 || this.centerY != 0) {
                render.rotateAt(this.centerX, this.centerY, this.rotation);
            } else
                render.rotate(this.rotation);
    }

    endRender(render) {
         //將渲染器的變換矩陣恢復原始狀態
        render.resetTransform();
    }
  1. 原來程式碼BLFSprite default情況下渲染網格背景,現在使用BLFGridSprite精靈做背景,因此基類render成純虛方法,子類如要顯示,必須override!
 //虛擬函式,如有需要,子類需override
    //default實現:原本繪製背景,現改成純虛方法
    render(render) {

    }

實現BLFGridSprite和BLFDemoSprite:

  1. BLFGridSprite:
class BLFGridSprite extends BLFSprite {
    constructor(name = '') {
        //super([基類建構函式引數列表])
        super(name);

        //this呼叫父類的成員屬性必須在super()呼叫後才ok!!!!
        this.typeName = "GridBackgroud";
    }

    render(render) {
        render.drawGrid('black', 'white', 20, 20);
    }
}
  1. BLFDemoSprite:
class BLFDemoSprite extends BLFSprite {
    constructor(center = false, name = '') {
        //super([基類建構函式引數列表])
        super(name);

        //this呼叫父類的成員屬性必須在super()呼叫後才ok!!!!
        this.typeName = "DemoSprite";

        this.x = 250;
        this.y = 250;

        this.rotateSpeed = 1;

        this.rect = new Rect(0, 0, 100, 50);
        this.lines = [new Point(100, 25), new Point(150, 25)];
        
        //如果center = true
        //旋轉中心為rect的中心點,否則為rect的左上角
        if (center) {
            this.centerX = this.rect.width * 0.5;
            this.centerY = this.rect.height * 0.5;
        }
    }

    //渲染rect
    //粗線渲染朝向標記線
   //細線渲染座標軸
    render(render) {
        render.drawRect(this.rect, 'blue');
        render.pushStates();
        render.setLineState(3.0);
        render.drawLine(this.lines[0].x, this.lines[0].y, this.lines[1].x, this.lines[1].y, 'green');
        render.popStates();
        render.drawCoords(this.rect);
    }

    //運動都在update中進行數值更新
    update(msec) {
        this.rotation += this.rotateSpeed;
    }
}

在引擎中封裝requestAnimationFrame,動起來了!

//修改上一節中BLFRender中定義的run函式,讓它真正的runLoop起來:
class BLFEngine2D {

     run(msec) {
        this.updateAll(msec);
        this.renderAll();

        //呼叫requestAnimationFrame
        requestAnimationFrame((msec) => { this.run(msec) });

        /*非箭頭函式解決方案
        let self = this;
        requestAnimationFrame(function(msec) {
            self.run(msec);
        });
        */
    }
}

這個函式花了我一點時間,歸根結底還是經典的this指向問題!

  • DOM中,使用requestAnimationFrame進行動畫操作
  • requestAnimationFrame的引數是型別為:
    function animate(time)為原型的回撥函式
  • 但是我這裡為了封裝到BLFRender2D中,剛開始死活不行,原因是this指向出了問題。
  • 解決this指向:箭頭函式/let self = this;這兩種方式!

測試程式碼:

<script>
        let canvas = document.getElementById("myCanvas");
        let context = canvas.getContext('2d');
        let engine = new BLFEngine2D(context);
        let spr = new BLFRectSprite(new Rect(0, 0, 50, 25), "spRect");

        let spr2 = new BLFDemoSprite();
        spr2.x = 400; //調整位置
        spr2.rotateSpeed = -1; //逆時針

        engine.sprMgr.addSprite(new BLFGridSprite("background"));
        engine.sprMgr.addSprite(spr);
        engine.sprMgr.addSprite(spr2); //逆時針左上角旋轉
        engine.sprMgr.addSprite(new BLFDemoSprite(true)); //順時針中心旋轉

        //引擎進入loop
        engine.run();
    </script>
2635028-1fa850a8799fd7ec.png
demoSprite.png

htmlpreview.github.io/?https://github.com/jackyblf/BLF_JS_Demos/blob/master/spritesystemtest.html

到目前為止,一個可演示用的渲染系統和簡單的非場景圖型別的動畫精靈系統已經完成,下一篇進入:動畫、數學與碰檢篇章

相關文章