• 游戏编程模式--享元模式


    享元模式

      享元模式是把数据分为两种类型,一种是不属于单一对象而是为所有对象共享的数据,GoF将其称为内部状态;而另一种数据则为单一对象独有的。比如我们要渲染很多的草和树,草和树的形状是共享的,每棵树的位置和大小等数据是对象唯一的。通过共享数据的使用来节省内存。

    地形

      我们使用一个地形的例子来解释享元模式是如何工作的。假设在我们的虚拟世界中由三种地形:草地、山地、河流,而我们使用基于瓦片(tile-based)的技术来构建地形,每个瓦片由一种地形覆盖。对于每一种地形,都有一些影响着游戏玩法的属性:

    •   移动开销决定角色穿过这个地形所使用的时间
    •   用来决定是否是一片能够行驶船只的水域的标志位
    •   纹理,用来渲染地形

      先来看一种常用的实现方法,游戏开发人员为了高性能,通常不会为每一个瓦片保存状态,为每一种地形创建一个枚举类型,每个瓦片都拥有一种地形:

    enum Terrain
    {
        TERRAIN_GRASS,
        TERRAIN_HILL,
        TERRAIN_RIVER,
        //other terrains....
    };
    
    class world
    {
    private:
        Terrain tiles_[WIDTH][HEIGHT];
    };

      这样,如果想获得瓦片的数据则使用这样的方式:

    int World::getMovementCost(int x, int y)
    {
        switch(tiles_[x][y])
        {
            case TERRAIN_GRASS:
                return 1;
            case TERRAIN_HILL:
                return 3;
            case TERRAIN_RIVER:
                return 2;
            //other Terrians..
        }
    }
    
    bool World::isWater(int x,int y)
    {
        switch(tiles_[x][y])
        {
            case TERRAIN_GRASS:
                return false;
            case TERRAIN_HILL:
                return false;
            case TERRAIN_RIVER:
                return true;
            //other terrains...
        }
    }

      这种做法是比较粗糙的,地形的数据分散在一个个的方法中,这与面向对象设计的原则不符。接下来我们看一种比较符合面向对象设计的做法:把地形的数据封装到一个地形类中,使用不同的数据实例化对应的地形对象,瓦片则引用这些地形对象即可。

      代码如下:

        class Texture
        {
        public:
            Texture(int id) :
                id_(id)
            {
    
            }
            int id_;
        };
        class Terrain
        {
        public:
            Terrain(int moveCost, bool isWater, Texture texture):
                moveCost_(moveCost),
                isWater_(isWater),
                texture_(texture)
            {
    
            }
    
            int getMoveCost()
            {
                return moveCost_;
            }
        private:
            int moveCost_;
            bool isWater_;
            Texture texture_;
        };
    
        class World
        {
        public:
            World() :
                grassTerrain_(1, false, Texture(1)),
                riverTerrain_(2, false, Texture(2)),
                hillTerrain_(3, false, Texture(3))
            {
    
            }
    
            void generateTerrain()
            {
                std::default_random_engine random;
                std::uniform_int_distribution<int> u(0, 9);
                for (int i = 0; i < kWidth; ++i)
                {
                    for (int j = 0; j < kHeight; ++j)
                    {
                        if (u(random) < 2)
                        {
                            titles_[i][j] = &hillTerrain_;
                        }
                        else
                        {
                            titles_[i][j] = &grassTerrain_;
                        }
    
    
                    }
                }
    
                std::uniform_int_distribution<int> u100(0, kWidth-1);
                int x = u100(random);
                for (int i = 0; i < kHeight; ++i)
                {
                    titles_[x][i] = &riverTerrain_;
                }
            }
    
            const Terrain& getTile(int x, int y) const
            {
                return *titles_[x][y];
            }
    
            
        private:
            const static int kWidth = 100;
            const static int kHeight = 100;
            Terrain* titles_[kWidth][kHeight];
    
            Terrain grassTerrain_;
            Terrain riverTerrain_;
            Terrain hillTerrain_;
        };

      对比之前的做法,我们可以看到其实就是枚举类型和指针的区别,有人可能会觉得指针的性能会略慢,主要考虑的是指针是间接引用,需要查找地址,而且为了获取地形的数据,需要先找到对象,然后再根据对象查找其属性,在跟踪这样的指针过程中会引起缓存未命中,拖慢程序的速度。但现代计算机非常的复杂,性能的问题通常都不是单一因素引起的,所以如果你想为了性能而不使用享元模式时,最好先分析性能的瓶颈在哪。享元模式下,内存数据存放的好,同样可以获得很高的性能

  • 相关阅读:
    vue 的模板编译—ast(抽象语法树) 详解与实现
    Vue 组件(component)之 精美的日历
    nvm 装 nodejs 重启终端失效的解决方法
    vue 2 仿IOS 滚轮选择器 从入门到精通 (一)
    np.stack() 与 tf.stack() 的简单理解
    PHP 之 Ci框架下隐藏index.php
    Boosting 简单介绍
    Adaboost算法流程及示例
    Python 之 解码汉字乱码(如果gbk、utf8都试过不行,可以试试这个)
    Linux 之 tar和nc传文件
  • 原文地址:https://www.cnblogs.com/xin-lover/p/10421451.html
Copyright © 2020-2023  润新知