//联系人:石虎 QQ: 1224614774昵称:嗡嘛呢叭咪哄
/**
注意点: 1.看 GIF 效果图.
2.看连线视图的效果图.
3.看实现代码(直接复制实现效果).
4.需要真机设备 才有效果 ...
5.关于 "gate" "ball1" "monster_move_1" "grass" 分别是加载的图片
*/
一、GIF 效果图:
二、连线视图的效果图:
图1:
图2:
图3:
图4:
三、实现代码:
=========================
===================================================
==========================控制器1: SHBallView.h
//
// SHBallView.h
// MonsterBall(怪物舞会)~demo
//
// Created by 石虎 on 2017/8/14.
// Copyright © 2017年 shihu. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>
@interface SHBallView : UIView
// 定义属性来记录足球的当前位置
@property (nonatomic ,assign) CGPoint currentPoint;
@property (nonatomic ,assign) CMAcceleration acceleration;
// 定义属性来记录足球滚动的X、Y轴方向的速度
@property (nonatomic ,assign) CGFloat xVelocity;
@property (nonatomic ,assign) CGFloat yVelocity;
- (void)update;
@end
控制器1: SHBallView.m
//
// SHBallView.m
// MonsterBall(怪物舞会)~demo
//
// Created by 石虎 on 2017/8/14.
// Copyright © 2017年 shihu. All rights reserved.
//
#import "SHBallView.h"
#define BALL_SIZE 24
// 定义怪物的数量
#define MONSTER_NUM 3
// 定义球门的起始位置
#define GATE_ORIGIN_X 85
// 定义球门的宽度
#define GATE_WIDTH 150
@interface SHBallView () <UIAlertViewDelegate>
{
NSArray* ballImages;
NSArray* monsterImages;
UIImage* gateImage;
// 定义变量记录足球动画帧的索引
NSInteger ballIndex , count;
// 定义变量记录每个怪物当前显示的动画帧的索引
NSInteger monsterImageIndexs[MONSTER_NUM];
// 定义数组来记录每个怪物的位置
CGPoint monsterPoints[MONSTER_NUM];
NSTimer* timer;
BOOL isPlaying; // 定义变量记录游戏的状态
}
@end
@implementation SHBallView
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [superinitWithCoder:aDecoder];
if (self) {
// 加载球门图片
gateImage = [UIImageimageNamed:@"gate"];
// 加载足球滚动的每一帧的图片
ballImages = [NSArrayarrayWithObjects:
[UIImageimageNamed:@"ball1"],
[UIImageimageNamed:@"ball2"],
[UIImageimageNamed:@"ball3"],
[UIImageimageNamed:@"ball4"],
[UIImageimageNamed:@"ball5"],
[UIImageimageNamed:@"ball6"],
[UIImageimageNamed:@"ball7"],
[UIImageimageNamed:@"ball8"],nil];
// 加载怪物走动的每一帧的图片
monsterImages = [NSArrayarrayWithObjects:
[UIImageimageNamed:@"monster_move_1"],
[UIImageimageNamed:@"monster_move_2"],
[UIImageimageNamed:@"monster_move_3"],
[UIImageimageNamed:@"monster_move_4"],nil];
monsterImageIndexs[1] =1;
monsterImageIndexs[2] =2;
[selfstartGame]; //开始游戏
}
returnself;
}
- (void) moveMonster
{
for (int i =0 ; i < MONSTER_NUM ; i++)
{
// 控制怪物动画显示下一帧的图片
monsterImageIndexs[i] =monsterImageIndexs[i] + 1;
// 改变第i个怪物的X坐标
monsterPoints[i].x =monsterPoints[i].x +arc4random() % 9 -4;
}
[selfsetNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
// 绘制足球
[ballImages[ballIndex++ %8] drawAtPoint:self.currentPoint];
// 绘制球门
[gateImagedrawAtPoint:CGPointMake(GATE_ORIGIN_X ,64)];
// 采用循环绘制3个怪物
for(int i =0 ; i < MONSTER_NUM ; i++)
{
[monsterImages[monsterImageIndexs[i] %4] drawAtPoint:monsterPoints[i]];
}
}
// 重写实现currentPoint属性的setter方法
- (void)setCurrentPoint:(CGPoint)newPoint
{
// 如果正在游戏中,且足球新的位置点与原来的位置点不位于同一个点。
if(isPlaying && (fabs(_currentPoint.x - newPoint.x) > 1
|| fabs(_currentPoint.y - newPoint.y) > 1))
{
_currentPoint = newPoint;
// 如果足球当前的X坐标小于0,就足球已经位于最左边
if (_currentPoint.x <0)
{
// 将足球设置在最左边,并将水平速度设为0
_currentPoint.x =0;
self.xVelocity =0;
}
// 球已经到了底线,且没进球门
if (_currentPoint.y <75 && (_currentPoint.x <GATE_ORIGIN_X + 2
|| _currentPoint.x >GATE_ORIGIN_X + GATE_WIDTH - 4))
{
// 将足球设置在底线上,并将垂直速度设为0
_currentPoint.y =75;
self.yVelocity =0;
}
// 球进了球门
if (_currentPoint.y <75 && (_currentPoint.x >=GATE_ORIGIN_X + 2
|| _currentPoint.x <=GATE_ORIGIN_X + GATE_WIDTH - 4))
{
// 使用UIAlertView提示用户游戏结束,并询问用户是否要开始下一盘游戏
[[[UIAlertViewalloc] initWithTitle:@"游戏结束"
message:@"您进球了,再来一盘?"delegate:self
cancelButtonTitle:@"好"otherButtonTitles:nil]show];
// 取消计时器
[timerinvalidate];
isPlaying =NO;
}
// 如果足球的X坐标大于该控件的高度,表明足球已经到了屏幕最下方
if (_currentPoint.x >self.bounds.size.width -BALL_SIZE)
{
// 将足球设置到屏幕最下方,并将垂直速度设为0
_currentPoint.x =self.bounds.size.width -BALL_SIZE;
self.xVelocity =0;
}
// 如果足球的Y坐标大于该控件的宽度,表明足球已经到了屏幕最右边
if (_currentPoint.y >self.bounds.size.height -BALL_SIZE)
{
// 将足球设置到屏幕最右边,并将水平速度设为0
_currentPoint.y =self.bounds.size.height -BALL_SIZE;
self.yVelocity =0;
}
// 遍历每个怪物,检测怪物与足球是否碰撞
for (int i =0 ; i < MONSTER_NUM ; i ++)
{
// 如果怪物所在矩形和足球所在矩形有交集,表明二者相撞
if(CGRectIntersectsRect([selfgetBallRect], [selfgetMonsterRect:i]))
{
// 怪物将足球快速向下踢出,也就是足球的Y方向上速度增加0.6
self.yVelocity =self.yVelocity +0.6;
}
}
[selfsetNeedsDisplay];
}
}
- (void)update
{
staticNSDate *lastUpdateTime;
// 如果初始化过lastUpdateTime变量
if (lastUpdateTime !=nil)
{
// 计算上次到现在的时间差
NSTimeInterval secondsSinceLastDraw =
-([lastUpdateTime timeIntervalSinceNow]);
// 根据加速度数据计算足球在X方向、Y方向的速度
self.yVelocity =self.yVelocity + -(self.acceleration.y *
secondsSinceLastDraw);
self.xVelocity =self.xVelocity +self.acceleration.x *
secondsSinceLastDraw;
// 根据足球的速度计算足球在单位时间内滚动的距离。
// 由于实际算出来的滚动距离太小,因此都需要乘以500(实际上可用400、600等)
CGFloat xDelta = secondsSinceLastDraw *self.xVelocity *500;
CGFloat yDelta = secondsSinceLastDraw *self.yVelocity *500;
// 设置足球的位置为新计算出来的位置
self.currentPoint =CGPointMake(self.currentPoint.x + xDelta,
self.currentPoint.y + yDelta);
}
lastUpdateTime = [[NSDatealloc] init];
}
// 获取怪物所在的矩形框
- (CGRect) getMonsterRect:(NSInteger) monsterIndex
{
CGRect rect;
// 获取索引为monsterIndex的怪物的左上角坐标
rect.origin =monsterPoints[monsterIndex];
// 获取图片的大小
rect.size = ((UIImage*)monsterImages[0]).size;
return rect;
}
// 获取足球所在的矩形框
- (CGRect) getBallRect
{
CGRect rect;
rect.origin =self.currentPoint;
rect.size = ((UIImage*)ballImages[0]).size;
return rect;
}
- (void) resetGame
{
// 依次设置每个怪物的位置
monsterPoints[0] =CGPointMake(20 ,150);
monsterPoints[1] =CGPointMake(190 ,150);
monsterPoints[2] =CGPointMake(80 ,280);
// 设置足球开始的位置,位于该UIView的中心
self.currentPoint =CGPointMake((self.bounds.size.width - BALL_SIZE ) / 2.0f,
(self.bounds.size.height - BALL_SIZE) / 2.0f);
// 设置足球的开始速度
self.xVelocity =0.0f;
self.yVelocity =0.0f;
}
// 当用户单击UIAlertView警告框上按钮时激发该方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:
(NSInteger)buttonIndex
{
// 如果用户单击第一个按钮
if(buttonIndex ==0)
{
[selfstartGame]; //重新开始游戏
}
}
- (void) startGame
{
// 设置游戏状态:正在游戏
isPlaying =YES;
// 重设足球和怪物的位置
[selfresetGame];
// 使用定时器控制怪物的动作
timer = [NSTimerscheduledTimerWithTimeInterval:0.2target:self
selector:@selector(moveMonster)userInfo:nilrepeats:YES];
}
@end
=========================
===================================================
控制器2: ViewController.m
//
// ViewController.m
// MonsterBall(怪物舞会)~demo
//
// Created by 石虎 on 2017/8/14.
// Copyright © 2017年 shihu. All rights reserved.
//
#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>
#import "SHBallView.h"
@interface ViewController ()
//运动管理器
@property (strong,nonatomic) CMMotionManager *motionManager;
@property (weak,nonatomic) SHBallView *ballView;
@end
@implementation ViewController
- (void)viewDidLoad {
[superviewDidLoad];
// 创建以grass.png图片平铺的颜色
UIColor* bgColor = [UIColorcolorWithPatternImage:
[UIImageimageNamed:@"grass"]];
// 将该视图控制器管理的View控件转换为SHBallView
self.ballView = (SHBallView *)self.view;
// 将背景设为以grass.png图片平铺的颜色
self.ballView.backgroundColor = bgColor;
// 创建CMMotionManager对象
self.motionManager = [[CMMotionManageralloc] init];
NSOperationQueue* queue = [[NSOperationQueuealloc] init];
// 设置CMMotionManager获取加速度数据的频率
self.motionManager.accelerometerUpdateInterval = 0.05;
// 使用代码块获取加速度数据
[self.motionManagerstartAccelerometerUpdatesToQueue:queuewithHandler:
^(CMAccelerometerData *accelerometerData,NSError *error)
{
// 将获取得到的加速度数据传给FKBallView对象
self.ballView.acceleration = accelerometerData.acceleration;
// 在主线程中调用FKBallView对象的update方法
[self.ballViewperformSelectorOnMainThread:@selector(update)
withObject:nilwaitUntilDone:NO];
}];
}
@end
=========================
===================================================
谢谢!!!