现在我们需要构建一个真实的发射机制,这之前我们需要了解一些基础知识。
你还记得前面创建的北极熊动画吗?我们改变了熊的手臂的锚点并将其放置在它的肩膀上。每当我们想用一个节点的旋转属性来旋转对象,我们需要选择一个锚点。我们的弹射器臂将被旋转了。不过,弹射器臂将是一个物理对象,在这里工作的原理有点不同。对于一个物理对象,我们使用一个共同连接点来连接两个部件,并确定旋转中心。我们将使用锚点作为基准点连接的关节,所以它应该是在弹射臂的底部。让我们通过从十字架在弹射器的中心拖动改变锚点:
在这一步中,我们将弹射器和弹射器臂转成物理对象。不像我们游戏之前的其他物理对象,弹射器组成是需要保持接头的两部分部件在一起使用的。
不明白为什么需要关节吗?我现在想你演示一下。打开Gameplay.ccb并将弹射器和弹射臂转成物理对象。请注意,您可以添加边缘点到物理身体通过点击行并拖动点来划出形状:
请确保弹射器臂和投石车是你的物理节点的子节点。在SpriteBuilder中每个物理对象必须在物理节点下:
现在,发布和运行。经过几秒钟的场景将类似于此:
投石车分崩离析,因为我们没有使用关节来保持他们的连接!cocos2d提供了一个很好的类来轻松创建接头:CCPhysicsJoint。让我们添加街头来联合我们的弹射器。
首先,让我们修改物理身体的弹射器。在右侧面板中设置弹射器(而不是手臂!)为一个静态的部件。我们不希望弹射器通过现场走动。
目前关节的创建只能在代码中进行而不能在SpriteBuilder中直接创建(这将在以后的版本中添加的功能)。这意味着你将需要为弹射器添加代码连接(称之为_catapult)。我们已经拥有了弹射器臂的代码连接,这里我们就不需要添加了。
现在,发布您的SpriteBuilder项目。
接下来,在Xcode中打开Gameplay.m,并添加一个成员变量_catapult。还添加另一个成员变量来存储新CCPhysicsJoint。
CCPhysicsJoint * _catapultJoint ;
我们将在didLoadFromCCB方法中创建关节。我们也将激活物理调试绘图。这是一个很酷的功能,可视化的物理机构和关节,将节省您大量的时间。
// visualize physics bodies & joints
_physicsNode.debugDraw = TRUE;
// catapultArm and catapult shall not collide
[_catapultArm.physicsBody setCollisionGroup:_catapult];
[_catapult.physicsBody setCollisionGroup:_catapult];
// create a joint to connect the catapult arm with the catapult
_catapultJoint = [CCPhysicsJoint connectedPivotJointWithBodyA:_catapultArm.physicsBody bodyB:_catapult.physicsBody anchorA:_catapultArm.anchorPointInPoints];
以上划线部分更新为下面内容(感谢网友“烛影斧声”提供的更新):
f现在开始添加关节(joint),在Node Library(左侧面板第三个tab)中有三种不同的关节可供使用,我们要用的是Physic Pivot Joint,这种关节可以连接两个物理对象到同一个点上,把Physic Pivot Joint拖到你想要连接的物理对象上。一旦Physic Pivot Joint被拖到舞台上,将会产生两个连接点,鼠标分别把它们拖到你想要连接的物理对象上。
这样你就设置了弹射器(catapult)和弹射器臂(catapult arm)的连接,你也可以改变关节中被连接的物理对象及属性,通过右侧的面板操作。
现在你的第一个关节已经设置完成,在SpriteBuilder中发布您的项目。然后打开Xcode中的Gameplay.m 我们要激活物理调试绘图。这是一个很酷的功能,可视化的物理对象和关节,将节省您大量时间。添加下面的代码到 didLoadFromCCB:
// visualize physics bodies & joints _physicsNode.debugDraw = TRUE;
这样就设置了你的第一个关节!注意,相同的碰撞组中物理对象不会发生碰撞。由于我们不希望弹射器臂和投石车相撞,我们把它们放在一组(任何类型的对象可以被用来标识一组)。运行游戏,并希望弹射器不散架。相反的结果应该是这样的:
接下来,我们将带回来在它的默认直立位置。还要注意的是发射机制被打破,因为我们改变了弹射器臂的锚点。这个我们将尽快修复它。
如果你之前没有接触过物理学,这里有一件事你需要清楚的。物理仿真本身是很现实的,你经常需要使用一些技巧让游戏显得更加真实。
你要学习如何运用这种技巧来使弹射器回到直立位置。我们将创建一个不可见的节点,在它和弹射器臂之间的建立关节!
在Spritebuilder
在Spritebuilder打开Gameplay.ccb。拖动一个CCNode到舞台中,并将其放置在弹射器之上,确保它是CCPhysicsNode的子类,还创建一个名为_pullbackNode的代码连接:
您还需要启用物理此节点,使其成为一个静态的且没有大小(我们只是希望这个节点坐在那里,保持弹射器手臂):
根据上面的更新我们可以知道在新版Spritebuilder中已支持创建关节了,现在补充内容(感谢网友“烛影斧声”提供):
我们需要添加另一种类型的关节(joint),它用于连接投射臂和不可见节点(就是_pullbackNode,投射臂正上方),它用于保持投射臂的直立。从Node Library()中选择Physics Spring Joint拖到舞台。因为_pullbackNode是一个不可见节点,我们需要把关节的一端连接到时间轴上_pullbackNode所属的CCNode,另一端连接到投射臂
现在弹簧关节(spring joint)的位置看起来有点奇怪,我们来设置它的锚点和一个复位长度(Rest length)。
最后,创建一个名为_pullbackNode的代码连接。现在是时候重新发布项目并打开Xcode。
在Xcode
再次,创建一个成员变量_pullbackNode的代码连接。再创建一个CCPhysicsJoint的成员变量 _pullbackJoint。现在在didLoadFromCCB方法中创建CCPhysicsJoint,这一次,我们将使用一个弹簧接头。我们需要这个弹簧街头负责发射以后弹射器回到原位。下面代码是如何创建弹簧接头:
// nothing shall collide with our invisible nodes _pullbackNode.physicsBody.collisionMask = @[]; // create a spring joint for bringing arm in upright position and snapping back when player shoots //_pullbackJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_pullbackNode.physicsBody bodyB:_catapultArm.physicsBody anchorA:ccp(0, 0) anchorB:ccp(34, 138) restLength:60.f stiffness:500.f damping:40.f];
请注意,我用的值不一定适合您的设置(你可能已经放置的对象略有不同)。随意调整这些值,并试图对其进行优化。我们设定collisionMask为空数组,因为我们不想无形节点碰撞场景中的任何对象。您的结果应该类似这样:
现在你已经添加了一个无形的物理元素到你的游戏中了!
现在,我们将要实现游戏的核心机制。我们希望玩家能够通过拖曳弹射手臂来发射企鹅。创建另一个物理节点,并创建与变量_mouseJointNode的代码连接。发布项目。在Xcode中打开Gameplay.m并添加这两个成员变量:
CCNode *_mouseJointNode; CCPhysicsJoint *_mouseJoint;
如果一个玩家开始接触弹射手臂,我们创建了mouseJointNode和catapultArm之间的springJoint
每当有触摸动作,我们更新了mouseJointNode的位置
当触摸结束,我们摧毁mouseJointNode和catapultArm之间的联合
首先,在didLoadFromCCB方法中加入这一行为这个看不见的节点停用碰撞:
_mouseJointNode.physicsBody.collisionMask = @[];
当触摸开始
我们将重写touchBegan 方法的实现:
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { CGPoint touchLocation = [touch locationInNode:_contentNode]; // start catapult dragging when a touch inside of the catapult arm occurs if (CGRectContainsPoint([_catapultArm boundingBox], touchLocation)) { // move the mouseJointNode to the touch position _mouseJointNode.position = touchLocation; // setup a spring joint between the mouseJointNode and the catapultArm _mouseJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_mouseJointNode.physicsBody bodyB:_catapultArm.physicsBody anchorA:ccp(0, 0) anchorB:ccp(34, 138) restLength:0.f stiffness:3000.f damping:150.f]; } }
当触摸移动
每当一个触摸动作,我们需要更新mouseJointNode的位置,从而使弹射器手臂被拖动的正确方向:
- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event { // whenever touches move, update the position of the mouseJointNode to the touch position CGPoint touchLocation = [touch locationInNode:_contentNode]; _mouseJointNode.position = touchLocation; }
当触摸结束
当触摸结束,我们想破坏我们的联合,让弹射器还原。先提取公共方法:
- (void)releaseCatapult { if (_mouseJoint != nil) { // releases the joint and lets the catapult snap back [_mouseJoint invalidate]; _mouseJoint = nil; } }
现在,我们要在touchEnded和touchCancelled方法中调用此方法:
-(void) touchEnded:(UITouch *)touch withEvent:(UIEvent *)event { // when touches end, meaning the user releases their finger, release the catapult [self releaseCatapult]; } -(void) touchCancelled:(UITouch *)touch withEvent:(UIEvent *)event { // when touches are cancelled, meaning the user drags their finger off the screen or onto something else, release the catapult [self releaseCatapult]; }
现在,仔细检查是否已正确应用的所有更改。然后运行游戏,看看结果:
原文:
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/spritebuilder-physics/
我们需要在代码中做一些改动来获取到企鹅。首先创建两个新的成员变量,因为我们将要发射的企鹅和我们将要用到的企鹅都连接到弹射器的关节上:
CCNode *_currentPenguin; CCPhysicsJoint *_penguinCatapultJoint;
当触摸开始,我们需要生成一个企鹅,并将它附加到弹射器臂的发射器上,因此,这些行添加到touchBegan方法的 if语句里面:
// create a penguin from the ccb-file _currentPenguin = [CCBReader load:@"Penguin"]; // initially position it on the scoop. 34,138 is the position in the node space of the _catapultArm CGPoint penguinPosition = [_catapultArm convertToWorldSpace:ccp(34, 138)]; // transform the world position to the node space to which the penguin will be added (_physicsNode) _currentPenguin.position = [_physicsNode convertToNodeSpace:penguinPosition]; // add it to the physics world [_physicsNode addChild:_currentPenguin]; // we don't want the penguin to rotate in the scoop _currentPenguin.physicsBody.allowsRotation = FALSE; // create a joint to keep the penguin fixed to the scoop until the catapult is released _penguinCatapultJoint = [CCPhysicsJoint connectedPivotJointWithBodyA:_currentPenguin.physicsBody bodyB:_catapultArm.physicsBody anchorA:_currentPenguin.anchorPointInPoints];
请注意,我们首先表达企鹅的位置是相对于弹射器臂而言的,然后我们将这个位置转换为世界坐标,最后我们把它转换成我们的节点空间_physicsNode。让我们继续前进,以释放企鹅!
现在,我们要扩展releaseCatapult方法。我们需要销毁的企鹅和铲之间的联合,激活旋转的企鹅,并添加摄像头的代码再次跟随企鹅(里面添加if语句的代码):
// releases the joint and lets the penguin fly [_penguinCatapultJoint invalidate]; _penguinCatapultJoint = nil; // after snapping rotation is fine _currentPenguin.physicsBody.allowsRotation = TRUE; // follow the flying penguin CCActionFollow *follow = [CCActionFollow actionWithTarget:_currentPenguin worldBoundary:self.boundingBox]; [_contentNode runAction:follow];
现在,运行游戏和发射一组企鹅!你可能会意识到,企鹅会飞得很高。如果你喜欢,你可以现在做一个小小的调整的(建筑物理游戏的重要组成部分)。Spritebuilder使得它很容易改变不同的物理性质,如密度,摩擦等:
干得好!现在你有第一个完全可玩的原型。
原文:
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/joints-cocos2d3/
现在,您将学习如何定义不同的碰撞类型并作出反应。你会使用这些知识来通过企鹅和冰块的崩塌来消灭Seal!
cocos2d已经提供了碰撞时物体的移动的支持。你作为开发者需要给不同类型的碰撞添加不同的意义。例如,在我们的游戏中,一个有意义的碰撞时发生在Seal与游戏中其他的部件之间的。
在cocos2d,我们可以通过实现一定的委托做到这一点。当一个对象从某些事件通知中得到信息的时候,在Objective-C中经常使用委托代理来解决。从碰撞中获取信息可在cocos2d的 CCPhysicsCollisionDelegate实现。让我们在Gameplay.h中添加此协议:
@interface Gameplay : CCNode <CCPhysicsCollisionDelegate> @end
现在,我们实现了协议,我们就可以注册成为我们的物理节点的碰撞委托。加在didLaodFromCCB方法中加入这一行:
_physicsNode.collisionDelegate = self;
接下来,我们需要建立一个碰撞类型为我们的Seal。
在cocos2d中每个physicsBody有一个名为collisionType的属性。这个collisionType属性用于识别在发生碰撞不同的参与者。
打开Seal.m为我们的Seal对象分配一个碰撞类型,需要我们在didLoadCCB方法中进行:
- (void)didLoadFromCCB { self.physicsBody.collisionType = @"seal"; }
现在,我们将能够确定Seal何时参与碰撞。
现在,我们需要实现一个委托方法时,在Seal碰撞发生时通知我们。
Chipmunk是集成在Cocos2D中的物理引擎,为我们提供了我们可以实现(它们都被称为在碰撞/模拟不同的步骤)的4种不同的委托方法。现在我们不需要研究每个委托方法的使用,我们只需要使用其中的一个委托方法:
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair typeA:(CCNode *)nodeA typeB:(CCNode *)nodeB;
我们将使用这个方法,因为它为我们提供了关于如何在激烈的碰撞信息。我们只想要在Seal被击中的时候移除Seal。
Chipmunk实现了一个巧妙的系统,它的委托方法,采用了碰撞式的名称来生成委托方法的名称。对于我们的例子中,我们想要被告知Seal和所有其他对象之间的碰撞,该方法将看起来像这样:
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA wildcard:(CCNode *)nodeB
在此方法名中的seal是来自collisionType seal的。第二个参数“通配符”是指任意对象。这意味着,此委托方法被调用时,对象与collisionType “seal”与任何其他物体相撞。现在,将这个方法添加到Gameplay.m:
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA wildcard:(CCNode *)nodeB { CCLOG(@"Something collided with a seal!"); }
运行游戏,并触发Seal的碰撞。您应该看到的日志信息显示在控制台中。
现在我们知道了碰撞处理程序的功能,让我们来实现实际的功能。基于totalKineticEnergy的碰撞,我们将决定一个Seal是否被移除。我们也将创建一个独立的Seal移除方法,因为我们将要给它添加一些更多的功能。改变碰撞的处理方法:
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA wildcard:(CCNode *)nodeB { float energy = [pair totalKineticEnergy]; // if energy is large enough, remove the seal if (energy > 5000.f) { [self sealRemoved:nodeA]; } }
然后将Seal的移除方法添加到你的方法中:
- (void)sealRemoved:(CCNode *)seal { [seal removeFromParent]; }
现在你的碰撞处理程序已经实现完成了,去试试你的游戏吧,应该会比之前的有趣多了吧!
原文:
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/collision-detection/
在下一节中将介绍SpriteBuilder强大的粒子效果系统,这将为你的游戏带来更加酷炫的效果哟!转载请说明出处,wealpan将和您一起学习Spritebuilder!谢谢大家!