I'm making simple game for two players on Ipad, just like ping pong, when one finger is on screen player can react with his paddle, but when second finger is on his paddle, he cant move his paddle, but he move first paddle instead, I set up some NSLog and it says that both of them moves, but that isn't right,here are sample of my code:
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
//First player
CGPoint touchLocation = [touch locationInNode:self];
SKPhysicsBody* body = [self.physicsWorld bodyAtPoint:touchLocation];
if (body && [body.node.name isEqualToString: paddleCategoryName]) {
NSLog(#"Began touch on first paddle");
self.isFingerOnPaddle = YES;
}
//Second player
CGPoint secondTouchLocation = [touch locationInNode:self];
SKPhysicsBody* secondbody = [self.physicsWorld bodyAtPoint:secondTouchLocation];
if (secondbody && [secondbody.node.name isEqualToString: secondPaddleCategoryName]) {
NSLog(#"Began touch on second paddle");
self.isSecondFingerOnPaddle = YES;
}
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
//for first player
if (self.isFingerOnPaddle) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* paddle = (SKSpriteNode*)[self childNodeWithName: paddleCategoryName];
int paddleY = paddle.position.y + (touchLocation.y - previousLocation.y);
paddleY = MAX(paddleY, paddle.size.height/2);
paddleY = MIN(paddleY, self.size.height - paddle.size.height/2);
paddle.position = CGPointMake(paddle.position.x, paddleY);
NSLog(#"First paddle moving");
}
//for second player
if (self.isSecondFingerOnPaddle) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* secondPaddle = (SKSpriteNode*)[self childNodeWithName: secondPaddleCategoryName];
int secondPaddleY = secondPaddle.position.y + (touchLocation.y - previousLocation.y);
secondPaddleY = MAX(secondPaddleY, secondPaddle.size.height/2);
secondPaddleY = MIN(secondPaddleY, self.size.height - secondPaddle.size.height/2);
secondPaddle.position = CGPointMake(secondPaddle.position.x, secondPaddleY);
NSLog(#"Second paddle moving");
}
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
self.isFingerOnPaddle = NO;
self.isSecondFingerOnPaddle = NO;
}
What I do wrong, and what do I need to change to my code work, like it should
A better approach would be to sub class paddle and do your touch code inside the paddle subclass. This eliminates having to loop through touch arrays to see if your paddle is being touched, plus it makes the code a lot neater and easier to read. The only thing is you will have to style your node set up a little differently, because touch moved will not work outside of the paddle.
Here is how you style it
1) Subclass Paddle to SKNode
2) Create a child of SKSpriteNode for your actual paddle gfx
3) Paddle frame should be set at the width of your scene, with a height that is allowed for the player to touch (probably the height of the paddle gfx)
4) override the touch code inside Paddle, all touch code in the set should only relate to what is being touched by the paddle
5) Do all of your sliding logic inside these overrides
Now you have Paddle defined for the behavior you are looking for, you can add it to the scene where ever you like.
The nice part about this method is you eliminate a lot of duplicate code, (Since the paddles on both sides behave differently) and if you want to add some fun, you can add even more paddles to both sides to provide more variation. (Each player has a paddle in the middle of the play field, and the bottom, which can be moved independantly)
your logic is wrong, just add a touchArray property to store first touch and second touch, set it when touch begin then determine when touches moved.
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKSpriteNode *node = (SKSpriteNode *)[self nodeAtPoint:touchLocation];
if (node && [node.name isEqualToString: paddleCategoryName]) {
NSLog(#"Began touch on first paddle");
[self.touchArray replaceObjectAtIndex:0 withObject:touch];
}else if (node && [node.name isEqualToString: secondPaddleCategoryName]) {
NSLog(#"Began touch on second paddle");
[self.touchArray replaceObjectAtIndex:1 withObject:touch];
}
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
UITouch *firstTouch = self.touchArray[0];
UITouch *secondTouch = self.touchArray[1];
SKSpriteNode *paddle = [[SKSpriteNode alloc] init];
if (touch == firstTouch) {
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* paddle = (SKSpriteNode*)[self childNodeWithName: paddleCategoryName];
int paddleY = paddle.position.y + (touchLocation.y - previousLocation.y);
paddleY = MAX(paddleY, paddle.size.height/2);
paddleY = MIN(paddleY, self.size.height - paddle.size.height/2);
paddle.position = CGPointMake(paddle.position.x, paddleY);
NSLog(#"First paddle moving");
}else if (touch == secondTouch) {
CGPoint touchLocation = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
SKSpriteNode* secondPaddle = (SKSpriteNode*)[self childNodeWithName: secondPaddleCategoryName];
int secondPaddleY = secondPaddle.position.y + (touchLocation.y - previousLocation.y);
secondPaddleY = MAX(secondPaddleY, secondPaddle.size.height/2);
secondPaddleY = MIN(secondPaddleY, self.size.height - secondPaddle.size.height/2);
secondPaddle.position = CGPointMake(secondPaddle.position.x, secondPaddleY);
NSLog(#"Second paddle moving");
}
}
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
for (UITouch *touch in touches) {
if (touch == self.touchArray[0]) {
[self.touchArray replaceObjectAtIndex:0 withObject:#""];
}else if (touch == self.touchArray[1]) {
[self.touchArray replaceObjectAtIndex:1 withObject:#""];
}
}
self.isFingerOnPaddle = NO;
self.isSecondFingerOnPaddle = NO;
}
Related
I am creating game in which user can hit the object falling from the top of the screen with the racket. user can continuously move the racket but if it is at minimal speed or is at rest it should not hit the object, but if it above the minimal speed user should hit them. I have achieved that but the issue is when user start touching the racket which continously move with the user touch, the speed varition is their it does not start with the same speed and while touch is moving at that time also some times speed is very less even though the movement is fast. Here is my piece of code
-(void)didMoveToView:(SKView *)view {
self.physicsWorld.contactDelegate = (id)self;
racketNode = [SKSpriteNode spriteNodeWithImageNamed:#"racket"];
racketNode.size = CGSizeMake(50,50);
racketNode.position = CGPointMake(self.frame.origin.x + self.frame.size.width - 50,50);
racketNode.name = #"racket";
[self addChild:racketNode];
}
-(void) didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *nodeA = (SKSpriteNode *)contact.bodyA.node ;
SKSpriteNode *nodeB = (SKSpriteNode *) contact.bodyB.node;
if (([nodeA.name isEqualToString:#"racket"] && [nodeB.name isEqualToString:#"fallingObject"])) {
if (racketNode.speed > kMinSpeed)
[nodeB removeFromParent];
else {
nodeB.physicsBody.contactTestBitMask = 0;
[self performSelector:#selector(providingCollsion:) withObject:nodeB afterDelay:0.1];
}
}
}
-(void) providingCollsion:(SKSpriteNode *) node {
node.physicsBody.contactTestBitMask = racketHit;
}
-(void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
start = location;
startTime = touch.timestamp;
racketNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:racketNode.frame.size];
racketNode.physicsBody.categoryBitMask = racket;
racketNode.physicsBody.contactTestBitMask = HitIt;
racketNode.physicsBody.dynamic = NO;
racketNode.physicsBody.affectedByGravity = NO;
[racketNode runAction:[SKAction moveTo:location duration:0.01]];
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
racketNode.physicsBody = nil;
racketNode.speed = 0;
}
-(void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGFloat dx = location.x - start.x;
CGFloat dy = location.y - start.y;
CGFloat magnitude = sqrt(dx*dx+dy*dy);
// Determine time difference from start of the gesture
CGFloat dt = touch.timestamp - startTime;
// Determine gesture speed in points/sec
CGFloat speed = magnitude/dt;
racketNode.speed = speed;
[handNode runAction:[SKAction moveTo:[touch locationInNode:self] duration:0.01]];
}
Please tell me which part my code is wrong so as to make same object collide with high speed only not on slow speed and also no collision on stable state.
Instead of doing it manually, use UIPanGestureRecognizer to handle your swipes. With it, there is a velocity property that you can use to check if the speed is greater than a given value.
Here is a great tutorial to do it:
https://www.raywenderlich.com/76020/using-uigesturerecognizer-with-swift-tutorial
I have an SKSpriteNode that is initialized with an image. I am using the -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method to figure out if the user touch is within the bounds of the sprite. For some reason, even when I tap inside the sprite, the coordinates are not even similar. They seem to be on a different coordinate system.
#import "GameScene.h"
SKSpriteNode *card;
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
self.backgroundColor = [SKColor whiteColor];
card = [SKSpriteNode spriteNodeWithImageNamed:#"card"];
card.position = CGPointMake(500, 500);
[self addChild:card];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
int cardX = card.frame.origin.x;
int cardwidth = card.frame.size.width;
int cardY = card.frame.origin.y;
int cardHeight = card.frame.size.height;
if(touchLocation.x >= card.frame.origin.x &&
touchLocation.x <= (card.frame.origin.x + card.frame.size.width) &&
touchLocation.y <= card.frame.origin.y &&
touchLocation.y >= (card.frame.origin.y + card.frame.size.height))
{
self.backgroundColor = [SKColor blackColor];
}
else{
self.backgroundColor = [SKColor whiteColor];
}
}
#end
Node and View have different coordinate system.
If you need to know the location of the tapped point in the node coordinate you may want to replace the line :
CGPoint touchLocation = [touch locationInView:self.view];
by :
CGPoint touchLocation = [touch locationInNode:self];
For Swift 4 and above:
if playButton.frame.contains(touch.location(in: scene!)) {
print("Play button touched")
}
I have two nodes and a boolean. Simple enough. When node A contacts Node B and the boolean is 0, nothing happens. However if the boolean is 1, Node A is removed through the didBeganContact method.
Extremely simple, however I have an annoying problem on when I want Node A removed.
Node B is a rectangle and node A is a square going in the middle of the rectangle, the boolean is called and turned into 1 when I tap and hold the Node B using the touchesBegan method. Now before Node A contacts Node B, I tap and hold Node B and when Node A contacts, its removed, but when Node A is already in the middle, and I tap Node B, nothing happens and I don't know why.
Rectangle Method
-(void)rectangle
{
SKSpriteNode *rectangle = [[SKSpriteNode alloc] init];
rectangle = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(75, 150)];
rectangle.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
rectangle.name = #"rect";
rectangle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rectangle.size];
rectangle.physicsBody.categoryBitMask = rectangleCategory;
rectangle.physicsBody.contactTestBitMask = fallingSquareCategory;
rectangle.physicsBody.collisionBitMask = 0;
[self addChild:rectangle];
}
touchesBeganMethod
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 1;
}
}
touchesEnded
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 0;
}
}
square Method
-(void)square
{
SKAction *move = [SKAction moveToY:CGRectGetMidY(self.frame) duration:1.75];
SKSpriteNode *fallingSquare = [[SKSpriteNode alloc] init];
fallingSquare = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(75, 75)];
fallingSquare.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame));
fallingSquare.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:fallingSquare.size];
fallingSquare.physicsBody.categoryBitMask = fallingSquareCategory;
fallingSquare.physicsBody.contactTestBitMask = rectangleCategory
fallingSquare.physicsBody.collisionBitMask = 0;
[self addChild:fallingSquare];
[fallingSquare runAction:move];
}
didBeginContact
static inline SKSpriteNode *nodeFromBody(SKPhysicsBody *body1, SKPhysicsBody *body2, uint32_t category) {
SKSpriteNode *node = nil;
if (body1.categoryBitMask & category) {
node = (SKSpriteNode *)body1.node;
}
else if (body2.categoryBitMask & category) {
node = (SKSpriteNode *)body2.node;
}
return node;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
SKSpriteNode *R1 = nil;
SKSpriteNode *fallingS = nil;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
R1 = nodeFromBody(firstBody, secondBody, rectangleCategory);
fallingS = nodeFromBody(firstBody, secondBody, fallingSquareCategory);
if (R1 && fallingS && radBool == 1)
{
[fallingS removeFromParent];
}
}
I believe your issue is the "begin" part of didBeginContact. It only gets called the first time they contact and not every loop. Because the bool was not set to YES when they first contacted it will never be evaluated again.
I believe I ran into this issue once before and the solution was to create a new physical body when you touch it. This "should" trigger didBeginContact the next go around. You might also be able to change a property on the physical body, but if I recall correctly I didn't get that to work and had to init a new physical body.
For example try updating your touchesBegan with this
touchesBeganMethod
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"rect"])
{
radBool = 1;
node.physicsBody = nil;
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rectangle.size];
node.physicsBody.categoryBitMask = rectangleCategory;
node.physicsBody.contactTestBitMask = fallingSquareCategory;
node.physicsBody.collisionBitMask = 0;
}
}
Hope that works for you.
I have an object that I wish to move 50 pixels in the direction the the touch event occurs. It is a fixed CCSprite called _eyeObject.
I know I need to use
- (void)touchBegan:(UITouch *)touches withEvent:(UIEvent *)event {
CGPoint touchLocation = [touches locationInNode:self];
//Not sure what to do now
}
How would I calculate the CCSprite to move 50 pixels towards the users touch? Then move back to the original position when the touch ends?
A starting point would be great....I don't need to know how to animate or anything, just the calculation.
Whenever a touch happens you want to move 50 pixels in that direction you can do (based on your desire to do it based on a statically positioned eye object sprite):
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchPos = [touch locationInNode:self];
CGPoint delta = ccpSub(touchPos, self.eyeObject.position);
float moveAmount = 50.0f;
CGPoint moveVec;
if (delta.x > 0.0f)
{
moveVec.x = moveAmount;
}
else
{
moveVec.x = -moveAmount;
}
if (delta.y > 0.0f)
{
moveVec.y = moveAmount;
}
else
{
moveVec.y = -moveAmount;
}
self.spriteStartPosition = self.sprite.position;
self.sprite.position = ccpAdd(self.sprite.position, moveVec);
}
- (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
self.sprite.position = self.spriteStartPosition;
}
If the object you are moving IS that eyeObject then it would be:
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchPos = [touch locationInNode:self];
CGPoint delta = ccpSub(touchPos, self.eyeObject.position);
float moveAmount = 50.0f;
CGPoint moveVec;
if (delta.x > 0.0f)
{
moveVec.x = moveAmount;
}
else
{
moveVec.x = -moveAmount;
}
if (delta.y > 0.0f)
{
moveVec.y = moveAmount;
}
else
{
moveVec.y = -moveAmount;
}
self.eyeObjStartPosition = self.eyeObject.position;
self.eyeObject.position = ccpAdd(self.eyeObject.position, moveVec);
}
- (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
self.eyeObject.position = self.eyeObjStartPosition;
}
To move in general if the static object was representing a joystick or gamepad (would do the same in touch began so I'd separate this into its own method):
- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchPos = [touch locationInNode:self];
CGPoint delta = ccpSub(touchPos, self.dPad.position);
float moveAmount = 50.0f;
if (delta.x > 0.0f)
{
self.sprite.position.x += moveAmount;
}
else
{
self.sprite.position.x -= moveAmount;
}
if (delta.y > 0.0f)
{
self.sprite.position.y += moveAmount;
}
else
{
self.sprite.position.y -= moveAmount;
}
}
Here's a simple solution - get the unit vector between the two points, multiply it by 50, and add it to the current position. AKA this:
- (void)touchBegan:(UITouch *)touches withEvent:(UIEvent *)event {
CGPoint touchLocation = [touches locationInNode:self];
//Not sure what to do now
CGPoint destinationVector = ccpSub(touchPos, self.eyeObject.position);
CGPoint movementVector = ccpNormalize(destinationVector);
movementVector = ccpMult(movementVector, 50);
[_eyeObject runAction:[CCActionMoveTo actionWithDuration:1.0 position:ccpAdd(_eyeObject, movementVector)]];
}
If you want to have it move back on touchEnded, just save the position in a variable before you move it the first time, and send it back to that spot on touchEnded.
I am trying to drag a sprite in the y axis but make the sprite "stick" to the users finger depending on where the touches began on the node.
The sprite is currently dragging but it seems to be snapping the anchorpoint of the sprite to the touch location within the node.
Im assuming it has something to do with getting the location within the node by doing [touch locationInNode:selectedNode]; but I am unsure where to go from there.
Here is my current code.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
CGPoint newPosition = CGPointMake(node.position.x, location.y);
if ([node.name isEqualToString:self.selectedNode] ) {
if (newPosition.y > 230) {
newPosition.y = 230;
}
node.position = newPosition;
}
}
}
You need to offset the newPosition based on the current touch position on the node.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:self.selectedNode] )
{
CGPoint previousLocation = [touch previousLocationInNode:self];
float diff = location.y - previousLocation.y;
CGPoint newPosition = CGPointMake(node.position.x, node.position.y + diff);
if (newPosition.y > 230) {
newPosition.y = 230;
}
node.position = newPosition;
}
}
}
There are a couple of ways of doing this. The sample code below tracks the user's touch location and moves the sprite towards that position during the update method. You can modify the code to only move on the y axis or x axis.
#implementation MyScene
{
SKSpriteNode *object1;
CGPoint destinationLocation;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
[self createObject];
destinationLocation = CGPointMake(300, 150);
}
return self;
}
-(void)createObject
{
object1 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
object1.position = CGPointMake(300, 150);
[self addChild:object1];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
destinationLocation = [touch locationInNode:self.scene];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
destinationLocation = [touch locationInNode:self.scene];
}
-(void)update:(CFTimeInterval)currentTime
{
float x = fabs(object1.position.x - destinationLocation.x);
float y = fabs(object1.position.y - destinationLocation.y);
float divider = 0;
if(x > y)
{
divider = x;
} else
{
divider = y;
}
float xMove = (x/divider)*8; // change number to increase or decrease speed
float yMove = (y/divider)*8; // change number to increase or decrease speed
if(object1.position.x > destinationLocation.x)
object1.position = CGPointMake(object1.position.x-xMove, object1.position.y);
if(object1.position.x < destinationLocation.x)
object1.position = CGPointMake(object1.position.x+xMove, object1.position.y);
if(object1.position.y > destinationLocation.y)
object1.position = CGPointMake(object1.position.x, object1.position.y-yMove);
if(object1.position.y < destinationLocation.y)
object1.position = CGPointMake(object1.position.x, object1.position.y+yMove);
}
#end