How can I update x and y position of an object from the amount of tilt?
I'm trying to update the x and y position of my _bg object, based on the amount of a tilt movement.
Also, if the device is put down on a table, the position should go back to it's original position;
I'm trying to do something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_motionManager = [[CMMotionManager alloc] init];
[_motionManager startGyroUpdates];
_timer = [NSTimer scheduledTimerWithTimeInterval:1/30
target:self
selector:#selector(updateGyro)
userInfo:nil repeats:YES];
}
- (void)updateFromGyro
{
self.x += _motionManager.gyroData.rotationRate.x;
self.y += _motionManager.gyroData.rotationRate.y;
_bg.center = CGPointMake(self.x, self.y);
}
Problem is that the object doesn't stop moving, ever!
Thank you!
A rate is the amount of change per unit time. Thus you are setting the coordinates relative to how quickly the device is moving, not its actual offset. You may want to look into its attitude (its actual offset from an arbitrary frame of reference). The documentation is here.
This might be helpful. Not sure though based on the limited data available from the question. You should probably also switch to using absolute position/rotation, not relative change between frames.
Simply set a minimum threshold. This will prevent minute movements from showing as updates:
if( _motionManager.gyroData.rotationRate.x > ROTATION_MIN )
{
self.x += _motionManager.gyroData.rotationRate.x;
}
if( _motionManager.gyroData.rotationRate.y > ROTATION_MIN )
{
self.y += _motionManager.gyroData.rotationRate.y;
}
_bg.center = CGPointMake(self.x, self.y);
I think you are making mistake in setting new center. Try this :
- (void)updateFromGyro
{
self.x = _bg.center.x + _motionManager.gyroData.rotationRate.x;
self.y = _bg.center.y + _motionManager.gyroData.rotationRate.y;
_bg.center = CGPointMake(self.x, self.y);
}
By the way, your application will keep receiving gyro updates even when you put device on table, because table's slope is not guaranteed to be 0 degree.
Related
I am making a reaction game, where you can destroy enemys and earn points. Now I would like to have combo points if you destroy them fast and if there is a specific time gap the combo multiplier should go to zero again.
I would like to multiple the points like this: 2 * 2 = 4 * 2 = 8 * 2 = 16 * 2...
(you get 2 points if you destroy an enemy).
I add the points here:
if (CGRectIntersectsRect(enemy.frame, player.frame)) {
points = points + 1;
[enemy removeFromParent];
}
I could always multiply the current points with 2, but I want to reset the combo multiplier if there is specific amount of time without getting points.
I hope someone can help me.
(code in objective c please)
It seems no more complicated than recording the time the last enemy was destroyed and then in the update: method deciding if the combo has elapsed as no more enemies were hit in whatever timeout period you allow.
I am not familiar with Sprite kit, but the update appears to pass the current time; excellent. You will need to record the following:
timeout (time): The current timeout. This will reduce as the game progresses, making it harder.
lastEnemyKillTime (time): the time the last enemy was killed.
comboPoints (integer): How many points the user gets per hit. This will increase as the combo extends.
points (integer): The current score.
So, something like this:
#interface MyClass ()
{
NSTimeInterval _timeout;
NSTimeInterval _lastEnemyKillTime;
BOOL _comboFactor;
NSUInteger _points;
}
#end
I guess Sprite Kit uses an init: method; use it to initialize the variables:
- (id)init
{
self = [super init];
if (self != nil) {
_timeout = 1.0;
_lastEnemyKillTime = 0.0;
_points = 0;
_comboPoints = 1;
}
}
The update: method would be something like:
- (void)update:(NSTimeInterval)currentTime
{
BOOL withinTimeout = currentTime - _lastEnemyKillTime <= _timeout;
if (CGRectIntersectsRect(enemy.frame, player.frame)) {
_inCombo = withinTimeout;
if (_inCombo)
_comboPoints *= 2;
_points += _comboPoint;
_lastEnemyKillTime = currentTime;
[enemy removeFromParent];
} else if (_comboPoints > 1 && !withinTimeout) {
_lastEnemyKillTime = 0.0;
_comboPoints = 1;
}
}
You need to keep track on the last enemy casual timestamp and the factor. When the next kill is processed, you check the timestamp, if it is below threshold, you raise the factor. The time of the current kill replaces the timestamp.
You could create a FightRecorder class as singleton, if you don't have a better place yet (services or sth).
NSDate *newKillTime = new NSDate;
FightRecorder recorder = [FightRecorder instance];
if([newKillTime timeIntervalSinceDate:recorder.lastKillTime] < SCORE_BOUNDS_IN_SEC) {
recorder.factor++; // could also be a method
points = points + [recorder calculateScore]; // do your score math here
}
else {
[recorder reset]; // set the inner state of the fight recorder to no-bonus
}
recorder.lastKillTime = newKillTime; // record the date for the next kill
The only way I can think to do it is to check velocities for all physics bodies during every collisions.
- (BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair piece:(CCNode *)pieceA piece:(CCNode *)pieceB{
float x = 0;
float y = 0;
for (int i = 0; i < [[_physicsWorld children] count]; i++) {
x = x + [[[_physicsWorld children][i] physicsBody] velocity].x;
y = y + [[[_physicsWorld children][i] physicsBody] velocity].y;
}
if ( x == 0 && y == 0 ) {
NSLog(#"stopped");
}
return YES;
}
This logs “stopped” multiple times when the scene first loads, then doesn’t log “stopped” again, even after physics bodies have clearly started moving and colliding and then come to a stop.
Ideally I'd like a delegate method that would notify me when all physics bodies have stopped moving, but I can't seem to find one.
FYI: I'm using the standard Chipmunk physics engine that's baked into Cocos2d V3.0
Chipmunk has a internal mechanism, which can, if activated, automatically deactivate physics bodies. My approach (I am using cocos2dx 3.11.1 and not -obj version with chipmunk 7.0.1) is:
activate the chipmunk idle mechanism (0.5 second - meaning, if an object is not moving for longer than 0.5 second it will be deactivated):
cpSpaceSetSleepTimeThreshold(space, 0.5f);
You do not need to use
cpSpaceSetIdleSpeedThreshold(space, <speed>);
because chipmunk calculates the threshold speed for you (according the gravitation used).
use this code for determination if all objects are not moving (static and kinetic bodies never sleep):
bool isAnyPhysicsBodyMoving(){
int i = 0; bool isMoving = false;
const Vector<PhysicsBody*>& bodies = getPhysicsWorld()->getAllBodies();
while( i < bodies.size() && !isMoving){
PhysicsBody *body = bodies.at(i);
isMoving = cpBodyGetType(body->getCPBody()) == CP_BODY_TYPE_DYNAMIC
&& !body->isResting();
i++;
}
return isMoving;
}
use static (and not kinetic) body for walls, in order to let objects sleep:
// wall
Size visibleSize = Director::getInstance()->getWinSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
float border = 10.0f;
Size wallBodySize = Size(visibleSize.width+2*border, visibleSize.height+2*border);
PhysicsBody *wallBody = PhysicsBody::createEdgeBox(wallBodySize, PhysicsMaterial(1.0f, 1.0f, 0.5f), border);
Node* wall = Node::create();
wall->addComponent(wallBody);
wall->setAnchorPoint(Vec2(0.5f, 0.5f));
wall->setPosition(Point(visibleSize.width/2+origin.x, visibleSize.height/2+origin.y));
cpVect tt;
tt.x = wall->getPosition().x; tt.y = wall->getPosition().y;
//set position manually and BEFORE adding the object into the space
cpBodySetPosition(wallBody->getCPBody(), tt);
cpBodySetType(wallBody->getCPBody(), CP_BODY_TYPE_STATIC);
addChild(wall);
Any dynamic body connected to a kinetic body (for example laying on) will never sleep.
test it with DEBUG activated
getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
the boxes (their content) must become grey (=sleeping) and not red (=active):
In order to let it work, I have:
added an access method (to get cpSpace) in CCPhysicsWorld.h:
inline cpSpace* getSpace() const { return _cpSpace; }
Fix call of
cpBodySetTorque(body, 0.0f);`
in CCPhysicsBody.cpp to
if (body->t != 0.0f){
cpBodySetTorque(body, 0.0f);
}
Fix call of
cpBodySetPosition(_cpBody, tt);`
in CCPhysicsBody.cpp to
if (!cpveql(tt, cpBodyGetPosition(_cpBody))){
cpBodySetPosition(_cpBody, tt);
}
Steps 2. and 3. are necessary to avoid setting of the same physics body properties, which wake up a sleeping body.
The advantage of this approach is, that the chipmunk does not make any calculations for such physical bodies - saving CPU and battery.
I found something that works.
tl;dr
The basic idea is to keep track of the positions of the sprites myself, and then periodically check them to see if any of them have moved since they were last checked.
Longer version
I created a subclass of CCNode with the class name Piece.
These are my objects that are added to the physics world.
#implementation Piece {
float _previousX;
float _previousY;
}
-(void)updatePreviousScreenXandY{
_previousX = self.position.x;
_previousY = self.position.y;
}
-(BOOL)hasntMoved{
float currentX = self.position.x;
float currentY = self.position.y;
if ( currentX == _previousX && currentY == _previousY ) {
return TRUE;
}else{
return FALSE;
}
}
This is in my CCNode that acts as the game scene
-(void)doStuffAfterPiecesStopMoving:(NSTimer*)timer{
BOOL noPiecesHaveMoved = TRUE;
for (int i = 0; i < [[_physicsWorld children] count]; i++) {
if ( [[_physicsWorld children][i] hasntMoved] == FALSE ) {
noPiecesHaveMoved = FALSE;
break;
}
}
if ( noPiecesHaveMoved ) {
[timer invalidate];
NSLog(“Pieces have stopped moving”);
}else{
NSLog(“Pieces are still moving”);
[self updateAllPreviousPiecePositions];
}
}
-(void)updateAllPreviousPiecePositions{
for (int i=0; i < [[_physicsWorld children] count]; i++) {
Piece *piece = (Piece*)[_physicsWorld children][i];
[piece updatePreviousScreenXandY];
}
}
All I have to do is
[NSTimer scheduledTimerWithTimeInterval:TIME_BETWEEN_CHECKS
target:_gamePlay
selector:#selector(doStuffAfterPiecesStopMoving:)
userInfo:nil
repeats:YES];
and it’ll run whatever code I want after all Piece nodes have stopped moving.
The key to getting it to work well is to get the values for the Chipmunk space’s sleepTimeThreshold and the timer above’s time as low as possible.
My experimenting suggests the following settings work okay, but anything lower will cause problems (i.e. collisions not taking place properly):
sleepTimeThreshold = 0.15
my timer = 0.05
If anyone has a different/better solution or improvements to the above code, please post.
I'm working on a Spritekit Tower Defence game. ARC is enabled. (And I intend to run this code in the background, though presently it's just running on the main thread.)
In my update loop (which is running up to 60 times a second) I call a method called getTargetsForTowers. After profiling this method, I've found two items in the list that are chewing up my CPU time: objc_object::sidetable_retain/release, and I'm trying to find out what they are.
I'd like to understand more about what this is and if I can improve performance by reducing them or getting rid of them altogether.
There are 300 enemies and 446 towers in my test scenario. The majority of the CPU time is reported in the tower loop.
- (void)getTargetsForTowers {
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
BOOL hasTarget = tower.hasTarget;
BOOL passes = !hasTarget;
if (!passes) {
CCUnit *tg = tower.target;
float tdte = tg.distanceToEnd;
passes = edte < tdte;
}
if (passes) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
}
}
}
}
}
}
}
Screenshots from Time Profile (after 60 seconds of running):
image one http://imageshack.com/a/img22/2258/y18v.png
image two http://imageshack.com/a/img833/7969/7fy3.png
(I've been reading about blocks, arc, strong/weak references, etc., so I tried making the variables (such as CCSKTower *tower) __weak, which did get rid of those two items, but that added a whole bunch of new items related to retaining/creating/destroying the weak variables, and I think they consumed more CPU time than before.)
I'd appreciate any input on this. Thanks.
EDIT:
There's another method that I would like to improve as well which is:
- (NSArray *)getTilesForEnemy:(CCUnit *)enemy {
NSMutableArray *tiles = [[NSMutableArray alloc] init];
float enemyWidthHalf = enemy.size.width/2;
float enemyHeightHalf = enemy.size.height/2;
float enemyX = enemy.position.x;
float enemyY = enemy.position.y;
CGVector topLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector topRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY+enemyHeightHalf)];
CGVector bottomLeft = [self getVectorForPoint:CGPointMake(enemyX-enemyWidthHalf, enemyY-enemyHeightHalf)];
CGVector bottomRight = [self getVectorForPoint:CGPointMake(enemyX+enemyWidthHalf, enemyY-enemyHeightHalf)];
CCTileInfo *tile = nil;
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
int xIndex = (int)(x+gameHalfCols);
int yIndex = (int)(y+gameHalfRows);
tile = tileGrid[xIndex][yIndex];
if (tile != nil) {
[tiles addObject:tile];
}
}
}
}
}
return tiles;
}
I've looked over it repeatedly and there's nothing I really can see. Perhaps there's nothing more that can be done.
Screenshots:
One issue is that you create a new reference to tower.target, but only use that reference once. So simply rewriting that section should improve your performance, e.g.
if (!passes) {
float tdte = tower.target.distanceToEnd;
passes = edte < tdte;
}
Based on your comment, it seems that there's no way to avoid a retain/release if you access a property on tower.target. So let's try radical surgery. Specifically, try adding a distanceToEnd property to the tower, to keep track of the distanceToEnd for the tower's current target. The resulting code would look like this.
- (void)getTargetsForTowers {
// initialization to copy 'distanceToEnd' value to each tower that has a target
for ( CCSKTower *tower in towersOnMap )
if ( tower.hasTarget )
tower.distanceToEnd = tower.target.distanceToEnd;
NSArray *enemiesCopy = [enemiesOnMap copy];
for (CCUnit *enemy in enemiesCopy) {
float edte = enemy.distanceToEnd;
CGPoint enemyPos = enemy.position;
[self calculateTravelDistanceForEnemy:enemy];
if (enemy.actualHealth > 0) {
NSArray *tiles = [self getTilesForEnemy:enemy];
for (CCTileInfo *tile in tiles) {
NSArray *tileTowers = tile.towers;
for (CCSKTower *tower in tileTowers) {
if ( !tower.hasTarget || edte < tower.distanceToEnd ) {
BOOL inRange = [self circle:tower.position withRadius:tower.attackRange collisionWithCircle:enemyPos collisionCircleRadius:1];
if (inRange) {
tower.hasTarget = YES;
tower.target = enemy;
tower.distanceToEnd = edte; // update 'distanceToEnd' on the tower to match new target
}
}
}
}
}
}
}
My impression is that there's not much to be done about the getTilesForEnemy method. Looking at the Running Time image for getTilesForEnemy it's clear that the load is fairly evenly spread among the various components of the method, with only three items above 10%. The top item getVectorForPoint isn't even in the innermost loop. The second item insertObject is apparently the result of the addObject call in the inner loop, but there's nothing to be done for that call, it's required to generate the final result.
At the next level up (see the wvry.png image), you can see that getTilesForEnemy is now 15.3% of the total time spent in getTargetsForTowers. So even if it were possible to reduce getVectorForPoint from 17.3% to 7.3% there would not be a significant reduction in running time. The savings in getTilesForEnemy would be 10%, but because getTilesForEnemy is only 15.3% of the time in getTargetsForTowers, the overall savings would only be 1.53%.
Conclusion, because the components of getTilesForEnemy are balanced and below 20%, and because getTilesForEnemy is only 15.3% of the higher level method, no significant savings will be gained by trying to optimize getTilesForEnemy.
So once again the only option is radical surgery, and this time I mean a total rewrite of the algorithm. Such action should only be taken if the app still isn't performing up to spec. You've run into the limitations of ARC and NSArray's. Both of those technologies are extremely powerful and flexible, and are perfect for high-level development. However, they both have significant overhead which limits performance. So the question becomes, "How do you write the getTargetsForTowers without using ARC and NSArray's?". The answer is to use arrays of C structs to represent the objects. The resulting top level pseudo code would be something like this
copy the enemy information into an array of C structs
copy the tower information into an array of C structs
(note that the target for a tower is just an 'int', which is the index of an enemy in the enemy array)
for ( each enemy in the enemy array )
{
create an array of C structs for the tiles
for ( each tile )
for ( each tower in the tile )
update the tower target if needed
}
copy the updated tower information back into the NSArray of tower objects
For your second method, this part seems unclear and inefficient:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
For instance, there's no point in spinning the y loop if your x is out of bounds. You could just do this:
for (float x = topLeft.dx; x < bottomRight.dx+1; x++) {
if (x > -(gameHalfCols+1) && x < gameHalfCols) {
for (float y = bottomLeft.dy; y < topRight.dy+1; y++) {
if (y < gameHalfRows && y > -(gameHalfRows+1)) {
More importantly, the point of the first for loop is to start x at some minimum and increment it to some maximum, and the if statement is there to make sure x is at least some minimum and less than some maximum, so there's no reason to have both a for() and an if(). I don't know what the values might look like for topLeft.dx and gameHalfCols, so I can't tell you the best way to do this.
But, for example, if topLeft.dx is always integral, you might say:
for (float x = MAX(topLeft.dx, ceil(-(gameHalfCols+1))); x < bottomRight.dx+1 && x < gameHalfCols; x++) {
for (float y = ...
You could similarly improve the 'y' for this way. This sin't just fewer lines of code, it also prevents the loops from spinning a bunch of extra times with no effect: the 'if' statements just make the loops spin quickly to their ends, but including the logic inside the 'for's themselves makes them only loop over values that you'll actually use in computations.
To expand my comments to a complete answer:
The normal, correct Objective-C behaviour when returning an object property is to retain and then autorelease it. That's because otherwise code like this (imagine you're in the world before ARC):
TYTemporaryWorker *object = [[TYTemporaryWorker alloc] initWithSomeValue:value];
NSNumber *someResult = object.someResult;
[object release];
return someResult;
would otherwise be invalid. object has been deallocated so if someResult hasn't been retained and autoreleased then it will become a dangling pointer. ARC makes this sort of slightly less direct (the strong reference in someResult would have retained the number beyond the lifetime of object but then it would have been autoreleased for the return) but the principle remains and, in any case, whether an individual .m file has been compiled with ARC is not supposed to affect callers.
(aside: notice that weak isn't just strong without retains — is has related costs because the runtime has to establish a link from the object to the weak reference in order to know find it again and nil it if the object begins deallocation)
Supposing you wanted to create a new type of property that isn't strong and isn't unsafe_unretained but is rather defined to be that the object returned is safe for use for as long as the original owner is alive but unsafe afterwards. So it's a strong set but an unsafe_unretained get.
It's untested but I think the correct means to do that would be:
// we can't write want a synthesised getter that doesn't attempt to retain
// or autorelease, so we'd better flag up the pointer as potentially being
// unsafe to access
#property (nonatomic, unsafe_unretained) NSNumber *someResult;
...
#implementation TYWhatever
{
NSNumber *_retainedResult; // a strong reference, since
// I haven't said otherwise —
// this reference is not publicly exposed
}
- (void)setSomeResult:(NSNumber *)number
{
// set the unsafe and unretained version,
// as the default setter would have
_someResult = number;
// also take a strong reference to the object passed in,
// to extend its lifecycle to match ours
_retainedResult = number;
}
It's going to get quite verbose as you add more properties but what you're doing is contrary to normal Objective-C conventions so limited compiler help is probably to be expected.
This is language-agnostic question, more about model of my game.
I have a snake game with elements, but I move the elements smoothly, they don't just move 1 block each time, but instead they move some amount of pixels every frame.
I have an update loop that calculates the positions of the element, but I am stuck on correct calculations.
I have heading for each element:
typedef NS_ENUM(int, kElementHeading)
{
kElementHeadingNorth = 1,
kElementHeadingSouth,
kElementHeadingEast,
kElementHeadingWest
};
I also have velocity (x, y) that determines in what direction snake is going. I have problem with snake movement, because my elements are in wrong positions. I managed to localize the thing for 2 elements, but my solution fails on more elements.
First solution I tried is to save point of rotation where the head changes direction. This worked, but due to different circumstances element can move different amount of pixels each turn. Often the element would skip the point. I tried increasing the zone where it should rotate, but it adds up error. I tried fixing this error, but element would still separate from snake (quite often).
On the second try I decided to keep the snake head in center of the screen and move the world around it. It worked good for 2 elements, as I just smoothly move the next element to desired position relatively to head. But this fails badly on more elements. If you make fast turns they start dancing and not following the path.
Third thing that I tried is leaving a path for other elements to follow. But that didn't work because I intend to keep my snake on center of the screen and technically it never moves to create a path.
I'm looking to replicate the movement pattern like in Nimble Quest (or any snake).
How should I implement snake elements moving to have no errors?
Here is my code for the first method, problem with it is that often the elements would fall off. The code is pretty self-explanatory. Rotation points are the places where to change direction.
CFTimeInterval delta = self.lastTime - currentTime;
CGPoint currentPosition = self.playerSnake.head.sprite.position;
CGPoint velocity = self.playerSnake.velocity;
self.playerSnake.head.sprite.position = CGPointMake(currentPosition.x + velocity.x * delta * CONSTANTSPEEDFACTOR , currentPosition.y + velocity.y * delta * CONSTANTSPEEDFACTOR);
for (SnakeElement *element in self.playerSnake.elements) {
CGPoint currentPositionE = element.sprite.position;
CGPoint velocityE = element.velocity;
element.sprite.position = CGPointMake(currentPositionE.x + velocityE.x * delta * CONSTANTSPEEDFACTOR , currentPositionE.y + velocityE.y * delta * CONSTANTSPEEDFACTOR);
}
BOOL markToDelete = NO;
NSDictionary *deleteDictionary;
for (NSDictionary *dict in self.playerSnake.rotationPoints) {
CGPoint positionCoordinate = CGPointFromString(dict[#"position"]);
CGPoint velocityNew = CGPointFromString(dict[#"velocity"]);
double newAngle = [dict[#"angle"] doubleValue];
for (SnakeElement *element in self.playerSnake.elements) {
int xDifference = element.sprite.position.x - positionCoordinate.x;
int yDifference = element.sprite.position.y - positionCoordinate.y;
if ((xDifference > -2 && xDifference < 2) && (yDifference > -2 && yDifference < 2) ) {
element.velocity = velocityNew;
element.sprite.position = CGPointMake(element.sprite.position.x + xDifference, element.sprite.position.y + yDifference);
SKAction *action = [SKAction rotateToAngle:newAngle duration:0.2 shortestUnitArc:YES];
[element.sprite runAction:action];
if ([element isEqual:[self.playerSnake.elements lastObject]]) {
markToDelete = YES;
deleteDictionary = dict;
}
}
}
}
[self.playerSnake.rotationPoints removeObject:deleteDictionary];
If I try increase the catch zone for the turning point, the elements tend to fall off more often then when it is 1 or 2 pixels wide. I'm not sure why this happens.
This is what I was suggesting you do in the comments in terms of handling your turning on points :
1.. calculate the distance that the element should move that frame based on speed and your elapsed time since last frame. (delta)
2.. calculate distance from element's current position to the turn point. This is the beforeDistance I spoke of in the comments
3.. calculate the distance the element should move towards the NEW target turning point AFTER the turn
afterDistance = distanceToMoveThisFrame - beforeDistance
4.. Calculate the new position for your element, starting at the current turning point towards the next target turning point of the element using afterDistance
If you follow this logic, you will NEVER overshoot or undershoot the turning point.
I'm trying to make my first game using Spritekit, so i have a sprite that i need to move around using my accelerometer. Well, no problem doing that; movement are really smooth and responsive, the problem is that when i try to rotate my sprite in order to get it facing its own movement often i got it "shaking" like he has parkinson. (:D)
i did realize that this happens when accelerometer data are too close to 0 on one of x, y axes.
So the question: Is there a fix for my pet parkinson?? :D
Here is some code:
-(void) update:(NSTimeInterval)currentTime{
static CGPoint oldVelocity;
//static CGFloat oldAngle;
if(_lastUpdatedTime) {
_dt = currentTime - _lastUpdatedTime;
} else {
_dt = 0;
}
_lastUpdatedTime = currentTime;
CGFloat updatedAccelX = self.motionManager.accelerometerData.acceleration.y;
CGFloat updatedAccelY = -self.motionManager.accelerometerData.acceleration.x+sinf(M_PI/4.0);
CGFloat angle = vectorAngle(CGPointMake(updatedAccelX, updatedAccelY));
_velocity = cartesianFromPolarCoordinate(MAX_MOVE_PER_SEC, angle);
if(oldVelocity.x != _velocity.x || oldVelocity.y != _velocity.y){
_sprite.physicsBody.velocity = CGVectorMake(0, 0);
[_sprite.physicsBody applyImpulse:CGVectorMake(_velocity.x*_sprite.physicsBody.mass, _velocity.y*_sprite.physicsBody.mass)];
_sprite.zRotation = vectorAngle(_velocity);
oldVelocity = _velocity;
}
}
static inline CGFloat vectorAngle(CGPoint v){
return atan2f(v.y, v.x);
}
i did try to launch the update of the _velocity vector only when updatedAccelX or updatedAccelY are, in absolute value >= of some values, but the result was that i got the movement not smooth, when changing direction if the value is between 0.1 and 0.2, and the problem wasn't disappearing when the value was under 0.1.
i would like to maintain direction responsive, but i also would like to fix this "shake" of the sprite rotation.
I'm sorry for my bad english, and thanks in advance for any advice.
You can try a low pass filter (cf. to isolate effect of gravity) or high pass filter (to isolate effects of user acceleration).
#define filteringFactor 0.1
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
//low pass
accelerX = (acceleration.x * filteringFactor) + (accelerX * (1.0 - filteringFactor));
//idem … accelerY
//idem … accelerZ
//or high pass
accelerX = acceleration.x - ( (acceleration.x * filteringFactor) + (accelerX * (1.0 - filteringFactor)) );
//idem … accelerY
//idem … accelerZ
}