I'm completely new to developing apps with XCode and Objective-C. I'm trying to create a simple iOS application that creates a rectangle at the position of your finger when you tap the screen. I'm having an issue where the rectangle (the class is called Ball) isn't correctly positioned. On tap, it is positioned off the left of the screen and slightly above your finger. I'm not sure how to fix this, but I feel like it is a problem with my init function. Can anyone help?
Thanks.
GameScene.m
#import "GameScene.h"
#import "Ball.h"
#implementation GameScene
double SCREEN_WIDTH, SCREEN_HEIGHT;
NSMutableArray *screenObjects;
SKView *mainView;
- (void) didMoveToView:(SKView*)view {
mainView = view;
SCREEN_WIDTH = self.view.frame.size.width;
SCREEN_HEIGHT = self.view.frame.size.height;
screenObjects = [[NSMutableArray alloc] init];
[Ball setDefaultView: self];
[Ball setRightBounds: SCREEN_WIDTH];
[Ball setLeftBounds: 0];
[Ball setBottomBounds: SCREEN_HEIGHT];
[Ball setTopBounds: 0];
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
double x = [touch locationInView: mainView].x;
double y = [touch locationInView: mainView].y;
Ball *object = [[Ball alloc] init];
[object setPosition: CGPointMake(x, y)];
[screenObjects addObject: object];
NSLog(#"%f %f", x, y);
}
}
- (void) update:(CFTimeInterval)currentTime {
for (int i = 0; i < screenObjects.count; i++) {
[screenObjects[i] applyVelocity];
[screenObjects[i] dealWithWallCollisions];
}
}
#end
Ball.h
#ifndef IndependentStudyPrototype_Ball_h
#define IndependentStudyPrototype_Ball_h
#import "GameScene.h"
#interface Ball : NSObject
#property double width;
#property double height;
#property double x;
#property double y;
#property double velocity;
+ (void) setDefaultView:(GameScene*)view;
+ (void) setTopBounds:(double)bounds;
+ (void) setBottomBounds:(double)bounds;
+ (void) setLeftBounds:(double)bounds;
+ (void) setRightBounds:(double)bounds;
- (void) setPosition:(CGPoint)point;
- (void) updatePosition;
- (void) dealWithWallCollisions;
- (void) applyVelocity;
#end
#endif
Ball.m
#import <Foundation/Foundation.h>
#import "Ball.h"
#import "GameScene.h"
static GameScene *defaultView;
static double leftBound, rightBound, topBound, bottomBound;
#implementation Ball {
SKSpriteNode *rectangle;
double stepX, stepY;
}
+ (void) setDefaultView:(GameScene*)view {
defaultView = view;
}
+ (void) setBottomBounds:(double)bounds {
bottomBound = bounds;
}
+ (void) setTopBounds:(double)bounds {
topBound = bounds;
}
+ (void) setLeftBounds:(double)bounds {
leftBound = bounds;
}
+ (void) setRightBounds:(double)bounds {
rightBound = bounds;
}
- (void) updatePosition {
self->rectangle.position = CGPointMake(self.x, self.y);
}
- (void) setPosition:(CGPoint)point {
self.x = point.x;
self.y = point.y;
[self updatePosition];
}
- (void) applyVelocity {
self.x += self->stepX;
self.y += self->stepY;
[self updatePosition];
}
- (void) dealWithWallCollisions {
if (self.x > leftBound) {
self->stepX = -self.velocity;
} else if (self.x < rightBound) {
self->stepX = self.velocity;
}
if (self.y > bottomBound) {
self->stepY = -self.velocity;
} else if (self.y < topBound) {
self->stepY = self.velocity;
}
}
- (id)init {
self = [super init];
self->rectangle = [[SKSpriteNode alloc] initWithColor: [UIColor blueColor] size: CGSizeMake(50, 50)];
self.velocity = 1;
self->stepX = self.velocity;
self->stepY = self.velocity;
[defaultView addChild: self->rectangle];
return self;
}
#end
If you are just looking to fine tune the position, you can do something like this:
[object setPosition: CGPointMake(x+20, y-10)];
Inside touchesBegan use locationInNode instead of locationInView to get your touch point.
The touch location in an SKScene should be calculated using locationInNode not locationInView. This is because the origin of SKScene is at the bottom left corner and origin of UIView is at the top left corner.
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
CGPoint point = [touch locationInNode:self]; // Changed
Ball *object = [[Ball alloc] init];
[object setPosition: CGPointMake(point.x, point.y)]; // Changed
[screenObjects addObject: object];
}
}
Related
I am trying to create sprite animations for a game where when a user clicks on a button, it will spawn 1 unit/enemy, which will move across the screen in a running animation. At the moment, when I run the game, it will play and once I attempt to spawn a unit, the game crashes and gives me a -[__NSCFConstantString texture]: unrecognized selector sent to instance 0x11a15c error.
This is the header file for the Unit itself:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "mainGameLayer.h"
#class MainGameLayer, Waypoint, Tower;
#interface Unit : CCSprite {
CGPoint myPosition;
int maxHP;
int currentHP;
float walkingSpeed;
Waypoint *destinationWaypoint;
BOOL active;
float centerToBottom;
float centerToSides;
}
#property (nonatomic, assign) MainGameLayer *theGame;
#property (nonatomic, assign) CCSprite *mySprite;
#property (nonatomic, strong) CCAction *walkAction;
+(id) nodeWithTheGame: (MainGameLayer *)_game;
-(id) initWithTheGame: (MainGameLayer *)_game;
-(void) doActivate;
-(void) getRemoved;
#end
Below is the implementation file. The 'self = super initWithSpriteFrame' line is currently throwing a warning saying "Incompatible pointer types sending NSString * to parameter of type 'CCSpriteFrame *'.
Also, the "CCSprite *frame = [[CCSpriterameCache....." line is throwing another warning saying 'Flag 0 results in undefined behaviour with p conversion specifier.
#import "Unit.h"
#import "Tower.h"
#import "Waypoint.h"
#define HEALTH_BAR_WIDTH 20
#define HEALTH_BAR_ORIGIN -10
#implementation Unit
#synthesize mySprite, theGame;
+(id) nodeWithTheGame:(MainGameLayer *)_game
{
return [[self alloc] initWithTheGame:_game];
}
-(id) initWithTheGame:(MainGameLayer *)_game
{
if ((self = [super initWithSpriteFrame:#"hero_walk_00.png"])) {
theGame = _game;
maxHP = 40;
currentHP = maxHP;
active = FALSE;
walkingSpeed = 0.5;
centerToBottom = 39.0;
centerToSides = 29.0;
CCArray *walkFrames = [CCArray arrayWithCapacity: 8];
for (int i = 0; i < 8; i++) {
CCSprite *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"hero_walk_%02.png", i]];
[walkFrames addObject: frame];
}
CCAnimation *walkAnimation = [CCAnimation animationWithSpriteFrames:[walkFrames getNSArray] delay:1.0/12.0];
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnimation]];
Waypoint *waypoint = (Waypoint *)[theGame.waypoints objectAtIndex:([theGame.waypoints count]-1)];
destinationWaypoint = waypoint.nextWaypoint;
CGPoint pos = waypoint.myPosition;
myPosition = pos;
[mySprite setPosition: pos];
[theGame addChild: self];
[self scheduleUpdate];
}
return self;
}
-(void) doActivate
{
active = TRUE;
}
-(void) update:(ccTime)dt
{
if (!active) {
return;
}
if ([theGame circle:myPosition withRadius:1 collisionWithCircle:destinationWaypoint.myPosition collisionCircleRadius:1]) {
if (destinationWaypoint.nextWaypoint) {
destinationWaypoint = destinationWaypoint.nextWaypoint;
} else {
[theGame getHpDamage];
[self getRemoved];
}
}
CGPoint targetPoint = destinationWaypoint.myPosition;
float movementSpeed = walkingSpeed;
CGPoint normalized = ccpNormalize(ccp(targetPoint.x - myPosition.x, targetPoint.y - myPosition.y));
mySprite.rotation = CC_RADIANS_TO_DEGREES(atan2(normalized.y, -normalized.x));
myPosition = ccp(myPosition.x + normalized.x * movementSpeed, myPosition.y + normalized.y * movementSpeed);
[mySprite setPosition: myPosition];
}
-(void) getRemoved
{
[self.parent removeChild:self cleanup:YES];
[theGame.units removeObject: self];
// Notify the game that we killed an enemy so we can check if we can send another wave
[theGame enemyGotKilled];
}
-(void) draw
{
ccDrawSolidRect(ccp(myPosition.x + HEALTH_BAR_ORIGIN, myPosition.y + 16), ccp(myPosition.x + HEALTH_BAR_ORIGIN + HEALTH_BAR_WIDTH, myPosition.y + 14), ccc4f(1.0, 0, 0, 1.0));
ccDrawSolidRect(ccp(myPosition.x + HEALTH_BAR_ORIGIN, myPosition.y + 16), ccp(myPosition.x + HEALTH_BAR_ORIGIN + (float)(currentHP * HEALTH_BAR_WIDTH)/maxHP, myPosition.y + 14), ccc4f(0, 1.0, 0, 1.0));
}
#end
Here is the header file of the main game layer:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface MainGameLayer : CCLayer {
CCSpriteBatchNode *_actors;
}
+ (CCScene *) scene;
- (BOOL) circle:(CGPoint)circlePoint withRadius:(float)radius collisionWithCircle:(CGPoint)circlePointTwo collisionCircleRadius:(float)radiusTwo;
void ccFillPoly (CGPoint *poli, int points, BOOL closePolygon);
- (void) enemyGotKilled;
- (void) getHpDamage;
#property (nonatomic, strong) NSMutableArray *towers;
#property (nonatomic, strong) NSMutableArray *waypoints;
#property (nonatomic, strong) NSMutableArray *units;
#end
And the implementation file:
#import "MainGameLayer.h"
#import "Tower.h"
#import "Waypoint.h"
#import "Unit.h"
#implementation MainGameLayer
#synthesize towers;
#synthesize waypoints;
#synthesize units;
+ (CCScene *) scene
{
CCScene *scene = [CCScene node];
MainGameLayer *layer = [MainGameLayer node];
[scene addChild: layer];
return scene;
}
- (id) init
{
if ((self = [super init])) {
// Initialize
self.isTouchEnabled = TRUE;
CGSize winSize = [CCDirector sharedDirector].winSize;
// Setting the background (Map)
CCSprite *background = [CCSprite spriteWithFile:#"layout.png"];
[self addChild: background];
[background setPosition: ccp(winSize.width/2, winSize.height/2)];
[self addWaypoints];
// In Game Buttons / Menu
CCMenuItem *sampleButton = [CCMenuItemImage itemWithNormalImage:#"sample.jpg" selectedImage:#"sample.jpg" target:self selector:#selector(samplePurchased:)];
CCMenu *PurchaseUI = [CCMenu menuWithItems:sampleButton, nil];
[PurchaseUI setScale:0.5];
[PurchaseUI setPosition:ccp(63, 51)];
[PurchaseUI alignItemsHorizontally];
PurchaseUI.isTouchEnabled = TRUE;
[self addChild: PurchaseUI];
// Set up the sprite sheets (Currently in testing)
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"pd_sprites.plist"];
_actors = [CCSpriteBatchNode batchNodeWithFile:#"pd_sprites.pvr.ccz"];
[_actors.texture setAliasTexParameters];
[self addChild: _actors];
}
return self;
}
-(BOOL) canBuyTower
{
return YES;
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
CCLOG(#"X: %f Y: %f", location.x, location.y);
if ([self canBuyTower]) {
// Spend the gold later
Tower *tower = [Tower nodeWithTheGame:self location: location];
[towers addObject: tower];
}
}
}
-(void) addWaypoints
{
waypoints = [[NSMutableArray alloc] init];
Waypoint * waypoint1 = [Waypoint nodeWithTheGame:self location:ccp(-25,360)];
[waypoints addObject:waypoint1];
Waypoint * waypoint2 = [Waypoint nodeWithTheGame:self location:ccp(73,360)];
[waypoints addObject:waypoint2];
waypoint2.nextWaypoint =waypoint1;
Waypoint * waypoint3 = [Waypoint nodeWithTheGame:self location:ccp(467,360)];
[waypoints addObject:waypoint3];
waypoint3.nextWaypoint =waypoint2;
Waypoint * waypoint4 = [Waypoint nodeWithTheGame:self location:ccp(905,360)];
[waypoints addObject:waypoint4];
waypoint4.nextWaypoint =waypoint3;
Waypoint * waypoint5 = [Waypoint nodeWithTheGame:self location:ccp(1050,360)];
[waypoints addObject:waypoint5];
waypoint5.nextWaypoint =waypoint4;
}
-(BOOL) circle:(CGPoint)circlePoint withRadius:(float)radius collisionWithCircle:(CGPoint)circlePointTwo collisionCircleRadius:(float)radiusTwo
{
float xdif = circlePoint.x - circlePointTwo.x;
float ydif = circlePoint.y - circlePointTwo.y;
float distance = sqrt(xdif*xdif + ydif*ydif);
if (distance <= radius + radiusTwo) {
return TRUE;
}
return FALSE;
}
-(void) samplePurchased: (id)sender
{
Unit *tempUnit = [Unit nodeWithTheGame: self];
[units addObject: tempUnit];
[tempUnit doActivate];
}
#end
I'm basically just working off some of the tutorials on the Ray Wenderlich website; I haven't run into any of these errors before and haven't been able to find much help on Google. Any help here is much appreciated! Thank you in advance!
EDIT: After making the changes I am now getting an invisible sprite move across the screen. The sprite's health bar will appear and move across the screen but not the actual sprite / animation itself. Any reasons for this?
[super initWithSpriteFrame:#"hero_walk_00.png"]
expects a CCSpriteFrame* as parameter, not a NSString*. This is why you get a compiler warning for this line - don't ignore compiler warnings!
The fix is simple, use the correct initializer:
[super initWithSpriteFrameName:#"hero_walk_00.png"]
And this returns a CCSpriteFrame* not a CCSprite*:
CCSprite *frame = [[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:[NSString stringWithFormat:#"hero_walk_%02.png", i]];
Hence the other warning. The fix:
CCSpriteFrame *frame = ...
i am going to make a very simple game like a sprite falling from top and i have to catch it with another sprite , if i don't the sprite will go outside the screen and remove ,, that part is ok , i also add collision detection , main problem is this collision only happen upper side of the screen not collision bottom side of the screen ,why is this happening please help ,(i am new :S),, here is full code
in my .h`then .m
{
CCSprite *right;
CCSprite *left;
NSMutableArray *_left;
}
-(void)ringcreate:(ccTime)dt
{
CGSize winsize = [[CCDirector sharedDirector] winSize];
int minX = left.contentSize.width / 2;
int maxX = winsize.width - left.contentSize.width/2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
left = [CCSprite spriteWithFile:#"2.png"];
left.position = ccp(actualX,winsize.height);
[self addChild:left];
id move3 = [CCMoveTo actionWithDuration:5 position:ccp(winsize.width/2,0)];
[left runAction:move3];
}
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if( (self=[super init]) ) {
self.touchEnabled = YES;
right = [CCSprite spriteWithFile:#"1.png"];
right.position = ccp(0,0);
[self addChild:right];
[self schedule:#selector(ringcreate:) interval:2];
[self schedule:#selector(update:)];
}
return self;
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch =[touches anyObject];
CGPoint location =[touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
id move = [CCMoveTo actionWithDuration:1 position:ccp(location.x,location.y)];
[right runAction:move];
}
-(void) update:(ccTime)dt
{
if (CGRectIntersectsRect(right.boundingBox, left.boundingBox)) {
CCLOG(#"collison hoyse");
id move1 = [CCScaleTo actionWithDuration:0.4 scale:0.3];
left.visible = NO;
[left runAction:move1];
}
}
i solved the problem , i just have to add array and update them wining that method ,
-(void) update:(ccTime)dt
{
NSMutableArray *crabToUpdate = [[NSMutableArray alloc] init];
for (CCSprite *crab in crabarray) {
NSMutableArray *ring_to_delete = [[NSMutableArray alloc] init];
for (ring1 in ringarray) {
if (CGRectIntersectsRect(crab.boundingBox, ring1.boundingBox)) {
[ring_to_delete addObject:ring1];
}
}
for (CCSprite *ring1 in ring_to_delete) {
[ringarray removeObject:ring1];
[self removeChild:ring1 cleanup:YES];
}
if (ring_to_delete.count >0) {
[crabToUpdate addObject:crab];
}
[ring_to_delete release];
}
}
It is so easy to write some code to achieve the bouncing ball based on the open source project cocos2d,here is the question.
So without cocos2d, how to implement this with the simplest code.
First you need to create a ball class. This class should have ivars for position, velocity, direction etc.
It should also have some methods for when the ball needs to change direction, like hitTop, hitBottom, hitLeft, hitRight. It should also have a method for updating the position, to move it one step forward.
I also have a BallView so that I can use a image or just a color as the ball.
To make it move you need to have a gameloop in you view controller, there are many methods for doing that but the one I find easiest is CADisplayLink.
Here are some code if you have problems implementing it:
Ball.h
#import <Foundation/Foundation.h>
#import "MovingObjectView.h"
#interface MovingObject : NSObject
#property(nonatomic, retain) MovingObjectView *ball;
#property CGRect bounds;
#property CGPoint position;
#property CGPoint direction;
#property float velocity;
-(id)initWithBounds:(CGRect)b andPosition:(CGPoint)p;
-(BOOL)positionIsInsideRect;
-(void)update;
-(void)hitLeft;
-(void)hitRight;
-(void)hitTop;
-(void)hitBottom;
#end
Ball.m
#import "MovingObject.h"
#implementation MovingObject
-(id)initWithBounds:(CGRect)b andPosition:(CGPoint)p
{
if (self = [super init]) {
self.bounds = b;
self.position = p;
self.direction = CGPointMake(1, 1);
//Change the position to middle if position is outside bounds
if (![self positionIsInsideRect]) {
NSLog(#"Position changed to center");
self.position = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
}
self.ball = [[[MovingObjectView alloc]initWithFrame:CGRectMake(self.position.x, self.position.y, 15, 15)]autorelease];
}
return self;
}
//checks if the balls position is correct
-(BOOL)positionIsInsideRect {
if (self.position.x < self.bounds.origin.x || self.position.x > self.bounds.size.width || self.position.y < self.bounds.origin.y || self.position.y > self.bounds.size.height) {
NSLog(#"Position is outside bounds");
return NO;
}else{
return YES;
}
}
//Call this method to move a ball
-(void)update {
//Checks if the ball is outside bounds
if (self.position.x-(self.ball.frame.size.width/2) <= self.bounds.origin.x) {
[self hitLeft];
}else if (self.position.x+(self.ball.frame.size.width/2) >= self.bounds.size.width){
[self hitRight];
}else if (self.position.y-(self.ball.frame.size.height/2) <= self.bounds.origin.y) {
[self hitTop];
}else if (self.position.y+(self.ball.frame.size.height/2) >= self.bounds.size.height){
[self hitBottom];
}
//Updates the balls position
CGPoint p = self.position;
p.x += self.direction.x*self.velocity;
p.y += self.direction.y*self.velocity;
self.position = p;
self.ball.center = self.position;
}
//Call this when the ball need to bounce
-(void)hitLeft
{
NSLog(#"hitLeft");
CGPoint d = self.direction;
d.x = fabsf(self.direction.x);
self.direction = d;
}
-(void)hitRight
{
NSLog(#"hitRight");
CGPoint d = self.direction;
d.x = -fabsf(self.direction.x);
self.direction = d;
}
-(void)hitTop
{
NSLog(#"hitTop");
CGPoint d = self.direction;
d.y = fabsf(self.direction.y);
self.direction = d;
}
-(void)hitBottom
{
NSLog(#"hitBottom");
CGPoint d = self.direction;
d.y = -fabsf(self.direction.y);
self.direction = d;
}
-(void)dealloc {
[super dealloc];
}
#end
BallView.h
#import <UIKit/UIKit.h>
#interface MovingObjectView : UIView
#property (nonatomic, retain) UIImage *img;
#end
BallView.m
#import "MovingObjectView.h"
#implementation MovingObjectView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
if (self.img == nil) {
CGRect bounds = [self bounds];
[[UIColor blackColor]set];
UIRectFill(bounds);
}else {
CGRect bounds = [self bounds];
[[UIColor whiteColor]set];
UIRectFill(bounds);
[self.img drawInRect:rect];
}
}
#end
Then finally in the viewcontroller you need this:
ViewController
In viewdidload:
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLoop)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
Then add this method:
-(void)gameLoop
{
//Updates the balls position
[self.movingBall update];
//Here you could add your collision detection code
}
I've found a little tutorial that would be useful for my game:
http://blog.mellenthin.de/archives/2012/02/13/an-one-finger-rotation-gesture-recognizer/
But I can't work out how to convert that gesture to work with cocos2d, I have found examples of pre made gestures in cocos2d, but no custom ones, is it possible?
EDIT STILL HAVING PROBLEMS WITH THIS:
I've added the code from Sentinel below, the Gesture and RotateGesture have both been added to my solution and are compiling. Although In the rotation class now I only see selectors, how do I set those up? As the custom gesture found in that project above looks like:
header file for custom gesture:
#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#protocol OneFingerRotationGestureRecognizerDelegate <NSObject>
#optional
- (void) rotation: (CGFloat) angle;
- (void) finalAngle: (CGFloat) angle;
#end
#interface OneFingerRotationGestureRecognizer : UIGestureRecognizer
{
CGPoint midPoint;
CGFloat innerRadius;
CGFloat outerRadius;
CGFloat cumulatedAngle;
id <OneFingerRotationGestureRecognizerDelegate> target;
}
- (id) initWithMidPoint: (CGPoint) midPoint
innerRadius: (CGFloat) innerRadius
outerRadius: (CGFloat) outerRadius
target: (id) target;
- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
#end
.m for custom gesture file:
#include <math.h>
#import "OneFingerRotationGestureRecognizer.h"
#implementation OneFingerRotationGestureRecognizer
// private helper functions
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2);
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
CGPoint endLineA,
CGPoint beginLineB,
CGPoint endLineB);
- (id) initWithMidPoint: (CGPoint) _midPoint
innerRadius: (CGFloat) _innerRadius
outerRadius: (CGFloat) _outerRadius
target: (id <OneFingerRotationGestureRecognizerDelegate>) _target
{
if ((self = [super initWithTarget: _target action: nil]))
{
midPoint = _midPoint;
innerRadius = _innerRadius;
outerRadius = _outerRadius;
target = _target;
}
return self;
}
/** Calculates the distance between point1 and point 2. */
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2)
{
CGFloat dx = point1.x - point2.x;
CGFloat dy = point1.y - point2.y;
return sqrt(dx*dx + dy*dy);
}
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
CGPoint endLineA,
CGPoint beginLineB,
CGPoint endLineB)
{
CGFloat a = endLineA.x - beginLineA.x;
CGFloat b = endLineA.y - beginLineA.y;
CGFloat c = endLineB.x - beginLineB.x;
CGFloat d = endLineB.y - beginLineB.y;
CGFloat atanA = atan2(a, b);
CGFloat atanB = atan2(c, d);
// convert radiants to degrees
return (atanA - atanB) * 180 / M_PI;
}
#pragma mark - UIGestureRecognizer implementation
- (void)reset
{
[super reset];
cumulatedAngle = 0;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if ([touches count] != 1)
{
self.state = UIGestureRecognizerStateFailed;
return;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStateFailed) return;
CGPoint nowPoint = [[touches anyObject] locationInView: self.view];
CGPoint prevPoint = [[touches anyObject] previousLocationInView: self.view];
// make sure the new point is within the area
CGFloat distance = distanceBetweenPoints(midPoint, nowPoint);
if ( innerRadius <= distance
&& distance <= outerRadius)
{
// calculate rotation angle between two points
CGFloat angle = angleBetweenLinesInDegrees(midPoint, prevPoint, midPoint, nowPoint);
// fix value, if the 12 o'clock position is between prevPoint and nowPoint
if (angle > 180)
{
angle -= 360;
}
else if (angle < -180)
{
angle += 360;
}
// sum up single steps
cumulatedAngle += angle;
// call delegate
if ([target respondsToSelector: #selector(rotation:)])
{
[target rotation:angle];
}
}
else
{
// finger moved outside the area
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if (self.state == UIGestureRecognizerStatePossible)
{
self.state = UIGestureRecognizerStateRecognized;
if ([target respondsToSelector: #selector(finalAngle:)])
{
[target finalAngle:cumulatedAngle];
}
}
else
{
self.state = UIGestureRecognizerStateFailed;
}
cumulatedAngle = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateFailed;
cumulatedAngle = 0;
}
#end
Then its initialised like this:
// calculate center and radius of the control
CGPoint midPoint = CGPointMake(image.frame.origin.x + image.frame.size.width / 2,
image.frame.origin.y + image.frame.size.height / 2);
CGFloat outRadius = image.frame.size.width / 2;
// outRadius / 3 is arbitrary, just choose something >> 0 to avoid strange
// effects when touching the control near of it's center
gestureRecognizer = [[OneFingerRotationGestureRecognizer alloc] initWithMidPoint: midPoint
innerRadius: outRadius / 3
outerRadius: outRadius
target: self];
[self.view addGestureRecognizer: gestureRecognizer];
The selector below is also in the same file where the initialisation of the gestureRecogonizer:
- (void) rotation: (CGFloat) angle
{
// calculate rotation angle
imageAngle += angle;
if (imageAngle > 360)
imageAngle -= 360;
else if (imageAngle < -360)
imageAngle += 360;
// rotate image and update text field
image.transform = CGAffineTransformMakeRotation(imageAngle * M_PI / 180);
[self updateTextDisplay];
}
I can't seem to get this working in the RotateGesture class can anyone help me please I've been stuck on this for days now.
Here is projec on GitHub: SFGestureRecognizers
It uses builded in iOS UIGestureRecognizer, and don't needs to be integrated into cocos2d sources. Using it, You can make any gestures, just like you could, if you whould work with UIGestureRecognizer.
For example:
I made a base class Gesture, and subclassed it for any new gesture:
//Gesture.h
#interface Gesture : NSObject <UIGestureRecognizerDelegate>
{
UIGestureRecognizer *gestureRecognizer;
id delegate;
SEL preSolveSelector;
SEL possibleSelector;
SEL beganSelector;
SEL changedSelector;
SEL endedSelector;
SEL cancelledSelector;
SEL failedSelector;
BOOL preSolveAvailable;
CCNode *owner;
}
- (id)init;
- (void)addGestureRecognizerToNode:(CCNode*)node;
- (void)removeGestureRecognizerFromNode:(CCNode*)node;
-(void)recognizer:(UIGestureRecognizer*)recognizer;
#end
//Gesture.m
#import "Gesture.h"
#implementation Gesture
- (id)init
{
if (!(self = [super init]))
return self;
preSolveAvailable = YES;
return self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer shouldReceiveTouch:(UITouch *)touch
{
//! For swipe gesture recognizer we want it to be executed only if it occurs on the main layer, not any of the subnodes ( main layer is higher in hierarchy than children so it will be receiving touch by default )
if ([recognizer class] == [UISwipeGestureRecognizer class])
{
CGPoint pt = [touch locationInView:touch.view];
pt = [[CCDirector sharedDirector] convertToGL:pt];
for (CCNode *child in owner.children)
{
if ([child isNodeInTreeTouched:pt])
{
return NO;
}
}
}
return YES;
}
- (void)addGestureRecognizerToNode:(CCNode*)node
{
[node addGestureRecognizer:gestureRecognizer];
owner = node;
}
- (void)removeGestureRecognizerFromNode:(CCNode*)node
{
[node removeGestureRecognizer:gestureRecognizer];
}
#pragma mark - Private methods
-(void)recognizer:(UIGestureRecognizer*)recognizer
{
CCNode *node = recognizer.node;
if (preSolveSelector && preSolveAvailable)
{
preSolveAvailable = NO;
[delegate performSelector:preSolveSelector withObject:recognizer withObject:node];
}
UIGestureRecognizerState state = [recognizer state];
if (state == UIGestureRecognizerStatePossible && possibleSelector)
{
[delegate performSelector:possibleSelector withObject:recognizer withObject:node];
}
else if (state == UIGestureRecognizerStateBegan && beganSelector)
[delegate performSelector:beganSelector withObject:recognizer withObject:node];
else if (state == UIGestureRecognizerStateChanged && changedSelector)
[delegate performSelector:changedSelector withObject:recognizer withObject:node];
else if (state == UIGestureRecognizerStateEnded && endedSelector)
{
preSolveAvailable = YES;
[delegate performSelector:endedSelector withObject:recognizer withObject:node];
}
else if (state == UIGestureRecognizerStateCancelled && cancelledSelector)
{
preSolveAvailable = YES;
[delegate performSelector:cancelledSelector withObject:recognizer withObject:node];
}
else if (state == UIGestureRecognizerStateFailed && failedSelector)
{
preSolveAvailable = YES;
[delegate performSelector:failedSelector withObject:recognizer withObject:node];
}
}
#end
Subclass example:
//RotateGesture.h
#import "Gesture.h"
#interface RotateGesture : Gesture
- (id)initWithTarget:(id)target
preSolveSelector:(SEL)preSolve
possibleSelector:(SEL)possible
beganSelector:(SEL)began
changedSelector:(SEL)changed
endedSelector:(SEL)ended
cancelledSelector:(SEL)cancelled
failedSelector:(SEL)failed;
#end
//RotateGesture.m
#import "RotateGesture.h"
#implementation RotateGesture
- (id)initWithTarget:(id)target
preSolveSelector:(SEL)preSolve
possibleSelector:(SEL)possible
beganSelector:(SEL)began
changedSelector:(SEL)changed
endedSelector:(SEL)ended
cancelledSelector:(SEL)cancelled
failedSelector:(SEL)failed
{
if (!(self = [super init]))
return self;
preSolveSelector = preSolve;
delegate = target;
possibleSelector = possible;
beganSelector = began;
changedSelector = changed;
endedSelector = ended;
cancelledSelector = cancelled;
failedSelector = failed;
gestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(recognizer:)];
gestureRecognizer.delegate = self;
return self;
}
#end
Use example:
- (void)addRotateGesture
{
RotateGesture *rotateRecognizer = [[RotateGesture alloc] initWithTarget:self
preSolveSelector:#selector(rotateGesturePreSolveWithRecognizer:node:)
possibleSelector:nil
beganSelector:#selector(rotateGestureStateBeganWithRecognizer:node:)
changedSelector:#selector(rotateGestureStateChangedWithRecognizer:node:)
endedSelector:#selector(rotateGestureStateEndedWithRecognizer:node:)
cancelledSelector:#selector(rotateGestureStateCancelledWithRecognizer:node:)
failedSelector:#selector(rotateGestureStateFailedWithRecognizer:node:)];
[rotateRecognizer addGestureRecognizerToNode:movableAreaSprite];
}
... and you need only implement needed selectons.
EDIT
Here is mine angle calculations. In my case, I just use a button, and it's two last positions.
- (void)onRotatorDraggedWithArgs:(EventArgs *)e
{
CGPoint buttonPosition = [e.params CGPointValue];
float angle = atanf((buttonPosition.x - currentCastPosition.x) / (buttonPosition.y - currentCastPosition.y));
if (buttonPosition.y > currentCastPosition.y)
angle -= M_PI_2;
else
angle += M_PI_2;
currentCastAngle = CC_RADIANS_TO_DEGREES(angle);
[worldState.activeCastIcon rotateToAngle:currentCastAngle animated:NO];
currentPosition = buttonPosition;
}
I have 4 elements: greenball, yellowball, orangeball, and redball that fall from the top of the screen
I also have an element, blueball, that follows my touch.
All of these things are working great! :D
However, I want to detect when the greenball intersects with the blueball
my blue ball is placed in the view at viewDidLoad, the 4 balls are placed on the view based on an nstimer that calls onTimer.
All 5 balls are created on the same layer:
[self.view insertSubview:greenImage belowSubview:bottomBar]
I can detect when the redball and greenball collide:
if (CGRectIntersectsRect(greenImage.frame, redImage.frame)) {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"title" message:#"points" delegate:nil cancelButtonTitle:#"cool" otherButtonTitles:nil];
[message show];
[message release];
}
If I replace "redImage" for "blueball" I don't get the alert. I assume this may be because they aren't declared in the same method.
Any ideas on how to detect this collision?
thanks!
--- EDIT ---
Using the suggestion below I'm doing the following:
greenImage = [[UIImageView alloc] initWithImage:green];
greenImage.frame = CGRectMake(startX,0,43,43);
[self.view insertSubview:greenImage belowSubview:bottomBar];
[UIView beginAnimations:nil context:greenImage];
[UIView setAnimationDuration:5 * speed];
greenImage.frame = CGRectMake(startX, 500.0, 43,43);
CGFloat r1 = greenImage.frame.size.width * .5;
CGFloat r2 = blueBall.frame.size.width * .5;
if(CGPointDistance(greenImage.center, blueBall.center) < (r1 + r2)){
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"notice" message:#"hit!" delegate:nil cancelButtonTitle:#"cool" otherButtonTitles:nil];
[message show];
[message release];
}
below this method I have:
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
get an error:
conflicting types for 'CGPointDistance'
and a warning:
implicit declaration of function 'CGPointDistance'
To determine if two circles intersect, you simply see if the distance between the centers is less than the sum if their radii. For example...
CGFloat CGPointDistance(CGPoint p1, CGPoint p2)
{
return sqrtf((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
It looks like your "balls" are views... Assuming the "ball" fills the frame, the radius will be 1/2 the width...
BOOL BallsCollide(UIView *v1, UIView *v2)
{
CGFloat r1 = v1.frame.size.width * .5;
CGFloat r2 = v2.frame.size.width * .5;
return CGPointDistance(v1.center, v2.center) < (r1 + r2);
}
OK -- I have hacked up a quick sample. You can almost drop it into your view controller. Don't use it for production stuff... just as an example of how you can do stuff... You can put this at the top and the rest of your controller below it...
// I do not recommend using this approach for naything non-trivial,
// but it demonstrates the very simple collision detection for circle shapes.
#interface Ball : UIView {
}
#property (nonatomic, strong) UIColor *origColor;
#property (nonatomic, strong) UIColor *color;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color;
- (BOOL)doesIntersectWith:(Ball*)ball;
#end
#implementation Ball
#synthesize color = _color;
#synthesize origColor = _origColor;
+ (id)ballWithRadius:(CGFloat)radius color:(UIColor*)color
{
CGFloat diameter = radius * 2;
Ball *ball = [[Ball alloc] initWithFrame:CGRectMake(0, 0, diameter, diameter)];
ball.color = ball.origColor = color;
ball.backgroundColor = [UIColor clearColor];
return ball;
}
- (BOOL)doesIntersectWith:(Ball *)ball
{
// Two circles overlap if the sum of their radii
// is less than the distance between the two centers.
CGFloat r1 = self.frame.size.width * .5;
CGFloat r2 = ball.frame.size.width * .5;
CGFloat diffX = self.center.x - ball.center.x;
CGFloat diffY = self.center.y - ball.center.y;
return (diffX*diffX) + (diffY*diffY) < (r1+r2)*(r1+r2);
}
- (BOOL)containsPoint:(CGPoint)point
{
// Contains a point if distance from center is less than radius
CGFloat r = self.frame.size.width *.5;
CGFloat diffX = self.center.x - point.x;
CGFloat diffY = self.center.y - point.y;
return (diffX*diffX) + (diffY*diffY) < r*r;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[self.color setFill];
[self.color setStroke];
CGContextSetLineWidth(context, 1);
CGContextFillEllipseInRect(context, self.bounds);
}
#end
#interface CollideVC() {
NSMutableDictionary *activeTouches;
NSMutableSet *currentCollisions;
}
#end
#implementation CollideVC
- (void)ball:(Ball*)ball touched:(UITouch*)touch
{
[activeTouches setObject:ball forKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (Ball*)getBallTouchedBy:(UITouch*)touch
{
return [activeTouches objectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)stopTouch:(UITouch*)touch
{
[activeTouches removeObjectForKey:[NSValue valueWithPointer:(__bridge void*)touch]];
}
- (void)ball:(Ball*)ball1 hasCollidedWith:(Ball*)ball2
{
[currentCollisions addObject:ball1];
[currentCollisions addObject:ball2];
ball1.color = ball2.color = [UIColor yellowColor];
[ball1 setNeedsDisplay];
[ball2 setNeedsDisplay];
}
// NOTE: You should delegate handling of collisions...
- (void)checkForCollisions
{
// Should filter those that don't actually need redisplay...
// For simplicity, all that were or are colliding get it...
[currentCollisions enumerateObjectsUsingBlock:^(Ball *ball, BOOL *stop) {
[ball setNeedsDisplay];
ball.color = ball.origColor;
}];
[currentCollisions removeAllObjects];
NSArray *views = self.view.subviews;
for (size_t i = 0; i < views.count; ++i) {
id v = [views objectAtIndex:i];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball1 = v;
for (size_t j = i+1; j < views.count; ++j) {
v = [views objectAtIndex:j];
if (![v isKindOfClass:[Ball class]]) continue;
Ball *ball2 = v;
if ([ball1 doesIntersectWith:ball2]) {
[self ball:ball1 hasCollidedWith:ball2];
}
}
}
}
- (void)addBallWithRadius:(CGFloat)radius point:(CGPoint)point color:(UIColor*)color
{
Ball *ball = [Ball ballWithRadius:radius color:color];
ball.center = point;
[self.view addSubview:ball];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
for (Ball *ball in self.view.subviews) {
if ([ball containsPoint:touchLocation]) {
[self ball:ball touched:touch];
}
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Use relative distance so center does nt jump to tap location.
CGPoint prev = [touch previousLocationInView:self.view];
CGPoint curr = [touch locationInView:self.view];
CGPoint c = [self getBallTouchedBy:touch].center;
[self getBallTouchedBy:touch].center = CGPointMake(c.x+curr.x-prev.x, c.y+curr.y-prev.y);
}
// NOTE: This does not check the path between the moves, only the new point.
// So a fast movement "past" the other object will not get detected.
// If you want to do comple detection, use an existing engine
[self checkForCollisions];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
[self stopTouch:touch];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
activeTouches = [NSMutableDictionary dictionary];
currentCollisions = [NSMutableSet set];
[self addBallWithRadius:20 point:CGPointMake(110, 100) color:[UIColor blueColor]];
[self addBallWithRadius:20 point:CGPointMake(200, 200) color:[UIColor redColor]];
[self addBallWithRadius:20 point:CGPointMake(235, 250) color:[UIColor greenColor]];
}
maybe this will help some, just a little modification to the code above.
-(CGFloat)CGPointDistance:(CGPoint)p1 p2:(CGPoint)p2
{
return sqrtf(powf(p1.x - p2.x) + powf(p1.y-p2.y));
}
-(BOOL)DidCollide:(View*)v1 v2:(View*)v2 v1Radius:(CGFloat)v1Radius v2Radius:(CGFloat)v2Radius
{
CGFloat r1 = v1.frame.size.width * v1Radius;
CGFloat r2 = v2.frame.size.width * v2Radius;
return [self PointDistance:v1.center p2:v2.center] < (r1 + r2)
}
and to check for collision your just do
if([self DidCollide:view1 v2:view2 v1Radius:radius v2Radius:radius])
{
//do something
}