加载游戏数据和代码重构
最后更新于:2022-04-01 20:15:05
昨天复习了关于COCOS2d的一些基础知识,今天就来学习了关于游戏加载方面的问题和代码重构。
写一个简单的游戏数据加载的demo。
1.如果想在进入游戏前,加载游戏,肯定有进度条什么的吧,当然,也就是我们要再游戏开始前有一个关于游戏加载的场景类。 由于我自己写的demo丢失,只好拿着实例写了。。。sorry,不过我会认真的写注释的
建立如下:
~~~
#import
#import "cocos2d.h"
@interface LoadingScreen : CCLayer {
CGSize winSize;
CGPoint winCenter;
int assetCount;
}
+(CCScene *) scene; //场景类的转化和生成
/**Called if there is any music to load, it loads the files one by one via the NSArray */
-(void) loadMusic:(NSArray *) musicFiles; //音乐文件的加载
/**Called if there are any sfx to load, it loads the files one by one via the NSArray */
-(void) loadSounds:(NSArray *) soundClips;
/**Called if there are any Sprite Sheets to load, it loads the files one by one via the NSArray */
-(void) loadSpriteSheets:(NSArray *) spriteSheets; //精灵表单的加载,我更喜欢叫一组精灵
/**Called if there are any images to load, it loads the files one by one via the NSArray.
Images can be a cache of backgrounds or anything else. You can add to this method
to have it do whatever you want with the list.
*/
-(void) loadImages:(NSArray *) images; //图片的加载
/**Called if there are any assets to load, it loads the files one by one via the NSArray.
Assets basically are anything not of the above. This pretty much should be modified
to your needs. (Such as models, etc)
*/
-(void) loadAssets:(NSArray *) assets; //季度条进度的加载
/**updates the progress bar with the next step. When progress bar reaches 100%
It calls loadingComplete which can change scenes, or do anything else you wish.
*/
-(void) progressUpdate; //主线程 的分线程更新完成之后的调用
/**Called by progressUpdate when all assets are loaded from the manifest. */
-(void) loadingComplete; // 全部完成调用
@end
~~~
2.m文件的准备工作
~~~
#import "LoadingScreen.h"
#import "SimpleAudioEngine.h"
//The next scene you wish to transition to.
#import "HelloWorldLayer.h"
@implementation LoadingScreen
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
NSString *className = NSStringFromClass([self class]);
// 'layer' is an autorelease object.
id layer = [NSClassFromString(className) node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if ( ( self = [ super init] ) )
{
winSize = [[CCDirector sharedDirector] winSize];//寻找导演视图的宽高
winCenter = ccp(winSize.width / 2, winSize.height / 2);
CCLabelTTF *loadingText = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Arial" fontSize:20];
loadingText.position = ccpAdd(winCenter, ccp(0,50));
[self addChild:loadingText];//放置加载BAR
}
return self;
}
~~~
3.进入场景之后的加载方法,也别注意不用的资源加载的方式的不同,主线程加载
~~~
-(void) onEnterTransitionDidFinish
{
[super onEnterTransitionDidFinish];
NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:@"preloadAssetManifest.plist"];
NSDictionary *manifest = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *spriteSheets = [manifest objectForKey:@"SpriteSheets"];
NSArray *images = [manifest objectForKey:@"Images"];
NSArray *soundFX = [manifest objectForKey:@"SoundFX"];
NSArray *music = [manifest objectForKey:@"Music"];
NSArray *assets = [manifest objectForKey:@"Assets"];
assetCount = ([spriteSheets count] + [images count] + [soundFX count] + [music count] + [assets count]);
if (soundFX)
[self performSelectorOnMainThread:@selector(loadSounds:) withObject:soundFX waitUntilDone:YES];
if (spriteSheets)
[self performSelectorOnMainThread:@selector(loadSpriteSheets:) withObject:spriteSheets waitUntilDone:YES];
if (images)
[self performSelectorOnMainThread:@selector(loadImages:) withObject:images waitUntilDone:YES];
if (music)
[self performSelectorOnMainThread:@selector(loadMusic:) withObject:music waitUntilDone:YES];
if (assets)
[self performSelectorOnMainThread:@selector(loadAssets:) withObject:assets waitUntilDone:YES];
}
~~~
4.单个加载方法 SEL方法
~~~
-(void) loadMusic:(NSArray *) musicFiles
{
CCLOG(@"Start loading music");
for (NSString *music in musicFiles)
{
[[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:music];
[self progressUpdate];
}
}
-(void) loadSounds:(NSArray *) soundClips
{
CCLOG(@"Start loading sounds");
for (NSString *soundClip in soundClips)
{
[[SimpleAudioEngine sharedEngine] preloadEffect:soundClip];
[self progressUpdate];
}
}
-(void) loadSpriteSheets:(NSArray *) spriteSheets
{
for (NSString *spriteSheet in spriteSheets)
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:spriteSheet];
[self progressUpdate];
}
}
-(void) loadImages:(NSArray *) images
{
CCLOG(@"LoadingScreen - loadImages : You need to tell me what to do.");
for (NSString *image in images)
{
//Do something with the images
[[CCTextureCache sharedTextureCache] addImage:image];
[self progressUpdate];
}
}
-(void) loadAssets:(NSArray *) assets
{
//Overwrite me
CCLOG(@"LoadingScreen - loadAssets : You need to tell me what to do.");
for (NSString *asset in assets)
{
//Do something with the assets
[self progressUpdate];
}
[self progressUpdate];
}
~~~
4.工具类方法 ,加载,加载一部分,和加载完成的方法
~~~
-(void) progressUpdate
{
if (--assetCount)
{
//留着后面显示进度条用
}
else {
[self loadingComplete];
CCLOG(@"All done loading assets.");
}
}
-(void) loadingComplete
{
CCDelayTime *delay = [CCDelayTime actionWithDuration:2.0f];
CCCallBlock *swapScene = [CCCallBlock actionWithBlock:^(void) {
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0f
scene:[HelloWorldLayer scene]]];
}];
CCSequence *seq = [CCSequence actions:delay, swapScene, nil];
[self runAction:seq];
}
@end
~~~
至此,已经结束了加载场景的vc。
我们将要把这个scene放到我们的game中去
5.一步一步走!
修改 Appdelegate.m方法中得启动方法
只是在启动方法得末尾加上加载的方法而已
~~~
// and add the scene to the stack. The director will run it when it automatically when the view is displayed.
[director_ pushScene: [LoadingScreen scene]];
~~~
6.修改HellpWorldLayer.m文件的init方法
1)增加游戏菜单
~~~
//15.add game start menu & relative game logic
_isGameStarted = NO;
[CCMenuItemFont setFontSize:20];
[CCMenuItemFont setFontName:@"Arial"];
CCMenuItemFont *startItem = [CCMenuItemFont itemWithString:@"开始游戏" block:^(id sender)
{
_isGameStarted = YES;
CCMenuItem *item = (CCMenuItemFont*)sender;
item.visible = NO;
//6.spawn enemy after 1.0 sec
[self performSelector:@selector(spawnEnemy)
withObject:nil
afterDelay:1.0f];
//7.enable accelerometer
self.isAccelerometerEnabled = YES;
//9.enable touch
self.isTouchEnabled = YES;
}];
startItem.position = ccp(winSize.width / 2, winSize.height / 2);
_startGameMenu = [CCMenu menuWithItems:startItem, nil];
_startGameMenu.position = CGPointZero;
[self addChild:_startGameMenu];
}
~~~
由于触摸 开始游戏按钮之后才可以进行游戏,所以MEnu的position一定要设置成CGPointZero
现在可以试试效果了~
二,代码重构
1,添加精灵表单
~~~
enum {
kTagPalyer = 1,
kTagBatchNode = 2,
};
~~~
~~~
//添加精灵表单
CCSpriteBatchNode *batchNode = [CCSpriteBatchNode batchNodeWithFile:@"gameArts.png"];
batchNode.position = CGPointZero;
[self addChild:batchNode z:0 tag:kTagBatchNode];
~~~
2.修改Sprite的初始化方式
~~~
//2.add background
CCSprite *bgSprite = [CCSprite spriteWithSpriteFrameName:@"background_1.jpg"];
bgSprite.position = ccp(winSize.width / 2,winSize.height/2);
[batchNode addChild:bgSprite z:-100];
//3.add player's plane,初始化方式 从 spriteWithFIle改为 spriteWithSpriteFrameName
CCSprite *playerSprite = [CCSprite spriteWithSpriteFrameName:@"hero_1.png"];
playerSprite.position = CGPointMake(winSize.width / 2, playerSprite.contentSize.height/2 + 20);
[batchNode addChild:playerSprite z:4 tag:kTagPalyer];
//4.init enemy sprites array
_enemySprites = [[CCArray alloc] init];
//5.initialize 10 enemy sprites & add them to _enemySprites array for future useage
const int NUM_OF_ENEMIES = 10;
for (int i=0; i < NUM_OF_ENEMIES; ++i) {
CCSprite *enemySprite = [CCSprite spriteWithSpriteFrameName:@"enemy1.png"];
enemySprite.position = ccp(0,winSize.height + enemySprite.contentSize.height + 10);
enemySprite.visible = NO;
[batchNode addChild:enemySprite z:4];
[_enemySprites addObject:enemySprite];
}
//8.game main loop
[self scheduleUpdate];
_isTouchToShoot = NO;
//10.init bullets
_bulletSprite = [CCSprite spriteWithSpriteFrameName:@"bullet1.png"];
_bulletSprite.visible = NO;
[batchNode addChild:_bulletSprite z:4];
~~~
3.为了减少修改的次数,定义一个 - (CCSprite *)getPlayerSPrite方法
~~~
-(CCSprite*)getPlayerSprite{
CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
return (CCSprite*)[batchNode getChildByTag:kTagPalyer];
}
~~~
4.修改发射子弹的碰撞检测方法
~~~
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
//修改成,必须点选中playerSprite才能够发射子弹
//方法1:因为boundingBox是相对于世界坐标系而言的,所以要用self convertTouchToNodeSpace转换成世界坐标系中的坐标
UITouch *touch = [touches anyObject];
CCSprite *playerSprite = [self getPlayerSprite];
CGPoint pt;
// pt = [touch locationInView:[touch view]];
// pt = [[CCDirector sharedDirector] convertToGL:pt];
// pt = [self convertToNodeSpace:pt];
//上面三句调用,可以简化为下面一句调用
//CGPoint pt = [self convertTouchToNodeSpace:touch];
// if (CGRectContainsPoint(playerSprite.boundingBox, pt)) {
// _isTouchToShoot = YES;
// }
//===================================================================
//方法2:
// pt = [touch locationInView:[touch view]];
// pt = [[CCDirector sharedDirector] convertToGL:pt];
// pt = [playerSprite convertToNodeSpace:pt];
//简化为下面的一句代码调用
CCSpriteBatchNode *batchNode = (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
pt = [batchNode convertTouchToNodeSpace:touch];
CCLOG(@"pt.x = %f, pt.y = %f",pt.x, pt.y);
if (CGRectContainsPoint(playerSprite.boundingBox, pt)) {
_isTouchToShoot = YES;
CCLOG(@"touched!");
}
}
~~~
';