Increase Character speed from a Joystick Input? - ios

Now i have a working Joystick, but I want to increase the characters speed.
I´m using this one at the moment: http://roadtonerdvana.wordpress.com/2013/09/20/jcinput-a-simple-joystick-for-sprite-kit/
I attached an Imagefile as the moving object, but it´s way too slow for my purpose.
Where and how can I change the speed?

If you read the post you'll know that the joystick returns a x and y value, each ranging from -1 to 1.
Every time you move the joystick, the properties x and y of it change accordingly, the maximum value is 1 and the minimum is -1.
In the update method, you could do something like this to adjust the speed :
-(void)update:(CFTimeInterval)currentTime {
// I'm using the magic number of 5 as an example of how to magnify the speed x5
float speedX = 5 * self.joystick.x;
float speedY = 5 * self.joystick.y;
[self.myLabel setPosition:CGPointMake(self.myLabel.position.x+speedX, self.myLabel.position.y+speedY)];
}

Related

Spawning Sprites In Space Invaders

I'm currently trying to spawn aliens for a space invaders game I'm creating in class. I'm using a while loop with a counter to adjust an array to a given variable value. This way I can increase the number of aliens without any re-writing. The problem is, although my aliens spawn, the x position is not increasing as I would like it to. I only see one Alien on the screen so I've concluded that they are all spawning, but only with a 1-pixel difference, therefore unnoticeable. Here's what I have so far, any help would be greatly appreciated!
//Add and display given amount of aliens...
while (alienAmount > displayLoopCounter) {
aliens.append(SKSpriteNode(texture: SKTexture(imageNamed: "ClassicAlien")))
self.addChild(aliens[displayLoopCounter])
//Location
aliens[displayLoopCounter].position.y = CGFloat(-15)
aliens[displayLoopCounter].position.x = CGFloat(displayLoopCounter + 25)
print(aliens[displayLoopCounter].position.x)
displayLoopCounter += 1
print(displayLoopCounter)
//Have we run out of aliens yet?
if displayLoopCounter > alienAmount {
displayAliens = false
}
You just need to change the spacing you're setting between aliens. In other terms, you need to set the x position of each alien sprite to have more spacing between them. Currently, you are just adding displayLoopCounter to the x position of each alien. Since displayLoopCounter only increases one at a time, the aliens are all spawned with a 1-pixel difference to each other. If we want a bigger difference, we will need to multiply displayLoopCounter by our intended spacing so that we get that spacing between each alien.
You can just set a spacingBetweenAliens variable with a numeric type (such as Int or CGFLoat or Double) and change this line:
aliens[displayLoopCounter].position.x = CGFloat(displayLoopCounter + 25)
To this:
let basePosition = displayLoopCounter * spacingBetweenAliens
aliens[displayLoopCounter].position.x = CGFloat(basePosition + 25)
This way, the aliens will be spawned with the value of spacingBetweenAliens in pixels between them.

Animating rotation changes of UIImageView

I'm making an app that (among other things) displays a simplified compass image that rotates according to the device's rotation. The problem is that simply doing this:
float heading = -1.0f * M_PI * trueHeading / 180.0f; //trueHeading is always between 0 and 359, never 360
self.compassNeedle.transform = CGAffineTransformMakeRotation(heading);
inside CLLocationManager's didUpdateHeading method makes the animation ugly and choppy.
I have already used Instruments to find out whether its simply my app not being able to render at more than 30-48 fps, but that's not the case.
How can I smooth out the image view's rotation so that it's more like Apple's own Compass app?
Instead of using the current instant value, try using the average of the last N values for the true heading. The value may be jumping around a lot in a single instant but settle down "in the average".
Assuming you have a member variable storedReadings which is an NSMutableArray:
-(void)addReading(float):newReading
{
[storedReadings addObject:[NSNumber numberWithFloat:newReading]];
while([storedReadings count] > MAX_READINGS)
{
[storedReadings removeObjectAtIndex:0];
}
}
then when you need the average value (timer update?)
-(float)calcReading
{
float result = 0.0f;
if([storedReadings count] > 0)
{
foreach(NSNumber* reading in storedReadings)
{
result += [reading floatValue];
}
result /= [storedReadings count];
}
return result;
}
You get to pick MAX_READINGS a priori.
NEXT LEVEL(S) UP
If the readings are not jumping around so much but the animation is still choppy, you probably need to do something like a "smooth" rotation. At any given time, you have the current angle you are displaying, theta (store this in your class, start it out at 0). You also have your target angle, call it target. This is the value you get from the smoothed calcReading function. The error is defined as the difference between the two:
error = target-theta;
Set up a timer callback with a period of something like 0.05 seconds (20x per second). What you want to do is adjust theta so that the error is driven towards 0. You can do this in a couple of ways:
thetaNext += kProp * (target - theta); //This is proportional feedback.
thetaNext += kStep * sign(target-theta); // This moves theta a fixed amount each update. sign(x) = +1 if x >= 0 and -1 if x < 0.
The first solution will cause the rotation to change sharply the further it is from the target. It will also probably oscillate a little bit as it swings past the "zero" point. Bigger values of kProp will yield faster response but also more oscillation. Some tuning will be required.
The second solution will be much easier to control...it just "ticks" the compass needle around each time. You can set kStep to something like 1/4 degree, which gives you a "speed" of rotation of about (1/4 deg/update) * (20 updates/seconds) = 5 degrees per second. This is a bit slow, but you can see the math and change kStep to suit your needs. Note that you may to "band" the "error" value so that no action is taken if the error < kStep (or something like that). This prevents your compass from shifting when the angle is really close to the target. You can change kStep when the error is small so that it "slides" into the ending position (i.e. kStep is smaller when the error is small).
For dealing with Angle Issues (wrap around), I "normalize" the angle so it is always within -Pi/Pi. I don't guarantee this is the perfect way to do it, but it seems to get the job done:
// Takes an angle greater than +/- M_PI and converts it back
// to +/- M_PI. Useful in Box2D where angles continuously
// increase/decrease.
static inline float32 AdjustAngle(float32 angleRads)
{
if(angleRads > M_PI)
{
while(angleRads > M_PI)
{
angleRads -= 2*M_PI;
}
}
else if(angleRads < -M_PI)
{
while(angleRads < -M_PI)
{
angleRads += 2*M_PI;
}
}
return angleRads;
}
By doing it this way, -pi is the angle you reach from going in either direction as you continue to rotate left/right. That is to say, there is not a discontinuity in the number going from say 0 to 359 degrees.
SO PUTTING THIS ALL TOGETHER
static inline float Sign(float value)
{
if(value >= 0)
return 1.0f;
return -1.0f;
}
//#define ROTATION_OPTION_1
//#define ROTATION_OPTION_2
#define ROTATION_OPTION_3
-(void)updateArrow
{
// Calculate the angle to the player
CGPoint toPlayer = ccpSub(self.player.position,self.arrow.position);
// Calculate the angle of this...Note there are some inversions
// and the actual image is rotated 90 degrees so I had to offset it
// a bit.
float angleToPlayerRads = -atan2f(toPlayer.y, toPlayer.x);
angleToPlayerRads = AdjustAngle(angleToPlayerRads);
// This is the angle we "wish" the arrow would be pointing.
float targetAngle = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
float errorAngle = targetAngle-self.arrow.rotation;
CCLOG(#"Error Angle = %f",errorAngle);
#ifdef ROTATION_OPTION_1
// In this option, we just set the angle of the rotated sprite directly.
self.arrow.rotation = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
#endif
#ifdef ROTATION_OPTION_2
// In this option, we apply proportional feedback to the angle
// difference.
const float kProp = 0.05f;
self.arrow.rotation += kProp * (errorAngle);
#endif
#ifdef ROTATION_OPTION_3
// The step to take each update in degrees.
const float kStep = 4.0f;
// NOTE: Without the "if(fabs(...)) check, the angle
// can "dither" around the zero point when it is very close.
if(fabs(errorAngle) > kStep)
{
self.arrow.rotation += Sign(errorAngle)*kStep;
}
#endif
}
I put this code into a demo program I had written for Cocos2d. It shows a character (big box) being chased by some monsters (smaller boxes) and has an arrow in the center that always points towards the character. The updateArrow call is made on a timer tick (the update(dt) function) regularly. The player's position on the screen is set by the user tapping on the screen and the angle is based on the vector from the arrow to the player. In the function, I show all three options for setting the angle of the arrow:
Option 1
Just set it based on where the player is (i.e. just set it).
Option 2
Use proportional feedback to adjust the arrow's angle each time step.
Option 3
Step the angle of the arrow each timestep a little bit if the error angle is more than the step size.
Here is a picture showing roughly what it looks like:
And, all the code is available here on github. Just look in the HelloWorldLayer.m file.
Was this helpful?

How to find an average of N number of x values in the draw function

I'm using open frameworks and opencv to track blobs on a webcam. I'm getting the x value of the blob centroid and tracking it. The problem is, it jumps around allot, I'm wondering if there is a better way to compute the average position over a certain number of frames and use that number it's all being computed in the draw() function.
void testApp::draw(){
ofVec2f centroid = contourFinder.blobs[0].centroid;
int width = ofGetWidth();
float pct = (float)centroid.x / (float)width;
float totFrame = fingerMovie.getTotalNumFrames ();
float gotFrame = totFrame * pct;
}
you should create a loop for N frames, sum all coordinates you get, then divide by N.
I am not experienced with ofx but there must be a function to get next frame.
After loop ends, move camera to the average coordinate and re-initialize the loop.

Issue with GLKVector2's

I'm having trouble setting up vectors for an object in my code. I tried modeling my code similarly to the answer in this question: Game enemy move towards player except that I'm using GLKVector2's. I thought my implementation seemed correct, but it's really only my first time using vectors with GLKit and in general I haven't used them too much before.
My current code looks something like:
GLKVector2 vector = GLKVector2Make(self.player.position.x - self.target.position.x, self.player.position.y - self.target.position.y);
float hypo = sqrt(vector.x*vector.x + vector.y*vector.y);
float speed = 0.25;
vector = GLKVector2Make(vector.x/hypo, vector.y/hypo);
vector = GLKVector2MultiplyScalar(vector, speed);
GLKVector2 sum = GLKVector2Add(vector, self.target.position);
self.target.moveVelocity = sum;
Is it possible that my logic just isn't correct here? I'd appreciate any help or suggestions. Thanks!
EDIT: just for clarification since I didn't really explain what happens.. Basically the "enemy" shapes either stutter/jump or just stick. They aren't moving toward the other object at all.
EDIT 2:
If I try using GLKVector2Normalize, then nothing moves. If I do something like:
GLKVector2 vector = GLKVector2Make(self.player.position.x - self.target.position.x, self.player.position.y - self.target.position.y);
float speed = 0.10;
// float distance = GLKVector2Distance(self.player.position, self.target.position);
// vector = GLKVector2Normalize(vector);
vector = GLKVector2MultiplyScalar(vector, speed);
self.target.moveVelocity = vector;
Then the movement works toward the player object, but only updates the one time even though it should be updating every second.
Two things:
There's no need to calculate the magnitude of the vector and divide yourself -- GLKit has a GLKVector2Normalize function, which takes a vector and returns the vector in the same direction with length 1. You can then use GLKVector2MultiplyScalar (as you do) to change the speed.
Your target's velocity should be set to vector, not sum, assuming that in the target's update method (which you should call once per timestep), you add self.moveVelocity.x to self.position.x and self.moveVelocity.y to self.position.y each timestep. As it is now, your sum variable will hold the position that your target should have one timestep in the future, not its velocity.

random bounce direction in Cocoa?

I have UIImageView (ball) and it moves around the screen but I want it to bounce in a random direction off a specific image (named computer.paddle)
This is my code:
if (CGRectIntersectsRect (ball.frame, computerPaddle.frame)) {
if (ball.center.x < computerPaddle.center.x) {
(ballVelocity.x =- ballVelocity.x);
}
}
NB I am using the rotation landscape right and I want it to bounce of the right side of the paddle to the right.
I am happy to make any UIIntegers or any thing else that is needed.
Add the following line in the inner block:
ballVelocity.y += arc4random() % (2 * MAX_VARIANCE) - MAX_VARIANCE;
MAX_VARIANCE, in this case, is the maximum amount of variation you want from the "normal" value of ballVelocity.y. If you need a finer degree of variation, you can increase MAX_VARIANCE by some double k and multiply the expression by 1/k. For example,
ballVelocity.y += (arc4random() % (2*100*MAX_VARIANCE) - 100*MAX_VARIANCE)*0.01;

Resources