My Sprite AI is working incorrectly. Why? - ios

I want my two enemies to be set on attack mode, however as it stands only the last enemy added is being set on attack mode.
Is there any way around this? Any tips or suggestions is appreciated. If you need more code please let me know.
-(void)ViewDidLoad {
for (_enemyPoint in [self.enemyGroup objects]) {
self.enemy = [[CCSprite alloc] initWithFile:#"Icon.png"];
self.enemy.scale = 32.0f/57.0f;
self.enemy.position = CGPointMake([_enemyPoint[#"x"] integerValue], [_enemyPoint[#"y"] integerValue]);
[self addChild:self.enemy];
}
self.pathfinder = [HUMAStarPathfinder pathfinderWithTileMapSize:self.tileMap.mapSize
tileSize:self.tileMap.tileSize
delegate:self];
[self enemyAttack];
}
- (void)enemyAttack{
self.epath = [self.pathfinder findPathFromStart:self.enemy.position
toTarget:self.player.position];
self.eactions = [NSMutableArray array];
for (_epointValueInPath in self.epath) {
self.epoint = _epointValueInPath.CGPointValue;
self.emoveTo = [CCMoveTo actionWithDuration:1.0f position:self.epoint];
[self.eactions addObject:self.emoveTo];
}
self.esequence = [CCSequence actionWithArray:self.eactions];
[self.enemy runAction:self.esequence];
}

Look at your loop in viewDidLoad. First, you use an iVar as the loop variable. Probably not what you want. Second, you assign self.enemy in each iteration, but you call enemyAttack after the loop has completed.
Further, enemyAttack does not take any parameters, so it uses internal state. Since it is called after the loop has iterated over all objects, self.enemy will always be the last object in the collection (if there is anything in the collection).
Thus, it is not surprising that you only see the last item being activated as an enemy.

Have you tried to put the [self enemyAttack]; invocation inside the for loop?

Related

pause a specific action on sprite

I've a player of Sprite * type in cocos2dx V3, I want it to run different animation on different time interval, I could not find method to pause and then resume a specific animation(Action). Although I can Pause and Resume all actions of Sprite Simultaneously using _player->pauseSchedulerAndActions().I'm using "CCRepeatForever" actions on sprite, so, I must have to pause one to resume other.Please help to Pause an action by tag or by any other method.
Thanks In Advance.
Oops
I made the assumption that this was Objective-C but #Droppy has informed me that it is not.
I didn't realise cocos2d-x was different. However, because this is a fairly high level framework the concept behind what I've done in the answer will still work. I'll keep the answer here for now.
The answer
It's been a while since I've done any Cocos2D stuff but I can give you the idea.
Instead of creating an action and repeating it forever you should have a method something like this...
- (void)twirlAround
{
// only create and perform the actions if variable set to YES
if (self.twirling) {
// this will do your action once.
CCAction *twirlAction = // create your twirl action (or whatever it is)
// this will run this function again
CCAction *repeatAction = [CCActionCallBlock actionWithBlock:^{
[self twirlAround];
}];
// put the action and method call in sequence.
CCActionSequence *sequence = [CCActionSequence actions:#[twirlAction, repeatAction]];
[self runAction:sequence];
}
}
This will run repeatedly as long as the twirling property is set to YES.
So, somewhere else in your code (probably where you are currently adding your repeating action) you can do this...
self.twirling = YES;
[self twirlAround];
This will start the repeated twirling.
To stop it you can then do...
self.twirling = NO;
This will stop the twirling.
Alternative method
- (void)twirlAround
{
// this will do your action once.
CCAction *twirlAction = // create your twirl action (or whatever it is)
// this will run this function again
CCAction *repeatAction = [CCActionCallBlock actionWithBlock:^{
if (self.twirling) {
[self twirlAround];
}
}];
// put the action and method call in sequence.
CCActionSequence *sequence = [CCActionSequence actions:#[twirlAction, repeatAction]];
[self runAction:sequence];
}
based on Fogmeister advice, this is cocos2d-x version of that
void MySprite::jumpForever(){
if (!twirling) return;
auto jump = JumpBy::create(0.5, Vec2(0, 0), 100, 1);
auto endCallback = CallFuncN::create(CC_CALLBACK_1(MySprite::jumpForever,this));
auto seq = Sequence::create(jump, endCallback, nullptr);
runAction(seq);
}

Store in memory NSArray with pictures or SKAction

i don't completely understand the best choice in sprite kit animation;
1) Apple in "Adventure" example use this methodology, they store in memory animation as pictures in nsarray :
static NSArray *sSharedTouchAnimationFrames = nil;
- (NSArray *)touchAnimationFrames {
return sSharedTouchAnimationFrames;
}
- (void)runAnimation
{
if (self.isAnimated) {
[self resolveRequestedAnimation];
}
}
- (void)resolveRequestedAnimation
{
/* Determine the animation we want to play. */
NSString *animationKey = nil;
NSArray *animationFrames = nil;
VZAnimationState animationState = self.requestedAnimation;
switch (animationState) {
default:
case VZAnimationStateTouch:
animationKey = #"anim_touch";
animationFrames = [self touchAnimationFrames];
break;
case VZAnimationStateUntouch:
animationKey = #"anim_untouch";
animationFrames = [self untouchAnimationFrames];
break;
}
if (animationKey) {
[self fireAnimationForState:animationState usingTextures:animationFrames withKey:animationKey];
}
self.requestedAnimation = VZAnimationStateIdle;
}
- (void)fireAnimationForState:(VZAnimationState)animationState usingTextures:(NSArray *)frames withKey:(NSString *)key
{
SKAction *animAction = [self actionForKey:key];
if (animAction || [frames count] < 1) {
return; /* we already have a running animation or there aren't any frames to animate */
}
[self runAction:[SKAction sequence:#[
[SKAction animateWithTextures:frames timePerFrame:self.animationSpeed resize:YES restore:NO],
/* [SKAction runBlock:^{
[self animationHasCompleted:animationState];
}]*/]] withKey:key];
}
I appreciate this methodology, but i can't understand. Is storing SKAction in memory not better choice and use animation always like this ?
[self runAction:action];
without making always new SKAction;
[SKAction animateWithTextures:frames timePerFrame:self.animationSpeed resize:YES restore:NO]
Storing SKTextures in an NSArray is the recommended methodology to be used for animation. I can't say that I found SpriteKit's documentation to be lacking on the subject either, as SKAction has the method animateWithTextures.
You could indeed have a SKAction that has a given animation defined by a given NSArray, and maybe store those in a NSMutableDictionary with an animation name for a key. However this methodology I see above just has the animateTextures line of code once in their fireAnimationState method, and you can pass parameters to it such as the animationState and a key.
I think you would need to take more than a surface look at their methodology to determine why they chose to go this route. You can see that the animationState was being utilized upon completion of the animation and likely triggered something else.
[self runAction:action] is indeed simple, however it's also easy to see that it's not in any way managing an animationState or key, which I have to assume they decided their game needed to do.
Also keep in mind that their methodology likely has a higher level where in a given player class it's calling a flexible method for enacting an animation for a given game entity and doing other things besides just changing the animation. So in their high level coding they might be doing something more like this :
[self runAnimation:#"jump"];
or even
[self jump];
And because they have designed a low level system that manages animation states and adds keys for a given management design, they don't ever have to code the long line that you are pointing out except once in that method you see above.
A few reasons I have found it useful to store the NSArray of frames and not storing wrapped in a SKAction is because I sometimes want to manipulate the start frame of the animation or run the animation at a different speed. But you may or may not have the need to manipulate those aspects of your animation.

insertObject:AtIndex:0 does not work properly

I have this function in which the function insertObject:AtIndex:0 behaves weird . After inserting all objects to the NSMutableArray cardViewControllers, the final element is always nil.I did alloc init the cardViewControllers at the beginning in the init method.
- (void)reloadCardViews;
{
// Add the restaurants onto view
[self removeAllCards];
for (int i = 0; i < NO_OF_CARDS; i++) {
SHCCardVC *vc = [[SHCCardVC alloc] initWithAppearanceIndex:i];
vc.delegate = self;
[cardViewControllers insertObject:vc atIndex:0];//it's behaving weird here
[self addChildViewController:vc];
// set card position to center of the container
vc.view.center = CGPointMake(_cardContainer.frame.size.width / 2, _cardContainer.frame.size.height / 2);
[_cardContainer addSubview:vc.view];
}
_currentCardViewIndex = 0;
_currentCardIndex = 0;
}
What does [self removeAllCards] do? I suspect that you call [cardViewControllers removeAllObjects]? Have you tried using [cardViewControllers addObject:vc]? If this works and the order is important, walk trough your for loop from behind with i--.
Also make sure your objects are not nil and your array is mutable and also initialized. I had a similar problem with an uninitialized mutable array.
You can't add nil objects to arrays, it's a runtime error. So something must be going weird with the retain count, that's all I can think at the moment. NSArray retains objects that are within it, so the object is being released one too many times somewhere.
Is this a possibility?

Objective C: Infinite getter method loop after converting to ARC

I have the following method:
-(NSMutableArray *)someArray{ // line 1
if(self.someArray == nil){ // line 2
self.someArray = [[NSMutableArray alloc]init];
}
return self.someArray;
}
I had this in non-ARC, and it works fine: If self.someArray in line 2 is nil, then go ahead and instantiate it.
However, after I switched to ARC, when it gets to line 2, it goes back to line 1, and the loop continues.
Any help will be greatly appreciated.
Thanks.
The proper form is
#synthesize someArray = _someArray;
- (NSMutableArray *)someArray {
if (!_someArray) {
_someArray = [NSMutableArray array];
}
return _someArray;
}
self.someArray calls the getter, hence the infinite loop. use _someArray within that method instead (assuming you're using the automatic ivar provided by the property. Otherwise, use whatever your backing variable is).
What is happening is that you are calling the accessor method inside the accessor. But if every accessor calls itself, you'll never break this cycle.
Use the auto synthesized property to avoid this:
-(NSMutableArray *)someArray{ // line 1
if(_someArray == nil){ // line 2
_someArray = [[NSMutableArray alloc]init];
}
return _someArray;
}

Why am I able to reuse CCActionInterval?

I'm building a simple 2D game in Cocos2d which involves having enemies cross the screen across different, predefined paths. The nextFrame method has the following code:
int indexCount = 0;
for (Enemy *e in enemies) {
if ([cocosGuy doesCollideWithRect: [e boundingBox]])
{
for (Enemy *e in enemies)
{
[self removeChild: e cleanup: YES];
}
[self startGame];
}
// ^ This code not relevant to the question
if ([e numberOfRunningActions] == 0)
{
[e setPosition: [[enemy_positions objectAtIndex:indexCount] CGPointValue]];
[e runAction: [beziers objectAtIndex: indexCount]];
}
++indexCount;
}
The code in the second if statement above is intended to take a CGPoint from the 'enemy_positions' array and a CCActionInterval from the 'beziers' array. It works - when an enemy completes its path, it is repositioned and the action reruns. But why doesn't this break after the action runs for the first time? Aren't CCActions supposed to be one time only?
I ask because I want to refactor the position and action into a single struct, and I want to make sure I know what's going on first. Am I misunderstanding the CCAction class?
Also, here is the current factory method for generating the 'beziers' array:
-(NSArray*) makeBeziers {
ccBezierConfig bezierconf1;
bezierconf1.controlPoint_1 = ccp(-200, 5);
bezierconf1.controlPoint_2 = ccp(300, 100);
bezierconf1.endPosition = ccp(1000,5);
ccBezierConfig bezierconf2;
bezierconf2.controlPoint_1 = ccp(-200, 5);
bezierconf2.controlPoint_2 = ccp(300, 100);
bezierconf2.endPosition = ccp(1000,5);
ccBezierConfig bezierconf3;
bezierconf3.controlPoint_1 = ccp(-200, 5);
bezierconf3.controlPoint_2 = ccp(300, 100);
bezierconf3.endPosition = ccp(1000,5);
NSArray *myarray;
myarray = [[NSArray arrayWithObjects: [CCBezierBy actionWithDuration:3 bezier: bezierconf1],
[CCBezierBy actionWithDuration:3 bezier: bezierconf2],
[CCBezierBy actionWithDuration:3 bezier: bezierconf3],
nil] retain];
return myarray;
}
This works "by accident". Actions are supposed to be one time only. After they've run, they will be released.
However since you store the actions in a separate array, those actions are retained. Therefore you can re-run them. This might work for some actions, other actions may show subtle issues, and some actions may not do anything, leak memory or crash immediately if you do so.
Re-using actions is generally considered bad practice, unless you know the code of each action and you have verified that reusing it doesn't do anything "bad".

Resources