The below code works great when I'm calling the void in the same .m file but I want my selector to go to a 2nd layer in my scene for its 'MoveUpSelected' void (which contains my actions for the sprites movement). How can I do this?
HUDLayer.m Button code in my Layer to communicate with other layer
self.dpad = [CCSprite spriteWithFile:#"dpad.png"];
CCSprite *dpadSelectedSprite = [CCSprite spriteWithTexture:[dpad texture]];
dpadSelectedSprite.color = ccGRAY;
//float dpadHeight = flareSprite.texture.contentSize.height;
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:Level1 selector:#selector(MoveUpSelected)];
dpadButtons.position = CGPointMake(size.width / 2, 150);
[menu addChild:dpadButtons];
Level1.m Void in my 2nd layer waiting to be called by 1st layer button
- (void)MoveUpSelected {
int yPosition = self.Player.position.y;
yPosition += [self.Player texture].contentSize.height/2;
CGSize size = [[CCDirector sharedDirector] winSize];
if (yPosition >= (size.height - [self.Player texture].contentSize.height/2)) {
yPosition = (size.height - [self.Player texture].contentSize.height/2);
}
self.Player.position = CGPointMake(self.Player.position.x, yPosition);
}
I have a GameScene1.m holding both layers in separate files.
+(id) scene
{
CCScene *scene = [CCScene node];
HudLayer *HUD = [HudLayer node];
[scene addChild:HUD z:2];
Level1 *layer = [Level1 node];
[scene addChild:layer];
return scene;
}
Please explain with lines of code.
It is VERY hard to understand what do you want to do. As Stephen mentioned in his comment, "using a void" and "void action" phrases have no sense. Anyway, if you just want to call some instance's method on btn click, just set it as target of your menu item. In your case
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:self selector:#selector(MoveUpSelected)];
uses self as target. Change it to self.parent or any other instance and menu item will try to call selector on this target.
EDIT:
CCMenuItemSprite *dpadButtons = [CCMenuItemSprite itemWithNormalSprite:dpad selectedSprite:dpadSelectedSprite target:levelInstance selector:#selector(MoveUpSelected)];
I your edits you tried to use class object, not it's instance as target. So it cannot find static methods.
Related
I have a custom class called PointsView, it is a subclass of SCNView.
#interface PointsView : SCNView <SCNSceneRendererDelegate>
I have initialised an object of PointsView in my ViewController class, in the method viewDidLoad.
#implementation ViewController
- (void)viewDidLoad {
pointsView = [[PointsView alloc] initView:sceneView withScene:pointsScene];
}
This initialises pointsView with a SCNView and a SCNScene, both are created in ViewController and are linked to my storyboard. Attributes for the SCNView and SCNScene are set in the PointsView class.
#implementation PointsView
- (id)initView:(SCNView *)ptsView withScene:(SCNScene *)curScene {
self = [super init];
mySCNView = ptsView;
currentScene = curScene;
mySCNView.delegate = self;
mySCNView.scene = currentScene;
mySCNView.allowsCameraControl = NO;
mySCNView.jitteringEnabled = YES;
mySCNView.showsStatistics = NO;
mySCNView.backgroundColor = [NSColor blackColor];
cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
cameraNode.position = SCNVector3Make(0, 0.4, 6);
cameraNode.transform = CATransform3DRotate(cameraNode.transform, -M_PI/7.0, 0, 0, 0);
cameraNode.orientation = SCNVector4Make(1, 0, 0, 0);
cameraNode.camera.yFov = 40.0;
cameraNode.camera.xFov = 0;
cameraNode.camera.zNear = 0.5;
[currentScene.rootNode addChildNode:cameraNode];
SCNLight *spotLight = [SCNLight light];
spotLight.type = SCNLightTypeSpot;
spotLight.color = [NSColor blackColor];
SCNNode *spotLightNode = [SCNNode node];
spotLightNode.light = spotLight;
spotLightNode.position = SCNVector3Make(-2, 1, 0);
[cameraNode addChildNode:spotLightNode];
return self;
}
I have a rightMouseDown event in the the PointsView class, the problem is when that method is called it is being called from an instance of PointsView that is different from the one that was initialised in ViewController, for example; location in memory after initialisation was 0x100b032a0, and when rightMouseDown is called, location in memory was 0x100908630. Which tells me it is not the same instance.
- (void)rightMouseDown:(NSEvent *)theEvent {
SCNHitTestResult *result = [self hitTestResultForEvent:theEvent];
}
I have no idea why another instance is being used for the rightMouseDown event. I need to be using the one that was initialised in ViewController. Am I doing anything wrong? Or is there something I have missed? Help would be greatly appreciated.
If anyone needs anymore information, please ask.
Thank you.
I managed to solve this problem. I used the firstResponder attribute from NSEvent, which I had set to the ViewController. From there I could access my already initialised PointsView class.
I am very fresher to game development and I need to develop a game like NinjaJump.
I have created a CCParallaxNode to setup scrolling background and added CCPhysicsNode to setup Physics world. I have created player object as shown below.
// Add a sprite
_sprite = [CCSprite spriteWithImageNamed:#"Icon.png"];
_sprite.position = ccp(self.contentSize.width/2,100);
_sprite.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _sprite.contentSize} cornerRadius:0.0];
_sprite.physicsBody.friction = 0.0f;
_sprite.physicsBody.collisionGroup = #"player";
_sprite.physicsBody.collisionType = #"Player";
//_sprite.physicsBody.collisionMask = 0;
//[self addChild:_sprite];
[foreground addChild:_sprite];
foreground is just a node added into CCScene to easily manage player in-focus.
// code for physics world
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
//_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
_foreground = [CCNode node];
//[self addChild: _foreground];
[_physicsWorld addChild: _foreground];
To make player always visible we have implemented update method as
- (void) update:(CFTimeInterval)currentTime {
// Calculate player y offset
if (_player.position.y > 200.0f) {
//_midgroundNode.position = CGPointMake(0.0f, -((_player.position.y - 200.0f)/4));
_foreground.position = CGPointMake(0.0f, -(_player.position.y - 200.0f));
}
}
I can't understand but the player scrolls off screen anyhow. The code is written in Cocos2d v3.
I have also setup a demo project to show what I implemented: https://www.dropbox.com/s/5s55d00kk80wun4/HumptyJump-Example.zip?dl=0
Any kind of help is appreciated. Thanks in advance.
I could not run your sample code but one thing, I can tell you for sure that there is no need of Physics Engine here.
You just keep your player at a particular height and just move your object right to left.
Apply moving parallax background image and objects to give a feel that your character is moving upwards or downwards.
For reference you can see the games like Swing Drop, made on the same approach as above.
I have implemented 2 fixed views changing their positions while physics body goes down ;).
See the same in action
#interface TestScene () {
CCNode *_background;
CCNode *_foreground;
NSArray *grounds; // Keeps track of each of the 2 views
CCPhysicsNode *_physicsWorld;
}
#implementation TestScene
- (id)init {
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
// Physics world setup
_physicsWorld = [CCPhysicsNode node];
_physicsWorld.gravity = ccp(0,-100);
_physicsWorld.debugDraw = YES;
_physicsWorld.collisionDelegate = self;
[self addChild:_physicsWorld];
// Foreground node in which platforms are there and moves downwards as player goes up
_foreground = [CCNode node];
// Adding background images
CCSprite *default1 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default1 setAnchorPoint: ccp(0, 0)];
CCSprite *default2 = [CCSprite spriteWithImageNamed: #"Default.png"];
[default2 setAnchorPoint: ccp(0, 0)];
[default2 setPosition: ccp(0, default1.contentSize.height)];
[_foreground addChild: default1];
[_foreground addChild: default2];
// Adding into array
grounds = #[default1, default2];
// Adding into physics world
[_physicsWorld addChild: _foreground];
// creating player
_player = [CCSprite spriteWithImageNamed: #"Assets.atlas/Player.png"];
[_player setPosition: ccp(160.0f, 160.0f)];
_player.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, _player.contentSize} cornerRadius:0]; // 1
_player.physicsBody.collisionGroup = #"playerGroup"; // 2
_player.physicsBody.collisionType = #"player";
//[_physicsWorld addChild:_player];
[_foreground addChild: _player];
// Multiple platforms can be added into body, they are static entities only
PlatformNode *platform = (PlatformNode *)[PlatformNode node];
[platform createPlatformAtPosition:CGPointMake(110, 50) ofType: PLATFORM_NORMAL];
[_foreground addChild: platform];
return self;
}
- (void) update:(CFTimeInterval)currentTime {
// Take background and physics world down, 163 was Y position of the player
_physicsWorld.position = ccp(_physicsWorld.position.x, 163 - _player.position.y);
// loop the ground
for (CCNode *ground in grounds) {
// Get the world position
CGPoint groundWorldPosition = [_physicsWorld convertToWorldSpace: ground.position];
// Get screen position
CGPoint groundScreenPosition = [self convertToNodeSpace: groundWorldPosition];
NSLog(#"Positioning ground ---> world: (%f, %f) & screen: (%f, %f)", groundWorldPosition.x, groundWorldPosition.y, groundScreenPosition.x, groundScreenPosition.y, nil);
if (groundScreenPosition.y <= -ground.contentSize.height) {
ground.position = ccp(ground.position.x, ground.position.y + 2 * ground.contentSize.height);
break;
}
}
}
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
// Take the player upside
[_player.physicsBody applyImpulse: ccp(0, 215)];
}
#end
This is how I coded the background :).
i'm trying to make a game where obstacles fall from the top of the screen and the player has to try to avoid them, but the didBeginContact method is not getting called and its driving me crazy. here are the parts of my code relevant to the node collisions...
//myScene.h
#interface MyScene : SKScene <SKPhysicsContactDelegate>
//myScene.m
#implementation MyScene
static const uint32_t playerCategory = 0x1 << 0;
static const uint32_t obstacleCategory = 0x1 << 1;
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
//setup scene
self.backgroundColor = [SKColor whiteColor];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
obstacles = [NSMutableArray array];
//add player node
player = [SKNode node];
SKSpriteNode *spritePlayer = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
spritePlayer.size = CGSizeMake(50, 50);
spritePlayer.position = CGPointMake(self.frame.size.width/2, 100);
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size];
player.physicsBody.dynamic = NO;
player.physicsBody.mass = 0.02;
[player addChild:spritePlayer];
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.contactTestBitMask = obstacleCategory;
player.physicsBody.collisionBitMask = 0;
player.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:player];
[self spawnNewObstacles];
}
return self;
}
- (void)spawnNewObstacles
{
//spawn obstacles
obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"black.png"];
obstacle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle.frame.size];
obstacle.physicsBody.dynamic = YES;
obstacle.physicsBody.categoryBitMask = obstacleCategory;
obstacle.physicsBody.contactTestBitMask = playerCategory;
obstacle.physicsBody.collisionBitMask = 0;
obstacle.physicsBody.usesPreciseCollisionDetection = YES;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"didBeginContact Called!");
}
the didBeginContact method is not getting called and i can't find what wrong with my code
all help is appreciated...
sknode size isn't set correctly because there is no image reference, and if you log its size if should be inf, and when I tested your code I returned an exception.
to fix this error change the following line
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.frame.size]; change that to
to the following and it should work
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spritePlayer.size];
as I said below just ditch the player sknode, it is simply redundant unless there is a strong reason to use it.
///////previous answer
I couldn't help but to notice that you didn't add the obstacle node to any parent node, namely the scene, like what you did with the player node using this line
[self addChild:player];
you need to do the same thing with the obstacle node
[self addChild:obstacle];
, I am surprised that you didn't mention not seeing those nodes on the screen, unless you added them in a different section of your code that you did not include, then at this point I suggest you log the positions of both nodes to give an idea of how things are located on the screen.
as for the array you won't need it all you have to do is give a name for every obstacle node like #"obstacle", then you can use the following method to access all the nodes that has that name
obstacle.name = #"obstacle";
/////later when you want to check for node removal
[self enumerateChildNodesWithName:#"obstacle" usingBlock:^(SKNode *node, BOOL *stop) {
SKSpriteNode *child = (SKSpriteNode*)node;
if (child.position.y <0){ //or any arbitrary condition
[child removeFromParent];
}
}];
Lastly, I can't see the point from using the player as a sknode and then adding an skspritenode to it, unless I am missing something !!! :) . You can simply ditch the player sknode all together and simply use the spritePlayer node instead.
Hope this helps.
I know this has been asked a lot before, but I can't find the solution. I have a CCSprite on the screen, the player, that is steered with the accelerometer. Then on top of the screen other CCSprites are spawned every 2 seconds, the enemies. I want all the enemies to follow the player, if the player moves the player the enemies should change direction and go towards that CCSprite. This is my code this far:
- (void)spawnEnemies
{
monsterTxt = [[CCTexture2D alloc] initWithCGImage:[UIImage imageNamed:#"obj.png"].CGImage resolutionType:kCCResolutionUnknown];
monster = [CCSprite spriteWithTexture:monsterTxt];
...
//random spawn position etc.
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
[_monsters addObject:monster]; //adds the sprite to a mutable array
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
...
//determines new position and move sprite there
[monster stopAllActions];
CCMoveTo *movemonster = [CCMoveTo actionWithDuration:7.0 position:ccp(_rocket.boundingBox.origin.x, _rocket.boundingBox.origin.y)];
[monster runAction:[CCSequence actions:movemonster, nil]];
}
Now when I start the game the sprites are going towards the player, but when the player moves the enemies doesn't update their destination, they just continue down and stops at the y-coordinate of the player. And after a while the app crashes. What am I doing wrong?
My guess is that the problem may be in that section of code that you did not post.
In v2.1 of Cocos2d, to receive accelerometer measurements in your layer:
Do this in your init or onEnterTransitionDidFinish call:
self.accelerometerEnabled = YES;
And overload the is method:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CCLOG(#"Acceleration (%f,%f,%f)",
acceleration.x,
acceleration.y,
acceleration.z);
// Do something meaningful...you probably want to send a notification to
// some other scene/layer/logic. It should cache that the acceleration value
// and then do something on an update(...) call with it. Then reset it so that
// it does not get used the next update cycle untila new value comes in (here).
}
It seems you have this...and you have indicated you are receiving them...so I'm a bit confused.
SO (SANITY CHECK TIME)...
I created an example project (here) which has a single player sprite being chased by multiple monster sprites. When the user touches the screen (I did not use the accelerometer), the player sprite changes location and the sprites "chase" him. I added some random offset to their target positions so they would not all cluster on top of the player.
Here is the code for (the most important parts of) the file, which is a modified version of the HellowWorldLayer that comes with a cocos2d v2.1 project (I created it from the template):
-(void)createPlayer
{
playerMoved = NO;
if(self.player != nil)
{
[self removeChild:player];
self.player = nil;
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
CCSprite* playerSprite = [CCSprite spriteWithFile:#"Icon.png"];
playerSprite.scale = 2.0;
playerSprite.position = ccp(scrSize.width/2,scrSize.height/2);
[self addChild:playerSprite];
self.player = playerSprite;
}
-(void)createMonsters
{
const int MAX_MONSTERS = 4;
if(self.monsters.count > 0)
{ // Get rid of them
for(CCSprite* sprite in monsters)
{
[self removeChild:sprite];
}
[self.monsters removeAllObjects];
}
CGSize scrSize = [[CCDirector sharedDirector] winSize];
for(int idx = 0; idx < MAX_MONSTERS; idx++)
{
CCSprite* sprite = [CCSprite spriteWithFile:#"Icon.png"];
float randomX = (arc4random()%100)/100.0;
float randomY = (arc4random()%100)/100.0;
sprite.scale = 1.0;
sprite.position = ccp(scrSize.width*randomX,scrSize.height*randomY);
[self addChild:sprite];
[monsters addObject:sprite];
}
}
-(void)update:(ccTime)delta
{
if(playerMoved)
{ // Modify all the actions on all the monsters.
CGPoint playerPos = player.position;
for(CCSprite* sprite in monsters)
{
float randomX = (1.0)*(arc4random()%50);
float randomY = (1.0)*(arc4random()%50);
CGPoint position = ccp(playerPos.x+randomX,playerPos.y+randomY);
[sprite stopAllActions];
[sprite runAction:[CCMoveTo actionWithDuration:3.0 position:position]];
}
playerMoved = NO;
}
}
I have a retained array of CCSprite* objects (monsters) and a CCSprite* for the player. I have a flag to tell me if the player has changed position (player moved). In the update loop, if the player has moved, stop all the actions on the monsters and update them.
On the screen it looks like this:
And then when I move the main sprite...
They follow it...
Was this helpful?
myself and my friend have been told Stackoverflow is the place to ask questions regarding code. :)
We're pretty new to Objective-C, Xcode and Cocos2d and are trying to complete simple tasks to further our knowledge.
Looking at our code below, we have made an Ant Object and placed him on screen. We now want to use a touch location to move the ant to to location the user touches.
We're unsure what the correct way to go about it is. At the moment we're staying away from creating classes so have all the code in one place but we can't figure out how to get the ant that's on screen to go to the touch location.
Can anyone help?
//
// HelloWorldLayer.m
// Timer
//
// Created by Lion User on 06/06/2012.
// Copyright __MyCompanyName__ 2012. All rights reserved.
//
// Import the interfaces
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super initWithColor:ccc4(225, 225, 225, 255)])) {
// enable touches
self.isTouchEnabled=YES;
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// set time to zero
myTime = currentTime;
timeLabel = [CCLabelTTF labelWithString:#"00:00" fontName:#"Arial" fontSize:48];
timeLabel.position = CGPointMake(size.width / 2, size.height);
// set label color
timeLabel.color = ccBLACK;
// Adjust the label's anchorPoint's y position to make it align with the top.
timeLabel.anchorPoint = CGPointMake(0.5f, 1.0f);
// Add the time label
[self addChild:timeLabel];
// run create ant method
[self createAnt];
//update
[self schedule:#selector(update:)];
}
return self;
}
-(void)update:(ccTime)dt{
totalTime += dt;
currentTime = (int)totalTime;
if (myTime < currentTime)
{
myTime = currentTime;
[timeLabel setString:[NSString stringWithFormat:#"%02d:%02d", myTime/60, myTime%60]];
}
}
////* method for creating an ant and moving it*////
-(void)createAnt{
////*requirements for animation setup*////
// create cache object to store spritesheet in
CCSpriteFrameCache *cache=[CCSpriteFrameCache sharedSpriteFrameCache];
// add the sprite list to the cache object
[cache addSpriteFramesWithFile:#"antatlas.plist"];
// create frame array to store the frames in
NSMutableArray *framesArray=[NSMutableArray array];
//loop through each frame
for (int i=1; i<3; i++){
// increment the name to include all frames in sprite sheet
NSString *frameName=[NSString stringWithFormat:#"ant%d.png", i];
// create frame object set it to the cache object and add the frameNames to the cache
id frameObject=[cache spriteFrameByName:frameName];
// add the frame object into the array
[framesArray addObject:frameObject];
}
////* setup the actions for running the animation*////
// create animation object and pass the list of frames to it (it expects this as an array)
id animObject=[CCAnimation animationWithFrames:framesArray delay:0.05];
// setup action to run the animation do not return to frame 1
id animationAction=[CCAnimate actionWithAnimation:animObject restoreOriginalFrame:NO];
//loop the animation indefinitely
animationAction = [CCRepeatForever actionWithAction:animationAction];
// move ant action
id moveAnt=[CCMoveTo actionWithDuration:3 position:ccp(60, 160)];
// create sprite, set location and add to layer (starts with the name of the first frame in the animation
CCSprite *ant=[CCSprite spriteWithSpriteFrameName:#"ant1.png"];
ant.position=ccp(240, 160);
[self addChild:ant];
//run animation action
[ant runAction: animationAction];
// run move ant action
[ant runAction:moveAnt];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[touches anyObject];
CGPoint loc=[touch locationInView:[touch view]];
loc=[[CCDirector sharedDirector]convertToGL:loc];
NSLog(#"touch (%g,%g)",loc.x,loc.y);
// Move ant to point that was pressed
[ant runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:loc],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
[super dealloc];
}
#end
Seems like your ant is local to your createAnt method. So in the touchesBegan statement, it doesn't "see" the ant. Go over to your header file, and in the #interface declare the ant...
CCSprite *ant;
Then back in your createAnt statement, you can just write...
ant=[CCSprite spriteWithSpriteFrameName:#"ant1.png"];
Now any other method in the implementation ( .m ) file will know what you mean when you write "ant"
Hope it helps!