ios开发中苹果2D引擎SpriteKit介绍
ios开发中苹果2d引擎spritekit介绍,最近研究了苹果自家开发的2d引擎spritekit和3d引擎scenekit,开篇之前,需要客观的讲,如果你要从事的是团队或者公司的项目,还是直接unity搞起,这涉及到开发与维护成本的问题,毕竟spritekit目前无法对跨平台给予支持。但是如果你是一个独立开发者,对苹果原生框架感兴趣,或者只关注与苹果的app store,我想spritekit和scenekit也是个不错的选择。
sprite译作精灵,可以这样理解,在spritekit的世界里,游戏里的怪兽是一个精灵,主角与主角发射的炮弹也是一个精灵,或者说游戏里的一个不会动的背景图,也可以是一个精灵。下面以精灵为切入点,讲解一下一个充满野心的苹果弄出来的2d引擎。
一、精灵与场景
1.新建一个xcode工程,可以看到,不管是ios,还是macos,甚至于tvos,都有一个叫game的项目新建方式。我们选择ios的game,新建出一个游戏项目。game与普通工程项目有什么不同,其实就一点项目默认的gameviewcontroller的view是以skview的形式load出来的,下面我们接着新建一个游戏场景skscene,用以装载即将new出来的精灵。
2.场景的新建。
注:scene场景的起始坐标是以左下角为(0,0)原点,而非传统view的左上角。
- (menuscene *)menuscene { if (!_menuscene) { _menuscene = [[menuscene alloc] initwithsize:self.view.bounds.size]; //@interface menuscene : skscene //@end } return _menuscene; } - (void)viewdidload { [super viewdidload]; [(skview *)self.view presentscene:self.menuscene]; // do any additional setup after loading the view, typically from a nib. }
@implementation menuscene -(instancetype)initwithsize:(cgsize)size { if (self = [super initwithsize:size]) { #if target_os_iphone #define skcolor uicolor #else #define skcolor nscolor #endif // skcolor主要是为了兼容mac的nscolor与ios的uicolor self.backgroundcolor = [skcolor whitecolor]; [self addchild:self.titlenode]; [self addchild:self.pathlabelnode]; [self addchild:self.colllabelnode]; [self addchild:self.physlabelnode]; [self addchild:self.physcollnode]; } return self; }
3.游戏场景的切换。与初始化的页面一致,游戏的转场为使用presentscene跳转到新建的scene当中。
- (sklabelnode *)titlenode { if (!_titlenode) { _titlenode = ({ sklabelnode *labelnode = [sklabelnode labelnodewithfontnamed:@"chalkduster"]; labelnode.text = @"spritekit test"; labelnode.fontsize = 30; labelnode.fontcolor = [skcolor bluecolor]; labelnode.position = cgpointmake(cgrectgetmidx(self.frame), self.frame.size.height * 0.75); labelnode.name = labelnode.text; labelnode; }); } return _titlenode; } -(void)touchesbegan:(nsset *)touches withevent:(uievent *)event { for (uitouch *touch in touches) { cgpoint location = [touch locationinnode:self]; sknode *node = [self nodeatpoint:location]; [self changetogamescenewithnodename:node.name]; } } -(void)changetogamescenewithnodename:(nsstring *)nodename { nslog(@"nodename=%@",nodename); if ([nodename isequaltostring:self.pathlabelnode.name]) { pathscene *pathscene = [pathscene scenewithsize:self.size]; // 定制转场类型 sktransition *reveal = [sktransition revealwithdirection:sktransitiondirectionup duration:0.5]; [self.scene.view presentscene:pathscene transition:reveal]; } else if ...
4.精灵的新建与添加。精灵的新建有两种形式,一个是直接以图片的形式新建,其size默认为图片的size,另一种则以纹理的形式新建。
// 以图片新建 - (skspritenode *)player { if (!_player) { _player = [skspritenode spritenodewithimagenamed:@"player"]; _player.name = @"player"; _player.position = cgpointmake(self.size.width /2, self.size.height /2); } return _player; }
// 以纹理新建 - (skspritenode *)walkman { if (!_walkman) { _walkman = [skspritenode spritenodewithimagenamed:@"walkr01"]; _walkman.name = @"walkman"; _walkman.position = cgpointmake(self.player.position.x, cgrectgetmaxy(self.player.frame) + 30); sktexture * texture1 = [sktexture texturewithimagenamed:@"walkr01"]; sktexture * texture2 = [sktexture texturewithimagenamed:@"walkr02"]; sktexture * texture3 = [sktexture texturewithimagenamed:@"walkr03"]; sktexture * texture4 = [sktexture texturewithimagenamed:@"walkr04"]; sktexture * texture5 = [sktexture texturewithimagenamed:@"walkr05"]; skaction *animation = [skaction animatewithtextures:@[texture1, texture2, texture3, texture4, texture5] timeperframe:1]; skaction *action = [skaction repeatactionforever:animation]; [_walkman runaction:action]; } return _walkman; }
5、精灵的添加运动事件
-(void)touchesbegan:(nsset *)touches withevent:(uievent *)event { for (uitouch *touch in touches) { // 添加武器 skspritenode * arms = [skspritenode spritenodewithimagenamed:@"projectile"]; arms.position = self.player.position; [self addchild:arms]; cgpoint location = [touch locationinnode:self]; // 直线轨迹 // skaction * movetoaction = [skaction moveto:location duration:0.5];; // 持续增加 // skaction * movebyaction = [skaction movebyx:100 y:100 duration:0.3]; // 改变大小 // skaction * sizeaction = [skaction resizebywidth:arms.size.width * 1.5 height:arms.size.height * 1.5 duration:0]; // 旋转 // skaction * radiansaction = [skaction rotatebyangle:m_pi * 4 duration:movetoaction.duration]; // 音效 // skaction * armssound = [skaction playsoundfilenamed:@"pew-pew-lei.caf" waitforcompletion:no]; skaction * armsaction = [skaction group:@[pathaction, sizeaction,armssound]]; [arms runaction:armsaction completion:^{ // 移除 [arms removefromparent]; }]; } }
二、精灵的接触检测
skscene有一个场景方法,改方法每帧都会触发一次,可供简单的事件分析与监测,比如精灵越界销毁,精灵的接触监测,故事板的得分情况的更新等等。
/** override this to perform per-frame game logic. called exactly once per frame before any actions are evaluated and any physics are simulated. @param currenttime the current time in the app. this must be monotonically increasing. */ - (void)update:(nstimeinterval)currenttime;
-(void)update:(cftimeinterval)currenttime { // 怪物与武器的越界移除 // 更新数字版 // 检测精灵事件(如点击精灵之后,给它设置个标识,在下一帧的时候做事件处理) }
注:后面讲述精灵的物理碰撞,能够更准确的进行精灵的碰撞检测
三、精灵的物理引擎
// 方形 - (skspritenode *)square { if (!_square) { _square = [skspritenode spritenodewithimagenamed:@"square"]; _square.position = cgpointmake(self.size.width * 0.8, cgrectgetmidx(self.frame)); _square.name = @"square_prey"; _square.physicsbody = [skphysicsbody bodywithrectangleofsize:_square.size]; } return _square; } // 圆形 - (skspritenode *)circle { if (!_circle) { _circle = [skspritenode spritenodewithimagenamed:@"circle"]; _circle.position = cgpointmake(self.size.width * 0.65, cgrectgetmidx(self.frame)); _circle.name = @"circle_prey"; _circle.physicsbody = [skphysicsbody bodywithcircleofradius:_circle.size.width / 2]; } return _circle; } // 三角形 - (skspritenode *)triangle { if (!_triangle) { _triangle = [skspritenode spritenodewithimagenamed:@"triangle"]; _triangle.position = cgpointmake(self.size.width * 0.5, cgrectgetmidx(self.frame)); _triangle.name = @"triangle_prey"; cgmutablepathref trianglepath = cgpathcreatemutable(); // 中心 cgpathmovetopoint(trianglepath, nil, -_triangle.size.width / 2, -_triangle.size.height / 2); // cgpathaddlinetopoint(trianglepath, nil, _triangle.size.width / 2, -_triangle.size.height / 2); cgpathaddlinetopoint(trianglepath, nil, 0, _triangle.size.height / 2); cgpathaddlinetopoint(trianglepath, nil, -_triangle.size.width / 2, -_triangle.size.height / 2); _triangle.physicsbody = [skphysicsbody bodywithpolygonfrompath:trianglepath]; cgpathrelease(trianglepath); } return _triangle; }
四、精灵的物理碰撞检测
-(instancetype)initwithsize:(cgsize)size { if (self = [super initwithsize:size]) { self.backgroundcolor = [uicolor whitecolor]; [self addchild:self.back]; [self addchild:self.square]; [self addchild:self.circle]; [self addchild:self.triangle]; self.physicsbody = [skphysicsbody bodywithedgeloopfromrect:self.frame]; self.scene.name = @"self"; self.physicsbody.categorybitmask = 0x00000001; self.physicsbody.collisionbitmask = 0x00000001; self.physicsbody.contacttestbitmask = 0x00000001; self.physicsworld.contactdelegate = (id )self; } return self; }
-(void)touchesbegan:(nsset *)touches withevent:(uievent *)event { for (uitouch *touch in touches) { cgpoint location = [touch locationinnode:self]; // 获取点击的sknode sknode * node = [self nodeatpoint:location]; // 新建一个黑球 node * ball = [skspritenode spritenodewithimagenamed:@"blackball"]; ball.position = cgpointmake(0, 0); ball.name = @"ball"; ball.physicsbody = [skphysicsbody bodywithcircleofradius:ball.size.width / 2]; cgpoint offset = cgpointmake(location.x - ball.position.x, location.y - ball.position.y); // 斜率 float ratio = (float) offset.y / (float) offset.x; // 速度 ball.physicsbody.velocity = cgvectormake(1000, 1000 * ratio); // 角速度 弧度/秒 ball.physicsbody.angularvelocity = m_pi * 4; // 密度 ball.physicsbody.density = 100; // 弹力 ball.physicsbody.restitution = 1; // 动量 /kg ball.physicsbody.mass = 100; // 光滑度 0 ~ 1 ball.physicsbody.friction = 0.5; // 是否受重力影响 default value is yes ball.physicsbody.affectedbygravity = no; // 是否受加速度影响 ball.physicsbody.allowsrotation = no; // 线性阻尼(0:速度从不减弱;1:速度立即减弱) ball.physicsbody.lineardamping = 0.5; // 角速度阻尼(0:速度从不减弱;1:速度立即减弱)default 0.1 ball.physicsbody.angulardamping = 0; // 物体的类别(一个16进制数) ball.physicsbody.categorybitmask = 0x00000001; // 设置哪个物体不可与之碰撞(即不可穿透) ball.physicsbody.collisionbitmask = 0x00000001; // 接触(触发检测函数) ball.physicsbody.contacttestbitmask = 0x00000001; [self addchild:ball]; } }
- (void)didbegincontact:(skphysicscontact *)contact { nslog(@"联系中的第一个物体:%@",contact.bodya.node.name); nslog(@"联系中的第二个物体:%@",contact.bodyb.node.name); nslog(@"联系点的坐标:%@",nsstringfromcgpoint(contact.contactpoint)); nslog(@"碰撞方向的法向量:%@",nsstringfromcgvector(contact.contactnormal)); nslog(@"两个物体的碰撞强度(牛顿每秒):%f",contact.collisionimpulse); if ([contact.bodya.node.name containsstring:@"prey"] && [contact.bodyb.node.name isequaltostring:@"ball"]) { [contact.bodya.node removefromparent]; } if ([contact.bodyb.node.name containsstring:@"prey"] && [contact.bodya.node.name isequaltostring:@"ball"]) { [contact.bodyb.node removefromparent]; } }
@end
上一篇: ios开发中编码问题解析
下一篇: 笔记本电脑怎么设置关闭盖子也不待机?