GVKun编程网logo

数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分

6

如果您对数据可视化有意思的小例子:TaylorSwift歌词数据分析和可视化——第二部分感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于数据可视化有意思的小例子:Taylor

如果您对数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分的详细内容,并且为您提供关于22. 如何制作一个类似tiny wings的游戏:第二部分(完)、8.如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分、added wide keds taylor swift foot、AJAX(第二部分:使用JavaScript和Ajax发出异步请求)的有价值信息。

本文目录一览:

数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分

数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分

接着重复这篇文章
Data Visualization and Analysis of Taylor Swift’s Song Lyrics

情感分析

情感分析是啥暂时不太关注,主要关注文章里的数据可视化部分,按照文章中的代码准备数据

lyrics<-read.csv("taylor_swift_lyrics_1.csv",header=T)
lyrics_text <- lyrics$lyric
lyrics_text<- gsub(''[[:punct:]]+'''''', lyrics_text)
lyrics_text<- gsub("([[:alpha:]])\1+""", lyrics_text)
library(syuzhet)
help(package="syuzhet")
ty_sentiment <- get_nrc_sentiment((lyrics_text))

gsub函数是用来替换字符的,基本的用法是

> gsub("A","a","AAAbbbccc")
[1"aaabbbccc"

第一个位置是要替换的字符,第二个位置是替换成啥,第三个位置是完整的字符串。
第一个位置应该是可以用正则表达式的,但是R语言的正则表达式自己还没有掌握
所以下面两行代码

lyrics_text<- gsub(''[[:punct:]]+'''''', lyrics_text)
lyrics_text<- gsub("([[:alpha:]])\1+""", lyrics_text)

干了啥自己还没看明白,原文的英文注释是
removing punctations and alphanumeric content
get_nrc_sentiment()应该是做情感分析的函数吧?
暂时先不管他了
ty_sentiment <- get_nrc_sentiment((lyrics_text))运行完得到了一个数据框

> head(ty_sentiment)
  anger anticipation disgust fear joy sadness surprise trust
1     0            0       0    0   0       1        0     0
2     0            0       1    1   0       1        0     0
3     1            0       1    0   0       1        0     0
4     0            0       1    0   0       0        0     1
5     0            0       0    0   0       0        0     0
6     0            0       0    0   0       0        0     0
  negative positive
1        0        0
2        1        0
3        1        0
4        1        0
5        0        0
6        0        0

构造接下来作图用到的数据集

sentimentscores<-data.frame(colSums(ty_sentiment[,]))
head(sentimentscores)
names(sentimentscores) <- "Score"
sentimentscores <- cbind("sentiment"=rownames(sentimentscores),sentimentscores)
rownames(sentimentscores) <- NULL

最终的数据集长这个样子

> sentimentscores
      sentiment Score
1         anger   538
2 anticipation   760
3       disgust   427
4          fear   637
5           joy   956
6       sadness   684
7      surprise   429
8         trust   665
9      negative 1086
10     positive  1335

ggplot2基本的柱形图

library(ggplot2)
ggplot(data=sentimentscores,aes(x=sentiment,y=Score))+
  geom_bar(aes(fill=sentiment),stat = "identity")+
  theme(legend.position="none")+
  xlab("Sentiments")+ylab("Scores")+
  ggtitle("Total sentiment based on scores")+
  theme_minimal() 
image.png
lyrics$lyric <- as.character(lyrics$lyric)
tidy_lyrics <- lyrics %>% 
unnest_tokens(word,lyric)
song_wrd_count <- tidy_lyrics %>% count(track_title)
lyric_counts <- tidy_lyrics %>%
left_join(song_wrd_count, by = "track_title") %>% 
rename(total_words=n)
lyric_sentiment <- tidy_lyrics %>% 
inner_join(get_sentiments("nrc"),by="word")

重复到这的时候遇到了报错
所以下面自己构造数据集,可视化的结果就偏离真实情况了

lyric_counts$sentiment<-sample(colnames(ty_sentiment),
                               dim(lyric_counts)[1],
                               replace = T)
df<-lyric_counts%>%
  count(word,sentiment,sort=T)%>%
  group_by(sentiment)%>%
  top_n(n=10)%>%
  ungroup()
head(df)
head(lyric_counts)

作图

library(ggplot2)
png("1.png",height = 800,width = 1000)
ggplot(df,aes(x=reorder(word,n),y=n,fill=sentiment))+
  geom_col(show.legend = F)+
  facet_wrap(~sentiment,scales = "free")+
  coord_flip()+
  theme_bw()+
  xlab("Sentiments") + ylab("Scores")+
  ggtitle("Top words used to express emotions and sentiments")
dev.off()
image.png

第二幅图


df1<-lyric_counts%>%
  count(track_title,sentiment,sort = T)%>%
  group_by(sentiment)%>%
  top_n(n=5)
png(file="2.png",width=1300,height=700)
ggplot(df1,aes(x=reorder(track_title,n),y=n,fill=sentiment))+
  geom_bar(stat="identity",show.legend = F)+
  facet_wrap(~sentiment,scales = "free")+
  xlab("Sentiments") + ylab("Scores")+
  ggtitle("Top songs associated with emotions and sentiments") +
  coord_flip()+
  theme_bw()
dev.off()
image.png

接下来是最想重复的一幅图


year_emotion<-lyric_counts%>%
  count(sentiment,year)%>%
  group_by(year,sentiment)%>%
  summarise(sentiment_sum=sum(n))%>%
  ungroup()
head(year_emotion)
year_emotion<-na.omit(year_emotion)
grid.col = c("2006" = "#E69F00""2008" = "#56B4E9"
             "2010" = "#009E73""2012" = "#CC79A7"
             "2014" = "#D55E00""2017" = "#00D6C9"
             "anger" = "grey""anticipation" = "grey"
             "disgust" = "grey""fear" = "grey"
             "joy" = "grey""sadness" = "grey"
             "surprise" = "grey""trust" = "grey")
circos.par(gap.after = c(rep(6, length(unique(year_emotion[[1]])) - 1), 15,
                         rep(6, length(unique(year_emotion[[2]])) - 1), 15))

chordDiagram(year_emotion, grid.col = grid.col, transparency = .2)
title("Relationship between emotion and song''s year of release")
circos.clear()
image.png

重复过程中遇到很多dplyr包中的函数,都是第一次使用,抽时间在回过头来看这些函数的用法!

欢迎大家关注我的公众号
小明的数据分析笔记本

公众号二维码.jpg


本文分享自微信公众号 - 小明的数据分析笔记本()。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

22. 如何制作一个类似tiny wings的游戏:第二部分(完)

22. 如何制作一个类似tiny wings的游戏:第二部分(完)

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

原文链接地址:http://www.raywenderlich.com/3913/how-to-create-a-game-like-tiny-wings-part-2

教程截图:

这是本系列教程的最后一部分,主要是教大家如何制作一个类似Tiny Wings的游戏。

在预备教程中,我们学会了如何创建动态山丘纹理和背景纹理。

在第一部分教程中,我们学会了如何动态创建游戏里所需要的山丘。

在这篇教程中,也是本系列教程的最后一篇,我们将会学习到更加有意思的部分—如何往游戏里面添加主角,同时使用Box2D来仿真主角的移动!

再说明一下,这个教程系列是基于Sergey Tikhonov所写的一个非常好的demo project制作的—所以我要特别感谢Sergey!

这个教程假设你对cocos2d和Box2d已经很熟悉了。如果你对这两者还很陌生的话,建议你先阅读本博客上翻译的cocos2d教程和box2d教程。

Getting Started

如果你还没有准备好,可以先下载上一篇教程中完成的样例工程。

接下来,我们将添加一些基本的Box2d代码。我们将创建一个Box2d world和一些代码来激活debug drawing,同时还会添加一些测试用shape,以此确保Box2D环境被正确搭建起来!

首先,打开HelloWorldLayer.h,然后作如下修改:

// Add to top of file
#import ”Box2D.h”
#define PTM_RATIO 32.0// Add inside @interface
b2World * _world;

这里包含了Box2d的头文件和debug draw的头文件,同时定义一个_world变量来追踪Box2d的world与debug draw类。

同时,我们也声明了一个像素/米的转换率(PTM_RATIO)为32.回顾一下,这个变量主要作用是在Box2d的单位(米)和cocos2d的单位(点)之间做转换。

然后,我们在HelloWorldLayer.mm中添加一下新的方法,添加位置在init方法上面:

- (void)setupWorld {
b2Vec2 gravity = b2Vec2(0.0f, -7.0f);
bool doSleep = true;
_world = new b2World(gravity,doSleep);
}- (void)createTestBodyAtPostition:(CGPoint)position {

b2BodyDef testBodyDef;
testBodyDef.type = b2_dynamicBody;
testBodyDef.position.Set(position.x/PTM_RATIO,position.y/PTM_RATIO);
b2Body * testBody = _world->CreateBody(&testBodyDef);

b2CircleShape testBodyShape;
b2FixtureDef testFixtureDef;
testBodyShape.m_radius = 25.0/PTM_RATIO;
testFixtureDef.shape = &testBodyShape;
testFixtureDef.density = 1.0;
testFixtureDef.friction = 0.2;
testFixtureDef.restitution = 0.5;
testBody->CreateFixture(&testFixtureDef);

}

如果你对Box2d很熟悉的话,上面这个方法只是一个回顾。

setupWorld方法创建一个有重力的world–但是比标准的重力-9.8m/s^2要小一点点。

createTestBodyAtPostition创建一个测试对象—一个25个点大小的圆。我们将使用这个方法来创建一个测试对象,每一次你点击屏幕就会在那个地方产生一个圆,不过这只是测试用,之后会被删除掉。

你现在还没有完成HelloWorldLayer.mm–现在再作一些修改,如下所示:

// Add to the TOP of init
[self setupWorld];// Replace line to create Terrain in init with the following
_terrain = [[[Terrain alloc] initWithWorld:_world] autorelease];

// Add to the TOP of update
static double UPDATE_INTERVAL = 1.0f/60.0f;
static double MAX_CYCLES_PER_FRAME = 5;
static double timeAccumulator = 0;

timeAccumulator += dt;
if (timeAccumulator > (MAX_CYCLES_PER_FRAME * UPDATE_INTERVAL)) {
timeAccumulator = UPDATE_INTERVAL;
}

int32 veLocityIterations = 3;
int32 positionIterations = 2;
while (timeAccumulator >= UPDATE_INTERVAL) {
timeAccumulator -= UPDATE_INTERVAL;
_world->Step(UPDATE_INTERVAL,
veLocityIterations,positionIterations);
_world->ClearForces();

}

// Add to bottom of cctouchesBegan
UITouch *anyTouch = [touches anyObject];
CGPoint touchLocation = [_terrain convertTouchToNodeSpace:anyTouch];
[self createTestBodyAtPostition:touchLocation];

第一段代码,我们调用setupWorld方法来创建一个Box2d世界。然后使用Box2d的world来初始化Terrain类。这样,我们就可以使用这个world来创建山丘的body了。为此,我们将会写一些桩代码(placeholder)。

第二段代码,我们调用_world->Step方法来运行物理仿真。注意,这里使用的是固定时间步长的实现方式,它比变长时间步长的方式物理仿真效果要更好。对于具体这个是怎么工作的,可以去看看我们的cocos2d书籍中关于Box2d的那一章节内容。

最后一段代码是添加到cctouchesBegan里面,不管什么时候你点击屏幕,就会创建一个Box2d的body。再说一下,这样做只是为了测试Box2d环境可以run起来了。

注意,我们这里得到的touch坐标是在地形的坐标之内。这是因为,地形将会滚动,而我们想知道地形的位置,而不是屏幕的位置。

接下来,让我们修改一下Terrain.h/m。首先,修改Terrain.h,如下所示:

// Add to top of file
#import ”Box2D.h”
#import ”GLES-Render.h”// Add inside @interface
b2World *_world;
b2Body *_body;
GLESDebugDraw * _debugDraw;

// Add after @interface
- (id)initWithWorld:(b2World *)world;

这里只是包含Box2d头文件,然后创建一些实例变量来追踪Box2d的world,以及山丘的body,还有支持debug drawing的对象。同时,我们还定义了初始化方法,它接收Box2d的world作为参数。

然后在Terrain.m中添加一个新的方法,位置在generateHills上面:

- (void) resetBox2DBody {if(_body) return;

CGPoint p0 = _hillKeyPoints[0];
CGPoint p1 = _hillKeyPoints[kMaxHillKeyPoints-1];

b2BodyDef bd;
bd.position.Set(0, 0);
_body = _world->CreateBody(&bd);

b2polygonShape shape;
b2Vec2 ep1 = b2Vec2(p0.x/PTM_RATIO, 0);
b2Vec2 ep2 = b2Vec2(p1.x/PTM_RATIO, 0);
shape.SetAsEdge(ep1,ep2);
_body->CreateFixture(&shape, 0);
}

这里仅仅是一个辅助方法,用来创建山丘的的底部body,代表“地面”。这里只是暂时用这个方法,用来防止随机生成的圆会掉到屏幕之外去。之后,在我们建模好山丘后,我们会再次修改。

目前,我们只是把第一个关键点和最后一个关键点用一条边连接起来。

接下来,在Terrain.m中添加一些代码来调用上面的代码,同时建立起debug drawing:

// Add inside resetHillVertices,right after “prevToKeyPointI = _toKeyPointI” line:
[self resetBox2DBody];// Add new method above init
- (void)setupDebugDraw {
_debugDraw = new GLESDebugDraw(PTM_RATIO*[[CCDirector sharedDirector] contentScaleFactor]);
_world->SetDebugDraw(_debugDraw);
_debugDraw->SetFlags(b2DebugDraw::e_shapeBit | b2DebugDraw::e_jointBit);
}

// Replace init with the following
- (id)initWithWorld:(b2World *)world {
if ((self = [super init])) {
_world = world;
[self setupDebugDraw];
[self generateHills];
[self resetHillVertices];
}
return self;
}

// Add at bottom of draw
gldisable(GL_TEXTURE_2D);
gldisableClientState(GL_COLOR_ARRAY);
gldisableClientState(GL_TEXTURE_COORD_ARRAY);

_world->DrawDebugData();

glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

每一次山丘顶点被重置的时候,我们调用resetBox2DBody来创建可见部分山丘的body。目前,这个body是不变的(它只是添加了一条线,当作地面)。但是,接下来,我们将修改这个来建模可见部分的山丘。

setupDebugDraw方法设置了激活Box2d 对象debug drawing所需要的一些配置。如果你熟悉Box2d的话,那么这个就是回顾啦。

然后,你可能会奇怪,为什么debug draw的代码要放在Terrain.m文件中呢?而不是放在HelloWorldLayer.mm中呢?这是因为,这个游戏中的滚动效果是在Terrain.m中实现的。因此,为了使Box2d的坐标系统和屏幕范围内可见部分的坐标系统匹配起来,我们就把debug drawing代码放在Terrain.m中了。

最后一步,如果你现在想要编译的话,可能会出现几百个错误。这是因为Terrain.m导入了Terrain.h文件,而Terrain.h文件又包含了HelloWorldLayer.h文件,而它又导入了Box2D.h头文件。而不管什么时候,只要你在.m文件中使用c++的话,那么就会产生一大堆的错误。

不过还好,解决办法非常简单—只要把Terrain.m改成Terrain.mm就可以了。

编译并运行,现在,你点击一下屏幕,你会看到许多圆形对象掉在屏幕里面拉!

在Box2d里面为山丘定义body边界

现在,我们只拥有一个Box2d的shape代表屏幕的底部边界,但是,我们真正想要的是代表山丘边界的shape。

幸运的是,因为我们拥有所有的线段了,所以添加边界会非常简单!

  • 我们有一个山丘顶部所有顶点的数组(borderVertices). 在上一个教程的resetHillVertices方法中,我们生成了这样一个数组。
  • 我们有一个方法,不管什么时候顶点为被改变了,它都会被调用,那就是resetBox2DBody.

因此,我们需要修改resetBox2DBody方法,我们要为borderVertices组织中的每一个实体创建一条边,具体方法如下:

- (void) resetBox2DBody {if(_body) {
_world->DestroyBody(_body);
}

b2BodyDef bd;
bd.position.Set(0, 0);

_body = _world->CreateBody(&bd);

b2polygonShape shape;

b2Vec2 p1,p2;
for (int i=0; i<_nBorderVertices-1; i++) {
p1 = b2Vec2(_borderVertices[i].x/PTM_RATIO,_borderVertices[i].y/PTM_RATIO);
p2 = b2Vec2(_borderVertices[i+1].x/PTM_RATIO,_borderVertices[i+1].y/PTM_RATIO);
shape.SetAsEdge(p1,p2);
_body->CreateFixture(&shape, 0);
}
}

这个新的实现首先看看是不是存在一个已有的Box2d body,如果是的话,就销毁原来的body。

然后,它创建一个新的body,循环遍历border vertices数组里面的所有顶点,这些顶点代表山丘顶部。对于每2个顶点,都将创建一条边来连接它们。

很简单,对不对?编译并运行,现在,你可以看到一个带有斜坡的Box2d body了,而且它沿着山丘的纹理边界。

添加海豹

我们之前把工程命名为Tiny Seal,可是并没有seal 啊!

接下来,让我们把海豹添加进去!

首先,下载并解压这个工程的资源文件,然后把”Sprite sheets“和”Sounds“直接拖到工程里去,对于每一个文件夹,都要确保“copy items into destination group’s folder”被复选中,然后点击”Finish”。

然后,点击File\New\New File,选择iOS\Cocoa Touch\Objective-C class,再点Next。选择CCSprite作为基类,再点Next,然后把文件命名为Hero.mm(注意,.mm是因为我们将使用到Box2d的东西),最后点击Finish.

接着,把Hero.h替换成下面的内容:

#import ”cocos2d.h”
#import ”Box2D.h”#define PTM_RATIO 32.0

@interface Hero : CCSprite {
b2World *_world;
b2Body *_body;
BOOL _awake;
}

- (id)initWithWorld:(b2World *)world;
- (void)update;

@end

这个也非常简单—只是导入Box2d.h头文件,然后定义一些变量来追踪world和海豹的body.

然后,打开Hero.mm,然后作如下修改:

#import ”Hero.h”@implementation Hero

- (void)createBody {

float radius = 16.0f;
CGSize size = [[CCDirector sharedDirector] winSize];
int screenH = size.height;

CGPoint startPosition = ccp(0,screenH/2+radius);

b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.lineardamping = 0.1f;
bd.fixedRotation = true;
bd.position.Set(startPosition.x/PTM_RATIO,startPosition.y/PTM_RATIO);
_body = _world->CreateBody(&bd);

b2CircleShape shape;
shape.m_radius = radius/PTM_RATIO;

b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.restitution = 0.0f;
fd.friction = 0.2;

_body->CreateFixture(&fd);

}

- (id)initWithWorld:(b2World *)world {

if ((self = [super initWithSpriteFrameName:@"seal1.png"])) {
_world = world;
[self createBody];
}
return self;

}

- (void)update {

self.position = ccp(_body->GetPosition().x*PTM_RATIO,_body->GetPosition().y*PTM_RATIO);
b2Vec2 vel = _body->GetLinearVeLocity();
b2Vec2 weightedVel = vel;
float angle = ccpToAngle(ccp(vel.x,vel.y));
if (_awake) {
self.rotation = -1 * CC_radians_TO_degrees(angle);
}
}

@end

createBody方法为海豹创建了一个圆形的shape。这个方法和之前写过的createTestBodyAtPosition方法几乎没有什么区别,除了圆的大小和海豹图片的大小要匹配(不过实际上要比图片大小小一些,这样子碰撞检测效果会更好)

同时,这里的摩擦系数(friction)设置为0.2(因为海豹是很滑的),同时反弹系数(restitution)设置为0(这样子,当海豹碰撞到山丘的时候就不会反弹起来了)。

同时,我们也设置body的线性阻尼( linear damping),这样子海豹就会随着时间慢慢减速。同时,设置body的固定旋转为真,这样子,海豹在游戏里面就不会旋转body了。

在initWithWorld方法里面,我们把精灵初始化为一个特定的精灵帧(seal1.png),同时保存一份world的指针,然后调用上面的createBody方法。

这里的update方法基于Box2d body的位置来更新海豹精灵的位置,同时基于海豹的body的速度来更新海豹精灵的旋转。

接下来,你需要修改一下Terrain.h和Terrain.mm,因为,我们将要在Terrain.mm中添加一个sprite batch node。

首先,打开Terrain.h,并作以下修改:

// Inside @interface
CCSpriteBatchNode * _batchNode;// After @implementation
@property (retain) CCSpriteBatchNode * batchNode;

然后,打开Terrain.mm,并作如下修改:

// Add to top of file
@synthesize batchNode = _batchNode;// Add at bottom of init
_batchNode = [CCSpriteBatchNode batchNodeWithFile:@"TinySeal.png"];
[self addChild:_batchNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@”TinySeal.plist”];

这里只是为TinySeal.png精灵表单创建了一个batch node,然后从TinySeal.plist文件中加载了精灵帧的定义信息到sprite frame cache中。

差不多完成了!接下来,让我们修改HelloWorldLayer.h:

// Add to top of file
#import ”Hero.h”// Add inside @interface
Hero * _hero;

同时修改HelloWorldLayer.mm:

// Add to bottom of init
_hero = [[[Hero alloc] initWithWorld:_world] autorelease];
[_terrain.batchNode addChild:_hero];// In update,comment out the three lines starting with PIXELS_PER_SECOND and add the following
[_hero update];
float offset = _hero.position.x;

编译并运行,你现在可以看到一只happy的海豹在屏幕左边了!

但是,看起来有起奇怪,它在屏幕之外!如果我们把它往右边挪一下,那样子看起来会更好。

当然,这个改起来很简单!打开Terrain.mm,然后把setoffsetX改成下面的样子:

- (void) setoffsetX:(float)newOffsetX {
CGSize winSize = [CCDirector sharedDirector].winSize;_offsetX = newOffsetX;
self.position = CGPointMake(winSize.width/8-_offsetX*self.scale, 0);
[self resetHillVertices];
}

这里把海豹的位置旋转在屏幕的1/8处,这样子海豹看起来就会往右边一点点了。编译并运行,现在可以看到海豹的全貌啦!

使海豹移动

我们离一个完整的游戏越来越近了—我们有一只海豹,我们只需要让它飞起来就可以啦!

我们采取的策略如下:

  • 第一次点击屏幕的时候,我们让海豹稍微往右边跳起来一点点,代表开始了!
  • 不管什么时候点击屏幕,我们应用一个冲力使海豹往下落。当海豹下山时,会使它的速度变得更快,这样到下一个山头的时候就可以飞起来了。
  • 添加一些代码让海豹移动的距离稍微远一点,我们可不想让我们的海豹卡住!

让我们来实现这些策略吧!打开Hero.h,作如下修改:

// Add after @implementation
@property (readonly) BOOL awake;
- (void)wake;
- (void)dive;
- (void)limitVeLocity;

然后对Hero.mm作如下修改:

// Add to top of file
@synthesize awake = _awake;// Add new methods
- (void) wake {
_awake = YES;
_body->SetActive(true);
_body->ApplyLinearImpulse(b2Vec2(1,2),_body->GetPosition());
}

- (void) dive {
_body->ApplyForce(b2Vec2(5,-50),_body->GetPosition());
}

- (void) limitVeLocity {
if (!_awake) return;

const float minVeLocityX = 5;
const float minVeLocityY = -40;
b2Vec2 vel = _body->GetLinearVeLocity();
if (vel.x < minVeLocityX) {
vel.x = minVeLocityX;
}
if (vel.y < minVeLocityY) {
vel.y = minVeLocityY;
}
_body->SetLinearVeLocity(vel);
}

这个wake方法应用一个冲力(impulse)使得海豹刚开始往右上方飞。

dive方法应用一个比较大的向下的冲力,和一个比较小的向右的力。这个向下的冲力会使得海豹往山丘上撞,这时,山丘的斜坡越大,那么小鸟就飞得越高。(应该是上山的时候,下山相反)

limitVeLocity方法确保海豹速度至少在 x轴方向5m/s²,Y轴方向-40m/s²。

基本上要完成了—只需要再修改一下HelloWorldLayer类。首先打开HelloWorldLayer.h,然后添加一个新的实例变量:

BOOL _tapDown;

// Add at the top of the update method
if (_tapDown) {
if (!_hero.awake) {
[_hero wake];
_tapDown = NO;
} else {
[_hero dive];
}
}
[_hero limitVeLocity];// Replace cctouchesBegan with the following
- (void)cctouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self genBackground];
_tapDown = YES;
}

// Add new methods
-(void)cctouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_tapDown = NO;
}

- (void)cctouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
_tapDown = NO;
}

编译并运行,现在你有一只可以飞的海豹啦!

修正海豹身体的摇晃

你可能注意到了,当海豹往下飞的时候,身体摇摇晃晃的。

一种方式就是,使用之前的线性速度和现在得到的速度作加权平均。

让我们来实现一下。先打开Hero.h:

// Add to top of file
#define NUM_PREV_VELS 5// Add inside @interface
b2Vec2 _prevVels[NUM_PREV_VELS];
int _nextVel;

然后修改Hero.mm的update方法:

- (void)update {self.position = ccp(_body->GetPosition().x*PTM_RATIO,_body->GetPosition().y*PTM_RATIO);
b2Vec2 vel = _body->GetLinearVeLocity();
b2Vec2 weightedVel = vel;

for(int i = 0; i < NUM_PREV_VELS; ++i) {
weightedVel += _prevVels[i];
}
weightedVel = b2Vec2(weightedVel.x/NUM_PREV_VELS,weightedVel.y/NUM_PREV_VELS);
_prevVels[_nextVeL++] = vel;
if (_nextVel >= NUM_PREV_VELS) _nextVel = 0;

float angle = ccpToAngle(ccp(weightedVel.x,weightedVel.y));
if (_awake) {
self.rotation = -1 * CC_radians_TO_degrees(angle);
}
}

这里使用之前的5个线性速度作加权平均,然后使用平均值来修正海豹的旋转。编译并运行,现在你可以看到更加平滑的海豹啦!

缩小

Tiny Wings有一个很酷的特性就是,你飞得越高,那么屏幕就会越小。这使得视觉感观更加逼真!

为了实现这个,我们只需要在HelloWorldLayer.mm的update方法里面的[_hero update]调用之后,再添加下面代码就行了:

CGSize winSize = [CCDirector sharedDirector].winSize;
float scale = (winSize.height*3/4) / _hero.position.y;
if (scale > 1) scale = 1;
_terrain.scale = scale;

如果hero在winSize.height*3/4以下,那么scale就为1.如果它大于winSize.height*3/4,那么scale就会小于1,就会有缩小的感觉了。

编译并运行,现在看看你能飞多高吧!

免费的动画和音乐

你懂的,我不能让你们这些粉丝没有一些免费的动画和音乐可玩。:)

只需要花上几秒钟的时间就可以使游戏变得更有趣!首先,打开Hero.h,并作如下修改:

// Add inside @interface
CCAnimation *_normalAnim;
CCAnimate *_normalAnimate;// Add after @interface
- (void)nodive;
- (void)runForceAnimation;
- (void)runnormalAnimation;

这里声明我们即将创建的动画,还有一个新方法将在海豹没有diving的时候被调用。

接下来,修改Hero.mm:

// Add new methods
- (void)runnormalAnimation {
if (_normalAnimate || !_awake) return;
_normalAnimate = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:_normalAnim]];
[self runAction:_normalAnimate];
}- (void)runForceAnimation {
[_normalAnimate stop];
_normalAnimate = nil;
[self setdisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@”seal_downhill.png”]];
}

- (void)nodive {
[self runnormalAnimation];
}

// Add at bottom of initWithWorld:
_normalAnim = [[CCAnimation alloc] init];
[_normalAnim addFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@”seal1.png”]];
[_normalAnim addFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@”seal2.png”]];
_normalAnim.delay = 0.1;

这里为海豹的正常飞行创建了动画效果,同时添加一个方法来播放这个动画。diving动画实际上只是一个精灵帧,因此我们添加了一个辅助方法来完成divng动画的播放。

最后,我们修改一下HelloWorldLayer.mm:

// At top of file
#import ”SimpleAudioEngine.h”// At end of init
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@”TinySeal.caf” loop:YES];

// At start of update,add an else case for the if (_tapDown):
else {
[_hero nodive];
}

// Inside cctouchesBegan
[_hero runForceAnimation];

// Inside cctouchesEnded AND cctouchesCancelled
[_hero runnormalAnimation];

最后,打开Terrian.mm,注释掉draw方法里面的_world->DrawDebugData。

编译并运行代码,大功造成了!

何去何从?

这里有本系列教程的全部源代码。

到目前为止,你有一个基本的游戏框架可以玩了。为什么不尝试着完善这个游戏呢?把海豹移动的行为修改得更加逼真、更加平滑一些?或者,你可以添加一些物品和道具,充分发挥你的想象力吧!

如果你扩展了本项目,不妨拿出来分享一下,大家一起学习一下吧!

PS:译者水平有限,翻译不准的地方望不吝指出,谢谢!

原创文章,转载请注明: 转载自DEVDIV博客-History

本文链接地址: (译)如何制作一个类似tiny wings的游戏:第二部分(完)

8.如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分

8.如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

原文链接地址:http://www.raywenderlich.com/505/how-to-create-a-simple-breakout-game-with-box2d-and-cocos2d-tutorial-part-22

程序截图:

这是《如何使用cocos2d和Box2d制作一个简单的breakout游戏》的第二部分,也是最后一部分教程。如果你还没有读过第一部分,请先阅读第一个教程!

在上一个教程中,我们创建了一个屏幕盒子,球可以在里面弹跳,同时,我们可以用手指拖着paddle移动。这部分教程中,我们将添加一些游戏逻辑,当篮球碰到屏幕底部的时候,就Gameover。

Box2D 和碰撞检测

在Box2D里面,当一个fixture和另一个fixture相互碰撞的时候,我们怎么知道呢?这就需要用到碰撞侦听器了(contact listener)。一个碰撞侦听器是一个C++对象,它继承至Box2d的b2ContactListner类,并且要设置给world对象。这样,当有两个对象发生相互碰撞的时候,world对象就会回调contact listener对象的方法,这样我们就可以在那些方法里面做相应的碰撞处理了。

如何使用contact listener呢?根据BOX2D用户手册,在一个仿真周期内,你不能执行任何修改游戏物理的操作。因为,在那期间,我们可能需要做一些额外的处理(比如,当两个对象碰撞的时候销毁另一个对象)。因此, 我们需要保存碰撞的引用,这样后面就可以使用它。

另外一点值得注意的是,我们不能存储传递给contact listener的碰撞点的引用,因为,这些点被Box2D所重用。因此,我们不得不存储这些点的拷贝。

好了,说得够多了,让我们亲手实践一下吧!

当我们碰到屏幕底部的时候

注意,在这部分里面,我们将使用一些C++代码和STL(标准模板库)。如果你还不熟悉C++或者是STL,不用太担心–你只需要复制粘贴代码就OK了,不过建议还是学习一下C++和STL,因为大部分游戏都是用C++做成的。

好。点开你的Classes文件夹,点击File\New File),选择左边的“Cocoa Touch Class”,再选择“Objective-C class”,确保“Subclass of NSObject”被选中,再点Next。把它命名为MyContactListener,然后点Finish。

右键点MyContactListener.m,并把它改成MyContactListener.mm。这是因为,我们现在要创建一个C++类,而我们使用C++的时候就需要把文件后缀改成.mm。

接下用下面的代码替换掉 MyContactListener.h里面的内容:

#import ”Box2D.h”
#import <vector>
#import <algorithm>

struct MyContact {
b2Fixture *fixtureA;
b2Fixture *fixtureB;
bool operator==(const MyContact& other) const
{
return (fixtureA == other.fixtureA) && (fixtureB == other.fixtureB);
}
};

class MyContactListener : public b2ContactListener {

public:
std::vector<MyContact>_contacts;

MyContactListener();
~MyContactListener();

virtual void BeginContact(b2Contact* contact);
virtual void EndContact(b2Contact* contact);
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);

};

 

这里,我们定义了一个数据结构,当碰撞通知到达的时候,用来保存碰撞点信息。再说一遍,我们需要存储其拷贝,因为它们会被重用,所以不能保存指针。注意,我们这里一定要重载=号,因为,我们将使用find算法来查找vector中的一个特定的元素,而这个标准算法find必须要求其查找的元素是可比较相等的。

在我们申明完contact listener类之后,我们只需要声明一些我们感兴趣的方法来实现就可以了。这里的vector用来缓存碰撞点信息。

现在,用下面的内容替换掉MyContactListener.mm:

#import ”MyContactListener.h”

MyContactListener::MyContactListener() : _contacts() {
}

MyContactListener::~MyContactListener() {
}

void MyContactListener::BeginContact(b2Contact* contact) {
// We need to copy out the data because the b2Contact passed in
// is reused.
MyContact myContact = { contact->GetFixtureA(),contact->GetFixtureB() };
_contacts.push_back(myContact);
}

void MyContactListener::EndContact(b2Contact* contact) {
MyContact myContact = { contact->GetFixtureA(),contact->GetFixtureB() };
std::vector<MyContact>::iterator pos;
pos = std::find(_contacts.begin(),_contacts.end(),myContact);
if (pos != _contacts.end()) {
_contacts.erase(pos);
}
}

void MyContactListener::PreSolve(b2Contact* contact,
const b2Manifold* oldManifold) {
}

void MyContactListener::PostSolve(b2Contact* contact,
const b2ContactImpulse* impulse) {
}

我们在构造函数中初使化vector。然后,我们只需要实现BeginContact和EndContact方法就可以了。这两个方法,一个是碰撞开始的时候world对象会回调,另一个就是碰撞结束的时候被回调。在BeginContact方法中,我们复制了刚刚发生碰撞的fixture的一份拷贝,并把它存储在vector中。在EndContact方法中,我们检查一下碰撞点是否在我们的vector中,如果在的话,就移除它!

好了,现在可以使用它吧。打开HelloWorldScene.h,然后做下面的修改:

// Add to top of file
#import ”MyContactListener.h”

// Add inside @interface
MyContactListener *_contactListener;

然后在init方法中增加下列代码:

// Create contact listener
_contactListener = new MyContactListener();
_world->SetContactListener(_contactListener);

这里,我们创建了contact listener对象,然后调用world对象把它设置为world的contact listener。

接下来,再我们忘记之前先做一些清理内存的操作:

delete _contactListener;

最后,在tick方法底部添加下列代码:

std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;

if ((contact.fixtureA == _bottomFixture && contact.fixtureB == _ballFixture) ||
(contact.fixtureA == _ballFixture && contact.fixtureB == _bottomFixture)) {
NSLog(@”Ball hit bottom!”);
}
}

这里遍历所有缓存的碰撞点,然后看看是否有一个碰撞点,它的两个碰撞体分别是篮球和屏幕底部。目前为止,我们只是使用NSLog来打印一个消息,因为我们只想测试这样是否可行。

因此,在debug模式下编译并运行,你会发现,不管什么时候,当球和底部有碰撞的时候,你会看到控制台输出一句话“Ball hit bottom”!

添加Game Over场景

增加GameOverScene.h and GameOverScene.mm files两个文件,它们可以在《如何使用cocos2d制作一个简单的iphone游戏教程》中找到。注意,你必须把GameOverScene.m改成GameOverScene.mm,因为我们要在里面使用一些C++,如果你不改的话,那么编译就会报错。

然后,在HelloWorldScene.mm文件中加入下列代码:

#import ”GameOverScene.h”

然后,把NSLog语句替换成下列代码:

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

好了,我们已经实现得差不多了。但是,如果你游戏你永远不能赢,那有什么意思呢?

增加一些方块

下载我制作的方块图片,然后把它拖到Resources文件夹下面,同时确保“copy items into destination group’s folder (if needed)”被复选中。

然后往init方法中添加下列代码:

for(int i = 0; i < 4; i++) {

static int padding=20;

// Create block and add it to the layer
CCSprite *block = [CCSprite spriteWithFile:@"Block.jpg"];
int xOffset = padding+block.contentSize.width/2+
((block.contentSize.width+padding)*i);
block.position = ccp(xOffset, 250);
block.tag = 2;
[self addChild:block];

// Create block body
b2BodyDef blockBodyDef;
blockBodyDef.type = b2_dynamicBody;
blockBodyDef.position.Set(xOffset/PTM_RATIO, 250/PTM_RATIO);
blockBodyDef.userData = block;
b2Body *blockBody = _world->CreateBody(&blockBodyDef);

// Create block shape
b2polygonShape blockShape;
blockShape.SetAsBox(block.contentSize.width/PTM_RATIO/2,
block.contentSize.height/PTM_RATIO/2);

// Create shape deFinition and add to body
b2FixtureDef blockShapeDef;
blockShapeDef.shape = &blockShape;
blockShapeDef.density = 10.0;
blockShapeDef.friction = 0.0;
blockShapeDef.restitution = 0.1f;
blockBody->CreateFixture(&blockShapeDef);

}

现在,你应该可以很好地理解上面的代码了。就像之前我们为paddle创建一个body类似,这里,我们每一次也会一个方块创建一个body。注意,我们把方块精灵对象的tag设置为2,这样将来可以用到。

编译并运行,你应该可以看到篮球和方块之间有碰撞了。

销毁方块

为了使breakout游戏是一个真实的游戏,当篮球和方块有交集的时候,我们需要销毁这些方块。我们已经添加了一些代码来追踪碰撞,因此,我们对tick方法做一改动。

具体改动方式如下:

std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;

if ((contact.fixtureA == _bottomFixture && contact.fixtureB == _ballFixture) ||
(contact.fixtureA == _ballFixture && contact.fixtureB == _bottomFixture)) {
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}

b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();

// Sprite A = ball,Sprite B = Block
if (spriteA.tag == 1 && spriteB.tag == 2) {
if (std::find(toDestroy.begin(),toDestroy.end(),bodyB)
== toDestroy.end()) {
toDestroy.push_back(bodyB);
}
}
// Sprite B = block,Sprite A = ball
else if (spriteA.tag == 2 && spriteB.tag == 1) {
if (std::find(toDestroy.begin(),bodyA)
== toDestroy.end()) {
toDestroy.push_back(bodyA);
}
}
}
}

std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}

好,让我们解释一下。我们又一次遍历所有的碰撞点,但是,这一次在我们测试完篮球和屏幕底部相撞的时候,我们将检查碰撞点。我们可以通过fixture对象的GetBody方法来找对象。

接着,我们基于精灵的tag,看看到底是哪个在发生碰撞。如果一个精灵与一个body相交的话,我们就需要添加一系列的对象来删除。

注意,我们把该销毁的全部加入到一个指定的销毁点去。但是,也需要注意的是,即使还没好,我也要等几分钟把它加进去。

最后,遍历我们想要删除的body列表。

编译并运行,现在你可以销毁bricks了!

加入游戏胜利条件

接下来,我们需要添加一些逻辑,让用户能够取得游戏胜利。修改你的tick方法的开头部分,像下面一样:

- (void)tick:(ccTime) dt {

bool blockFound = false;
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if (sprite.tag == 2) {
blockFound = true;
}
//…

我们需要做的,仅仅是遍历一下场景中的所有对象,看看是否还有一个方块—-如果我们确实找到了一个,那么就把blockFound变量设置为true,否则就设置为false.

然后,在这个函数的末尾添加下面的代码:

if (!blockFound) {
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:@"You Win!"];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
}

这里,如果方块都消失了,我们就会显示一个游戏结束的场景。编译并运行,看看,你的游戏现在有胜利终止条件了!

完成touch事件

这个游戏非常酷,但是,毫无疑问,我们需要音乐!你可以下载我制作的一些背景音乐,还有好听的blip声音。和之前一样,把它拖到你的resources文件夹下。

顺便提一下,我制作这些声音效果使用一个非常不错的程序,叫做cfxr.不管怎么说,你旦你加入进去之后,把HeloWorldScene.mm中加入下面的代码:

#import ”SimpleAudioEngine.h”

接着,在init方法中加入下列代码:

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

最后,在tick方法的末尾添加下面的代码:

if (toDestroy.size() > 0) {
[[SimpleAudioEngine sharedEngine] playEffect:@”blip.caf”];
}

恩,终于完成了!你现在拥有一个使用Box2d物理引擎制作的breakout游戏了1

And there you have it – your own simple breakout game with Box2D physics!

给我代码!

这里是本系列教程的完整源代码。

何去何从?

很明显,这是一个非常简单的beakout游戏,但是,你还可以在此教程的基础上实现更多。我可以添加一些逻辑,比如打击一个白色块就计一分,或者有些块需要打中很多下才消失。或者你也可以添加新的不同类型的block,并且让paddle可以发射出激光等等。你可以充分发挥想象。

如果大家有什么问题可以提出来,大家一起讨论解决!如果有什么新的发现的,也欢迎贴出代码来,大家一起学习下!

PS:译者水平有限,如果有翻译的不好,或者翻译错误的,希望大家在看的时候给我指出来。谢谢!

原创文章,转载请注明: 转载自DEVDIV博客-History

本文链接地址: (译)如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分(完)

added wide keds taylor swift foot

added wide keds taylor swift foot

How To Select Youngsters Sneakers From A Shoe Store If you have some old tennies, slippers, boots, sandals or other pairs of footwear lying around the residence, in your closet or in the garage now is the time to make use of them, once again! If your footwear are in also negative of form to hand down, make them into stepping stones for your garden, landscaping, yard or lawn. This exciting outdoor task of recycling your previous tennies into stepping stones will not only search distinctive but has the potential to include character and persona. If this is the very first pair of sneakers you are getting for your kids or grand-youngsters keds review , then it is crucial black keds to get your product investigation completed right. The following are things you''ll want to search for when acquiring a pair of sneakers for tiny youngsters. Geox is a single of the foremost makers for the footwear of the youngsters. This Italian brand has now grow to be popular all in excess of the globe due to the wide range of functions that it provides. Priorkeds shoe to you acquire a pair of the geox keds shoes australia for your youngster, you must have a look at handful of of the features of these shoes. We are sure that you would absolutely be pleased and therefore pleased with your obtain. Lelli Kelly footwear are incredibly stylish and cozy. Ideal for minor women, these sneakers are common for a quantity of motives. Little ladies not only search stunning, but also grow to be the centre of attraction. Some women opt Lelli Kelly sneakers to hold themselves fit, some for leisure, and other folks to appear genuinely gorgeous. A perfect mixture of fitness and glamour, these shoes are a favourite selection of each mothers and fathers and children. Audio Sneakers Build audio circuitry into keds kids so when you get a stage the shoe tends to make a sound such as Clip-Klop, or squish squish, and make a number of selectable sounds. C. You require to request the salesperson to measure the width and length of both of the kid''s feet. Numerous kids have an added wide keds taylor swift foot. They may possibly need particular sized shoe. Electronic bill counters and coin counters are readily obtainable and come in all sizes and speeds to match your organization requirements. You can get a great dependable counter for as minor as a handful of hundred bucks or as considerably as several thousand, depending on the wants of your certain business. It is effortless to establish the funds-counting needs for your business and to match these demands with the appropriate machine. It is not a significant investment and is a business device that can simply shell out for itself in time and accuracy, as well as guarding you from these pesky germs!

AJAX(第二部分:使用JavaScript和Ajax发出异步请求)

AJAX(第二部分:使用JavaScript和Ajax发出异步请求)

本文中,您将开始接触最基本和基础性的有关Ajax的全部对象和编程方法:XMLHttpRequest对象。该对象实际上仅仅是一个跨越所有Ajax应用程序的公共线程,您可能已经预料到,只有彻底理解该对象才能充分发挥编程的潜力。事实上,有时您会发现,要正确地使用XMLHttpRequest,显然不能使用XMLHttpRequest。这到底是怎么回事呢?

Web1.0,实际上它指的就是具有完全不同的请求和响应模型的传统Web。

Web2.0,(在很大程序上)消除了这种看得见的往复交互。


XMLHttpRequest的方法和属性

open():建立到服务器的新请求。

send():向服务器发送请求。

abort():退出当前请求。

readyState:提供当前HTML的就绪状态。

responseText:服务器返回的请求响应文本。


简单的 new
首先需要创建一个新变量并赋给它一个XMLHttpRequest 对象实例。这在 JavaScript 中很简单,只要对该对象名使用new 关键字即可

<script language="javascript" type="text/javascript">
var request = new XMLHttpRequest();
</script>

<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (Failed) {
request = false;
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>
1、创建一个新变量request并赋值false。后面将使用false作为判定条件,它表示还没有创建XMLHttpRequest对象。

2、增加try/catch快:尝试创建XMLHttpRequest对象。如果失败(catch(Failed))则保证request的值仍然为false。

3、检查request是否仍然为false(如果一切正常就不会是false)。

4、如果出现问题(request是false)则使用JavaScript警告通知用户出现了问题。

<script language="javascript" type="text/javascript">
var request = false;
try {
	request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
	request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
	request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (Failed) {
	request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>

不放到方法或函数体中的JavaScript代码称为静态JavaScript。就是说代码是在页面显示给用户之前的某个时候运行。


用XMLHttpRequest发送请求

得到请求对象之后就可以进入请求/响应循环了。记住,XMLHttpRequest惟一的目的是让您发送请求和接收响应。其他一切都是JavaScript、CSS或页面中其他代码的工作:改变用户界面、切换图像、解释服务器返回的数据。准备好XMLHttpRequest之后,就可以向服务器发送请求了。

escape()方法,它用于转义不能用明文正确发送的任何字符。


打开请求

有了要连接的URL后就可以配置请求了。可以用XMLHttpRequest对象的open()方法来完成。该方法有五个参数:

request-type:发送请求的类型。典型的值是GET或POST,但也可以发送HEAD请求。

url:要连接的URL。

asynch:如果希望使用异步连接则为true,否则为false。该参数是可选的,默认为true。

username:如果需要身份验证,则可以在此指定用户名。该可选参数没有默认值。

password:如果需要身份验证,则可以在此指定口令。该可选参数没有默认值。


异步请求不等待服务器响应。发送请求后应用程序继续运行。用户仍然可以在Web表单中输入数据,甚至离开表单。没有旋转的皮球或者沙漏,应用程序也没有明显的冻结。服务器悄悄地响应请求,完成后告诉原来的请求者工作已经结束。结果是,应用程序感觉不那么迟钝或者缓慢,而是响应迅速、交互性强,感觉快多了。


发送请求 send()


指定回调方法

onreadystatechange属性,该属性允许指定一个回调函数。回调允许服务器反向调用Web页面中的代码。

异步原因:用户在一层上操作表单,而在另一层上服务器响应请求并触发onreadystatechange属性指定的回调方法。


处理服务器响应


回调和Ajax

现在我们已经看到如何告诉服务器完成后应该做什么:将XMLHttpRequest对象的onreadystatechange属性设置为要运行的函数名。这样,当服务器处理完请求后就会自动调用该函数。也不需要担心该函数的任何参数。


HTTP就绪状态

服务器在完成请求之后会在XMLHttpRequest的onreadystatechange属性中查找要调用的方法。这是真的,但还不完整。事实上,每当HTTP就绪状态改变时它都会调用该方法。

HTTP就绪状态表示请求的状态或情形。它用于确定该请求是否已经开始、是否得到了响应或者请求/响应模型是否已经完成。它还可以帮助确定读取服务器提供的响应文本或数据是否安全。在Ajax应用程序中需要了解五种就绪状态:

0:请求没有发出(在调用open()之前)。

1:请求已经建立但还没有发出(调用send()之前)

2:请求已经发出正在处理之中(这里通常可以从响应得到内容头部)。

3:请求已经处理,响应中通常有部分数据可用,但是服务器还没有完成响应。

4:响应已完成,可以访问服务器响应并使用它。

对于Ajax编程,需要直接处理的唯一状态就是就绪状态4,它表示服务器响应已经完成,可以完全地使用响应数据了。


HTTP状态码

除了就绪状态外,还需要检查HTTP状态。我们期望的状态码是200,它表示一切顺利。如果就绪状态是4而且状态码是200,就可以处理服务器的数据了,而且这些数据应该就是要求的数据(而不是错误或者其他有问题的信息)。


读取响应文本

现在可以确保请求已经处理完成(通过就绪状态),服务器给出了正常的响应(通过状态码),最后我们可以处理服务器返回的数据了。返回的数据保存在XMLHttpRequest对象的responseText属性中。


XMLHttpRequest的属性responseXML,如果服务器选择使用XML响应则该属性包含XML响应。处理XML响应和处理普通文本有很大不同,涉及到解析、文档对象模型(DOM)和其他一些问题。

今天关于数据可视化有意思的小例子:Taylor Swift 歌词数据分析和可视化——第二部分的讲解已经结束,谢谢您的阅读,如果想了解更多关于22. 如何制作一个类似tiny wings的游戏:第二部分(完)、8.如何使用cocos2d和box2d来制作一个Breakout游戏:第二部分、added wide keds taylor swift foot、AJAX(第二部分:使用JavaScript和Ajax发出异步请求)的相关知识,请在本站搜索。

本文标签:

上一篇Swift for TensorFlow 0.3.1 发布,专为 TensorFlow 设计的编程模型(tensorflow2.0 helloworld)

下一篇[Swift]通天遁地Swift(通天通地)