So this is my first practice game I'm developing and first time using accelerometer so apologizes in advance if the answer is obvious. Would also like answered with code and explanation.
So part of game I have a ball being rolled around no the screen. The ball moves according to the tilt. The device orientation for this app is only landscape right. When I tilt the phone up and down while holding the phone landscape right the ball moves accordingly. (tilt the top end down the ball rolls up, tilt the bottom end down the ball rolls down). So those 2 tilts are fine. Now when I tilt the screen left side down the ball rolls right and vice versa. What I want is the screen to tilt left and the ball to go left and tilt the screen right and the ball goes right. How is this fixed with my code below. I know its as simple as changing two lines probably.
//delegate method
-(void)outputAccelertionData:(CMAcceleration)acceleration
{
currentMaxAccelX = 0;
currentMaxAccelY = 0;
if(fabs(acceleration.x) > fabs(currentMaxAccelX))
{
// this needs to be currentMaxAccelY not currentMaxAccelX for those of you thinking this is the solution
currentMaxAccelY = acceleration.x;
}
if(fabs(acceleration.y) > fabs(currentMaxAccelY))
{
// this needs to be currentMaxAccelX not currentMaxAccelY for those of you thinking this is the solution
currentMaxAccelX = acceleration.y;
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
float maxY = 480;
float minY = 0;
float maxX = 320;
float minX = 0;
float newY = 0;
float newX = 0;
//Im pretty sure the problem is in this if statement as this is what deals with the left and right tilt
if(currentMaxAccelX > 0.05){
newX = currentMaxAccelX * 10;
}
else if(currentMaxAccelX < -0.05){
newX = currentMaxAccelX*10;
}
else{
newX = currentMaxAccelX*10;
}
newY = currentMaxAccelY *10;
newX = MIN(MAX(newX+self.ball.position.x,minY),maxY);
newY = MIN(MAX(newY+self.ball.position.y,minX),maxX);
self.ball.position = CGPointMake(newX, newY);
}
So I solved the problem. The issue was with my math. Instead of multiplying by 10 I should have been multiplying by negative 10 to get in the inverse effect.
if(currentMaxAccelX > 0.05){
newX = currentMaxAccelX * -10;
}
else if(currentMaxAccelX < -0.05){
newX = currentMaxAccelX*-10;
}
else{
newX = currentMaxAccelX*-10;
}
Related
I was faced with this question in one of my interviews and was completely stumped. The only solution I could think of was storing the currentAngle in a NSArray to calculate the next angle.
Question:
Move a 35px ball across the screen utilizing the iPhone's compass. Once the ball is in the center of the screen, let the user tap it to 'reset' the position. Once reset, the ball will go back to the Min position. Remember that the compass may start somewhere between 0-359, the task is to find the nearest capture angle and focus on that angle until the ball is aligned. Once the ball is aligned & reset, the iPhone will move to the next angle and so forth until the ball has been reset 18 times. 18 resets * 20 degree angles = 360.
Assigned Variables:
int currentAngle = (Ranging between 0-359) (Constant updates as the user twirls around)
int captureAngle = 20
int centerX = view.center.x (160) - 35 (size of ball)
int ballSize = 35 (ball.width/2)
The paper looked something like this:
Function so far:
-(void)testMotion{
motionQueue = [[NSOperationQueue alloc] init];
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 1.0f / 60.0f;
if (([CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXTrueNorthZVertical) != 0) {
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical
toQueue:motionQueue
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
if (!error) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
CMAttitude *attitude = motion.attitude;
CMRotationMatrix rm = attitude.rotationMatrix;
// Get the heading.
double heading = M_PI + atan2(rm.m22, rm.m12);
heading = heading*180/M_PI;
int currentAngle = (int)heading;
NSLog(#"Current Angle: %d",currentAngle);
int captureAngle = 20; // 20 Degress Capture Angle
}];
}
}];
}
}
If I understood you, then it's something like this:
calculate the x and y movement from the angle
(see
https://en.wikipedia.org/wiki/Rotation_of_axes
http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1223522781
)
Then move the ball according to those values, and if it moved by an angle of 20 - allow reset it or get out of the loop (for your choice)
while(1) {
x = r \cos(currentAngle)
y = r \sin(currentAngle)
//change the ball position,
ball.position.x += x*speed
ball.position.y += y*speed
//check if angel is +20 or -20
if (((currentAngle + 20) % 360) != captureAngle && (abs(currentAngle - 20) % 360) != captureAngle)) {
allow_reset_ball = true
break;
}
}
I am trying to make a scene that extends up off the screen for a certain distance, and I want the camera to stay centered on the player node. When the player reaches the top of the screen, I want the bottom nodes to disappear below the screen bounds and the new off screen part of the scene to become visible. Like Mario!
I create the scene size like this:
CGSize screenSize = CGSizeMake(skView.bounds.size.width, skView.bounds.size.height + (skView.bounds.size.height/3));//+ (skView.bounds.size.height/3)
scene = [GameScene sceneWithSize:screenSize];
And then, add all of my nodes to a WorldNode as in This question.
I have modified it to be:
- (void)didSimulatePhysics
{
CGFloat margin = self.size.height/3;// previously defined
// make sure the cat's position is defined in scene coordinates
CGPoint catPosition = [self convertPoint:cat.position fromNode:cat.parent];
if (catPosition.y > (self.size.height - margin))
{
CGPoint worldPosition = worldNode.position;
worldPosition.y -= catPosition.y - (self.size.height - margin);
worldNode.position = worldPosition;
}
else if (catPosition.y < (self.size.height - margin))
{
CGFloat maxWorldHeight = self.size.height;
CGPoint worldPosition = worldNode.position;
// this keeps the cat on the margin line
if (worldPosition.y != worldStartPosition.y)
{
worldPosition.y += (self.size.height - margin) - catPosition.y;
}
if (worldPosition.y > maxWorldHeight) // assume maxWorldHeight is defined
{
worldPosition.y = maxWorldHeight;
}
worldNode.position = worldPosition;
}
}
Ok, I have it almost working. I can't seem to stop the world at the top boarder of the scene, and the character does not fall when he reaches below the margin, as in the world keeps following him down past the worlds initial start point. How can I get the screen to only move up and down if the character is above the margin, and then if the character is below the margin, the screen does not move?
You want to move your world node down by an amount equal to how far the cat is above where you want it to be. Your didSimulatePhysics method would end up looking something like this:
- (void)didSimulatePhysics
{
CGFloat margin; // set this to be whatever you want it to be
// make sure the cat's position is defined in scene coordinates
CGPoint catPosition = [self convertPoint:cat.position fromNode:cat.parent];
if (catPosition.y > (self.size.height - margin))
{
// the cat is above the margin, we need to move the world node down until the
// cat is at the margin
// pull world position into local variable so you can modify the y component
CGPoint worldPosition = worldNode.position;
// move the world down so the cat is on the margin
worldPosition.y -= catPosition.y - (self.size.height - margin);
worldNode.position = worldPosition;
}
else
{
// the cat is below the margin, we need to move the world node up, but only
// until the world node is at its starting position
CGPoint worldPosition = worldNode.position;
// move the cat up to the margin line
worldPosition.y += (self.size.height - margin) - catPosition.y;
// if this caused the world to be too high, move it back down
if (worldPosition.y > maxWorldHeight)
{
worldPosition.y = maxWorldHeight;
}
worldNode.position = worldPosition;
}
}
One thing to take note of is that maxWorldHeight is not how tall the world is, but rather how far above the bottom of the screen it's allowed to be. You should set it to the y-component of the word node's position when you first place it in the scene.
- (void)didSimulatePhysics
{
CGPoint target = [self pointToCenterViewOn:cat.position];
CGPoint newPosition = _worldNode.position;
newPosition.x += (target.x - worldNode.position.x) * 0.1f;
newPosition.y += (target.y - worldNode.position.y) * 0.1f;
worldNode.position = newPosition;
}
- (CGPoint)pointToCenterViewOn:(CGPoint)centerOn
{
CGFloat x = Clamp(centerOn.x, self.size.width / 2, worldNode.size.width - self.size.width / 2);
CGFloat y = Clamp(centerOn.y, self.size.height / 2, worldNode.size.height - self.size.height/2);
return CGPointMake(-x, -y);
}
I am trying to make a platform game for the iphone, using cocos2d and Tiled (for the maps).
All of the tutorials i've seen on the net are using Layers in Tiled to do collision detection.
I want to use objects to do that...not Layers.
With objects you can create custom shapes that can give a better 'reality' into the game.
To give an example of what i mean :
I've drawn the ground as a background and created an object layer on top.
I want to detect player collision with that, instead of the background tile.
Now using the most famous tutorial out there : http://www.raywenderlich.com/15230/how-to-make-a-platform-game-like-super-mario-brothers-part-1
I am trying to rewrite checkForAndResolveCollisions method to check collisions for the objects instead.
The problem is that in Tiled the coordinates system is different than cocos2d. Tiled starts from top left corner, cocos2d from bottom left corner....and not only that...I noticed that the width and height of the object properties in Tiled (probably) dont correspond to the same in iphone devices.
The above rectangle has properties:
its w/h is 480/128 in tiled (for retina devices) which means its probably huge inside the map if i keep them like this. My guess is i have to divide this by 2.
So far i got this:
-(void)checkForAndResolveObjCollisions:(Player *)p {
CCTiledMapObjectGroup *objectGroup = [map objectGroupNamed:#"Collision"];
NSArray* tiles = [objectGroup objects];
CGFloat x, y, wobj, hobj;
for (NSDictionary *dic in tiles) {
CGRect pRect = [p collisionBoundingBox]; //3
x = [[dic valueForKey:#"x"] floatValue];
y = [[dic valueForKey:#"y"] floatValue];
wobj = [[dic valueForKey:#"width"] floatValue];
hobj = [[dic valueForKey:#"height"] floatValue];
CGPoint position = CGPointMake(x, y);
CGPoint objPos = [self tileForPosition:position];
CGRect tileRect = CGRectMake(objPos.x, objPos.y, wobj/2, hobj/2);
if (CGRectIntersectsRect(pRect, tileRect)) {
CCLOG(#"INTERSECT");
CGRect intersection = CGRectIntersection(pRect, tileRect);
NSUInteger tileIndx = [tiles indexOfAccessibilityElement:dic];
if (tileIndx == 0) {
//tile is directly below player
p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height);
p.velocity = ccp(p.velocity.x, 0.0);
p.onGround = YES;
} else if (tileIndx == 1) {
//tile is directly above player
p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - intersection.size.height);
p.velocity = ccp(p.velocity.x, 0.0);
} else if (tileIndx == 2) {
//tile is left of player
p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y);
} else if (tileIndx == 3) {
//tile is right of player
p.desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y);
} else {
if (intersection.size.width > intersection.size.height) {
//tile is diagonal, but resolving collision vertially
p.velocity = ccp(p.velocity.x, 0.0);
float resolutionHeight;
if (tileIndx > 5) {
resolutionHeight = -intersection.size.height;
p.onGround = YES;
} else {
resolutionHeight = intersection.size.height;
}
p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + resolutionHeight );
} else {
float resolutionWidth;
if (tileIndx == 6 || tileIndx == 4) {
resolutionWidth = intersection.size.width;
} else {
resolutionWidth = -intersection.size.width;
}
p.desiredPosition = ccp(p.desiredPosition.x + resolutionWidth , p.desiredPosition.y);
}
}
}
// }
}
p.position = p.desiredPosition; //8
}
- (CGPoint)tileForPosition:(CGPoint)p
{
NSInteger x = (NSInteger)(p.x / map.tileSize.width);
NSInteger y = (NSInteger)(((map.mapSize.height * map.tileSize.width) - p.y) / map.tileSize.width);
return ccp(x, y);
}
I am getting the object x,y,w,h and try to convert them to cocos2d dimensions and sizes.
The above translates to this:
Dimens: 480.000000, 128.000000
Coord: 0.000000, 40.000000
Basically its a mess. And its not working .....at all. The player just falls right through.
I am surprised noone has done collision detection based on objects before...unless i am wrong.
Does anyone know if this can be done or how it can be done ?
Kinda what he does here : https://www.youtube.com/watch?feature=player_detailpage&v=2_KB4tOTH6w#t=30
Sorry for the long post.
Thanks for any answers in advance.
Right now I'm trying to implement a pinch-to-zoom feature in my Cocos2D game for iOS and I'm encountering really strange behavior. My goal is to use the handler for UIPinchGestureRecognizer to scale one of the CCNodes that represents the game level when a player pinches the screen. This has the effect of zooming.
The issue is if I set the anchor for zooming to some arbitrary value such as .5, .5 (the center of the level CCNode) it scales perfectly around the center of the level, but I want to scale around the center of the player's view. Here is what that looks like:
- (void) handlePinchFrom:(UIPinchGestureRecognizer*) recognizer
{
if(recognizer.state == UIGestureRecognizerStateEnded)
{
_isScaling = false;
_prevScale = 1.0;
}
else
{
_isScaling = true;
float deltaScale = 1.0 - _prevScale + recognizer.scale;
// Obtain the center of the camera.
CGPoint center = CGPointMake(self.contentSize.width/2, self.contentSize.height/2);
CGPoint worldPoint = [self convertToWorldSpace:center];
CGPoint areaPoint = [_area convertToNodeSpace:worldPoint];
// Now set anchor point to where areaPoint is relative to the whole _area contentsize
float areaLocationX = areaPoint.x / _area.contentSize.width;
float areaLocationY = areaPoint.y / _area.contentSize.height;
[_area moveDebugParticle:areaPoint];
[_area setAnchorPoint:CGPointMake(areaLocationX, areaLocationY)];
if (_area.scale*deltaScale <= ZOOM_RADAR_THRESHOLD)
{
_area.scale = ZOOM_RADAR_THRESHOLD;
}
else if (_area.scale*deltaScale >= ZOOM_MAX)
{
_area.scale = ZOOM_MAX;
}
else
{
// First set the anchor point.
_area.scale *= deltaScale;
}
_prevScale = recognizer.scale;
}
}
If I set the anchor point to .5, .5 and print the calculated anchor point (areaLocationX, areaLocationY) using a CCLog it looks right, but when I set the anchor point to these values the layer scales out of control and entirely leaves the view of the player. The anchor point takes on crazy values like (-80, 10), although generally it should be relatively close to something in the range of 0 to 1 for either coordinate.
What might be causing this kind of behavior?
Ok it looks like I solved it. I was continually moving the anchor point -during- the scaling rather than setting it at the very beginning once. The result was really erratic scaling rather than something smooth and expected. The resolved code looks like this:
- (void) handlePinchFrom:(UIPinchGestureRecognizer*) recognizer
{
if(recognizer.state == UIGestureRecognizerStateEnded)
{
_isScaling = false;
_prevScale = 1.0;
}
else
{
if (!_isScaling)
{
// Obtain the center of the camera.
CGPoint center = CGPointMake(self.contentSize.width/2, self.contentSize.height/2);
CGPoint areaPoint = [_area convertToNodeSpace:center];
// Now set anchor point to where areaPoint is relative to the whole _area contentsize
float anchorLocationX = areaPoint.x / _area.contentSize.width;
float anchorLocationY = areaPoint.y / _area.contentSize.height;
[_area moveDebugParticle:areaPoint];
[_area setAnchorPoint:CGPointMake(anchorLocationX, anchorLocationY)];
CCLOG(#"Anchor Point: (%f, %f)", anchorLocationX, anchorLocationY);
}
_isScaling = true;
float deltaScale = 1.0 - _prevScale + recognizer.scale;
if (_area.scale*deltaScale <= ZOOM_RADAR_THRESHOLD)
{
_area.scale = ZOOM_RADAR_THRESHOLD;
}
else if (_area.scale*deltaScale >= ZOOM_MAX)
{
_area.scale = ZOOM_MAX;
}
else
{
_area.scale *= deltaScale;
}
_prevScale = recognizer.scale;
}
}
Alright, here we go. I have a cocos2d app, and there are targets that move toward the player. When the player moves, I would like for them to slowly change their destination toward the player again, so they aren't just moving into empty space. Is it possible to change the destination of a sprite mid-runAction?
edit:
This is the code in - (void)changeTargetDest
- (void)changeTargetDest {
NSMutableArray* deleteArray = [[NSMutableArray alloc] init];
for(CCSprite* s in _targets) {
float offX = s.position.x - player.position.x;
float offY = s.position.y - player.position.y;
float adjustX;
float adjustY;
float offDistance = sqrt(powf(offX, 2.0f) + powf(offY, 2.0f));
if(offDistance < 15) {
[deleteArray addObject:s];
deaths++;
[deathLabel setString:[NSString stringWithFormat:#"Deaths: %ld", deaths]];
if(deaths == 0)
[kdLabel setString:[NSString stringWithFormat:#"K/D ratio: %ld.00", score]];
else
[kdLabel setString:[NSString stringWithFormat:#"K/D ratio: %.2f", ((float)score / (float)deaths)]];
}
else {
adjustX = offX * .99;
adjustY = offY * .99;
CGPoint point = CGPointMake(player.position.x + adjustX, player.position.y + adjustY);
[s setPosition:point];
}//else
}//for
for (CCSprite *target in deleteArray) {
[_targets removeObject:target];
[self removeChild:target cleanup:YES];
}
}
This works well, except for one problem. Because the new position is calculated by just taking .99 of the previous offset, the closer the target gets to the player, the more slowly it moves. How can I make its speed constant?
You can stop the action and run a new action each few frames in a scheduled method.
but the better way is to compute the position of targets according to players position and use setPosition to manualy change their positions each frame in your update method.