完全な円または部分的な円のいずれかの円形パスで CCNode を移動するために使用するユーティリティ関数があります。
この関数は非常にうまく機能しますが、CCNode が継続的にパスをたどるようにしたい場合は、渡された Block を介して同じ関数を呼び出します (一種の再帰的ですが、実際にはそうではありません)。
私が見つけた問題は、関数が内部でブロックを使用するため、アクションが実行されている CCNode が保持され、stopAllActions または removeFromParentAndCleanup:YES を呼び出した後でも、CCNode がクリーンアップされ、メモリに残り、割り当て解除されません。これは、ノードが CCNode として表示されていなくても、パフォーマンスに影響を与えているように見えます。
CCNode を移動する関数は次のとおりです。
@interface CocosUtil : NSObject {
}
typedef void (^NodeCompletionBlock)(CCNode *sprite);
+ (void) moveOperand:(CCNode*)operand throughCircleWithCentre:(CGPoint)centreOfElipse
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise
completionBlock:(NodeCompletionBlock)handler;
@end
@implementation CocosUtil
+ (float) angleFromDegrees:(float)deg {
return fmodf((450.0 - deg), 360.0);
}
// Calculates the angle from one point to another, in radians.
//
+ (float) angleFromPoint:(CGPoint)from toPoint:(CGPoint)to {
CGPoint pnormal = ccpSub(to, from);
float radians = atan2f(pnormal.x, pnormal.y);
return radians;
}
+ (CGPoint) pointOnCircleWithCentre:(CGPoint)centerPt andRadius:(float)radius atDegrees:(float)degrees {
float x = radius + cos (CC_DEGREES_TO_RADIANS([self angleFromDegrees:degrees])) * radius;
float y = radius + sin (CC_DEGREES_TO_RADIANS([self angleFromDegrees:degrees])) * radius;
return ccpAdd(centerPt, ccpSub(CGPointMake(x, y), CGPointMake(radius, radius)));
}
+ (void) moveOperand:(CCNode*)operand throughCircleWithCentre:(CGPoint)centreOfElipse
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise
completionBlock:(NodeCompletionBlock)handler {
float range;
if (clockwise == YES) {
if (endingDegrees <= startingDegrees) {
range = (360.0 + endingDegrees) - startingDegrees;
} else {
range = endingDegrees - startingDegrees;
}
} else {
if (endingDegrees >= startingDegrees) {
range = (360.0 + startingDegrees) - endingDegrees;
} else {
range = startingDegrees - endingDegrees;
}
}
__block float degrees = startingDegrees;
__block float radius = startingRadius;
const float incrementAngle = 10.0;
float intervals = (range / incrementAngle) - 1;
ccTime interval = duration / intervals;
float radiusStep = (endingRadius - startingRadius) / intervals;
if (clockwise == YES) {
degrees += incrementAngle;
} else {
degrees -= incrementAngle;
}
radius += radiusStep;
__block void (^moveToNextPoint)();
moveToNextPoint = [^(){
if (fabsf(degrees - endingDegrees) < 1.0) {
[operand runAction:[CCSequence actions:
[CCEaseBounceOut actionWithAction:
[CCMoveTo actionWithDuration:interval position:[self pointOnCircleWithCentre:centreOfElipse andRadius:radius atDegrees:degrees]]],
[CCCallBlock actionWithBlock:
^{
if (handler != nil) {
handler(operand);
}
}],
nil]];
} else {
[operand runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:interval position:[self pointOnCircleWithCentre:centreOfElipse andRadius:radius atDegrees:degrees]],
[CCCallBlock actionWithBlock:moveToNextPoint],
nil]];
if (clockwise == YES) {
degrees += incrementAngle;
if (degrees > 360.0) {
degrees = degrees - 360.0;
}
} else {
degrees -= incrementAngle;
if (degrees < 0.0) {
degrees = degrees + 360.0;
}
}
radius += radiusStep;
}
} copy];
[operand runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:initialDuration position:[self pointOnCircleWithCentre:centreOfElipse andRadius:startingRadius atDegrees:startingDegrees]],
[CCCallBlock actionWithBlock:moveToNextPoint],
nil]];
}
@end
ノードが移動する円弧が 10 度のステップに分割されていることに注意してください。これは、CCActionInterval サブクラスを記述せずに循環モーションを取得するために行われますが、ブロックまたはセレクターを使用してモーションを完了まで実行し続けることを意味します。
ここで、CCNode が完全な円を継続的に移動するようにするために、次のようにこの関数を呼び出します。
- (void) moveLayer:(CCNode*)layer
startingDegrees:(float)startingDegrees
endingAtDegrees:(float)endingDegrees
startingRadius:(float)startingRadius
endingRadius:(float)endingRadius
withInitialDuration:(ccTime)initialDuration
withMainDuration:(ccTime)duration
clockwise:(BOOL)clockwise {
[CocosUtil moveOperand:layer throughCircleWithCentre:CGPointMake(240.0, 160.0) startingDegrees:startingDegrees endingAtDegrees:endingDegrees startingRadius:startingRadius endingRadius:endingRadius withInitialDuration:initialDuration withMainDuration:duration clockwise:clockwise completionBlock:^(CCNode *sprite) {
[self moveLayer:layer startingDegrees:startingDegrees endingAtDegrees:endingDegrees startingRadius:startingRadius endingRadius:endingRadius withInitialDuration:initialDuration withMainDuration:duration clockwise:clockwise];
}];
}
ブロックをまったく渡さないなど、いくつかの異なることを試しましたが、関数をまったく使用しないことを除いて、保持を妨げるものは何もありません。
私が知る限り、XCode doco を読むと、次のようになります。
「メソッドの実装内でブロックを使用する場合、オブジェクト インスタンス変数のメモリ管理のルールはより微妙になります。
参照によってインスタンス変数にアクセスする場合、self は保持されます。インスタンス変数に値でアクセスすると、変数は保持されます。」
したがって、これは、関数内でブロックを使用することで、隠れた保持が発生していることを示しています。
後で stopAllActions を呼び出しても、リリースはトリガーされません。
これが機能する唯一の方法は、ノードの cleanup() メッセージに[self release]
.
保持を行うコードから分離されているため、これは好きではありません。
私が持っていた新しい考えの 1 つは、何らかの方法で新しい CCActionInterval サブクラスとして書き直すことですが、それで問題が解決するかどうかはまだわかりません。
助言がありますか?