Automatic sprite animation with Cocos2d - iphone

I have a class representing my character in Cocos2d.
I've exported the spritesheet + .plist file.
The character has multiple animations.
The frames are simply called "Character_1.png".
Up to four. This is the basic walking animation.
In many sprite animation tutorials I found this sample code, which loops though the sprite frame cache and just adds it to an array so you can animate it:
for(int i = 1; i <= 4; ++i) {
[animationFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Character_%d.png", i]]];
}
However, because most of my sprites will be animated just like this, I'd like to delegate this to an super class.
I thought about calling it something like
spriteWithAnimations:(NSArray *)animationNames
and then I'd look though them, just like in the example above.
So as a parameter in the array I'd get a string #"Character_%d", I'd make a while loop and check if the file exists, and while so, I'd add it to the array.
The only problem is, that I cannot check if a frame "Character_05.png" exists, because CCSpriteFrameCache has no such method.
How is this usually solved?

If CCSpriteFrameCache returns nil for a given frame name you know it doesn't exist. Not elegant but it works.

Related

How to programmatically assign a random sprite to game object?

I have a function that generates a game object on screen every 100 frames:
var trash : Transform ;
function Update()
{
if(count == 1)
{
Instantiate(trash,new Vector3(UnityEngine.Random.Range(-3f,3f),UnityEngine.Random.Range(-3f,3f),UnityEngine.Random.Range(-3f,3f)), Quaternion.identity);
}
else if(count == 100)
{
count = 0;
}
}
Now, I want each of those generated objects to pick a random sprite from my assets. I thought about making a sprite array and picking from that, but I'm not sure how to go about doing so or how to assign the sprite to the object.
Anu help would be much appreciated!
I know it's late, but I just did it for myself, so I'm sharing.
Basically - put your assets in a folder and choose randomly from them to assign to the object you're duplicating.
Create a Resources folder under Assets.
Create another sub-folder for your relevant sprite assets, under Resources, lets call it CookiesImages.
Create your prefab object, and locate it under Assets/Prefabs/Resources (this is the object that you want to duplicate multiple times with different assets).
Create a script for this prefab object.
Create a static Object array (will hold the sprites).
In your Awake() method, init this Object array by loading all the resources from your assets folder:
if (objectsArray == null)
{
objectsArray= Resources.LoadAll("CookiesImages", typeof(Sprite));
}
In the Start() method, get a random index 0 --> objectsArray.Length and call Instantiate to create a sprite to assign to your prefab object.
this.GetComponent<SpriteRenderer>().sprite = Instantiate(objectsArray[index]) as Sprite;
At the beginning, I tried assigning them to an array variable from the unity UI, but I realized that in the way I described it's much more flexible.
Well, you can generate an array of sprites of the length of your choosing with:
Sprite[] _sprites = new Sprite[quantity desired]
Then using the resources folder and the Resources.Load method (found here: http://docs.unity3d.com/ScriptReference/Resources.Load.html), you could load all your sprites into the array one after the other. Then using your random number generator, you can access sprites at random from it.
Still probably not the best way of doing it, but I would imagine it is considerably better and easier to maintain than the prefab method, but different situations call for different approaches.

Redrawing GameHUD multiple times causes cocos2d frame rates to drop significantly

I'm currently building a game for the iPhone with cocos2d and have the following problem:
I have a singleton class called GameHUD which displays a HUD in front of the current level scene. Once in a while I want the HUD to be redrawn, so it changes accordingly to the current game status. The problem is the more often I redraw the HUD, the more the frame rate drops.
I'm guessing that I fail to release some ressources, but cannot figure out which ones I have to release. (I'm pretty new to memory management.. I understand that I have to release objects that are created with one of following keywords: "new", "alloc", "copy" or "retain". But cocos2d mainly generates autorelease objects, therefor I mustn't release them manually.. correct me, if I'm wrong ;))
//static, so it can be called from other classes
+(void)redrawGameHUD{
CGSize winSize = [CCDirector sharedDirector].winSize;
//get reference to background-sprite
CCSprite *background = [[[GameHUD class] sharedHUD] towerBackground];
//remove the child from the HUD, if it exists
[[[GameHUD class] sharedHUD] removeChild:background cleanup:YES];
//create sprite containing the background-image
background = [CCSprite spriteWithFile:#"background.png"];
//add background image to HUD
[[[GameHUD class] sharedHUD] addChild:background];
//load images that should be displayed into an array
NSArray *images = [NSArray arrayWithObjects:#"image1.png", #"image2.png", #"image3.png", #"image4.png", nil];
//remove sprites from HUD before drawing them again.
//the "buildable" array contains all those already drawn sprites
for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable]) {
[[[GameHUD class] sharedHUD] removeChild:entity cleanup:YES];
}
[[[[GameHUD class] sharedHUD] buildable] removeAllObjects];
//loop over sprites, initialize them and add them to the HUD
for(int i = 0; i < images.count; ++i) {
NSString *image = [images objectAtIndex:i];
CCSprite *sprite = [CCSprite spriteWithFile:image];
//add sprite to HUD and memorize them in the "buildable" array
[[[GameHUD class] sharedHUD] addChild:sprite];
[[[[GameHUD class] sharedHUD] buildable] addObject:sprite];
}
}
So every time this method is called, the frame rate drops a little bit and stays down.. Can somebody please tell me, what I am doing wrong? Thanks.
Try not to create and remove sprites at runtime, ie try to avoid doing this frequently:
[CCSprite spriteWithFile:#"background.png"];
This allocates new memory for the sprite, and theres quite a bit of things going on behind the scenes when you create a new sprite. And of course you're releasing the already existing sprites. All of that is unnecessary.
In your redrawGameHUD method I see no indication why you actually want to create the sprites anew. The sprites are using the same images every time. So why not just keep the old ones? Unless you edited the code before you posted it in the questions, there's no need to remove and re-create the HUD sprites.
You also might want to create a texture atlas for all the HUD sprite images. For one, you can then render all HUD sprites with one draw call by using a CCSpriteBatchNode. Secondly, whenever you do want to assign a new texture to an existing sprite, you would simply change the CCSpriteFrame of that sprite instead of throwing away the sprite and re-creating it.
Something else that bothers me, you keep writing this:
[[[GameHUD class] sharedHUD] addChild:sprite];
First, this is the same as writing the following, the message to class is absolutely unnecessary (makes me wonder where you picked that up?):
[[GameHUD sharedHUD] addChild:sprite];
And since you do this multiple times over, you should keep a temporary local copy of GameHUD, this again removes several unnecessary Objective-C messages:
GameHUD* gameHUD = [GameHUD sharedHUD];
// from now on use gameHUD instead of [GameHUD sharedHUD]
[gameHUD addChild:sprite];
This is particularly good advice for loops, because doing this:
for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable])
will send two extra messages (class and sharedHUD) for every entity in the buildable array. Those extra calls can quickly add up, although they're certainly not enough for the drop in framerate you're experiencing.
You also unnecessarily keep all the HUD sprites in the "buildable" array. Why not use the already existing children array that cocos2d uses? Simply add each HUD sprite that is "buildable" with the same tag, for example 123.
[gameHUD addChild:sprite z:0 tag:123];
If you need to do something with all the "buildable" sprites you can then iterate over the children like this:
CCNode* node;
CCARRAY_FOREACH([gameHUD children], node)
{
if (node.tag == 123)
{
CCSprite* buildable = (CCSprite*)node;
// do stuff with buildable sprite ...
}
}
Again, this avoids the unnecessary adding, retaining, removing and releasing of objects in the buildable array. And you can be sure that you don't accidentally remove sprites from the node hierarchy but not the buildable array, or vice versa.
I'd like to conclude with an assumption: in your code I saw a general tendency that you're doing many extra things unnecessarily. So I'm guessing this is the case throughout the project. You might want to go back and ask yourself (better: find out) what other things there are that you're having the device perform that are rather unnecessary.
I am 90% sure that you are testing this on the iPhone simulator, am I right?
If you are, then keep in mind that you can't profile an OpenGL app properly on the simulator .. The performance is too variable.
Test it on a real device and check the frame rate again.
If not, then you got a problem somewhere else. All the processing done in this method are trivial, unless your images are 5000x5000 px or something..

Trouble with getChildByTag (cocos2d iPhone)

I have a scene called Level1, which takes the hero and the enemy from a layer called GameLayer. I heard that an efficient way of doing this is using tags and retrieving it using getChildByTag. I tried this out, but I'm having many issues. I'm using SneakyInput. On Level1, there is a leftJoystick (for enemy movement), and a rightJoystick (for firing projectiles). I have an addEnemy and addHero method in my GameLayer, which I call. Everything I've mentioned works.
In my Level1 scene I have a method called moveHero (which obviously is supposed to move the hero with the joystick.). Through basic debugging I know the problem is the geteChildByTag line. I test out the hero's position through NSLog, and it's saying 0,0. Which is weird because on screen you can see the hero. But I also realized I'm calling the addHero method without using getChildByTag.
I hope I'm being clear here. I've uploaded GameLayer.h and GameLayer.m onto 4shared. http://www.4shared.com/file/PqhjoMFy/GameLayer.html
Hopefully you can take a look at it and point me in the right direction.
BTW: There are no errors or crashes. It's just not working.
Thanks in advance.
getChildByTag will never crash, it's a pretty nicely coded method that just loops through the children array of the object and checks to see if any objects match, that way you don't get assertion issues.
You have some serious issues here with your code.
Firstly..
GameLayer *heroInstance = [[GameLayer alloc] init];
CCSprite *hero = (CCSprite *)[heroInstance getChildByTag:1];
NSLog(#"Hero position X: %f", hero.position.x);
NSLog(#"Hero position Y: %f", hero.position.y);
This will never work, heroInstance is a brand new object, it has no children, also you've just created a memory leak here.
Your hero is a child of the spritesheet, which is a child of the scene.
To reference your child you must call getChildByTag on your spritesheet (which you probably need to reference by calling getChildByTag on your scene..
something like this.
[[self getChildByTag:spritesheet] getChildByTag:hero];
Also, use an enum, so that you don't have to remember what numbers certain tags are (look at the cocos2d example projects).

iPhone SDK: Block Animation Ignore Error with Curved Path

I'm creating an application where the user can draw a line on a screen from an object to the location they want to send it and the object will then follow the line to the final location. In order to do this, I've already created working methods to allow the user to draw the lines and then to store the coordinates of the line in a MutableArray. However, I'm having some trouble when I try to animate. As I'm pretty new to the iPhone OS, this could be a simple problem, but I haven't been able to find a solution yet.
I am NOT using Bezier Paths as the user is drawing the line manually, I'm not drawing it programmatically.
Here's the code that I've tried
-(void)animateButtonWasPressed
{
for (int f = 0; f < [cordArrayY count]; f++) {
NSString *newY = [cordArrayY objectAtIndex:f];
NSString *newX = [cordArray objectAtIndex:f];
[self myAnimate:newX :newY];
}
}
-(void)myAnimate:(NSString *)PntX :(NSString *)PntY
{
[UIView animateWithDuration:.5 animations:
^{
object.center = CGPointMake([PntX floatValue], [PntY floatValue]);
}];
}
SYNTAX:
object - the object I am trying to move
cordArray - the mutable array containing the x-coordinates
cordArrayY - the mutable array containing the y-coordinates
Everything else is either defined within the code or Apple methods
The problem: the object moves instantly from its original location directly to the final location. I get a NSLog which tells me this:
-[UIApplication endIgnoringInteractionEvents] called without matching -beginIgnoringInteractionEvents. Ignoring.
Any help would be appreciated!
The method you're using to animate "object" seems to be ok. I believe the problem is the loop in which you are invoking that method. You are trying to animate the same property of the object over and over in every step of that loop. I think this causes that "jump".
Take a look at this quote from Apple's docs:
Important: Changing the value of a property while an animation
involving that property is already in progress does not stop the
current animation. Instead, the current animation continues and
animates to the new value you just assigned to the property.
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6
As a consequence of invoking the animation in each step, you will end up animating object in approximately 0.5 secs to the last position of your coordinates array.
I think you should link those animations together, but you should wait for each animation to finish to start the following one. Take a look at this
Another thing that both the animateWithDuration:animations:completion:
and animateWithDuration:delay:options:animations:completion: methods
support is the ability to specify a completion handler block. You
might use a completion handler to signal your application that a
specific animation has finished. Completion handlers are also the way
to link separate animations together.
Hope this helps,
Cheers.
I was finally able to solve this problem:
At first I tried to continue using the method that I had above. Lio's advice to use the "completion" block was perfect, but as I needed it to loop for an undefined number of times, I would have to use a counter variable. iPhone block programming doesn't allow the modification of external variables or the use of the _block declaration, so this didn't work out for me.
However, I eventually created my entire animation using NSTimer in the method described here just using my array coordinates instead:
http://www.icodeblog.com/2008/10/28/iphone-programming-tutorial-animating-a-ball-using-an-nstimer/

Creating pointer Attributes in cocos2d iPhone

I am working on a game. There are balls that fall from the top of the screen, and the player has to catch them, as the are caught they stack ontop of one another. I have a method that creates each new ball and adds it to an Array that i use to move the sprites. Problem is that the after they collide I need them to stop moving, since the array is called on to move them they all move. And if i try to make them stop they all stop. So I was hoping to create a pointer attribute if ther is such a think, for example "sprite.position" I need a new attribute that i can check like a boolean. I was hoping to create a attribute like sprite.hasCollided and if it returns YES then the ball should no longer move. Is this possible or is there a better way to do it?
Thanks
Tanner
I would suggest you create a ball object. And add the boolean as as part of the object.
CCNodes (and, by inheritence, CCSprites) have a userData property, which is a void*. You can use this to relate a custom object to a cocos2d object. Keep in mind if you use the userData option, you will, in most cases, need to allocate any memory when you create/assign the sprite, and release it when you are done.
int* myInt = (int*)malloc(sizeof(int));
*myInt = 0;
sprite.userData = myInt;
//some time later, when you are done with the sprite
free(sprite.userData);
As an improvement on the userData property, you can do what xuanweng suggests and create a ball object containing various game-related properties for the balls, and assign an instance of this to each of your ball CCSprites using the method above.