Cocos2D getting progress of CCAction - iphone

I have a Cocos2D game with Box2D physics. In my GameScene.mm, I'm working on a method to zoom to a given scale:
-(void) zoomToScale:(float)zoom withDuration:(ccTime)duration
{
id action = [CCScaleTo actionWithDuration:duration scale:zoom];
[scrollNode runAction:action];
currentZoomLevel = zoom;
}
The problem that I'm having is that currentZoomLevel (which is used in the Scene's update() method) is set to the zoom immediately, and isn't gradually adjusted as per the animation. So while the animation is in progress, the currentZoomLevel variable is totally wrong.
I'm trying to figure out a way to have the currentZoomLevel variable match the progress of the animation as it's happening. According to the CCAction API Reference, the CCAction's update method takes a ccTime that's between 0 and 1 based on the progress of the animation (0 is just started, 1 is just finished).
How can I access this ccTime from outside of the action? I want to have something in my Scene's update method like this:
if(animating)
{
float progress = [action getProgress]; // How do I do this?
// Do math to update currentZoomLevel based on progress
}
Am I missing something obvious here, or am I going to have to subclass CCScaleTo?

You should be able to access the scale directly as it animates.
instead of
float progress = [action getProgress];
try
float current_scale = some_node.scale ;
where "some_node" is the thing you're animating/scaling.

Actually, your best bet is to use the new Cocos2D extension "CCLayerPanZoom", which handles all of this marvellously for you! It should be part of any new cocos2D install (v.1.0+).

Related

SKSpriteNode with physicsBody auto correct orientation

What would be the best way to make a Swift SKSpriteNode auto correct its orientation after it has had a couple of physicsBody knocks and is now not in the default orientation?
For example I have have a 2D stick-man who gets knocked to the side by another object and would like the stick-man to tilt and reorient itself to the default standing position.
The way I have done this in the past is to wait until the physics body comes to rest, turn off the physics engine for the stickman, reorient the stickman, turn the physics engine back on again.
Write yourself an action called standupAction which animates the stickman's reorientation, maybe just a rotate or a jump up. Then do something like the following.
Sorry this Objective-C not Swift, don't know swift, but should be easy to translate.
if (stickman.physicsBody.resting) {
stickman.physicsBody.dynamic = NO;
[stickman runAction:[SKAction sequence:#[
standupAction,
[SKAction runBlock:^{ stickman.physicsBody.dynamic = YES;}]
]]];
}
The only downside of this approach is that sometimes it can take a full second until the stickman comes comes to rest. Box2d takes quite a long time to settle down. But it looks very realistic if you wait.
Alternatively, you could just check the orientation of stickman and if he is >45degrees off balance then reorient him with impulses. I find impulses to be very difficult to know how much force to apply when you need precise movement like this.
Just to update, I couldn't afford to set the physicsBody to not be dynamic.
My scene updates each node, similarly to Apple's Adventure game, as such I implemented the rotation in the subclassed SKSpriteNode's update method as such:
if (isTouchingGround) {
if self.zRotation != 0 && !rotating {
let actions = [
SKAction.runBlock {
self.rotating = true
},
SKAction.rotateToAngle(0, duration: 1.0),
SKAction.runBlock {
self.rotating = false
}]
runAction(SKAction.sequence(actions))
}
}

CoreGraphics prevents frame modifications on other objects?

Just getting started with iOS development, so please forgive my ignorance here. I've also searched for a while without success on this topic, but I'm sure I'm just not searching the right terms.
If I comment out the only line in this first for loop, the next for loop seems to function exactly how I expect. If I leave them both in then, I only see the CG stuff happening and the other objects sit still.
What does the transformation on the object currentGear have to do with the frame being changed on another object within the same view? Why would performing the transformation invalidate the frame change after it?
for (UIImageView *currentGear in self.imageGearCollection)
{
currentGear.transform = CGAffineTransformRotate(currentGear.transform, (90*M_PI)/180);
}
for (UIButton *currentCrate in self.buttonCrateCollection)
{
CGRect rectFrame = currentCrate.frame;
rectFrame.origin.x += 10;
currentCrate.frame = rectFrame;
}
Your question doesn't really have anything to do with Core Graphics.
The UIView Class Reference says this about frame:
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
So what you're doing is not really allowed.
Since you're just trying to move the view, not change its size, you can do that by modifying its center property instead:
CGPoint center = currentCrate.center;
center.x += 10;
currentCrate.center = center;

Unity3D OffNavMesh jump issue

I have set up Unity navigation meshes (four planes), navigation agent (sphere) and set up automatic and manual off mesh links. It should now jump between meshes. It does jump between meshes, but it does that in straight lines.
In other words, when agent comes to an edge, instead of actually jumping up (like off mesh link is drawn) it just moves straight in line but a bit faster. I tried moving one plane higher than others, but sphere still was jumping in straight line.
Is it supposed to be like this? Is it possible to set up navigation to jump by some curve? Or should I try to implement that myself?
I came by this question, and had to dig through the Unity sample. I just hope to make it easier for people by extracting the important bits.
To apply your own animation/transition across a navmesh link, you need to tell Unity that you will handle all offmesh link traversal, then add code that regularly checks to see if the agent is on an offmesh link. Finally, when the transition is complete, you need to tell Unity you've moved the agent, and resume normal navmesh behaviour.
The way you handle link logic is up to you. You can just go in a straight line, have a spinning wormhole, whatever. For jump, unity traverses the link using animation progress as the lerp argument, this works pretty nicely. (if you're doing looping or more complex animations, this doesn't work so well)
The important unity bits are:
_navAgent.autoTraverseOffMeshLink = false; //in Start()
_navAgent.currentOffMeshLinkData; //the link data - this contains start and end points, etc
_navAgent.CompleteOffMeshLink(); //Tell unity we have traversed the link (do this when you've moved the transform to the end point)
_navAgent.Resume(); //Resume normal navmesh behaviour
Now a simple jump sample...
using UnityEngine;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshAnimator : MonoBehaviour
{
private NavMeshAgent _navAgent;
private bool _traversingLink;
private OffMeshLinkData _currLink;
void Start()
{
// Cache the nav agent and tell unity we will handle link traversal
_navAgent = GetComponent<NavMeshAgent>();
_navAgent.autoTraverseOffMeshLink = false;
}
void Update()
{
//don't do anything if the navagent is disabled
if (!_navAgent.enabled) return;
if (_navAgent.isOnOffMeshLink)
{
if (!_traversingLink)
{
//This is done only once. The animation's progress will determine link traversal.
animation.CrossFade("Jump", 0.1f, PlayMode.StopAll);
//cache current link
_currLink = _navAgent.currentOffMeshLinkData;
//start traversing
_traversingLink = true;
}
//lerp from link start to link end in time to animation
var tlerp = animation["Jump"].normalizedTime;
//straight line from startlink to endlink
var newPos = Vector3.Lerp(_currLink.startPos, _currLink.endPos, tlerp);
//add the 'hop'
newPos.y += 2f * Mathf.Sin(Mathf.PI * tlerp);
//Update transform position
transform.position = newPos;
// when the animation is stopped, we've reached the other side. Don't use looping animations with this control setup
if (!animation.isPlaying)
{
//make sure the player is right on the end link
transform.position = _currLink.endPos;
//internal logic reset
_traversingLink = false;
//Tell unity we have traversed the link
_navAgent.CompleteOffMeshLink();
//Resume normal navmesh behaviour
_navAgent.Resume();
}
}
else
{
//...update walk/idle animations appropriately ...etc
Its recommended to solve your problems via animation. Just create a Jump animation for your object, and play it at the correct time.
The position is relative, so if you increase the Y-position in your animation it will look like the object is jumping.
This is also how the Unity sample is working, with the soldiers running around.
Not sure what version of unity you are using but you could also try this, I know it works just fine in 4:
string linkType = GetComponent<NavMeshAgent>().currentOffMeshLinkData.linkType.ToString();
if(linkType == "LinkTypeJumpAcross"){
Debug.Log ("Yeah im in the jump already ;)");
}
also just some extra bumf for you, its best to use a proxy and follow the a navAgent game object:
Something like:
AIMan = this.transform.position;
AI_Proxy.transform.position = AIMan;
And also be sure to use:
AI_Proxy.animation["ProxyJump"].blendMode = AnimationBlendMode.Additive;
If you are using the in built unity animation!
K, that's my good deed for this week.
Fix position in update()
if (agent.isOnOffMeshLink)
{
transform.position = new Vector3(transform.position.x, 0f, transform.position.z);
}

Best way to go about making ccTime function just for a sprite's movement?

I'm trying to make a simple game and I am currently trying to make an arrow shoot out.
So far I have two functions,
-(void)ccTouchesBegan
Touches began does some math and gets a vector from the initial shooting point of arrow and the thumbpress, then passes this data into another function:
-(void)shatArrow:(CGPoint)cl:(CGPoint)nv{
}
What I want is for the shatArrow function to call a ccTime function that runs solely for the purpose of making the arrow move, and once the arrow is done with it's projection, the ccTime function will stop, and can be called again later when needed.
How would I go about this?
Schedule the update selector (ie in the init method of your class):
[self scheduleUpdate];
Then implement the update method:
-(void) update:(ccTime)delta
{
if (isArrowMoving)
{
// arrow move code here
if (arrow movement should end)
{
isArrowMoving = NO;
}
}
}
You can keep the update method running, unless you have hundreds of arrows it won't affect performance.
-(void) shootArrow:(CGPoint)cl:(CGPoint)nv
{
isArrowMoving = YES;
// other arrow movement init code here
}
Btw, it's "shoot, shot, shot" and not "shoot, shot, shat" or something like that. (I suppose you didn't ask for a #LinguisticsOverflow answer) ;)

Placing camera with animation

I want to know that how to place camera near and far (By increasing and decreasing value of eyeZ self.camera setEyeX:0 eyeY:0 eyeZ:1]; self.camera setEyeX:0 eyeY:0 eyeZ:180];) including animation (for smoothness), as normally it provide jerky zooming.
My suggestion is creating your own subclass of CCActionInterval, say CCCameraZoomAnimation and override its update method. The main advantage of having an action, aside from being able to control the camera movement finely, is also the possibility of using this action through CCEaseOut/CCEaseIn (etc.) to obtain nice graphical effects.
CCCameraZoomAnimation would have the node whose camera you want to modify as a target and another parameter to the constructor specifying the final Z value.
#interface CCActionEase : CCActionInterval <NSCopying>
{
CCActionInterval * other;
}
/** creates the action */
+(id) actionWithDuration:(ccTime)t finalZ:(float)finalZ;
/** initializes the action */
-(id) initWithDuration:(ccTime)t finalZ:(float)finalZ;
#end
The update method is called with an argument dt which represents the elapsed time since the start of the action and would allow you to easily calculate the current Z position:
-(void) update: (ccTime) t
{
// Get the camera's current values.
float centerX, centerY, centerZ;
float eyeX, eyeY, eyeZ;
[_target.camera centerX:&centerX centerY:&centerY centerZ:&centerZ];
[_target.camera eyeX:&eyeX eyeY:&eyeY eyeZ:&eyeZ];
eyeZ = _intialZ + _delta * t //-- just a try at modifying the camera
// Set values.
[_target.camera setCenterX:newX centerY:newY centerZ:0];
[_target.camera setEyeX:newX eyeY:newY eyeZ:eyeZ];
}
You would also need to implement copyWithZone:
-(id) copyWithZone: (NSZone*) zone
{
CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] finalZ:_finalZ];
return copy;
}
and make use of startWithTarget to
-(void) startWithTarget:(CCNode *)aTarget
{
[super startWithTarget:aTarget];
_initialZ = _target.camera....; //-- get the current value for eyeZ
_delta = ccpSub( _finalZ, _initialZ );
}
Nothing more, nothing less.
Sorry if copy/paste/modify produced some bugs, but I hope that the general idea is clear.
if you increase the 'z' by 180 from the off, you are bound to get a jerky animation, try running this in an animation context loop,increasing the value over a period of time will allow you to have a smooth 'zoom'.