I am programming with Cocos2d 3.0 now, In Cocos2d 2.0, we can use the following code to add accelerometer to app, but this example was based on class CCLayer which has deprecate in Cocos2d 3.0, and UIAccelerometer also replaced by CMMotionManager in IOS 5.0, so I am wondering how to do this in Cocos2d 3.0? I googled for a while, didn't find anything useful.
-(id) init
{
if ((self = [super init]))
{
// ...
self.isAccelerometerEnabled = YES;
// ...
}
}
-(void) accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
// ...
}
===
We've written a tutorial on exactly this: https://www.makegameswith.us/gamernews/371/accelerometer-with-cocos2d-30-and-ios-7
You need to use the CoreMotion framework.
Well, there are two problems in the tutorial example given above.
Single instance of CMMotionManager.
Acceleration data become +Ve or -Ve according to the orientation of the device. You also need to add Scene as observer of device orientation change notification.
If you don't want handle these overheads, you can use CCAccelerometer class. It solves both the problems.
HOW TO USE
Add CoreMotion Framework in your project from Build Phases.
Copy CCAccelerometer.h and CCAccelerometer.m files in your project.
Import CCAccelerometer.h file in the Prefix.pch.
Implement the <CCSharedAccelerometerDelegate> in the CCScene where you want to use the accelerometer.
Create shared instance in init method by simply calling [CCAcceleroMeter sharedAccelerometer];
Start accelerometer in -(void)onEnterTransitionDidFinish by calling [CCAcceleroMeter sharedAccelerometer]startUpdateForScene:self];
Define delegate method -(void)acceleroMeterDidAccelerate:(CMAccelerometerData*)accelerometerData in your scene.
Stop accelerometer in -(void)onExitTransitionDidStart by calling [CCAcceleroMeter sharedAccelerometer]stopUpdateForScene:self];
You can find out the example project in GitHub.
Here is example:
Device::setAccelerometerEnabled(true);
auto accelerometerListener = EventListenerAcceleration::create([this](Acceleration* acc, Event* event)
{
});
getEventDispatcher()->addEventListenerWithSceneGraphPriority(accelerometerListener, this);
Also video tutorial https://www.youtube.com/watch?v=Xk6lXK6trxU
Related
I've got a project that is a mix of pure C++ and Objective-C++ in order to incorporate some C++ libraries.
I've tried adding some basic SCNScenes into the mix. (By basic I mean a scene with a box node in it and that's it). Every time i get the error:
Assertion failed: (renderSize.x != 0), function -[SCNRenderContextMetal _setupDescriptor:forPass:isFinalTechnique:], file /BuildRoot/Library/Caches/com.apple.xbs/Sources/SceneKit/SceneKit-332.6/sources/Core3DRuntime/NewRenderer/SCNRenderContextMetal.mm, line 688.
Does anyone know what causes this, and if so how can I get round it?
EDIT:
In my ViewController.mm I've got:
self.sceneView = [[SCNView alloc] initWithFrame:frame];
self.sceneView.scene = [SCNScene scene];
SCNNode *cube = [SCNNode nodeWithGeometry:[SCNBox boxWithWidth:1.0 height:1.0 depth:1.0 chamferRadius:0]];
cube.geometry.firstMaterial.diffuse.contents = [UIColor redColor];
[self.sceneView.scene.rootNode addChildNode:cube];
[self.view addSubview:self.sceneView];
Sounds like you are starting up your SceneKit scene using a storyboard.
If so, the recent version of the SDK now requires that you set the constraints on views or else they end up having trivial size. It might just be a function of setting constraints on your SceneKit scene.
You also need to set the frame to something valid if it isn't. eg
CGRect frame = [[UIScreen mainScreen] applicationFrame];
I discovered that SceneKit throws a fit if you set the SCNView frame to CGRectZero. There has to be at least 1 pixel of rendering real estate. Simple as that.
In cocos2d tutorials people initialize sprites/physical nodes in the init method, but in the cocos2d + SpriteBuilder tutorials it's written in this method:
- (void)didLoadFromCCB {}
In cocos2d only tutorials there is a method like that,but there is a line of code, that says what a kind of method it is,right? :
- (void)onEnter{
[super onEnter];
}
So, 'didLoadFromCCB' is a magical method for SpriteBuilder projects?
The didLoadFromCCB method is sent to each node after the node has been loaded by CCBReader. It runs that method when it is implemented by that node's class. It behaves like a protocol method without having to implement a protocol (nor is there a protocol). See also this github issue.
The CCBReader method that runs didLoadFromCCB does so like this after loading is complete:
+ (void) callDidLoadFromCCBForNodeGraph:(CCNode*)nodeGraph
{
for (CCNode* child in nodeGraph.children)
{
[CCBReader callDidLoadFromCCBForNodeGraph:child];
}
if ([nodeGraph respondsToSelector:#selector(didLoadFromCCB)])
{
[nodeGraph performSelector:#selector(didLoadFromCCB)];
}
}
The onEnter method is defined by the CCNode class, so it is documented in the CCNode class reference and Xcode is able to suggest the method when starting to type it.
I have been building the SpaceViking project described in the book "Learning Cocos2D". I had got to chapter 10 on Box2D when I started to experience problems. I then attempted to upgrade cocos2D from version 1 to version 2.0. After making the necessary changes to fix deprecations, the code no longer works. Specifically, I have found that when game objects or enemy objects are created, their init methods are no longer being called. For example, when the following line is executed:
RadarDish *radarDish = [[RadarDish alloc] initWithSpriteFrameName:#"radar_1.png"];
the RadarDish init method is not called. I then looked into the execution of initWithSpriteFrameName in both version 1 and version 2 and found that CCSprite.m has change such that the init method is no longer called. In the version 2 source code, initWithSpriteFrameName method calls initWithSpriteFrame which calls initWithTexture which calls:
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect rotated:(BOOL)rotated
{
if( (self = [super init]) ) {
Consequently, RadarDish init method is not called. Instead, CCNode init method is called. However in version 1, initWithSpriteFrameName calls initWithSpriteFrame which calls initWithTexture which has this code:
// IMPORTANT: [self init] and not [super init];
if( (self = [self init]) ){
[self setTexture:texture];
[self setTextureRect:rect];
}
That allows the RadarDish init method to be called.
What can I do to resolve this? It seems unlikely that the book would need to change to support the upgrade to cocos2d v2.0, so I suspect I must be missing something else. But if I am wrong, then what would be the way to change this code to cause the RadarDish init method to be called?
Ok, I was wrong. "cocos2d 2.x is different in many details from v1.x so can't expect v1.x code to just work under 2.x". If you are going to follow the book "Learning Cocos2D", I would recommend loading cocos2d-iphone version 1.0.1. Otherwise, you will be faced with many incompatibilities as well as a lot of deprecations to fix.
But if you really want to use the latest cocos2d, then there are some things you should do. 1) Follow the instructions in this link. 2) You are going to have a lot of deprecations and changes to fix, so use this link to understand how to fix those deprecations and changes. 3) You'll need to update the Joystick classes also, so go to this link to get those changes, 4) you'll have to google the rest to find solutions.
Now as for the solution to the problem I mentioned here, there are likely multiple solutions but I will offer one below (thanks to Sylvan's answer above):
In each of the GameObjects, EnemyObjects, and PowerUps, I added a method to override initWithFrameName. This will circumvent the use of the objects init method. For example, for the RadarDish, I added the following:
-(id) initWithSpriteFrameName:(NSString*)frameName {
if( (self=[super init]) ) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### RadarDish initialized");
[self initAnimations]; // 1
characterHealth = 100.0f; // 2
gameObjectType = kEnemyTypeRadarDish; // 3
[self changeState:kStateSpawning]; // 4
}
}
return self;
}
This allows the GameObject and GameCharacter init methods to run before the CCSprite's initWithSpriteFrameName method to run.
The Viking GameObject had to have a slightly different solution because it is initialized with initWithSpriteFrame rather than initWithSpriteFrameName. But the override implementation is basically the same as the example of RadarDish above.
(Aside from all the changes necessary to overcome the deprecations) The above change allowed everything else in the examples of "Learning Cocos2D" to remain intact.
Good Luck.
I think you are having trouble because you are keeping your init method as it was and not modifying it. You are calling a method initWithSpriteFrameName: on your RadarDish class but that class doesn't have that method name, so it looks to its superclass. I think you could have simply renamed your init method to initWithSpriteFrameName: and you would have been fine. Like this:
// RadarDish.m
-(id) initWithSpriteFrameName:(NSString*)frameName {
if ((self = [super initWithSpriteFrameName:frameName])) {
// init anything here
}
return self;
}
Just don't also have a method named init and expect it to get called.
I've recently added the CCLayerPanZoom cocos2d extension to my project and got my game scene zooming and scrolling just like I want. Now when the player takes certain actions, I want to be able to disable the pan/zoom temporarily while they perform an action but can't figure out how to do it. I searched around and found the following code in a forum but it doesn't work or I don't know how to use it.
Does anyone know how to do this properly either with different code or the code below?
-(void)enableTouches:(BOOL)enable {
if(enable) {
[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0];
_panZoomLayer.isTouchEnabled = YES;
CCLOG(#"LayerPanZoom enabled.");
} else {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
_panZoomLayer.isTouchEnabled = NO;
CCLOG(#"LayerPanZoom disabled.");
}
}
I finally figured it out and figured I would post the answer back up here to share. The code I posted wasn't working because I was sending back self instead of the _panZoomLayer. So here are the steps to get this working yourself.
Implement the CCLayerPanZoom into your project as described by the documentation.
Add the following code as a method to call on your new CCLayerPanZoom class.
-(void)enableTouches:(BOOL)enable {
if(enable) {
[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:_panZoomLayer priority:0];
CCLOG(#"LayerPanZoom enabled.");
} else {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:_panZoomLayer];
CCLOG(#"LayerPanZoom disabled.");
}}
NOTE: Make sure to put the instance of the parent class as the delegate to remove.
In order to re-enable and have it function properly, you have to remove all the entries from the array in the CCLayerPanZoom class before calling to re-register the delegate. I created a new method in the CCLayerPanZoom class as follows and just call it right before the addStandardDelegate method above.
-(void)removeTouchesFromArray {
[self.touches removeAllObjects];
}
Then it all works great! Took me a while to learn how to use this extension but it works perfect once you figure it all out. I can single finger pan, double finger zoom/pan, set center location for entire scene, limit panning past edges, and set min/max scales. I know people have had a lot of issues with this but it is a great extension, just takes some messing around with to understand it. Let me know if you have any questions. Hope this helps someone else.
I'm currently porting an ObjC cocos2d game to cocos2d-x, but I'm encountering some problems when trying to create a registerWithTouchDispatcher method, at the moment I'm doing
void GameLayer::registerWithTouchDispatcher()
{
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this,0,true);
}
but this gives an error 'No member named sharedDispatcher' in cocos2d::CCTouchDispatcher'.
Is there another way that this must be done in cocos2d-x?
If you are using 2.0, they have been merged in to CCDirector.
please use
CCDirector::sharedDirector()->getTouchDispatcher()
use those code instead ccdirector. put the code to cclayer init function.
setTouchMode(kCCTouchesOneByOne);
registerWithTouchDispatcher();
In the cocos2d-x you can do like this.
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0);