UIAccelerometer *accelerometer = [UIAccelerometersharedAccelerometer];
accelerometer.delegate =self;
accelerometer.updateInterval =1.0/30.0f;
下面是加速计的代理方法,需要符合协议<UIAccelerometerDelegate>.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// NSString *str = [NSString stringWithFormat:@"x:%g\ty:%g\tz:%g",acceleration.x,acceleration.y,acceleration.z];
// NSLog(@"%@",str);
//检测摇动, 1.5为轻摇,2.0为重摇
// if (fabsf(acceleration.x)>1.8||
// fabsf(acceleration.y)>1.8||
// fabsf(acceleration.z>1.8)) {
// NSLog(@"你摇动我了~");
// }
static NSInteger shakeCount = 0;
static NSDate *shakeStart;
NSDate *now = [[NSDate alloc]init];
NSDate *checkDate = [[NSDate alloc]initWithTimeInterval:1.5fsinceDate:shakeStart];
if ([now compare:checkDate] == NSOrderedDescending || shakeStart == nil) {
shakeCount =0;
[shakeStart release];
shakeStart = [[NSDatealloc]init];
}
[now release];
[checkDate release];
if (fabsf(acceleration.x)>1.7||
fabsf(acceleration.y)>1.7||
fabsf(acceleration.z)>1.7) {
shakeCount ++;
if (shakeCount >4) {
NSLog(@"你摇动我了~");
shakeCount =0;
[shakeStartrelease];
shakeStart = [[NSDatealloc]init];
}
}
}
iphone静止时受到的地球引力为1g,这是加速计确定手机朝向的基础。分为三个轴x,y,z,如果手机以任何方式垂直水平放置,这1g的力将分布在一条轴上,如果设备倾斜,将分布在多条轴上。
可通过UIAccelerometer单例来使用加速计。UIAccelerometer定义了一种协议————UIAcceleromerterDelegateProtocol,必须实现他才能获得加速计的测量值,代理将以设定的频率收到更新。
让应用程序使用加速器有三步:获取单例,指定代理,指定频率
UIAccelerometer *accelerometer=[UIAccelerometer sharedAccelerometer];
accelerometer.delegate=self;
accelerometer.updateInterval=0.1//10 times per sec
其中UIAccelerometerDelegateProtocol只有一个方法:accelerometer:didAccelerate 他将UIAccelerate单例和一个UIAcceleration对象作为参数。UIAcceleration对象通过属性提供了三个轴的当前读数以及测量读数的时间。
检测朝向,我们来创建个小实验,来指出iphone当前处于正立,倒立,左立,右立,正面朝向和正面朝下。
首先在h文件中添加一个label 并property一下
在xib中连接好设置的控件,我们用label显示手机的位置情况
下面我们来实现UIAccelerometerDelegate协议,所以我们将视图控制器指定为加速器代理,频率也不用太高,每秒两次就够了。可以在viewdidload方法中指定代理和更新间隔。
我们剩下的唯一工作就是实现accelerometer:didAccelerate方法了,我们使用UIAcceleration对象的xyz属性来判断那个轴上的重力最大,因此可将读数设置为大于0.5或小于-0.5
下面我们来修改m中的代码,
#pragma mark -
#pragma mark UIAccelerometerDelegate
-(void)accelerometer:(UIAccelerometer *)accelerometer
didAcceler:(UIAcceleration *)acceleration
{
if(acceleration.x>0.5)
{
orientation.text=@"Right side";
}
else if(acceleration.x<-0.5)
{
orientation.text=@"left side";
}
else if(acceleration.y>0.5)
{
orientation.text=@"upsid down";
}
else if(acceleratrion.y<-0.5)
{
orientation.text=@"standing up";
}
else if(acceleration.z>.5)
{
orientation.text=@"facedown";
}
else if(acceleration.z<-0.5)
{
orientation.text=@"face up";
}
}
下面我在viewdidload中添加刚才上述的获取加速器的三步,就可以完成这个小实验了
检测手机倾斜
我们玩的赛车游戏中,应该就是用的这个检测倾斜来控制方向的,学会了这个,我们就可以做个小游戏了,下面我们来做个小实验
首先还是在h中添加UIAccelerometerDelegate,然后创建个UIView,property一下,在xib中,吧background设置为一种颜色。把做好的背景和刚刚的UIview连接。
下面我们在UIAccelerometerDelegate中实现协议,我们在这个试验中只考虑x轴的倾斜程度,也就是说读数越接近1.0 -1.0颜色越纯。接近0设置为透明。我们用fabs()方法来获取绝对值,这样就不用考虑iphone向哪个方向倾斜了
下面我们来实现这个delegate的方法
-(void)accelerometer:(UIAcclerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
UIAccelerationValue value=fabs(acceleration.x);
if(value>1.0){value=1.0}
colorView.alpha=value;
}
最后我们还是在viewdidload中加入开始提到的那三步。
到深夜了,天津海河边上就是冷啊,这屋子里面和冰箱一样,很困了,我们再来说最后的,加速器如何检测iphone的移动呢。
其实iphone有两种方法检测,一种是找出大于1g的力,适合快速移动时候的检测,一种是实现一个过滤器,来计算重力与加速计测量到的力的差,并认为这种差导致iphone在空间中移动。
下面我们还是写一个小程序,让用户能在6个方向之一快速移动来改变屏幕颜色。
-(void)setBaseColor:(UIAcceleration *)acceleration
{
if(acceleration.x>1.3)
{
colorView。backgroundcolor=[UIcolor greencolor];
}
else if(acceleration.x<-1.3)
{
colorVIew.backgroundcolor=[UIcolor orangecolor];
}
.....(此处略去其他方向的代码了)
}
-(void)accelerometer:(UIAcclerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
[self setbasecoulor:acceleration];
UIAccelerationValue value=fabs(acceleration.x);
if(value>1.0){value=1.0}
colorView.alpha=value;
}
}
UIAccelerometer加速计是用来检测iphone手机在x.y.z轴三个轴上的加速度。要获得此类调用:
加速计物理学
通过感知特定方向的惯性力总量,加速计可以测量出加速度和重力。iPhone内的加速计是一个三轴加速计,这意味着他能够检测到三维空间中的运动或重力引力。因此,加速计不但可以指示握持电话的方式(如自动旋转功能),而且如果电话放在桌子上的话,还可以指示电话的正面朝下还是朝上。
加速计可以测量g引力,因此加速计返回值为1.0时,表示在特定方向上感知到1g。如果是静止握持iPhone而没有任何运动,那么地球引力对其施加的力大约为1g。如果是纵向竖直地握持iPhone,那么iPhone会检测并报告其y轴上施加的力大约为1g。如果是以一定角度握持iPhone,那么1g的力会分布到不同的轴上,这取决于握持iPhone的方式。在以45度角握持时,1g的力会均匀地分解到两个轴上。
如果检测到的加速计值远大于1g,那么即可以判断这是突然运动。正常使用时,加速计在任一轴上都不会检测到远大于1g的值。如果摇动、坠落或投掷iPhone,那么加速计便会在一个或多个轴上检测到很大的力。
iPhone加速计使用的三轴结构是:iPhone长边的左右是X轴(右为正),短边的上下是Y轴(上为正),垂直于iPhone的是Z轴(正面为正)。需要注意的是,加速计对y坐标轴使用了更标准的惯例,即y轴伸长表示向上的力,这与Quartz2D的坐标系相反。如果加速计使用Quartz 2D做为控制机制,那么必须要转换y坐标轴。使用OpenGL ES时则不需要转换。
访问加速计
UIAccelerometer类是单独存在的。要获取对此类的引用,请调用sharedAccelerometer方法:
UIAccelerometer *accelerometer=[UIAccelerometersharedAccelerometer];
从加速计获取信息与从CoreLocation获取信息相似。创建一个符合UIAccelerometerDelegate协议的类,执行可以获取加速计信息的方法。
在分配委托时,需要以秒指定更新间隔。iPhone的加速计支持最高以每秒100次的频率进行轮询,但无法保证真正达到这么多次更新,或者可以精确均匀分隔这些更新。要分配委托或指定轮询间隔为每秒60次,可以如下所示:
accelerometer.delegate=self;
accelerometer.updateInterval=1.0f/60.0f;
完成之后,剩余的事情是实现加速计用于更新委托的方法,accelerometer:didAccelerate:。第二个变量包含了来自加速计的真实数据,嵌入在类UIAcceleration的一个对象中。
UIAcceleration
如前所述,iPhone加速计可以检测3个轴上的加速度,并且对使用UIAcceleration类实例的委托提供此信息。每个UIAcceleration实例都有x、y和z的属性,分别有一个带符号的浮点值。值0表示加速计在此轴上没有检测到任何运动。正值或负值表示一个方向上的力。例如,y的负值表示感受到了向下的力,这可能表示电话是纵向竖直握持的。y的正值表示在向上的方向施加了某些力,这可能意味着电话是倒置的或者电话正在向下运动。(这里有点疑问?说反了吧?)
请在头脑中牢记图15-1的示意图,并查看加速计结果。注意,在现实生活中,几乎不可能获得如此精确的值,因为加速计非常敏感,可以感知非常微笑的运动,而我们通常只能感知立体空间3个轴上的某一个微小受力。这是现实世界中的物理,而不是高中物理实验室。
实现accelerometer:didAccelertae:方法
- (void)accelerometer:(UIAccelerometer *)accelerometerdidAccelerate:(UIAcceleration *)acceleration {
NSString *newText=[[NSString alloc] initWithFormat:@"Max: x:%g\ty:%g\tz:
%g",acceleration.x,acceleration.y,acceleration.z];
label.text=newText;
[newText release];
}
此方法可以在每次调用时更改界面上的标签,调用此方法的频率取决于以前指定的updateInterval的值。
1,检测摇动
加速计通常用于检测摇动。与手势相似,摇动可以作为应用程序输入的一种形式。例如,对于绘图程序GLPaint,iPhone的示例代码之一,用户可以通过摇动iPhone擦除绘图,就像Etch-a-Sketch一样。摇动检测功能相对来说是微不足道的,主需要检查某个轴上的绝对值是否大于阈值(yu)。在正常使用期间,3个轴之一所注册的值通常在1.3g左右,若要远高于此值,则需要刻意施加力量。加速计好像不能注册高于2.3g左右的值。因此,请勿将阈值设置得高于此值。
要检测摇动,请检查绝对值是否大于1.5(表示轻微摇动)和2.0(表示剧烈摇动),代码如下:
- (void)accelerometer:(UIAccelerometer *)accelerometerdidAccelerate:(UIAcceleration *)acceleration {
if (fbasf(acceleration.x)>2.0 ||fbasf(acceleration.y)>2.0 ||fbasf(acceleration.z)>2.0 ) {
//do something here...
}
}
上述方法可以检测到任一轴上任何超过2g力的运动。通过要求用户前后摇动一定次数才能注册为一次摇动,我们可以执行更复杂的摇动检测,代码如下:
- (void)accelerometer:(UIAccelerometer *)accelerometerdidAccelerate:(UIAcceleration *)acceleration {
static NSInteger shakeCount=0;
static NSDate *shakeStart;
NSDate *now=[[NSDate alloc] init];
NSDate *checkDate=[[NSDate alloc] initWithTimeInterval:1.5fsinceDate:shakeStart];
if ([now compare:checkDate]==NSOrderedDescending || shakeStart==nil) {
shakeCount=0;
[shakeStart release];
shakeStart=[[NSDate alloc] init];
}
[now release];
[checkDate release];
if (fbasf(acceleration.x)>2.0 ||fbasf(acceleration.y)>2.0 ||fbasf(acceleration.z)>2.0 ) {
shakeCount++;
if (shakeCount>4) {
//do something
shakeCount=0;
[shakeStart release];
shakeStart=[[NSDate alloc] init];
}
}
}
此方法可以查明加速计报告的值大于2的次数,如果在1.5s的时间内发送了4次,则注册为一次摇动。
2.加速计用作方向控制器
或许加速计在第三方应用程序中最常见的用法是作为游戏控制器。在游戏中不是使用按钮控制字符或对象的移动,而是使用加速计。例如,在赛车游戏中,像转动方向盘那样转到iPhone也许就可以驾驶汽车,而向前倾斜表示加速,向后倾斜表示刹车。
具体如何将加速计用作控制器,这很大程度上取决于游戏的特定机制。在很简单的情况下,可能只需要获取一个轴的值,乘以某个数,然后添加到所控制对象的坐标系中。
使用加速计作为控制器的一个棘手问题是,委托方法并不能保证以指定的间隔回调。如果告诉加速计每秒钟更新60次委托类,所能确定的仅仅是他每秒钟更新的次数绝对不会多于60次。因此不能保证每秒钟得到60次均匀分隔的更新,所以如果所做的动画是基于来自加速计的输入,那么必须要弄清楚委托方法调用之间的时间。
说明 本章中的应用程序在仿真器上不起作用,因为仿真器中没有加速计。
摇动与击碎
我们要编写一个应用程序,他在检测到摇动之后会使电话看起来和听起来好像他因为摇动而破碎一样。启动此应用程序后,程序会显示一张图片,他看起来像是iPhone的首页。
摇动电话时,发出破碎的声音,并更换图片。
然后只需触摸屏幕,就可以重置其初始状态。
用于击碎的代码
创建一个基于视图的项目ShakeAndBreak。在 15ShakeAndBreak文件夹中找到两张图像和一个声音文件,拖到Resources文件夹中。还有一个icon.png,也添加到Resources文件夹。
展开Resources文件夹中的info.plist,在属性列表中添加一个条目,告诉应用程序不要使用状态栏。单击InformationPropertyList行,单击出现在本行末端的按钮,添加一个新的子值,将新行的Key改为UIStatusBarHidden,然后在右键单击刚才添加的行的空Value列。此时应该出现一个上下文菜单,选择ValueType为Boolean。此行应该变为一个复选框,单击选中此复选框。最后,修改IconFile为icon.png。
然后,展开Classes文件,单击ShakeAndBreakViewController.h:
#define kAccelerationThreshold 2.2
#define kUpdateInterval (1.0f/10.0f)
#import<AudioToolbox/AudioToolbox.h>
@interface ShakeAndBreakViewController:UIViewController<UIAccelerometerDelegate> {
IBOutlet UIImageView *imageView;
BOOL brokenScreenShowing;
SystemSoundId soundID;
UIImage *fixed;
UIIMage *broken;
}
@property ..
@end
我们定义更新频率为1s10次,这已经足够检测到一次摇动。通常来说,轮询时会使用能满足要求的最低频率。在将加速计用作控制器时,需要以相当快的速率轮询,通常达到每秒30次到60次更新。
打开ShakeAndBreakViewController.xib。单击View图标,Cmd+3修改其大小,高度460=》480,将Image View从库中拖到View窗口中。
单击ShakeAndBreakViewController.m:
- (void)viewDidLoad {
UIAccelerometer *accel=[UIAccelerometer sharedAccelerometer];
accel.delegate=self;
accel.updateInterval=kUpdateInterval;
NSString *path=[[NSBundle mainBundle] pathForResource:@"glass"ofType:@"wav"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURLfileURLWithPath:path],&soundID);
self.fixed=[UIImage imageNamed:@"home.png"];
self.broken=[UIImage imageNamed:@"homebroken.png"];
imageView.image=fixed;
brokenScreenShowing=NO;
}
- (void)accelerometer:(UIAccelerometer *)accelerometerdidAccelerate:(UIAcceleration *)acceleration {
if (! brokenScreenShowing) {
if (acceleration.x >kAccelerationThreshold ||acceleration.y >kAccelerationThreshold ||acceleration.z
>kAccelerationThreshold) {
imageView.image=broken;
AudioServicePlaySystemSound(soundID);
brokenScreenShowing=YES;
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event
{
imageView.image=fixed;
brokenScreenShowing=NO;
}
@end
滚弹珠程序
我们的下一个小游戏,是通过倾斜电话在iPhone的屏幕上移动弹珠。此处,我们使用Quartz2D来处理动画。在处理游戏或其他需要平滑动画的程序时,通常的规则是使用OpenGL。这里使用Quartz2D是因为他比较简单。
使用基于视图的模版创建一个新项目 Ball。在15Ball文件夹中,找到ball.png,将其添加到Resources文件夹中。
然后单击Classes文件夹,Cmd+N新建UIView subClass,命名为BallView.m。
双击BallViewController.xib,单击View图标,将视图的类改为BallView,将视图的背景颜色改为黑色,然后重新设置File'sOwner的view输出口为BallView。
实现BallView控制器
单击BallViewController.h:
#define kUpdateInterval (1.0f/60.0f)
@interface BallViewController:UIViewController<UIAccelerometerDelegate> {
}
@end
转到BallViewController.m:
- (void)viewDidLoad {
UIAccelerometer *accel=[UIAccelerometer sharedAccelerometer];
accel.delegate=self;
accel.updateInterval=kUpdateInterval;
[super viewDidLoad];
}
- (void)accelerometer:(UIAccelerometer *)accelerometerdidAccelerate:(UIAcceleration *)acceleration {
[(BallView *)self.view setAcceleration:acceleration];
[(BallView *)self.view draw];
}
编写Ball View
单击BallView.h:
#define kVelocityMultiplier 500
@interface BallView:UIView {
UIImage *image;
CGPoint currentPoint;
CGPoint previousPoint;
UIAcceleration *acceleration;
CGFloat ballXVelocity;
CGFloat ballYVelocity;
}
- (void)draw;
@end
切换到BallView.m:
- (id)initWithCoder:(NSCoder *)coder {
if (self=[super initWithCoder:coder]) {
self.image=[UIIMage imageNamed:@"ball.png"];
self.currentPoint=CGPointMake((self.bounds.size.width/2.0f)+(image.size.width/ 2.0f),(self.bounds.size.height/2.0f)+(image.size.height /2.0f));
ballXVelocity=0.0f;
ballYVelocity=0.0f;
}
return self;
}
- (void)drawRect:(CGRect) rect {
[image drawAtPoint:currentPoint];
}
- (CGPoint)currentPoint {
return currentPoint;
}
- (void)setCurrentPoint:(CGPoint)newPoint {
previousPoint=currentPoint;
currentPoint=newPoint;
if (currentPoint.x<0) {
currentPoint.x=0;
ballXVelocity=0;
}
if (currentPoint.y<0) {
currentPoint.y=0;
ballYVelocity=0;
}
if (currentPoint.x > self.bounds.size.width-image.size.width ) {
currentPoint.x=self.bounds.size.width -image.size.width;
ballXVelocity=0;
}
if (currentPoint.y > self.bounds.size.height-image.size.height ) {
currentPoint.y=self.bounds.size.height -image.size.height;
ballYVelocity=0;
}
CGRectcurrentImageRect=CGRectMake(currentPoint.x,currentPoint.y,currentPoint.x+image.size.width,currentPoint.y+image.size.height);
CGRectpreviousImageRect=CGRectMake(previousPoint.x,previousPoint.y,previousPoint.x+image.size.width,previousPoint.y+image.size.height);
[selfsetNeedsDisplayInRect:CGRectUnion(currentImageRect,previousImageRect)];
}
- (void)draw {
static NSDate *lastDrawTime;
if (lastDrawTime !=nil) {
NSTimeInterval secondsSinceLastDraw=-([lastDrawTimetimeIntervalSinceNow]);
ballYVelocity=ballYVelocity+-(acceleration.y*secondsSinceLastDraw);
ballXVelocity=ballXVelocity+(acceleration.x*secondsSinceLastDraw);
CGFloat xAcceleration=secondsSinceLastDraw *ballXVelocity*500;
CGFloat yAcceleration=secondsSinceLastDraw *ballYVelocity*500;
self.currentPoint =CGPointMake(self.currentPoint.x+xAcceleration,self.currentPoint.y+yAcceleration);
}
//Update last time with current time
[lastDrawTime release];
lastDrawTime=[[NSDate alloc] init];
}
@end