cocos2d-x 中粒子系统的加载优化

发表于2015-10-31
评论0 1.6k浏览

Cocos2d-x的粒子系统是通过加载plist生成的。plist包含两部分内容:粒子系统属性和粒子纹理。然而每次调用create都会对plist进行读取解析,如果重复地使用同一个粒子效果,这样的调用明显是低效冗余的。所以我们要做的是,将粒子系统属性和粒子纹理分别抽出。

(1)将粒子系统属性预加载并全局保存,避免每次进行读取。

(2)粒子纹理可视且可以进行纹理打包,加载粒子纹理就和加载普通的图片一样。


本文通过增加ParticleSystemQuad的接口实现对粒子系统属性和纹理帧的直接载入,来提高粒子系统的加载效率和实现内存纹理的优化。


1.ParticleSystemQuad

首先先看下ParticleSystemQuad,ParticleSystemQuad继承于ParticleSystem,拥有后者的所有特性,并且增加了一些新的特性:

(1)粒子大小支持浮点数

(2)支持缩放

(3)支持选择

(4)支持subrect

(5)支持批渲染


ParticleSystemQuad同时也是其他特效的父类,创建一个粒子系统的函数调用顺序为:

1
Create→initWithFile→initWithDictionary

 

在initWithDictionary中对粒子数据和纹理进行了读取和解析(这部分有兴趣的可以直接看源码)。


2.如何优化?

参考initWithDictionary的函参

1
static bool initWithDictionary(ValueMap& dictionary, const std::string& dirname);


设计如下接口,当然你要传入文件名也可以:

1
2
static ParticleSystemQuad * create(ValueMap& valueMap, SpriteFrame *frame);  
bool initWithValueMap(ValueMap &valueMap, SpriteFrame* frame);

  

源码如下:

注意,这里只是提供了通过粒子系统属性和纹理创建粒子系统的接口,并没有实现对粒子属性的全局保存(可以参考Earth Warriors 3D中ParticleManager的实现)和图片帧的预加载。

1
2
3
4
5
6
7
8
9
10
11
ParticleSystemQuad * ParticleSystemQuad::create( ValueMap& map, SpriteFrame *frame)  
 {  
     ParticleSystemQuad *ret = new ParticleSystemQuad();  
     if (ret && ret->initWithValueMap(map, frame))  
     {  
         ret->autorelease();  
         return ret;  
     }  
     CC_SAFE_DELETE(ret);  
     return ret;  
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
bool ParticleSystemQuad::initWithValueMap(ValueMap &valueMap, SpriteFrame* frame)
 {
     std::string dirname = "";
     bool ret = false;
     unsigned char *buffer = nullptr;
     unsigned char *deflated = nullptr;
   
     do
     {
         int maxParticles = valueMap["maxParticles"].asInt();
         // self, not super
         if(this->initWithTotalParticles(maxParticles))
         {
             // Emitter name in particle designer 2.0
             _configName = valueMap["configName"].asString();
   
             // angle
             _angle = valueMap["angle"].asFloat();
             _angleVar = valueMap["angleVariance"].asFloat();
   
             // duration
             _duration = valueMap["duration"].asFloat();
   
             // blend function
             if (_configName.length()>0)
             {
                 _blendFunc.src = valueMap["blendFuncSource"].asFloat();
             }
             else
             {
                 _blendFunc.src = valueMap["blendFuncSource"].asInt();
             }
             _blendFunc.dst = valueMap["blendFuncDestination"].asInt();
   
             // color
             _startColor.r = valueMap["startColorRed"].asFloat();
             _startColor.g = valueMap["startColorGreen"].asFloat();
             _startColor.b = valueMap["startColorBlue"].asFloat();
             _startColor.a = valueMap["startColorAlpha"].asFloat();
   
             _startColorVar.r = valueMap["startColorVarianceRed"].asFloat();
             _startColorVar.g = valueMap["startColorVarianceGreen"].asFloat();
             _startColorVar.b = valueMap["startColorVarianceBlue"].asFloat();
             _startColorVar.a = valueMap["startColorVarianceAlpha"].asFloat();
   
             _endColor.r = valueMap["finishColorRed"].asFloat();
             _endColor.g = valueMap["finishColorGreen"].asFloat();
             _endColor.b = valueMap["finishColorBlue"].asFloat();
             _endColor.a = valueMap["finishColorAlpha"].asFloat();
   
             _endColorVar.r = valueMap["finishColorVarianceRed"].asFloat();
             _endColorVar.g = valueMap["finishColorVarianceGreen"].asFloat();
             _endColorVar.b = valueMap["finishColorVarianceBlue"].asFloat();
             _endColorVar.a = valueMap["finishColorVarianceAlpha"].asFloat();
   
             // particle size
             _startSize = valueMap["startParticleSize"].asFloat();
             _startSizeVar = valueMap["startParticleSizeVariance"].asFloat();
             _endSize = valueMap["finishParticleSize"].asFloat();
             _endSizeVar = valueMap["finishParticleSizeVariance"].asFloat();
   
             // position
             float x = valueMap["sourcePositionx"].asFloat();
             float y = valueMap["sourcePositiony"].asFloat();
             this->setPosition( Point(x,y) );
             _posVar.x = valueMap["sourcePositionVariancex"].asFloat();
             _posVar.y = valueMap["sourcePositionVariancey"].asFloat();
   
             // Spinning
             _startSpin = valueMap["rotationStart"].asFloat();
             _startSpinVar = valueMap["rotationStartVariance"].asFloat();
             _endSpin= valueMap["rotationEnd"].asFloat();
             _endSpinVar= valueMap["rotationEndVariance"].asFloat();
   
             _emitterMode = (Mode) valueMap["emitterType"].asInt();
   
             // Mode A: Gravity + tangential accel + radial accel
             if (_emitterMode == Mode::GRAVITY)
             {
                 // gravity
                 modeA.gravity.x = valueMap["gravityx"].asFloat();
                 modeA.gravity.y = valueMap["gravityy"].asFloat();
   
                 // speed
                 modeA.speed = valueMap["speed"].asFloat();
                 modeA.speedVar = valueMap["speedVariance"].asFloat();
   
                 // radial acceleration
                 modeA.radialAccel = valueMap["radialAcceleration"].asFloat();
                 modeA.radialAccelVar = valueMap["radialAccelVariance"].asFloat();
   
                 // tangential acceleration
                 modeA.tangentialAccel = valueMap["tangentialAcceleration"].asFloat();
                 modeA.tangentialAccelVar = valueMap["tangentialAccelVariance"].asFloat();
   
                 // rotation is dir
                 modeA.rotationIsDir = valueMap["rotationIsDir"].asBool();
             }
   
             // or Mode B: radius movement
             else if (_emitterMode == Mode::RADIUS)
             {
                 if (_configName.length()>0)
                 {
                     modeB.startRadius = valueMap["maxRadius"].asInt();
                 }
                 else
                 {
                     modeB.startRadius = valueMap["maxRadius"].asFloat();
                 }
                 modeB.startRadiusVar = valueMap["maxRadiusVariance"].asFloat();
                 if (_configName.length()>0)
                 {
                     modeB.endRadius = valueMap["minRadius"].asInt();
                 }
                 else
                 {
                     modeB.endRadius = valueMap["minRadius"].asFloat();
                 }
                 modeB.endRadiusVar = 0.0f;
                 if (_configName.length()>0)
                 {
                     modeB.rotatePerSecond = valueMap["rotatePerSecond"].asInt();
                 }
                 else
                 {
                     modeB.rotatePerSecond = valueMap["rotatePerSecond"].asFloat();
                 }
                 modeB.rotatePerSecondVar = valueMap["rotatePerSecondVariance"].asFloat();
   
             } else {
                 CCASSERT( false, "Invalid emitterType in config file");
                 CC_BREAK_IF(true);
             }
   
             // life span
             _life = valueMap["particleLifespan"].asFloat();
             _lifeVar = valueMap["particleLifespanVariance"].asFloat();
   
             // emission Rate
             _emissionRate = _totalParticles / _life;
   
             //don't get the internal texture if a batchNode is used
             if (!_batchNode)
             {
                 // Set a compatible default for the alpha transfer
                 _opacityModifyRGB = false;
   
   
                 if (!_configName.empty())
                 {
                     _yCoordFlipped = valueMap["yCoordFlipped"].asInt();
                 }
   
             }
             setDisplayFrame(frame);
             ret = true;
         }
     } while (0);
     free(buffer);
     free(deflated);
     return ret;
   
 }



粒子属性的获取:

1
ValueMap FileUtilsApple::getValueMapFromFile(const std::string& filename);


图片帧的获取(这个获取方式就比较多了):

1
SpriteFrame* create(const std::string& filename, const Rect& rect);


3.如何使用?

1
2
3
4
auto plistData = FileUtils::getInstance()->getValueMapFromFile("Particles/emissionPart.plist");  
auto emission_frame = SpriteFrame::create("Images/engine.jpg", Rect(0,0,25,32));   
auto emitter = ParticleSystemQuad::create(plistData, emission_frame);  
_background->addChild(_emitter, 10);

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引