使用cocos2d 2.1制作一条河游戏(4): 主要的游戏逻辑BaseLayer设计
前段时间一直忙着。没有时间更新博客。今天,仍然需要一段时间才能实现对游戏的一小部分,最后打动他。
BaseLayer.h:
#import <GameKit/GameKit.h>
#import "cocos2d.h" #import "AppDelegate.h"
#import "PersonSprite.h"
#import "PriestSprite.h"
#import "DevilSprite.h"
#import "BoatSprite.h"
#import "BankSprite.h" @interface BaseLayer : CCLayer <MoveSpriteDelegate> @property (strong,nonatomic) BankSprite* rightBank;
@property (strong,nonatomic) BankSprite* leftBank; @property (strong,nonatomic) BoatSprite* boat; @property (assign,nonatomic) int time; //set the timer +(CCScene *) scene; @end
记得在(2)讲PersonSprite的时候讲过,我们须要BaseLayer来取代PersonSprite来移动图层上的精灵。
因此我们在BaseLayer头文件定义的时候就须要遵守MoveSpriteDelegate协议了。(忘记了的能够翻回文章(2)看一下详细细节)看看BaseLayer的类成员。
一共同拥有两个河岸和一条船,另一个整数记录剩余时间。因为BaseLayer并不须要开放变量给其它类使用。所以这些河岸对象和船的定义放在头文件中还是实现文件中的匿名类别区域里面关系都不大。
我把这几个类成员放在这里是想将整个游戏的框架表示得清晰一些。严格地来说,放在.m文件的扩展类别区域更为安全。
有人可能会问:“为什么不把6个过河的人也定义在头文件中呢?”事实上我觉得这样做也是能够的。仅仅是由于考虑到BaseLayer对人和船、河岸的处理是不一样的:对于人来说,BaseLayer是通过代理的形式操作人的。即当玩家点击某个人的时候,事件从该人相应的PersonSprite对象開始传递,而PersonSprite通过delegate的形式交由BaseLayer来实现。在delegate參数传递的过程中,BaseLayer是知道哪个人产生了事件的(能够类比一下UIButton,当点击button产生事件的时候,Controller接收到事件时是能够知道由哪个button被按下导致触发了事件的)。
故对于BaseLayer来说。须要使用到PersonSprite的情况仅仅有:1.
对PersonSprite的初始化; 2. 调用托付函数moveSprite。所以并不须要把PersonSprite也作为成员变量;而对于船来说,由于其是由BaseLayer直接操纵的(控制船的移动),故在BaseLayer中会有多个函数使用到BoatSprite的对象。因此将BoatSprite设置为BaseLayer的成员变量更为方便。BankSprite同理。
来到.m文件,首先看一下静态变量以及扩展类别的声明:
#import "BaseLayer.h"
#import "PublicArg.h"
#import "SimpleAudioEngine.h"
#include "AI.h" static NSString *PRIEST_IMAGE_NAME = @"priest.png";
static NSString *DEVIL_IMAGE_NAME = @"devil.png";
static NSString *BOAT_IMAGE_NAME = @"boat.png";
static NSString *BACKGROUND_IMAGE_NAME = @"background.jpg";
static NSString *WATER_IMAGE_NAME = @"water.png"; static NSString *BG_MUSIC = @"background.mp3";
static NSString *CLICK_EFF = @"click.mp3";
static NSString *FAIL_EFF = @"fail.mp3";
static NSString *PERSON_EFF = @"person.mp3";
static NSString *WATER_EFF = @"water.wav";
static NSString *WIN_EFF = @"win.mp3"; static int timeLimit = 200; //set the time limit @interface BaseLayer() @property (strong,nonatomic) AppController *delegate; //list the items here because the title would be changed
@property CCMenuItem *beginItem;
@property CCMenuItem *helpItem;
@property CCMenuItem *pauseItem;
@property CCMenuItem *resumeItem;
@property CCMenuItem *endItem;
@property CCMenuItem *transitItem;
@property CCLabelTTF *timeShow; @property CCMenu *goMenu;
@property CCMenu *systemMenu; @property CCSprite *water; @end
静态变量中设置的都是图片的地址以及倒计时的总时长。而类别扩展中我定义了一些button成员变量。这些就是游戏中的菜单。
因为点击不同选项的时候有可能造成其它选项的转台变化(如点击暂停的时候,helpbutton应该是不可用的),所以我将这些button都放到成员变量中统一管理。注意到最后另一个water的sprite对象,这个是画面中在船上面的水图层,用来遮挡船的下沿。让效果更逼真一些。
看看几个初始化函数:
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
BaseLayer *layer = [BaseLayer node]; [scene addChild: layer]; return scene;
} //initialize instance
-(id) init
{
if( (self=[super init]) ) { //initiate the game state
_delegate = [[UIApplication sharedApplication] delegate];
_delegate.gameState = BeforeGame;
_delegate.screenSize = [[CCDirector sharedDirector] winSize]; //initiate the public arguments
[PublicArg sharedArg]; //set the background image which is also a sprite
CCSprite *background = [CCSprite spriteWithFile:BACKGROUND_IMAGE_NAME];
[background setScale:0.5f];
[background setPosition:ccp(_delegate.screenSize.width/2, _delegate.screenSize.height/2)];
[self addChild:background];
[self initMenu]; //preload the background music & effects
//[[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:BG_MUSIC];
//[[SimpleAudioEngine sharedEngine] preloadEffect:CLICK_EFF];
//[[SimpleAudioEngine sharedEngine] preloadEffect:FAIL_EFF];
//[[SimpleAudioEngine sharedEngine] preloadEffect:PERSON_EFF];
//[[SimpleAudioEngine sharedEngine] preloadEffect:WATER_EFF];
//[[SimpleAudioEngine sharedEngine] preloadEffect:WIN_EFF];
}
return self;
} -(void)initSprites
{
//set the 2 banks & 6 persons
_rightBank = [[BankSprite alloc] initWithSide:Right]; //start place
_leftBank = [[BankSprite alloc]initWithSide:Left]; //end place _rightBank.tag = 101;
_leftBank.tag = 100; [self addChild:_rightBank];
[self addChild:_leftBank]; for(int i = 0;i<3;++i)
{
PriestSprite *temp_priest = [PriestSprite initPriestWithFile:PRIEST_IMAGE_NAME withNumber:i];
DevilSprite *temp_devil = [DevilSprite initDevilWithFile:DEVIL_IMAGE_NAME withNumber:i]; temp_priest.delegate = self;
temp_devil.delegate = self; temp_priest.tag = i;
temp_devil.tag = i + 3; [self addChild:temp_priest];
[self addChild:temp_devil];
} //set the boat
_boat = [BoatSprite initBoatWithFile:BOAT_IMAGE_NAME];
[self addChild:_boat]; //set the water
_water = [CCSprite spriteWithFile:WATER_IMAGE_NAME];
[_water setScale:0.5];
[_water setPosition:ccp(_delegate.screenSize.width/2-40, _delegate.screenSize.height/2)];
[self addChild:_water];
} -(void)initMenu
{
//set the menu list
[CCMenuItemFont setFontSize:28]; _beginItem = [CCMenuItemFont itemWithString:@"Start" target:self selector:@selector(beginGame)];
_helpItem = [CCMenuItemFont itemWithString:@"Help" target:self selector:@selector(help)];
_pauseItem = [CCMenuItemFont itemWithString:@"Pause" target:self selector:@selector(pauseGame)];
_resumeItem = [CCMenuItemFont itemWithString:@"Resume" target:self selector:@selector(resumeGame)];
_endItem = [CCMenuItemFont itemWithString:@"End" target:self selector:@selector(endGame)]; _systemMenu = [CCMenu menuWithItems:_beginItem, _pauseItem, _resumeItem, _endItem, _helpItem, nil];
[_systemMenu alignItemsHorizontallyWithPadding:45.0];
[_systemMenu setPosition:ccp( _delegate.screenSize.width/2 , _delegate.screenSize.height/2 + 100)];
[self addChild:_systemMenu]; _transitItem = [CCMenuItemFont itemWithString:@"Go" target:self selector:@selector(transit)];
[_transitItem setColor:ccc3(0, 0, 0)];
[_transitItem setVisible:NO]; _goMenu = [CCMenu menuWithItems:_transitItem, nil];
[_goMenu setPosition:ccp( _delegate.screenSize.width/2 , _delegate.screenSize.height/2 +10)];
[self addChild:_goMenu]; //set the items
[_resumeItem setIsEnabled:NO];
[_endItem setIsEnabled:NO];
[_pauseItem setIsEnabled:NO];
[_helpItem setIsEnabled:NO]; //set the time shower
_timeShow = [[CCLabelTTF alloc] init];
[_timeShow setFontName:@"Marker Felt"];
[_timeShow setPosition:ccp(_delegate.screenSize.width/2, _delegate.screenSize.height/2 + 50)];
[_timeShow setFontSize:24.0f];
[self addChild:_timeShow];
}
顺带打开AppDelegate.h:
#import <UIKit/UIKit.h>
#import "cocos2d.h" typedef enum
{
Left = 100,Right = 101,BOAT = 102
} Side; //list out the game state
typedef enum
{
BeforeGame, //state: before game start
InGame, //state: game running
BlockGame, //state: boat is moving the nothing can be done
PauseGame, //state: game pause
EndGame //state: game end
} GameState; // Added only for iOS 6 support
@interface MyNavigationController : UINavigationController <CCDirectorDelegate>
@end @interface AppController : NSObject <UIApplicationDelegate>
{
UIWindow *window_;
MyNavigationController *navController_; CCDirectorIOS *__unsafe_unretained director_; // weak ref
} @property (nonatomic, strong) UIWindow *window;
@property (readonly) MyNavigationController *navController;
@property (unsafe_unretained, readonly) CCDirectorIOS *director; @property (assign,nonatomic) CGSize screenSize; //the screen size
@property (assign,nonatomic) GameState gameState; //game state @end
Scene函数是BaseLayer的初始化函数:首先建立一个新的场景。然后向场景中增加BaseLayer布景。大多数Layer都有Scene这个函数,这个是由系统自己主动生成的。在init函数中,初始化了delegate变量。
delegate是AppDelegate的单例。
在AppDelegate的头文件里,我们能够知道它记录了游戏的状态和屏幕大小。由于这些是与游戏总体有关的变量,所以放在了全局的AppDelegate中,并在BaseLayer中使用delegate对其进行引用。之后创建了一个background对象并增加到布景中。同一时候初始化菜单以及计时器。在initSprite方法中,我们初始化了6个人而且分别以tag值0到5记录他们。
这是为后面将其放在岸上的指定位置做准备的。
接下来看看代理方法moveSprite的实现:
#pragma mark MoveSprite Delegate
-(void)moveSprite:(PersonSprite *)sprite
{
if (_delegate.gameState != InGame)
return; //[[SimpleAudioEngine sharedEngine]playEffect:PERSON_EFF]; PublicArg *arg = [PublicArg sharedArg]; switch (sprite.personSide) {
case Left: case Right: //person is on the left
{
//when person and boat are at different side
if ( (sprite.personSide == Left && _boat.boatSide == Right) ||
(sprite.personSide == Right && _boat.boatSide == Left))
return; //go on the boat
switch (_boat.priestNumber + _boat.devilNumber ) {
case 0:
{
[sprite setPosition:ccp(_boat.position.x-20, _boat.position.y+25)];
[_boat.personList addObject:sprite];
break;
}
case 1:
{
[_boat.personList addObject:sprite];
PersonSprite *temp = [_boat.personList objectAtIndex:0];
[sprite setPosition:ccp(2 * _boat.position.x - temp.position.x, _boat.position.y+25)];
break;
}
case 2: return;
} if ([sprite isKindOfClass:[PriestSprite class]])
{
_boat.priestNumber++;
BankSprite * temp_bamk = (BankSprite *)[self getChildByTag:sprite.personSide];
temp_bamk.priestNumber --;
} else if ([sprite isKindOfClass:[DevilSprite class]])
{
_boat.devilNumber++;
BankSprite * temp_bamk = (BankSprite *)[self getChildByTag:sprite.personSide];
temp_bamk.devilNumber --;
} [sprite setPersonSide:BOAT]; break;
} case BOAT: //person is on the boat
{
if ([sprite isKindOfClass:[PriestSprite class]])
{
if (_boat.boatSide == Left)
{
[sprite setPosition:ccp(arg.priest_begin_left.x + sprite.number * arg.person_interval, arg.priest_begin_left.y )];
[sprite setPersonSide:Left];
_leftBank.priestNumber ++;
}
else if (_boat.boatSide == Right)
{
[sprite setPosition:ccp(arg.priest_begin_right.x + sprite.number * arg.person_interval, arg.priest_begin_right.y )];
[sprite setPersonSide:Right];
_rightBank.priestNumber ++; }
_boat.priestNumber--;
}
else if ([sprite isKindOfClass:[DevilSprite class]])
{
if (_boat.boatSide == Left)
{
[sprite setPosition:ccp(arg.devil_begin_left.x + sprite.number * arg.person_interval, arg.devil_begin_left.y )];
[sprite setPersonSide:Left];
_leftBank.devilNumber++;
}
else if (_boat.boatSide == Right)
{
[sprite setPosition:ccp(arg.devil_begin_right.x + sprite.number * arg.person_interval, arg.devil_begin_right.y )];
[sprite setPersonSide:Right];
_rightBank.devilNumber ++;
}
_boat.devilNumber--;
} [_boat.personList removeObject:sprite];
if([self judgeWin])
{
[[SimpleAudioEngine sharedEngine] stopBackgroundMusic];
[[SimpleAudioEngine sharedEngine]playEffect:WIN_EFF];
[_timeShow setString:@"Win!"];
[_timeShow setColor:ccc3(255.0, 215.0, 0.0)];
_delegate.gameState = EndGame;
[self clearItem];
}
break;
}
} //set the go button according to the number of persons on the boat
if (_boat.priestNumber + _boat.devilNumber > 0) [_transitItem setIsEnabled:YES];
else [_transitItem setIsEnabled:NO];
}
当游戏不是在“游戏中”状态时(InGame),点击精灵应当不会响应。这个非常easy理解。最重要的是理解一下moveSprite究竟会面对哪些情况,以及在每种情况下做了什么事情:当玩家点击一个牧师/魔鬼的时候。这人有可能在船上。也有可能在岸上。于是我们分下面情况讨论:
1. 当人在岸上时。上船的时候要推断船上有没有人。来决定人在船上的位置。(避免在船上出现人物重叠的情况)
2. 当人在船上时。查看船在哪个岸边。然后将人物放上去就可以。
接下来是船过河的函数,比較简单,仅仅需改变船的side属性,然后对船和船上的人都设置移动就可以。
-(void)transit
{
if (_boat.priestNumber + _boat.devilNumber == 0) return; [[SimpleAudioEngine sharedEngine]playEffect:WATER_EFF];
_delegate.gameState = BlockGame; // sprites can not be moved
[_transitItem setIsEnabled:NO];
[_helpItem setIsEnabled:NO]; int moveDis = 0; if (_boat.boatSide == Right)
{
moveDis = -160;
_boat.boatSide = Left;
}
else if (_boat.boatSide == Left)
{
moveDis = 160;
_boat.boatSide = Right;
} CCMoveBy *move = [CCMoveBy actionWithDuration:1.0 position:ccp(moveDis, 0)];
id clear = [CCCallFunc actionWithTarget:self selector:@selector(clear)];
[_boat runAction:[CCSequence actions:move,clear, nil]]; //move the persons on the boat
for(int i = 0;i< _boat.priestNumber + _boat.devilNumber ;++i)
{
CCMoveBy *myMove =[CCMoveBy actionWithDuration:1.0 position:ccp(moveDis, 0)];
PersonSprite *sprite = [_boat.personList objectAtIndex:i];
[sprite runAction:myMove];
}
}
推断成功与失败的函数:
-(BOOL)judgeWin
{
if (_leftBank.priestNumber == 3 && _leftBank.devilNumber == 3)
return true;
return false;
} -(BOOL)judgeLose
{
int leftPriestNumber = _leftBank.priestNumber + abs(_boat.boatSide - Right) * _boat.priestNumber;
int rightPriestNumber = _rightBank.priestNumber + abs(_boat.boatSide - Left) * _boat.priestNumber; int leftDevilNumber = _leftBank.devilNumber + abs(_boat.boatSide - Right) * _boat.devilNumber;
int rightDevilNumber = _rightBank.devilNumber + abs(_boat.boatSide - Left) * _boat.devilNumber; if ( (leftDevilNumber > leftPriestNumber && leftPriestNumber > 0 ) ||
(rightDevilNumber > rightPriestNumber && rightPriestNumber >0))
return true;
}
用于游戏状态转换的函数(開始、暂停、结束、继续等)
-(void)beginGame
{
//play background music
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:BG_MUSIC loop:YES];
[[SimpleAudioEngine sharedEngine]playEffect:CLICK_EFF]; _delegate.gameState = InGame; [self initTimer:timeLimit];
[self schedule:@selector(updateTimeDisplay) interval:1]; [_beginItem setIsEnabled:NO];
[_pauseItem setIsEnabled:YES];
[_endItem setIsEnabled:YES];
[_helpItem setIsEnabled:YES]; [_transitItem setVisible:YES];
[_transitItem setIsEnabled:NO]; [_timeShow setColor:ccc3(255.0f, 255.0f, 255.0f)]; NSString *time = [NSString stringWithFormat:@"%.2d:%.2d", _time / 60, _time % 60];
[_timeShow setString:time]; [self removePreviousSprite];
[self initSprites];
} -(void)pauseGame
{
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
[[SimpleAudioEngine sharedEngine]playEffect:CLICK_EFF]; _delegate.gameState = PauseGame;
[self unschedule:@selector(updateTimeDisplay)]; [_resumeItem setIsEnabled:YES];
[_pauseItem setIsEnabled:NO];
[_helpItem setIsEnabled:NO];
[_transitItem setVisible:NO];
} -(void)resumeGame
{
[[SimpleAudioEngine sharedEngine] resumeBackgroundMusic];
[[SimpleAudioEngine sharedEngine]playEffect:CLICK_EFF]; _delegate.gameState = InGame; [_resumeItem setIsEnabled:NO];
[_pauseItem setIsEnabled:YES];
[_helpItem setIsEnabled:YES];
[_transitItem setVisible:YES]; [self schedule:@selector(updateTimeDisplay) interval:1];
} -(void)endGame
{
[[SimpleAudioEngine sharedEngine]stopBackgroundMusic];
[[SimpleAudioEngine sharedEngine]playEffect:CLICK_EFF]; _delegate.gameState = EndGame; [self clearItem];
[self setTime:0]; NSString *time = [NSString stringWithFormat:@"%.2d:%.2d",_time / 60, _time % 60];
[_timeShow setString:time]; [self clearGame];
}
清空屏幕上的sprites,用于一场游戏结束后清空屏幕上元素,避免内存泄露。
-(void)clearItem
{
[_resumeItem setIsEnabled:NO];
[_endItem setIsEnabled:NO];
[_pauseItem setIsEnabled:NO];
[_beginItem setIsEnabled:YES];
[_helpItem setIsEnabled:NO];
[_transitItem setVisible:NO]; [self unschedule:@selector(updateTimeDisplay)];
} -(void)clear
{
_delegate.gameState = InGame; // sprites can not be moved
[_transitItem setIsEnabled:YES];
[_helpItem setIsEnabled:YES]; if([self judgeLose])
{
[[SimpleAudioEngine sharedEngine] stopBackgroundMusic];
[[SimpleAudioEngine sharedEngine]playEffect:FAIL_EFF];
_delegate.gameState = EndGame; [_timeShow setString:@"Lose!"];
[_timeShow setColor:ccc3(255.0, 0.0, 0.0)];
_delegate.gameState = EndGame;
[self clearItem];
}
} //remove the sprites of last scene
-(void)removePreviousSprite
{
for(int i = 0;i<6;++i)
{
PersonSprite *temp = (PersonSprite *)[self getChildByTag:i];
if (temp != NULL) [self removeChildByTag:i];
}
if (_boat != NULL) [self removeChild:_boat];
if (_rightBank != NULL) [self removeChild:_rightBank];
if (_leftBank != NULL) [self removeChild:_leftBank];
if (_water != NULL) [self removeChild:_water];
} -(void)clearGame
{
//change to a new scene
[[CCDirector sharedDirector]replaceScene:[BaseLayer scene]];
}
其它辅助函数:
-(void)updateTimeDisplay
{
_time--;
NSString *time = [NSString stringWithFormat:@"%.2d:%.2d",_time / 60, _time % 60];
[_timeShow setString:time]; if (_time <= 0) [self endGame];
} -(void)initTimer:(int)interval
{
_time = interval;
}
最后是help函数。
help功能主要做的是依据当前游戏中人物的位置,自己主动为玩家走下一步。算是一种比較简单的决策的体现吧。原理非常easy。就是找出一条能通关的路径,然后把各种合理的游戏状态连接到路径上,即设置一个有限状态机,储存路径图,给定输入和当前状态后,系统便能算出下一个状态。
help函数:
-(void)help
{
[[SimpleAudioEngine sharedEngine]playEffect:CLICK_EFF];
int leftPriestNumber = _leftBank.priestNumber + abs(_boat.boatSide - Right) * _boat.priestNumber;
int rightPriestNumber = _rightBank.priestNumber + abs(_boat.boatSide - Left) * _boat.priestNumber; int leftDevilNumber = _leftBank.devilNumber + abs(_boat.boatSide - Right) * _boat.devilNumber;
int rightDevilNumber = _rightBank.devilNumber + abs(_boat.boatSide - Left) * _boat.devilNumber; int boatSide = _boat.boatSide - 100; int deltaPriest = 0, deltaDevil = 0; getHelp(leftPriestNumber,leftDevilNumber,rightPriestNumber,rightDevilNumber,boatSide,deltaPriest,deltaDevil); int tempNum = _boat.priestNumber + _boat.devilNumber;
for(int i = 0;i< tempNum ; ++i)
[self moveSprite:[_boat.personList objectAtIndex:0]]; int i = 0; while (deltaPriest > 0) { PriestSprite *mySprite = (PriestSprite *)[self getChildByTag:i]; if (mySprite.personSide == _boat.boatSide)
{
[self moveSprite:mySprite];
deltaPriest --;
}
i++;
} i = 3;
while (deltaDevil > 0) { DevilSprite *mySprite = (DevilSprite *)[self getChildByTag:i]; if (mySprite.personSide == _boat.boatSide)
{
[self moveSprite:mySprite];
deltaDevil --;
}
i++;
}
[self transit];
}
这里调用的是一个AI.cpp文件。cpp文件里储存了路径。
AI.h:
#ifndef RiverCrossing_AI_h
#define RiverCrossing_AI_h struct state{
int boat_side;
int left_pastor;
int left_devil;
int right_pastor;
int right_devil;
};
//
//void initWays(int * ways);
//int hash(state s);
//state deHash(int key);
//void change(state now,state next,
// int & pastor,int & devil);
/*
input:state
boat_side(0=left,1=right)
return:void
*/
void getHelp (int left_pastor,int left_devil, int right_pastor,int right_devil, int boat_side,int& pastor,int &devil); #endif
AI.cpp:
#include "AI.h"
#include<iostream>
#include<cmath>
using namespace std; void initWays(int * ways)
{
ways[10033]=231;
ways[231]=10132;
ways[10132]=330;
ways[330]=10231;
ways[10231]=2211;
ways[2211]=11122;
ways[11122]=3102;
ways[3102]=13003;
ways[13003]=3201;
ways[3201]=13102;
ways[13102]=3300;
ways[3300] = 3300; ways[1122]=10132;
ways[132]=10033;
} int hash(state s)
{
int key;
key=s.boat_side*10000+s.left_pastor*1000+s.left_devil*100+s.right_pastor*10+s.right_devil; return key;
} state deHash(int key)
{
int boat_side=key/10000;
int left_pastor=key%10000/1000;
int left_devil=key%1000/100;
int right_pastor=key%100/10;
int right_devil=key%10;
struct state s={boat_side,left_pastor,left_devil,right_pastor,right_devil};
return s;
} void change(state now,state next,
int & pastor,int & devil)
{
pastor=abs(now.left_pastor-next.left_pastor);
devil=abs(now.left_devil-next.left_devil);
} /*
input:state
boat_side(0=left,1=right)
return:void
*/
void getHelp(int left_pastor,int left_devil,
int right_pastor,int right_devil,
int boat_side,
int & pastor,int & devil)
{
int ways[13334];
initWays(ways); struct state now={boat_side,left_pastor,left_devil,right_pastor,right_devil};
int now_state_key=hash(now);
int next_state_key=ways[now_state_key]; struct state next=deHash(next_state_key);
change(now,next,pastor,devil);
}
特别说明,在.m编译文件cpp文件。需要.m该后缀.mm工作。
使用cocos2d 2.1制作一条河游戏(4): 主要的游戏逻辑BaseLayer设计的更多相关文章
- 用cocos2d 2.1制作一个过河小游戏(4): 游戏主逻辑BaseLayer设计
前段时间一直在忙.没有时间更新博客.今天还是抽点时间把最后一小部分游戏的实现放上来吧. BaseLayer.h: #import <GameKit/GameKit.h> #import & ...
- Unity3D NGUI制作进度条
利用GUI可以制作进度条,但是NGUI更加方便 我是用的NGUI3.5.3, 先找到NGUI Slider的预制体,利用自带的UISlider来制作. 主要是利用UISlider的Value来控制进 ...
- html5中的progress兼容ie,制作进度条样式
html5新增的progress标签用处很大,它可以制作进度条,不用像以前那样用css来制作进度条! 一.progress使用方法 progress标签很好使用,他有两个属性,value和max,va ...
- 每日CSS_纯CSS制作进度条
每日CSS_纯CSS制作进度条 2020_12_26 源码 1. 代码解析 1.1 html 代码解析 设置整个容器 <div class="container"> . ...
- 使用CSS3制作导航条和毛玻璃效果
导航条对于每一个Web前端攻城狮来说并不陌生,但是毛玻璃可能会相对陌生一些.简单的说,毛玻璃其实就是让图片或者背景使用相应的方法进行模糊处理.这种效果对用户来说是十分具有视觉冲击力的. 本次分享的主题 ...
- 制作进度条(UISlider)
怎样判断是否应当使用进度条 用进度条的主要目的是为了用一根管子的充满程度来直观地表示某种数值的百分比,进度条分为可拖动和不可拖动两种. 可拖动进度条和不可拖动进度条的原理几乎是一模一样,唯一的区别是可 ...
- 分针网—IT教育:使用CSS3制作导航条和毛玻璃效果
导航条对于每一个Web前端攻城狮来说并不陌生,但是毛玻璃可能会相对陌生一些.简单的说,毛玻璃其实就是让图片或者背景使用相应的方法进行模糊处理.这种效果对用户来说是十分具有视觉冲击力的.本次分享的主题: ...
- js - 预加载+监听图片资源加载制作进度条
这两天遇到一个新需求:一个一镜到底的h5动画.因为功能的特殊性,就要求我们提前监听页面的静态图片是否全部加载完毕.即处理预加载. 总结下来,下次这种需求需要提前注意以下几点: 一.图片而不是背景图 本 ...
- cocos2d js ClippingNode 制作标题闪亮特效
1.效果图: 之前在<Android 高仿 IOS7 IPhone 解锁 Slide To Unlock>中制作了文字上闪亮移动的效果,这次我们来看下怎样在cocos2d js 中做出类似 ...
随机推荐
- jdom dom4j解析xml不对dtd doctype进行验证(转)
一.写在所有之前:因为dom4j和jdom在这个问题上处理的方法是一模一样的,只是一个是SAXBuilder 一个SAXReader,这里以jdom距离,至于dom4j只需要同理替换一下就可以了.二. ...
- MySQL 执行计划里的rows
<pre name="code" class="html">SQL> alter session set statistics_level=a ...
- ActiveX控件的安全初始化和脚本操作 和 数字签名SIGN
摘要:数字签名SIGN保证控件在下载时候的安全性.如果你的代码已经经过数字签名,即使用户IE的安全设置很高也能下载,安装并登记.但是在页面上初始化,或者用脚本运行这个控件,为了保证安全性,还需要进行M ...
- 注册Dev的帮助文件
Download the CHM files from… Code: https://www.devexpress.com/Support/Documentation/download.xml?pla ...
- Java Core和HeapDump
什么是Java Core和Heap Dump Java程序运行时,有时会产生Java Core及Heap Dump文件,它一般发生于Java程序遇到致命问题的情况下. 发生致命问题后,Java进程有时 ...
- 百度GPSutil
================================================= package com.qcar.benz.biz.common; import com.aliba ...
- SilkTest高级进阶系列7-用PostMessage模拟鼠标
SilkTest可以通过调用Windows API来向控件发送消息,从而进行特定的操作.下面这段code使用PostMessage来向计算器上的清除键发送WM_LBUTTONDOWN和WM_LBUTT ...
- mysql+ssh整合样例,附源代码下载
项目引用jar下载:http://download.csdn.net/detail/adam_zs/7262727 项目源代码下载地址:http://download.csdn.net/detail/ ...
- 降低成本是永恒的追求(xamarin)
减少为主线的成本始终是一个社会经济发展.经济活动似乎很.商业模式的出现相关.我记得早起写Web程序,真正的企业并不多忙.大部分时间处理与浏览器的问题之间的差异所带来. 有些型号也做了屏蔽这样的差别,有 ...
- aix 下 实现goldengate 随os启动而自己主动启动的脚本
aix 下 实现goldengate 随os启动而自己主动启动的脚本: 1.用oracle用户建立/u01/info.txt,文件内容例如以下: sh date start mgr 2.chmod + ...