闲聊js13: 实现一个关键的,最小化的,非场景图类型的精灵系统(上)

本篇目的:

  • 两种类型的精灵系统:场景图类型和非常场景图类型
  • UML静态类结构图
  • 一个关键的,最小化的,非场景图类型的精灵系统的实现源码

最小化的概念:

为了演示动画和数学方面的基础知识,在前面花了一点时间,撰写了10多篇文章,描述了如何实现一个可以演示用的渲染引擎(系统)。

而本篇继续上面的内容,实现一个最小化的,但是可运行的,并且蕴含关键技术的精灵系统 。后面我们会按需进行渲染系统和这个动画精灵系统的扩展。

场景图概念:

场景图是指:
将整个场景中每个entity(2D动画中,可以称为精灵)以层次节点的方式表示,他们之间形成具有父子关系的树结构,该树可以称为场景树(可以将树结构堪称是图结构的一种特殊方式)。

一直以来,场景图在UI引擎,2D动画引擎中占绝对地位。

3D引擎中,场景图使用也非常广泛(当然也有蛮多引擎没有使用场景图)。

可以说,2D中,场景图是必须的,但是3D中,场景图是可选的。

简单来说,2D使用场景图方式可以解决前后顺序,深度关系的问题,而3D中,因为有z轴深度值,因此使用场景图是非必须的

树结构的表示的威力巨大,但是今天我们就不闲聊这块内容。

后续我会花时间来详谈这块东西,这个是整个UI/2D引擎的精华部分!

为什么我们现在实现的是非场景图类型:

很简单,时间来不及。我先实现非场景图类型的系统,推进我们的文章往前走。

空闲时间,实现场景图基础结构,然后再融合到我们的精灵系统中。

这样也有好处,让大家非常清晰的了解到非场景图的好处和劣处,以及场景图的优点和缺点!

一张简单的UML类结构图(成员操作就懒得列出,以后有时间再添加进去):

sprite_system_uml.png

源码实现:

  • BLFRender:

闲聊js:nodejs中的类定义和继承的套路
闲聊js:创建一个演示用的渲染库1
闲聊js:js面向对象编程(es6和jsface库技术选型)
闲聊js:创建一个演示用的渲染库2 (es6版本)
闲聊js:创建一个演示用的渲染库3(尺寸这些事)
闲聊js:创建一个演示用的渲染库4(渲染表面,像素格式,光栅化,位块传输,图形与图像)
闲聊js:创建一个演示用的渲染库5(封装常用的渲染方法)
闲聊js:创建一个演示用的渲染库6(图像显示)
闲聊js:创建一个演示用的渲染库7(渲染状态及点集绘制)
闲聊js:创建一个演示用的渲染库8(颜色和像素操作)
闲聊js:创建一个演示用的渲染库9(关键的裁剪操作)
闲聊js:创建一个演示用的渲染库10(坐标轴绘制、空间变换及总结与展望)

  • BLFSprite:
class BLFSprite {
    constructor(name = '') {
        this.typeName = "BASE";
        this.name = name;
        this.color = "rgba(255,0,0,1)";
    }

    //点的碰检:
    /*虚函数,如有需要,子类需override
    default实现:目前暂时返回false
    todo:后面会实现绑定盒/圈系统,再重新实现default基类行为
    */
    hitTest(x, y) {
        return false;
    }

    //更新:
    /*虚函数,如有需要,子类需override
    default实现,啥都不干
    */
    update(msec) {
        //console.log("update BLESprite");
        console.log("update sprite:" + this.name + " with type:" + this.typeName);
    }

    //渲染:
    /*虚函数,如有需要,子类需override
    default实现:绘制背景
    */
    render(render) {
        render.clear();
        render.drawGrid('white', 'black', 20, 20);
    }

     //尺寸:
    /*虚函数,如有需要,子类需要override
    default实现: 返回背景大小
    */
    getSize() {
        return new Size(render.getCanvasWidth(), render.getCanvasHeight());
    }
}
  • BLFSpriteManager(非场景图的场景管理类):
class BLFSpriteManager {

    constructor() {
        this.sprites = [];
    }

    addSprite(sprite) {
        this.sprites.push(sprite);
    }

    //动态类型语言的好处
    //根据参数类型有针对性处理
    removeSprite(sprite) {
        let idx = -1;
        if (typeof(sprite) === "number")
            idx = sprite;
        else
            idx = this.sprites.indexOf(sprite);

        if (idx === -1)
            return false;

        this.sprites.splice(idx, 1);
        return true;
    }

    getSprite(idx) {
        if (idx < 0 || idx >= this.sprites.length)
            return;

        return this.sprites[idx];
    }

    getCount() {
        return this.sprites.length;
    }
}
  • BLFEngine2D:
class BLFEngine2D {
    constructor(context) {
        this.render = new BLFRender(context);
        this.sprMgr = new BLFSpriteManager();
    }

    //依次调用所有精灵的update方法
    //各个精灵的更新都在精灵的update中进行
   //依次调用所有精灵的update方法
    //各个精灵的更新都在精灵的update中进行
    updateAll(msec) {
        for (let i = 0; i < this.sprMgr.getCount(); i++) {
            this.sprMgr.getSprite(i).update(msec);
        }
    }

    renderAll() {
        for (let i = 0; i < this.sprMgr.getCount(); i++) {
            this.sprMgr.getSprite(i).render(this.render);
        }
    }

    printAll() {
        let arr = [];
        for (let i = 0; i < this.sprMgr.getCount(); i++) {
            arr.push(this.sprMgr.getSprite(i).name);
        }

        console.log(JSON.stringify(arr, null, ""));
    }

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

很简单的代码,但是的确,UI/2D/3D引擎最基础的宏观运行就是如此!

  • 上面算是基本框架,我们实现一个rect精灵用于演示:
/*
关于es6中的super关键字一个前提,两个用法有:
一个前提:
       只有使用了extends的子类才能使用super关键字
两个用法:
       1. 函数调用形式:
               super([基类构造函数参数列表]),必须在子类构造函数中调用super()
               this调用父类的成员属性必须在super()调用后才ok!!!!
       2. 非函数调用形式:
               在子类的成员函数中调用基类类方法时使用super关键字而不是super()函数,切记!
*/
class BLFRectSprite extends BLFSprite {
    constructor(rect = new Rect(0, 0, 100, 50), name = '') {
        //super([基类构造函数参数列表])
        super(name);

        //this调用父类的成员属性必须在super()调用后才ok!!!!
        this.typeName = "RECT";
        this.rect = rect;
    }

    render(render) {
        render.drawRect(this.rect, this.color);
    }

    getSize() {
        return new Size(this.rect.with, this.rect.height);
    }
}

测试代码:

<body>
    <canvas id="myCanvas" width="800" height="600" style="border: 1px solid black">你的浏览器还不支持哦</canvas>
    <script>
        let canvas = document.getElementById("myCanvas");
        let context = canvas.getContext('2d');

        let engine = new BLFEngine2D(context);
        engine.sprMgr.addSprite(new BLFSprite("background"));
        engine.sprMgr.addSprite(new BLFRectSprite(new Rect(0, 0, 50, 25), "spRect"));
        engine.run();
        engine.printAll();
    </script>
</body>
engine_result.png

下一篇,动起来,转起来!

附:
场景图实际是树结构,关于树结构核心是各种遍历算法,在我以前的一篇文章中设计模式在UI系统开发中的应用(导读),有提供一张详细的树结构遍历总结表,大家可以去看一下(虽然是c++描述,但可以用于任何语言)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,444评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,867评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,157评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,312评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,673评论 3 289
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,802评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,010评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,743评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,470评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,696评论 2 250
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,187评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,538评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,188评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,127评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,889评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,741评论 2 274

推荐阅读更多精彩内容