Gowhich

Durban's Blog

搜索了一下PhpStorm这个开发PHP的IDE,但是需要注册,找到了下面很好用的结果

用户名:EMBRACE

===== LICENSE BEGIN =====
43136-12042010
00002UsvSON704l”dILe1PVx3y4”B3
49AU6oSDJrsjE8nMOQh”8HTDJHIUUh
gd1BebYc5U”6OxDbVsALB4Eb10PW8”
===== LICENSE END =====

不知道是不是真的好用,不过先用用再说吧,IDE总有其不完善的地方的

网上找到了一个可以使用的,用来开发Django应用的好的IDE,虽然说弄的我好累,但是还是被我找到了,Professional的哦,就是需要注册码,不过还是被我搜索到了。

用户名:yueting3527

注册码:
===== LICENSE BEGIN =====
93347-12042010
00001FMHemWIs”6wozMZnat3IgXKXJ
2!nV2I6kSO48hgGLa9JNgjQ5oKz1Us
FFR8k”nGzJHzjQT6IBG!1fbQZn9!Vi
===== LICENSE END =====
亲测可用

绝对可靠的哦,就是不知道有木有什么漏洞,或者侵犯隐私啥的

Cocos2D是一款功能强大的iphone游戏引擎,它可以很大程度上节约你的游戏开发时间。它包含sprite(精灵),绚丽的特效,animations(动画),物理引擎,声音引擎,以及其他一些相当实用的功能。

我也是刚接触Cocos2D,虽然目前市面上有很多cocos2d相关的教程,但我没能找到我想到的那种入门级别的教程-如何制作一款非常简单但是功能齐全的游戏。对于入门教程,这款游戏应当仅仅包含一些较为基础的功能,如animation(动画), collisions(碰撞)和声音。

下载并安装Cocos2D

你可以从以下地址下载到最新的Cocos2Dthe Cocos2D Google Code page.

下载完成后,你需要安装实用的project templates(工程模板)。在mac下打开一个终端窗口,

进入到刚刚下载到的Cocos2D的目录并输入以下指令: ./install-templates.sh -f -u

注意,如果xcode没有安装在默认目录下,在这里你可以选择性的在指令后添加参数(如果你的机器曾经安装过多个版本的SDK的话,那么很可能之前你已经会用这种方法了)。

Hello, Cocos2D!

让我们从建立一个简单的Hello World程序开始吧!

启动Xcode,选择cocos2d Application template建立一个新工程。将其命名为 “Cocos2DSimpleGame”。

编译(Cmd+B)并运行(Cmd+R)

Cocos2D是以场景(scenes)组织的,对一个游戏来说,场景可以是关卡或者是屏幕。比如游戏一开始的主菜单场景,游戏中运行起来的场景,还有游戏结束game over的场景。在场景中,可以有很多的层layers(很像photoshop中的层),层中又可以包含很多节点nodes比如精灵sprite,文本labels,菜单menus和其他的。同时每个节点又可以包含其他的节点(例如一个sprite节点可以包含另一个sprite节点作为他的child)。

观察下Hello World示例工程,会发现里边只有一个层-HelloWorldLayer-我们准备在这儿实现我们主要的游戏逻辑。打开这个文件,会看到在init方法里边被加入了一个写着”Hello World”的label,现在删除掉它,我们以后将用一个sprite来替换它。

添加一个Sprite

在添加一个sprie之前,我们需要一些图片,你可以使用自己创建的,或者直接使用我(ray)可爱的妻子为这个项目制作的图片资源: a Player image, a Projectile image, and a Target image.

获得这些图片后,把它们从finder里拖拽进Xcode工程的resources文件夹下,确保”Copy items into destination group’s folder (if needed)是选中的。

好的,现在有了图片资源,下面要计算出该往哪里放置我们的主人公。需要注意的是在Cocos2D里,屏幕的左下角是(0,0)点,随着你往右上方向移动,x和y值会随之增加。因为本项目是landscape模式的(手机横向放置),所以这意味着右上角的坐标是(480,320)。

还需要注意的是,每当设置一个对象的坐标,默认情况下设置的是该对象自身中心的位置。所以如果想把主人公sprite放置到屏幕的横向左边缘,纵向屏幕一半的位置,需要执行以下两步:

x坐标,设置其为[player sprite's width]/2

y坐标,设置其为[window height]/2

这就试试看!打开Classes文件夹并点击HelloWorldLayer.m,用以下内容替换掉init方法:

1
2
3
4
5
6
7
8
9
10
11
-(id) init
{
if( (self=[super init] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *player = [CCSprite spriteWithFile:@"Player.png"
rect:CGRectMake(0, 0, 27, 40)];
player.position = ccp(player.contentSize.width/2, winSize.height/2);
[self addChild:player];
}
return self;
}

编译并运行,主人公sprite跃然屏幕之上,但注意背景默认是黑色的,白色看起来也许会更好点儿。使用CCLayerColor class可以简单的完成这一目标。打开HelloWorldLayer.h并修改其interface 声明部分:

1
@interface HelloWorldLayer : CCLayerColor

然后打开HelloWorldLayer.m,对init方法做一个轻微的修改:

1
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {

背景就顺利变为了白色。

编译并运行,主人公出现在白色背景之上了,哈哈,我们的小忍者看起来跃跃欲试了!

移动靶子Moving Targets

接下来为了让我们的忍者不至于独孤求败,我们加入一些靶子,为了让游戏更有趣,可以让靶子移动起来-否则游戏将不会有很好的挑战性。将这些靶子稍微向右移出屏幕一点儿,并给他们设置一个向左移动的动作。

在init方法之前添加以下方法:

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
-(void)addTarget {

CCSprite *target = [CCSprite spriteWithFile:@"Target.png"
rect:CGRectMake(0, 0, 27, 40)];

// Determine where to spawn the target along the Y axis
CGSize winSize = [[CCDirector sharedDirector] winSize];
int minY = target.contentSize.height/2;
int maxY = winSize.height - target.contentSize.height/2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;

// Create the target slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
target.position = ccp(winSize.width + (target.contentSize.width/2), actualY);
[self addChild:target];

// Determine speed of the target
int minDuration = 2.0;
int maxDuration = 4.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;

// Create the actions
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(-target.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:@selector(spriteMoveFinished:)];
[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];

}

为了让事情更容易理解,我使用了有点儿冗长的方式来讲解。这一部分其实就是和之前主人公sprite同样的方式来计算对象该放到哪儿,设置坐标并将其加入到场景中去。

唯一不同的是这里加入了actions(动作)。Cocos2D内部提供了许多极为便利的动作来给你的sprite动起来,例如move actions(移动),jump actions(跳跃),fade actions(渐隐渐现),animation actions(动画)等等,这里我们只用其中的三个action:

  • CCMoveTo: 使用CCMoveTo action来让对象从右侧屏幕外移动到屏幕左侧。注意可以通过指定duration参数控制这一过程需要多久,这里我们随机给他2-4秒的时间。
  • CCCallFuncN: CCCallFuncN action 允许我们指定一个在动作执行完成后执行的回调函数。暂时写一个空的回调“spriteMoveFinished”,以后再填入内容。
  • CCSequence: CCSequence action 允许将一系列动作按先后顺序组合成一个动作,一次执行,这里,让CCMoveTo首先执行,当它完成时,再执行CCCallFuncN动作。

接下来,添加上文提到的那个CCCallFuncN action中需要的回调函数,在addTarget:方法之前添加以下内容:

1
2
3
4
-(void)spriteMoveFinished:(id)sender {
CCSprite *sprite = (CCSprite *)sender;
[self removeChild:sprite cleanup:YES];
}

此方法的目的是一旦sprite超出屏幕范围,就将其从屏幕上移除。随着靶子数量的不断增加,如果不及时清理之,将会有严重的内存泄漏问题出现。解决这个问题也可以使用另一种更为高端的手段-使用数组存储一系列可以重复使用的sprites对象,但对于这篇初学者教学来说,我们用目前这个基础的方法即可。

在继续前还有一件事儿要做。我们需要实际调用创建靶子的方法。为了让游戏更有趣,我们让靶子每隔一小段时间就出现。通过每隔一段时间就会被调用的schedule(调度)回调函数,就能完成这个目标。每隔一秒刷新一个靶子,在init方法里的return之前加入以下内容:

1
[self schedule:@selector(gameLogic:) interval:1.0];

接下来在回调函数里填入如下内容:

1
2
3
-(void)gameLogic:(ccTime)dt {
[self addTarget];
}

编译并运行,你会看到靶子们欢快地在屏幕上移动着:

射击Shooting Projectiles

到这里,主人公忍者多么渴望着被添加一个射击动作。虽然有很多方法可以实现射击,但是这个游戏里,我们希望每当用户触摸屏幕时,让主人公向着触摸的方向发射一个飞镖。

我使用CCMoveTo action来让所有逻辑尽量简单,但是使用它还是需要一丁点儿数学计算。CCMoveTo需要被指定一个目标点,我们不能直接使用用户触摸屏幕的点,因为这个点仅仅能表示主人公射击靶子的方向,实际上我们想让飞镖持续飞行到一直飞出屏幕为止。

首先我们在层里启用触摸,在init方法中加入:

1
self.isTouchEnabled = YES;

由于启用了触摸,在层里会接收到相应的触摸回调函数。每当用户的手指从屏幕抬起,将会触发ccTouchesEnded方法,我们这就实现它:

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
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];

// Set up initial location of projectile
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *projectile = [CCSprite spriteWithFile:@"Projectile.png"
rect:CGRectMake(0, 0, 20, 20)];
projectile.position = ccp(20, winSize.height/2);

// Determine offset of location to projectile
int offX = location.x - projectile.position.x;
int offY = location.y - projectile.position.y;

// Bail out if we are shooting down or backwards
if (offX <= 0) return;

// Ok to add now - we've double checked position
[self addChild:projectile];

// Determine where we wish to shoot the projectile to
int realX = winSize.width + (projectile.contentSize.width/2);
float ratio = (float) offY / (float) offX;
int realY = (realX * ratio) + projectile.position.y;
CGPoint realDest = ccp(realX, realY);

// Determine the length of how far we're shooting
int offRealX = realX - projectile.position.x;
int offRealY = realY - projectile.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;

// Move projectile to actual endpoint
[projectile runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallFuncN actionWithTarget:self selector:@selector(spriteMoveFinished:)],
nil]];

}

在第一部分里,我们从touches对象集合中选择一个,得到他在当前屏幕上的坐标,通过调用convertToGL来把此坐标转换为当前屏幕模式适应的,这一点很重要,因为我们使用的是landscape模式。

接下来加载飞镖sprite并像往常一样设置其初始坐标。根据之前讨论过的相似三角形算法,我们计算得到飞镖的终点。

注意这个算法并不完美。即使飞镖的Y坐标已经出了屏幕,它还是会被强制移动到X坐标移出屏幕为止。想解决这个有很多手段,包括检测射击点到屏幕边缘的最短距离,在游戏逻辑的回调里检测飞镖坐标是否在屏幕之外(比如visit),虽然这些方法都可以解决它,但简单起见,这篇初学者教程还是会使用之前的方式。

最后一件事儿是决定运动所需的时间。飞镖最好是能以固定的速率射出,所以我们还需要一丁点儿数学计算。使用 勾股定理可以轻松地得到斜边的长度。

一旦有了距离,只需要将其除以一个固定的速率,便可得到时间。

剩余部分就像我们之前做的,给靶子设置上actions。编译并运行,哈哈,你的忍者能给予冲过来的鬼头靶子致命打击了!

碰撞检测Collision Detection

飞镖四处飞,但我们的小忍者并没有看到飞镖击倒鬼头靶子。是时候加入一些检测飞镖与靶子碰撞检测的代码了。

使用Cocos2D有很多途径解决这个,包括使用其中一个引擎包含的物理引擎:Box2D或者Chipmunk。为了有效说明问题的本质并使事情简单,我们将自己实现一个简单的碰撞检测。

首先,我们需要记录所有在当前场景里的靶子和飞镖对象。在HelloWorldLayer类的声明中加入:

1
2
NSMutableArray *_targets;
NSMutableArray *_projectiles;

并在init方法里加入初始化数组的代码:

1
2
_targets = [[NSMutableArray alloc] init];
_projectiles = [[NSMutableArray alloc] init];

在dealloc方法里边清理之:

1
2
3
4
[_targets release];
_targets = nil;
[_projectiles release];
_projectiles = nil;

现在,修改addTarget方法,在靶子数组中加入一个新靶子并设置其标识(tag)留待后用:

1
2
target.tag = 1;
[_targets addObject:target];

同样的,把在ccTouchesEnded里新建的飞镖加入到飞镖数组中并设置tag留待后用:

1
2
projectile.tag = 2;
[_projectiles addObject:projectile];

最后,修改spriteMoveFinished方法,根据tag分类把即将删除的对象从数组中也移除掉:

1
2
3
4
5
if (sprite.tag == 1) { // target
[_targets removeObject:sprite];
} else if (sprite.tag == 2) { // projectile
[_projectiles removeObject:sprite];
}

编译并运行,确保到目前为止一切都OK。虽然目前还看不到什么显著的变化,但我们已经有了做碰撞检测的基础了。

添加以下方法到HelloWorldLayer:

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
- (void)update:(ccTime)dt {

NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
for (CCSprite *projectile in _projectiles) {
CGRect projectileRect = CGRectMake(
projectile.position.x - (projectile.contentSize.width/2),
projectile.position.y - (projectile.contentSize.height/2),
projectile.contentSize.width,
projectile.contentSize.height);

NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets) {
CGRect targetRect = CGRectMake(
target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height);

if (CGRectIntersectsRect(projectileRect, targetRect)) {
[targetsToDelete addObject:target];
}
}

for (CCSprite *target in targetsToDelete) {
[_targets removeObject:target];
[self removeChild:target cleanup:YES];
}

if (targetsToDelete.count > 0) {
[projectilesToDelete addObject:projectile];
}
[targetsToDelete release];
}

for (CCSprite *projectile in projectilesToDelete) {
[_projectiles removeObject:projectile];
[self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];
}

以上内容很清晰。我们遍历了一下飞镖和靶子数组,在遍历中得到每一个对象的bounding box(碰撞框),使用CGRectIntersectsRect来检测两个矩形是否相交。如果有相交,则将飞镖和靶子分别从场景和数组中移除。注意我们必须将这些对象加入到“toDelete”结尾的数组中,因为我们没法在当前循环中从数组中删掉它。还是一样,有很多更优的方法来解决这类问题,简单起见,我使用最简单的方法。

你仅仅需要一步就可以欢呼了,使用schedule调度这个方法,使其在每一帧都执行:

1
[self schedule:@selector(update:)];

编译并运行,所有和飞镖碰撞的靶子都会灰飞烟灭了!

完成触摸Finishing Touches

我们已经非常接近制作出一个成品(但很简单)的游戏了。只需要再加入一些音效和音乐(没有游戏不带声音的!)以及一些简单的游戏逻辑。

如果你阅读过我的blog series on audio programming for the iPhone这篇教程,你会非常欣喜地发现在Cocos2D中播放声音原来如此简单。

首先,把背景音乐和一个射击音效拖拽进你的工程的resources文件夹。你可以随意使用以下资源cool background music I made or my awesome pew-pew sound effect, 或者制作你自己的。

接下来在HelloWorldLayer.m的一开头导入头文件:

1
#import "SimpleAudioEngine.h"

在init方法中,如下所示播放背景音乐:

1
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];

在ccTouchesEnded方法中播放音效:

1
[[SimpleAudioEngine sharedEngine] playEffect:@"pew-pew-lei.caf"];

现在,我们加入一个提示“You Win”或者“You Lose”的游戏结束场景。右键点击Classes文件夹,选择FileNew File,并选择Objective-C class,确保继承的类是NSObject。点击Next,在filename位置输入GameOverScene作为文件名,确保“Also create GameOverScene.h”是选中的。

用以下内容替换掉GameOverScene.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
#import "cocos2d.h"

@interface GameOverLayer : CCLayerColor {
CCLabelTTF *_label;
}
@property (nonatomic, retain) CCLabelTTF *label;
@end

@interface GameOverScene : CCScene {
GameOverLayer *_layer;
}
@property (nonatomic, retain) GameOverLayer *layer;
@end

用以下内容替换掉GameOverScene.m:

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
#import "GameOverScene.h"
#import "HelloWorldLayer.h"

@implementation GameOverScene
@synthesize layer = _layer;

- (id)init {

if ((self = [super init])) {
self.layer = [GameOverLayer node];
[self addChild:_layer];
}
return self;
}

- (void)dealloc {
[_layer release];
_layer = nil;
[super dealloc];
}

@end

@implementation GameOverLayer
@synthesize label = _label;

-(id) init
{
if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {

CGSize winSize = [[CCDirector sharedDirector] winSize];
self.label = [CCLabelTTF labelWithString:@"" fontName:@"Arial" fontSize:32];
_label.color = ccc3(0,0,0);
_label.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:_label];

[self runAction:[CCSequence actions:
[CCDelayTime actionWithDuration:3],
[CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
nil]];

}
return self;
}

- (void)gameOverDone {

[[CCDirector sharedDirector] replaceScene:[HelloWorldLayer scene]];

}

- (void)dealloc {
[_label release];
_label = nil;
[super dealloc];
}
@end

注意这里有两个不同的对象:一个scene和一个layer。一个scene可以包含很多layer。不过目前这个只有一个,这个layer只是放置了一个label在屏幕中心,并schedule了一个3秒会自动返回Hello World场景的事件。

最后,加入一些极为简单的游戏逻辑进去。首先,记录一下被主人公的飞镖干掉的靶子。在HelloWorldLayer类中加入一个成员变量,在HelloWorldLayer.h中的@interface块儿加入如下:

1
int _projectilesDestroyed;

在HelloWorldLayer.m里,导入GameOverScene类:

1
#import "GameOverScene.h"

在update方法中,增加飞镖摧毁靶子的数量并检测获得胜利的条件,在removeChild:target:之后加入:

1
2
3
4
5
6
7
_projectilesDestroyed++;
if (_projectilesDestroyed > 30) {
GameOverScene *gameOverScene = [GameOverScene node];
_projectilesDestroyed = 0;
[gameOverScene.layer.label setString:@"You Win!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}

当有鬼头靶子越过了屏幕左侧,你就输了,修改spriteMoveFinished方法,在tag == 1 case中removeChild:sprite:方法之后加入:

1
2
3
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];

编译并执行,现在你胜利或失败后会进入game over场景中,根据条件的不同会显示相应信息。

源码拿来!Gimme The Code!

就到这里!这是到目前为止所有的源码下载地址simple Cocos2D iPhone game

最近开始学习cocos2d, 查阅资料发现有个Cocos2d-X和Cocos2d-iphone, 不解,为什么会有两种

不得不说要说总结,因为Cocos2d-X的代码和Cocos2d-iphone两个引擎除了语言不同外(Cocos2d-X使用C++,Cocos2d-iphone使用Object-C)可以说没有其他差异。

下面Himi举例对比几段代码来说明吧:

创建添加一个精灵代码对比:

使用Cocos2d-X

1
2
3
4
5
6
7
//---------Cocos2d-X代码部分--  
//创建一个精灵
CCSprite *spriteTemp =CCSprite::spriteWithFile("icon.png");
//设置精灵的坐标
spriteTemp->setPosition(ccp(size.width*0.5,size.height*0.5));
//将精灵添加到layer中
this->addChild(spriteTemp,1);

使用Cocos2d-iphone

1
2
3
4
5
6
7
//---------Cocos2d-iphone代码部分--  
//创建一个精灵
CCSprite *spriteTemp =[CCSprite spriteWithFile:@"icon.png"];
//设置精灵的坐标
spriteTemp.position=ccp(size.width*0.5,size.height*0.5);
//将精灵添加到layer中
[self addChild:spriteTemp z:0 tag:1];

添加一个粒子代码对比:

使用Cocos2d-X

1
2
3
4
//---------Cocos2d-X代码部分--   
CCParticleSystem *tempSystem =ARCH_OPTIMAL_PARTICLE_SYSTEM::particleWithFile("himi.plist");
tempSystem->setPosition(ccp(100,100));
this->addChild(tempSystem);

使用Cocos2d-iphone:

1
2
3
4
//---------Cocos2d-iphone代码部分--  
CCParticleSystem *tempSystem =[ARCH_OPTIMAL_PARTICLE_SYSTEM particleWithFile:@"himi.plist"];
tempSystem.position=ccp(100,100);
[self addChild:tempSystem];

OK,不在举例了,通过以上随便举例的两段代码童鞋们很清楚的看出来,基本上没有差异。。So~这也是Himi没有在更新Cocos2d-X博文的必要,以后主要会继续更新Cocos2d(Cocos2d-iphone)博文的,以后童鞋们不管是做Cocos2d-X还是做Cocos2d-iphone都可以看我的【iOS-Cocos2d游戏开发系列博文的】谁让它们通用呢

这里还有三点要说下:

  • 第一:现在很多游戏公司都趋向于Cocos2d-X引擎开发游戏的趋势,不得不说Cocos2d-X多平台平移确实很诱惑人,而且通过网龙《91部落》手机网游的成功发布,不得不说Cocos2d-X已成熟!

  • 第二:不少童鞋肯定会很想知道Cocos2d-X能多平台平移,那么平移稳定性如何?

    通过Himi的了解,Cocos2d-X开发都会使用统一的C++语言开发,例如在iOS开发中使用Xcode+cocos2d-X,在Android上一般使用Eclipse+Android NDK+ADT….

其实总结一句,Cocos2d-iphone并不像很多童鞋认为的只要的一人开发就可打包多平台包这样子~ 如果说用一句话更贴切的形容Cocos2d-X,Himi会用“Cocos2d-X代码通用”来形容;

从CC(CocoChina)上我看到王哲walzer(cocos2d-x 游戏引擎作者)在回复一童鞋问题时候这么说的:

问:关于如何打包cocos2d-x程序的问题 本人新手,想请教一下如何将Cocos2d-x win32的程序打包成iphone能够运行的程序,请各位大大不吝赐教!谢谢!

王哲walzer 答:win32上只是让你调试方便,要发布到appstore上,你还是得有个mac机,装好cocos2d-x template,新建工程,然后把win32上代码拷过来,手工地加入xcode工程里面,编译运行

从上面的对话可以证实Himi所说的Cocos2d-X是代码通用!

INSERT语法

1
2
3
4
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
VALUES ({expr | DEFAULT},...),(...),...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

或:

1
2
3
4
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
SET col_name={expr | DEFAULT}, ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

或:

1
2
3
4
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

一、DELAYED 的使用

使用延迟插入操作

DELAYED调节符应用于INSERT和REPLACE语句。当DELAYED插入操作到达的时候,

服务器把数据行放入一个队列中,并立即给客户端返回一个状态信息,这样客户

端就可以在数据表被真正地插入记录之前继续进行操作了。如果读取者从该数据

表中读取数据,队列中的数据就会被保持着,直到没有读取者为止。接着服务器

开始插入延迟数据行(delayed-row)队列中的数据行。在插入操作的同时,服务器

还要检查是否有新的读取请求到达和等待。如果有,延迟数据行队列就被挂起,

允许读取者继续操作。当没有读取者的时候,服务器再次开始插入延迟的数据行。

这个过程一直进行,直到队列空了为止。

几点要注意事项:

INSERT DELAYED应该仅用于指定值清单的INSERT语句。服务器忽略用于INSERT DELAYED…SELECT语句的DELAYED。

服务器忽略用于INSERT DELAYED…ON DUPLICATE UPDATE语句的DELAYED。

因为在行被插入前,语句立刻返回,所以您不能使用LAST_INSERT_ID()来获取AUTO_INCREMENT值。AUTO_INCREMENT值可能由语句生成。

对于SELECT语句,DELAYED行不可见,直到这些行确实被插入了为止。

DELAYED在从属复制服务器中被忽略了,因为DELAYED不会在从属服务器中产生与主服务器不一样的数据。

注意,目前在队列中的各行只保存在存储器中,直到它们被插入到表中为止。这意味着,如果您强行中止了mysqld(例如,使用kill -9)

或者如果mysqld意外停止,则所有没有被写入磁盘的行都会丢失。

二、IGNORE的使用

IGNORE是MySQL相对于标准SQL的扩展。如果在新表中有重复关键字,

或者当STRICT模式启动后出现警告,则使用IGNORE控制ALTER TABLE的运行。

如果没有指定IGNORE,当重复关键字错误发生时,复制操作被放弃,返回前一步骤。

如果指定了IGNORE,则对于有重复关键字的行,只使用第一行,其它有冲突的行被删除。

并且,对错误值进行修正,使之尽量接近正确值。

1
insert ignore into tb(...) value(...)

这样不用校验是否存在了,有则忽略,无则添加

三、ON DUPLICATE KEY UPDATE的使用

如果您指定了ON DUPLICATE KEY UPDATE,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行旧行UPDATE。例如,如果列a被定义为UNIQUE,并且包含值1,则以下两个语句具有相同的效果:

1
2
3
mysql> INSERT INTO table (a,b,c) VALUES (1,2,3)
-> ON DUPLICATE KEY UPDATE c=c+1;
mysql> UPDATE table SET c=c+1 WHERE a=1;

如果行作为新记录被插入,则受影响行的值为1;

如果原有的记录被更新,则受影响行的值为2。

注释:如果列b也是唯一列,则INSERT与此UPDATE语句相当:

1
mysql> UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;

如果a=1 OR b=2与多个行向匹配,则只有一个行被更新。通常,您应该尽量避免对带有多个唯一关键字的表使用ON DUPLICATE KEY子句。

您可以在UPDATE子句中使用VALUES(col_name)函数从INSERT…UPDATE语句的INSERT部分引用列值。换句话说,如果没有发生重复关键字冲突,则UPDATE子句中的VALUES(col_name)可以引用被插入的col_name的值。本函数特别适用于多行插入。VALUES()函数只在INSERT…UPDATE语句中有意义,其它时候会返回NULL。

示例:

1
2
mysql> INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
-> ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

本语句与以下两个语句作用相同:

1
2
3
4
mysql> INSERT INTO table (a,b,c) VALUES (1,2,3)
-> ON DUPLICATE KEY UPDATE c=3;
mysql> INSERT INTO table (a,b,c) VALUES (4,5,6)
-> ON DUPLICATE KEY UPDATE c=9;

当您使用ON DUPLICATE KEY UPDATE时,DELAYED选项被忽略。

总结:

DELAYED做为快速插入,并不是很关心失效性,提高插入性能。

IGNORE只关注主键对应记录是不存在,无则添加,有则忽略。

ON DUPLICATE KEY UPDATE 在添加时操作,关注非主键列,注意与ignore的区别。有则更新指定列,无则添加。

一、环境的搭建

开发工具的下载:Mono

我下载的时候名称好像不对,但是网络上说的是这个我列一下自己下载的文件吧

XamarinStudio-4.0.12-3.dmg

Mono MRE installer

Mono MDK installer

这是文件下载完后,直接安装就好了。

二、项目的创建

启动XamarinStudio,开始创建项目,如下图

写上项目的名称,然后确定

接下来是创建Controller和View

创建Controller是这样的,请看下图

创建View是这样的,如下图,右击键Views目录,创建一个Home目录,然后在Home目录创建一个Index.aspx文件

最后的结构目录如下图:

HomeController文件的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace HelloWorld.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View ("Index");
}
}
}

Index.aspx文件的内容如下:

1
2
3
4
5
6
7
8
9
10
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<div>
hello world
</div>
</body>

三、直接运行

点击运行按钮

查看服务启动运行的端口(如下图):

浏览器访问就可以了。

参考文章:

ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第三篇:ASP.NET MVC全局观

ASP.NET MVC3实战系列(一):简单示例

ASP.NET视频教程

ASP.net Development on Mac 的開發環境選擇

以上文章内容更新于 2019年4月15日 , 其实版本已过期,当日重新试了一下发现会提示mcs的问题

建议使用新的版本及新的mono

termcolor是一个python包,可以改变控制台输出的颜色,支持各种terminal(WINDOWS的cmd.exe除外)。

它支持下列的文字颜色:

grey, red, green, yellow, blue, magenta, cyan, white

支持下列的背景高亮:

on_grey, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white

支持下列属性:

bold, dark, underline, blink, reverse, concealed

fabric是什么?

Fabric是一个Python库,可以通过SSH在多个host上批量执行任务。你可以编写任务脚本,然后通过Fabric在本地就可以使用SSH在大量远程服务器上自动运行。这些功能非常适合应用的自动化部署,或者执行系统管理任务。

让我们首先看一个例子。我们知道在 *NIX 下面,uname命令是查看系统的发行版。可以写这样一个Fabric脚本:

1
2
3
from fabric.api import run
def host_type():
run('uname -s')

将上面的脚本保存为fabfile.py,就可以通过fab命令在多个主机上执行host_type脚本了:

1
2
3
4
5
$ fab -H localhost,linuxbox host_type
[localhost] run: uname -s
[localhost] out: Darwin
[linuxbox] run: uname -s
[linuxbox] out: Linux

执行过程中可能需要你输入系统密码。

安装

如果你看到这里了,说明你开始对Fabric感兴趣了。但是上述操作在你那里无法执行,因为你还没有安装Fabric。安装Fabric很简单,可以用pip或者easy_install,也可以下载原代码安装。

任务函数

很好,安装Fabric并没有难住你。可能你已经成功的执行了前面的任务,现在让我们更深入一些。

Fabric中的任务就是一个python函数,姑且让我们称之为“任务函数”。既然是python函数,那么对函数的一些用法也适用于任务函数。比如传递参数、互相调用、返回值等等。首先看一个传递参数的例子:

1
2
def hello(name="world"):
print("Hello %s!" % name)

在执行任务的时候,可以通过fab的命令行参数为任务函数传递参数:

1
2
$ fab hello:name=Holbrook
Hello Holbrook!

组合任务的例子如下:

1
2
3
4
5
6
7
8
from fabric.api import run
def host_type():
run('uname -s')
def hello(name="world"):
print("Hello %s!" % name)
def composite(name="world"):
hello(name)
host_type()

Fabric提供的命令

前面我们见过了fabric.api模块中的run函数,其功能是在远端主机上执行命令。fabric.api中还提供了local函数,用于执行本地(Fabric所在的主机)命令。如下:

1
2
3
from fabric.api import local
def lslocal():
local('ls')

类似远端命令和本地命令,Fabric也区分远端目录和本地目录。Fabric提供的对远端和本地目录的操作分别是cd和lcd。如果你用过命令行的ftp,这很容易理解。让我们看一个例子:

1
2
3
4
def filepath():
remote_dir = '/opt/xxx'
with cd(remote_dir):
run("touch README")

上面代码的功能是进入远端的/opt/xxx目录,并创建一个README文件。

Fabric还提供了很多的命令,比如文件操作等,可以参考Fabric的operations模块。

管理服务器连接

前面的例子中,都需要在fab命令行参数中指定server。当要管理大量服务器时很麻烦。Fabric提供了环境变量的字典env,其中就包含了hosts字典项,可以定义需要连接的server。如下:

1
2
3
4
from fabric.api import env, run
env.hosts = ['host1', 'host2']
def mytask():
run('ls /var/www')

也可以为每个任务单独指定要执行该任务的host列表:

1
2
3
4
5
from fabric.api import env, run
def set_hosts():
env.hosts = ['host1', 'host2']
def mytask():
run('ls /var/www')

这样执行 fab set_hosts mytask时,就可以为set_hosts中指定的两个host执行mytask任务了。如果你懒得写函数,在fab命令行中指定也是一样的:

1
fab mytask:hosts="host1;host2"

为了更方便的执行批量任务,Fabric中还定义了Role,有兴趣可以阅读其官方文档。

管理SSH密码、用户、端口

尽管更推荐使用SSH公钥认证,但是Fabric还是提供了管理密码的机制。Fabric提供了两层密码。

如果你的server有相同的密码,可以在env.password中设置默认的密码;如果server密码不同,还可以在env.passwords中设置(host,password)对,为每个server设置单独的ssh密码。

上面的host字符串采用这种格式:username@hostname:port。所以,在指定ssh密码的同时,也就指定了ssh用户。同密码一样,你也可以在env.user中指定一个默认的用户。如果都没有指定,执行fab命令时会提示你输入密码。

小结

使用Fabric,你可以管理一系列host的SSH连接(包括主机名,用户,密码),定义一系列的任务函数,然后灵活的指定在哪些host上执行哪些任务。这非常使用于需要管理大量host的场景,比如运维,私有云管理,应用自动化部署等。

本文只是一篇入门文档,远没有体现出Fabric的强大。实际上,Fabric还包括大量的功能,比如Role的定义,远程交互及异常处理,并发执行,文件操作等,并且不仅仅局限于命令行方式,可以在你的应用中调用Fabric。

希望本文能够引起你对Fabric的兴趣,并在你的实际应用中解决问题。

Ruhoh简介

Ruhoh(http://ruhoh.com/) is a static site generator made for publishing content on the Internet. It’s similar to jekyll, nanoc, and others.

A static site is a website with no moving parts: no dependencies, no database, no code execution, no admin panel. This is different from a “web application” which requires many layers of software.

Creating a static site in ruhoh is done by creating standard HTML, CSS, and javascript files on your local computer as you would normally. Ruhoh does magic based on the way you organize your files and settings. Ruhoh offers baked-in automation, templating, and bindings for powerful features but you don’t have to know or touch the underlying programming logic if you don’t want to.

Publish anywhere by “compiling” your website and hosting the contents on nearly any web-host on the planet. Ruhoh also supports automatic hosting so you don’t have to worry about finding or setting up a web-host.

Why a static site?

In terms of publishing textual content and images, a static site is much faster, more secure, costs less, and is highly scalable – it won’t slow to a crawl or crash due to increased traffic.

But maybe you just want to use Wordpress or Tumblr?

There comes a time in your life when as a creator you must create! Your technical curiousity compels you to return to the joy and freedom of using your own tools and owning your content.

You fire up your text-editor and create a masterpiece. A quick switch over to the terminal allows you to execute “publish” like some hipster techno-wizard – lines of output race down your black terminal screen until finally you see “completed!”

You verify your website is updated, bring up Facebook, twitter, gchat, and snapchat(?), CTRL+V your domain into the appropriate boxes and you pause for three triumphant seconds before finally proclaiming…

“I made that!”

Why ruhoh?

Ruhoh is straightforward.

Ruhoh doesn’t want you to program your website; it wants you to publish content! Use HTML, CSS, and javascript like you would naturally, but have the power of automation and tooling there when you want it.

Focus on content not programming.

Ruhoh is language-agnostic, it’s not strictly tied to any one language so you don’t need to know ruby or do any programming if you don’t want to. However, it turns out to be a good way to learn.

Focus on Web Publishing and nothing else. Ruhoh prioritizes web-publishing specific optimizations like SEO-optimized permalink structures, cononical redirects, microformats, RSS/ATOM feed generation, robust internationalization, textual search, comments, and so on.

Onions

Ruhoh should be immediately usable and effective for beginners and experts alike. All functionality should be intuitive and simple to uncover and learn. As your experience grows and you dive deeper into customizations, the layers of power, functionality, and extensibility should elegantly peel away from ruhoh like an onion. You don’t have to use them, but they will be there.

Who should use ruhoh?

Ruhoh is built for users looking to publish content online. Publishing content is not the same as creating web apps so ruhoh is not an application framework and you don’t have to be a programmer.

You:

  • Are technically curious.
  • Are interested in basic web programming such as HTML, CSS, and javascript.
  • Value owning your own content.
  • May be jaded by hosted solutions like Wordpress and Tumblr.
  • Are probably not cool with NSA surveillance.

Knowledge of ruby is not required, but since ruby can be a pain to install, it’s best if you are capable of setting up a 1.9+ ruby environment locally.

Ruhoh is pretty technical, but the goal is to be accessible and intuitive for beginners.

Beginners are welcomed and encouragd to grow with ruhoh’s functionality as experience and curiousity dictates. Experts can immediatelytake full advantage of ruhoh’s modular architecture and straightforward APIs. Nearly all features are powered by standalone “service libraries” that can be overloaded or swapped.

Core Goals

  1. Prioritize Web Publishing.

    Work will focus on delivering the best publishing experience and utilizing the web’s latest standards for maintaining published content on the Internet.

  2. Beginner Friendly.

    Simplicity will be the highest meausure of success. Publishing should be modeled from standard HTML, CSS, and javascript workflows. Ruhoh should be a transparent layer that enables power, automatation, and extensibility but only when desired.

  3. Language-agnostic.

    A language-specific workflow places focus on the tool rather than the content. Therefore ruhoh’s core design and interface should be completely free from language-dependent knowledge or paradigms.

  4. Freely Customizable.

    Ruhoh provides many core features such as URL formatting, markdown rendering, tags, automatic summarization, etc. These functions should expose themselves merely as “services” that the user can override, extend, replace, or re-implement without worry.

  5. Obsessive Separation of Concerns

    Ruhoh is designed around three principle layers of functionality which are obsessively kept separated as much as possible.

Bottle是一个非常精致的WSGI框架,它提供了 Python Web开发中需要的基本支持:URL路由,Request/Response对象封装,模板支持,与WSGI服务器集成支持。整个框架的全部代码约有 2000行,它的核心部分没有其他任何依赖,只要有Python环境就可以运行。

Bottle适用于小型的Web开发,在应用程序规模比较小的情况下可以实现快速开发。但是由于自身功能所限,对于大型的Web程序,Bottle的功能略显不足,程序员需要手动管理模块、数据库、配置等等,与Pylons等框架相比Bottle的优势就难以体现出来了。

快速入门

通过一个简单的、典型的例子描述Bottle的使用:

1
2
3
4
5
6
7
8
9
10
11
from bottle import Bottle, run, mako_view, request

myapp = Bottle()

@myapp.get('/hello/:name/:count#\\d+#')
@mako_view('hello')
def hello(name, count):
ip = request.environ.get('REMOTE_ADDR')
return dict(n=name, c=int(count), ip=ip)

run(app=myapp)

我们创建一个Bottle对象,通过decorator配置一条路由记录。Bottle的url映射支持具名参数,“/hello/:name/:count#\d+#” 格式的参数,可以匹配/hello/(.+?)/(\d+?)的 URL。在方法体中,通过environ字典获得客户端IP,这个操作和其他WSGI框架是一致的。接着通过一个字典类型将Model数据传递给 View。View模板通过decorator定义,采用mako模板引擎实现,模板名为hello,他表示在当前目录下一个名叫hello.tpl的文 件。

剖析

Bottle主要可以分成4个模块:

Router和WSGI Application

Request和Response, Web helpers

Template Adapters

WSGI Server Adapters

Router和WSGI Application

一个Bottle对象是一个标准的WSGI App,这使他可以与很多WSGI Server集成。每个app下管理一个Router用于针对URL映射到handler方法。根据Python decorator的特性,路由规则是在程序启动时自动执行的。

Bottle的route方法用于注册handler到router中。除此以外,还有很多function currying帮助您简单地路由GET / POST 等特定HTTP方法。

Bottle最近以来支持了hook,可以注册一些方法在每个请求之前、或之后执行。

当接受一个HTTP请求时,WSGI Server会调用WSGI App的wsgi方法。它的流程是:

  1. 根据标准的WSGI接口规范,从WSGI environ中生成一个Request对象,初始化一个response对象。
  2. 在寻找方法之前,实际上Bottle会自动执行您注册的before_hook。(Bottle._add_hook_wrapper
  3. 根据request.path的URL在router中寻找对应的handler方法(Bottle.match_url, Router.match)。这一步中除了找到合适的handler方法,还要负责提取url中的具名参数,将结果以tuple的形式返回。
  4. 以返回的方法和参数,执行handler,获得返回值。
  5. 执行所有的after_hooks。
  6. 根据返回的不同类型,写入Response的头部。在这一步,Bottle还会应用一些filter,在Bottle中,filter是用于处理 handler返回类型的。例如,一个典型的filter即内置的dict2json,他将handler返回的dict类型自动换成 json。(Bottle._cast
  7. 根据返回的HTTP状态码,对handler返回对象进行处理。调用WSGI Server的start_response方法将返回对象写给客户端。

Request和Response, Web helpers

Bottle对HTTP的请求和响应封装了Request和Response对象。它采用threadlocal的方式,由Bottle app管理生命周期。您可以在声明handler方法时不必像Java Servlet那样将他们以参数传入,这样增加了方法设计的灵活性,也使得单元测试变得相对轻松。

Request对象是对WSGI environ属性的封装,可以从中取得的属性取决于WSGI Server对PEP333的实现。

关于Request API,可以参考文档

类似的,可以参考Response API了解如何对HTTP响应进行操作

和很多Web框架一样,你不必手动去设置HTTP重定向,helper方法会简化这些操作:abort, redirect, static_file。当然,你也可以自己创建一些helper。

Template Adapters

template方法用于渲染视图,您可以使用不同的模板实现:mako, jinja2, cheetah, simpletal以及Bottle自己的简单实现。

view作为一个decorator可以简化模板的选择。与route类似,作者也提供了一些function currying来支持mako_view这样简便的写法。

Server Adapters

Bottle的Server Adapters简直可以说是WSGI Server的博览会,从这里您可以了解目前比较流行的WSGI实现:

  • flup
  • wsgiref
  • cherrypy
  • paste
  • fapws3
  • tornado
  • Google Appengine
  • twisted
  • diesel
  • meinheld
  • gunicorn
  • eventlet
  • gevent
  • rocket

当然,这些不是全部,如果要使用不在其中的WSGI Server,您只需实现一个ServerAdapter的run方法即可,需要做的就是将Bottle app传给server并启动它。

内置的run方法用于启动服务,您还可以指定一个reloader参数使Bottle在后台检查源文件的修改情况,实现热加载。

除了run方式的启动,由于Bottle app本身就是一个符合标准的WSGI app,所以也可以通过一些服务器特有的方式启动服务,例如gunicorn:

1
gunicorn -w 2 -D -b 127.0.0.1:18080 module:app

实战Middlewares

Middleware是WSGI的重要概念http://www.python.org/dev/peps/pep-0333 /#middleware-components-that-play-both-sides 借助一些成熟的middleware可以添加一些Bottle目前不具备的功能:没错,Session。

Session Middleware最著名的选择叫做Beaker,要其用Beaker,只需要一个装饰器模式的App声明即可,您可以参考Beaker的文档.

Pylons以使用Middleware著称,而除了Routing这样核心的Middleware,包括Beaker和Authkit都可以应用在Bottle程序上。

App Mounting

Bottle的App提供一个很有用的mount方法帮助你将Web应用模块化。您可以将一个Bottle App挂载到一个父App上的某个路径,以父App启动后,父App可以为子App在一个路径下提供路由。

不过你会遇到这样的问题:

1
2
3
4
5
6
7
child = Bottle()
@child.get("/")
def hello():
return "hello world"

parent = Bottle()
parent.mount(child, "/child")

很自然的,您希望打开浏览器访问http://localhost:8000/child可能看到hello world,然后却得到404.这个问题不难察觉,于是访问http://localhost:8000/child/,工作正常。

我曾给作者提过这个问题http://github.com/defnull/bottle/issues#issue/88,(补一 句,Bottle的作者少有的Nice,对你提出的问题他通常都会认真解答),作者提了一个重定向的方式,不过还是没有决定把它直接加进mount方法 里。

那么,我们需要一个新的mount来支持http://localhost:8000/child

1
2
3
4
5
def mount(parent, child, path):
parent.mount(child, path)
@parent.get(path)
def redir():
redirect(path+"/")

Google AppEngine

因为有WSGI标准,你可以用Bottle开发Google AppEngine程序。您只需要在handler文件中加入这样的代码即可:

1
2
3
4
5
from google.appengine.ext.webapp.util import run_wsgi_app
def main():
run_wsgi_app(my_bottle_app)
if __name__ == '__main__':
main()

依赖?Bottle本身是self-contained,不过你可能还需要一个强大一些的模板引擎来完成你的应用,比如Mako。这样,你需要把依赖和自己的程序一起上传到GAE。对于Mako来说,除了它自己,还依赖marksave,别忘了它。

以上就是我对Bottle的理解和使用心得,希望您在合适的场景下使用Bottle,只有这样,才能感受到这个框架所带来的乐趣。

0%