CCSprite not displayed when placed on CCNode or position is wrong - objective-c

I have this code in my init:
-(id)init {
if ((self = [super init])) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
mapSize = CGSizeMake(4000, 4000);
rotateWorld = [CCNode node];
[rotateWorld setContentSize:mapSize];
rotateWorld.position = CGPointMake(screenSize.width / 2, screenSize.height / 2);
positionWorld = [CCNode node];
[positionWorld setContentSize:mapSize];
positionWorld.position = CGPointMake(mapSize.width / 2, mapSize.height / 2);
[rotateWorld addChild:positionWorld z:0];
[self addChild:rotateWorld z:0];
// Test enemy
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"EnemiesSpritesheet.plist"];
spriteSheetNonPlayer = [[CCSpriteBatchNode alloc] initWithFile:#"EnemiesSpritesheet.png"capacity:10];
[positionWorld addChild:spriteSheetNonPlayer z:0];
enemy = [[Enemy_01 alloc] initWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"enemy_01_01.png"]];
enemy.position = CGPointMake(mapSize.width / 2, mapSize.height / 2);
[spriteSheetNonPlayer addChild:enemy];
}
return self;
}
Now I would expect my enemy sprite to show up in the middle of the screen, but it does not and I do not know if it is show at all. The funny thing is that if I change the positionWorld from a CCNode to a CCSprite containing a background image of 4000x4000 it works perfectly, but why not with a CCNode with its contetSize set? How do I get this to work with a CCNode?
Thank you
Søren

You need to set the anchorPoint to be ccp(0.5,0.5) for CCNode.
rotateWorld.anchorPoint = ccp(0.5f,0.5f);
positionWorld.anchorPoint = ccp(0.5f,0.5f);
CCSprite does the same thing internally in its init method (line 149 of CCSprite.m).

Related

SceneKit – Playing DAE and USDZ animation using Objective-C

I want to create walking man animation in SceneKit.
I'm exporting animated .dae files from 3DSMax + OpenCollada , I also use ConvertToXcodeCollada to combine all animations in one.
How i get animation:
SCNScene *humanScene = [SCNScene sceneNamed:#"art.scnassets/myScene.DAE"];
CAAnimation *Animation = [[humanScene rootNode] animationForKey:#"myScene-1"];
I also try to get animation from "SCNSceneSource"
How i add animation:
SCNNode *humanNode = [humanScene.rootNode childNodeWithName:#"myScene-1" recursively:YES];
[humanNode addAnimation:walkingAnimation forKey:#"myScene-1"];
or:
SCNNode* humanNode = [SCNNode new];
for(SCNNode* node in humanScene.rootNode.childNodes){
[humanNode addChildNode:node];
}
[humanNode addAnimation:walkingAnimation forKey:#"myScene-1"];
My object "walkingAnimation" is "CAAnimationGroup".
But it doesn't animate in application.
I can see animation only in Xcode sceneKit editor.
example of my .DAE file
Try the following code with your model. It works. I used Xcode 13.2.1 in macOS 12.1.
At first fill viewDidLoad method:
#import "GameViewController.h"
#implementation GameViewController
SCNView *sceneView;
bool notRunningSwitch;
NSMutableDictionary<NSString*, CAAnimation*> *animations;
SCNNode *node;
- (void)viewDidLoad
{
[super viewDidLoad];
sceneView = (SCNView *)self.view;
notRunningSwitch = YES;
animations = #{}.mutableCopy;
node = [SCNNode node];
SCNScene *scene = [SCNScene scene];
sceneView.scene = scene;
sceneView.autoenablesDefaultLighting = YES;
sceneView.allowsCameraControl = YES;
[self anime];
}
Then anime and loadAnime methods:
- (void)anime
{
SCNScene *standStillScene = [SCNScene sceneNamed:#"art.scnassets/Idle"];
for (SCNNode *childNode in standStillScene.rootNode.childNodes)
{
[node addChildNode:childNode];
}
node.scale = SCNVector3Make(0.1, 0.1, 0.1);
node.position = SCNVector3Make(0, 0,-2.5);
[sceneView.scene.rootNode addChildNode: node];
[self loadAnime:#"running" inScene:#"art.scnassets/Running" withID:#"Running-1"];
}
- (void)loadAnime:(NSString*)withKey inScene:(NSString*)scene withID:(NSString*)id
{
NSURL *url = [NSBundle.mainBundle URLForResource:scene withExtension:#"usdz"];
SCNSceneSource *source = [SCNSceneSource sceneSourceWithURL:url options:Nil];
CAAnimation *charAnimation = [source entryWithIdentifier:id
withClass:CAAnimation.self];
charAnimation.repeatCount = 1;
charAnimation.fadeInDuration = 1;
charAnimation.fadeOutDuration = 1;
[animations setValue:charAnimation forKey:withKey];
}
And at last touchesBegan:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
CGPoint point = [touches.allObjects.firstObject locationInView: sceneView];
NSDictionary<SCNHitTestOption, id> *options = #{ SCNHitTestBoundingBoxOnlyKey: #(YES) };
NSArray<SCNHitTestResult *> *hitTestResults = [sceneView hitTest: point options: options];
if (notRunningSwitch == YES) {
[sceneView.scene.rootNode addAnimation:animations[#"running"] forKey:#"running"];
} else {
[sceneView.scene.rootNode removeAnimationForKey:#"running" blendOutDuration:1.0];
}
notRunningSwitch = !notRunningSwitch;
NSLog(#"%#", hitTestResults.firstObject.node.name);
NSLog(#"%f", hitTestResults.firstObject.modelTransform.m43);
}
#end

SpriteKit SKCropNode Masks everything

I am trying to mask a node with SKCropNode class. I am doing it with my own init method in SKCropNode inherited class:
- (id)init {
self = [super init];
if (self) {
self.maskNode = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:(CGSize){50,50}];
SKSpriteNode *contentNode = [[SKSpriteNode alloc] initWithImageNamed:#"Spaceship"];
[self addChild: contentNode];
self.userInteractionEnabled = YES;
}
return self;
}
It works as expected:
Now, I want to add background to the scene and a cropped spaceship with following code:
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
//
//background
//
SKSpriteNode * background = [SKSpriteNode spriteNodeWithImageNamed:#"background_game"];
background.position = CGPointMake(CGRectGetMidX(view.frame), CGRectGetMidY(view.frame) - CGRectGetMinY(view.frame));
[self addChild:background];
TurretCropNode * cropNode = [[TurretCropNode alloc] init];
cropNode.position = (CGPoint){view.frame.size.width/2,view.frame.size.height/2};
[self addChild:cropNode];
}
I have just added few lines with adding background at the beginning (cropped node lines remain the same)
Here what I got now:
Is it an expected behavior? Why cropped spaceship disappeared?
I am guessing that the image as shown below is your designated background. Have you checked your nodes zPosition?
If you want to show your spaceship infront of your background, you will have to adjust the zPosition to be higher than that of the background sprite.

Variables not updating while using CADisplayLink

I cannot get the variables to change when I update them. The method "reCenterYaw" fires when I touch the button and the variable "self.hasYawCenterPointBeenSet" gets updated within the method but when "updateCycle" method comes back around the variable is not showing it was updated. updateCycle is my CADisplaylink driven method. I've tried updating the variables to atomic, referenced them as their instance variables with "_" and with "self." Not sure what is going on.
#import "GameViewController.h"
#import <CoreMotion/CoreMotion.h>
#import <SpriteKit/SpriteKit.h>
#import "HUDOverlay.h"
#interface GameViewController ()
#property (strong, nonatomic) CMMotionManager *motionManager;
#property (strong, nonatomic) CMAttitude *currentAttitude;
#property (strong, nonatomic) SCNNode *cameraNode;
#property (strong, nonatomic) SCNScene *scene;
#property (nonatomic) float roll;
#property (nonatomic) float yaw;
#property (nonatomic) float pitch;
#property (atomic) float yawCenterPoint;
#property (atomic) BOOL hasYawCenterPointBeenSet;
#end
GameViewController* sharedGameViewController = nil;
#implementation GameViewController
+(GameViewController*)sharedController{
if (sharedGameViewController == nil) {
sharedGameViewController = [[GameViewController alloc] init];
}
return sharedGameViewController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// create a new scene
//SCNScene *scene = [SCNScene sceneNamed:#"art.scnassets/ship.dae"];
_scene = [SCNScene new];
self.hasYawCenterPointBeenSet = NO;
// create and add a camera to the scene
_cameraNode = [SCNNode node];
_cameraNode.camera = [SCNCamera camera];
[_scene.rootNode addChildNode:_cameraNode];
// place the camera
_cameraNode.position = SCNVector3Make(0, 0, 0);
// create and add a light to the scene
SCNNode *lightNode = [SCNNode node];
lightNode.light = [SCNLight light];
lightNode.light.type = SCNLightTypeOmni;
lightNode.position = SCNVector3Make(0, 10, 10);
[_scene.rootNode addChildNode:lightNode];
// create and add an ambient light to the scene
SCNNode *ambientLightNode = [SCNNode node];
ambientLightNode.light = [SCNLight light];
ambientLightNode.light.type = SCNLightTypeAmbient;
ambientLightNode.light.color = [UIColor whiteColor];
[_scene.rootNode addChildNode:ambientLightNode];
// retrieve the SCNView
SCNView *scnView = (SCNView *)self.view;
scnView.delegate = self;
scnView.playing = YES;
// set the scene to the view
scnView.scene = _scene;
scnView.overlaySKScene = [HUDOverlay sceneWithSize:scnView.frame.size];
// allows the user to manipulate the camera
scnView.allowsCameraControl = NO;
// show statistics such as fps and timing information
scnView.showsStatistics = YES;
// configure the view
scnView.backgroundColor = [UIColor blueColor];
// // add a tap gesture recognizer
// UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
// NSMutableArray *gestureRecognizers = [NSMutableArray array];
// [gestureRecognizers addObject:tapGesture];
// [gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers];
// scnView.gestureRecognizers = gestureRecognizers;
SCNNode *sampleNode = [SCNNode new];
SCNPlane *samplePlane = [SCNPlane planeWithWidth:3 height:5];
SCNMaterial *angryFrontMaterial = [SCNMaterial material];
angryFrontMaterial.diffuse.contents = [UIImage imageNamed:#"angryfront110.png"];
sampleNode.geometry = samplePlane;
sampleNode.geometry.firstMaterial = angryFrontMaterial;
sampleNode.position = SCNVector3Make(0, 0, -10);
[_scene.rootNode addChildNode:sampleNode];
// SCNAction *moveToCamera = [SCNAction moveTo:SCNVector3Make(0, 0, -10) duration:10];
// [sampleNode runAction:moveToCamera];
[self setupRotationGrid];
[self setupMotionCapture];
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateCycle)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
//This is the update loop for the sceneView. When the view updates per frame this is where the logic goes
-(void)renderer:(id<SCNSceneRenderer>)aRenderer updateAtTime:(NSTimeInterval)time{
}
-(void)updateCycle{
_currentAttitude = _motionManager.deviceMotion.attitude;
_roll = _currentAttitude.roll + (.5 * M_PI);
_yaw = _currentAttitude.yaw;
_pitch = _currentAttitude.pitch;
_cameraNode.eulerAngles = SCNVector3Make(-_roll, _yaw, _pitch);
//NSLog(#"yawCenterPoint = %d", _hasYawCenterPointBeenSet);
if (self.hasYawCenterPointBeenSet == NO) {
_yawCenterPoint = _yaw;
self.hasYawCenterPointBeenSet = YES;
NSLog(#"Yawcenter point set to %f", _yawCenterPoint);
}
//NSLog(#"Roll: %f Yaw: %f Pitch: %f", _roll, _yaw, _pitch);
}
-(void)setupRotationGrid {
double radius = 30;
double numberOfItems = 8;
SCNGeometry *geometry = [SCNBox boxWithWidth:4.5 height:8 length:3.25 chamferRadius:0.5];
double x = 0.0;
double z = radius;
double theta = (M_PI)/(numberOfItems/2);
double incrementalY = (M_PI)/(numberOfItems*2);
SCNNode *nodeCollection = [SCNNode node];
nodeCollection.position = SCNVector3Make(0, 4, 0);
for (int index = 1; index <= numberOfItems; index++) {
x = radius * sin(index*theta);
z = radius * cos(index*theta);
SCNNode *node = [SCNNode node];
node.geometry = geometry;
node.position = SCNVector3Make(x, 0, z);
float rotation = incrementalY * index;
node.rotation = SCNVector4Make(0, 1, 0, rotation);
[nodeCollection addChildNode:node];
}
[_scene.rootNode addChildNode:nodeCollection];
}
- (void) handleTap:(UIGestureRecognizer*)gestureRecognize
{
// retrieve the SCNView
SCNView *scnView = (SCNView *)self.view;
// check what nodes are tapped
CGPoint p = [gestureRecognize locationInView:scnView];
NSArray *hitResults = [scnView hitTest:p options:nil];
// check that we clicked on at least one object
if([hitResults count] > 0){
// retrieved the first clicked object
SCNHitTestResult *result = [hitResults objectAtIndex:0];
// get its material
SCNMaterial *material = result.node.geometry.firstMaterial;
// highlight it
[SCNTransaction begin];
[SCNTransaction setAnimationDuration:0.5];
// on completion - unhighlight
[SCNTransaction setCompletionBlock:^{
[SCNTransaction begin];
[SCNTransaction setAnimationDuration:0.5];
material.emission.contents = [UIColor blackColor];
[SCNTransaction commit];
}];
material.emission.contents = [UIColor redColor];
[SCNTransaction commit];
}
}
-(void)reCenterYaw{
self.hasYawCenterPointBeenSet = NO;
NSLog(#"Tapped recenter");
}
-(void)setupMotionCapture{
_motionManager = [[CMMotionManager alloc] init];
_motionManager.deviceMotionUpdateInterval = 1.0/60.0;
if (_motionManager.isDeviceMotionAvailable) {
[_motionManager startDeviceMotionUpdates];
_currentAttitude = _motionManager.deviceMotion.attitude;
}
// if (!_hasYawCenterPointBeenSet) {
// _yawCenterPoint = _currentAttitude.yaw;
// _hasYawCenterPointBeenSet = YES;
// }
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskLandscape;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end
I figured it out. I had created a singleton so that I could reference the view controller from the sprite kit hud class. The problem is that I load the view controller from the storyboard and then later, from the hud class init, I instantiate a new view controller because the "sharedViewController" never got initiated by the storyboard load. So, I just created a view controller property on the hud class and set it to self from the view controller did load code.

adding CCPhysicsSprite to CCLayer cocos2d

I'm trying to reorganize HelloWorld project in cocos2d to our needs.
The thing I did - made a class, which is inherent from CCPhysicsSprite and wanted to add it to CCLayer (HelloWorldLayer). But something goes wrong. According to debugger my instance is created, but I can't see it in the iOS emulator. Need your help and explanations.
HelloWorldLayer.h
#interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
CCTexture2D *spriteTexture_; // weak ref
b2World* world_; // strong ref
GLESDebugDraw *m_debugDraw; // strong ref
}
HelloWorldLayer.mm (only changed by me functions:)
-(id) init
{
if( (self=[super init])) {
// enable events
self.touchEnabled = YES;
self.accelerometerEnabled = YES;
CGSize s = [CCDirector sharedDirector].winSize;
// init physics
[self initPhysics];
// create reset button
//[self createMenu];
//Set up sprite
//#if 1
// // Use batch node. Faster
// CCSpriteBatchNode *parent = [CCSpriteBatchNode batchNodeWithFile:#"blocks.png" capacity:100];
// spriteTexture_ = [parent texture];
//#else
// // doesn't use batch node. Slower
// spriteTexture_ = [[CCTextureCache sharedTextureCache] addImage:#"blocks.png"];
// CCNode *parent = [CCNode node];
//#endif
// [self addChild:parent z:0 tag:kTagParentNode];
//
//
// [self addNewSpriteAtPosition:ccp(s.width/2, s.height/2)];
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Tap screen" fontName:#"Marker Felt" fontSize:32];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( s.width/2, s.height-50);
[self scheduleUpdate];
}
return self;
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
if([self getChildByTag:kTagParentNode] == nil)
{
BloodRobotUnit *unit = [[BloodRobotUnit alloc] initWithOwner:world_ at:p];
[self addChild:unit z:0 tag:kTagParentNode];
}
}
And Creating unit: (header and mm file:)
#interface BloodRobotUnit : CCPhysicsSprite
{
b2Body *body_;
b2World *owner_;
}
-(id) initWithOwner:(b2World*)owner at:(CGPoint)pt;
mm:
-(id) initWithOwner:(b2World*)owner at:(CGPoint)pt
{
if(self = [super initWithFile:#"blocks.png" rect:CGRectMake(0, 0, 32, 32)])
{
owner_ = owner;
//create body at position
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(pt.x/PTM_RATIO, pt.y/PTM_RATIO);
body_ = owner->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body_->CreateFixture(&fixtureDef);
[self setB2Body: body_];
[self setPosition:pt];
return (self);
}
return nil;
}
Where is my mistake? Any help will be very appreciated
The trick was in setting position to self
Debugger showed that my texture was at position inf:inf
Code change to make everything work is the following:
in mm file of creating a CCPhysicsSprite iheriter do the following:
[self setB2Body: body_];
self.PTMRatio = PTM_RATIO;
//[self setPosition:CGPointMake(pt.x/PTM_RATIO, pt.y/PTM_RATIO)];
That is - you need to set only body position and set PTMRatio (thanx to #giorashc). Setting sprite texture is not necessary.

Use of undeclared identifier winSize

Not really sure why I am getting this error but I have looked in the .h file and I can't seem to come up with the reason xCode is throwing this error:
// on "init" you need to initialize your instance
-(id) init
{
if ((self=[super init])) {
_batchNode = [CCSpriteBatchNode batchNodeWithFile:#"Sprites.pvr.ccz"]; //1
[self addChild:_batchNode]; //2
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"Sprites.plist"]; //3
_ship = [CCSprite spriteWithSpriteFrameName:#"SpaceFlier_sm_1.png"]; //4
CGSize winSize = [CCDirector sharedDirector].winSize; //5
_ship.position = ccp(winSize.width * 0.1, winSize.height * 0.5); //6
[_batchNode addChild:_ship z:1]; //7
}
return self;
// 1) Create the CCParallaxNode
_backgroundNode = [CCParallaxNode node];
[self addChild:_backgroundNode z:-1];
// 2) Create the sprites we'll add to the CCParallaxNode
_spacedust1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_spacedust2 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_planetsunrise = [CCSprite spriteWithFile:#"bg_planetsunrise.png"];
_galaxy = [CCSprite spriteWithFile:#"bg_galaxy.png"];
_spacialanomaly = [CCSprite spriteWithFile:#"bg_spacialanomaly.png"];
_spacialanomaly2 = [CCSprite spriteWithFile:#"bg_spacialanomaly2.png"];
// 3) Determine relative movement speeds for space dust and background
CGPoint dustSpeed = ccp(0.1, 0.1);
CGPoint bgSpeed = ccp(0.05, 0.05);
// 4) Add children to CCParallaxNode
[_backgroundNode addChild:_spacedust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height/2)];
[_backgroundNode addChild:_spacedust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spacedust1.contentSize.width,winSize.height/2)];
[_backgroundNode addChild:_galaxy z:-1 parallaxRatio:bgSpeed positionOffset:ccp(0,winSize.height * 0.7)];
[_backgroundNode addChild:_planetsunrise z:-1 parallaxRatio:bgSpeed positionOffset:ccp(600,winSize.height * 0)];
[_backgroundNode addChild:_spacialanomaly z:-1 parallaxRatio:bgSpeed positionOffset:ccp(900,winSize.height * 0.3)];
[_backgroundNode addChild:_spacialanomaly2 z:-1 parallaxRatio:bgSpeed positionOffset:ccp(1500,winSize.height * 0.9)];
}
First off, unless C2D somehow magically changes the way return works, you proceed to do a lot of stuff after returning which can never be reached. You've just returned; you've exited the method. It's like telling the painter to leave the room he's working on and go home for the day... then screaming out to nothingness that he should paint the walls red. Actually, you wouldn't get to scream at all. Here's an example:
- (void) foo {
int x = 10;
return; //return control to caller
self->someValue = x; //never done; we've returned
}
x would be assigned ten and then you would return. Any lines afterwards are meaningless. Second of all, there's this line here.
CGSize winSize = [CCDirector sharedDirector].winSize;
It occurs within an if statement. The variable winSize can only be used within the constraints of the if statement you declared it in. It's "out of scope" of anything else. For example, the following will give the same warning.
- (void) bar {
if (0){
int x = 1;
}
int y = x;
}
Why not place all the stuff below return inside the if loop, right before it closes?